ipsec: add SAD create/destroy implementation
[dpdk.git] / lib / librte_ipsec / ipsec_sad.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019 Intel Corporation
3  */
4
5 #include <rte_eal_memconfig.h>
6 #include <rte_errno.h>
7 #include <rte_hash.h>
8 #include <rte_jhash.h>
9 #include <rte_malloc.h>
10 #include <rte_random.h>
11 #include <rte_rwlock.h>
12 #include <rte_tailq.h>
13
14 #include "rte_ipsec_sad.h"
15
16 /*
17  * Rules are stored in three hash tables depending on key_type.
18  * Each rule will also be stored in SPI_ONLY table.
19  * for each data entry within this table last two bits are reserved to
20  * indicate presence of entries with the same SPI in DIP and DIP+SIP tables.
21  */
22
23 #define IPSEC_SAD_NAMESIZE      64
24 #define SAD_PREFIX              "SAD_"
25 /* "SAD_<name>" */
26 #define SAD_FORMAT              SAD_PREFIX "%s"
27
28 #define DEFAULT_HASH_FUNC       rte_jhash
29 #define MIN_HASH_ENTRIES        8U /* From rte_cuckoo_hash.h */
30
31 struct hash_cnt {
32         uint32_t cnt_dip;
33         uint32_t cnt_dip_sip;
34 };
35
36 struct rte_ipsec_sad {
37         char name[IPSEC_SAD_NAMESIZE];
38         struct rte_hash *hash[RTE_IPSEC_SAD_KEY_TYPE_MASK];
39         /* Array to track number of more specific rules
40          * (spi_dip or spi_dip_sip). Used only in add/delete
41          * as a helper struct.
42          */
43         __extension__ struct hash_cnt cnt_arr[];
44 };
45
46 TAILQ_HEAD(rte_ipsec_sad_list, rte_tailq_entry);
47 static struct rte_tailq_elem rte_ipsec_sad_tailq = {
48         .name = "RTE_IPSEC_SAD",
49 };
50 EAL_REGISTER_TAILQ(rte_ipsec_sad_tailq)
51
52 int
53 rte_ipsec_sad_add(__rte_unused struct rte_ipsec_sad *sad,
54                 __rte_unused const union rte_ipsec_sad_key *key,
55                 __rte_unused int key_type, __rte_unused void *sa)
56 {
57         return -ENOTSUP;
58 }
59
60 int
61 rte_ipsec_sad_del(__rte_unused struct rte_ipsec_sad *sad,
62                 __rte_unused const union rte_ipsec_sad_key *key,
63                 __rte_unused int key_type)
64 {
65         return -ENOTSUP;
66 }
67
68 struct rte_ipsec_sad *
69 rte_ipsec_sad_create(const char *name, const struct rte_ipsec_sad_conf *conf)
70 {
71         char hash_name[RTE_HASH_NAMESIZE];
72         char sad_name[IPSEC_SAD_NAMESIZE];
73         struct rte_tailq_entry *te;
74         struct rte_ipsec_sad_list *sad_list;
75         struct rte_ipsec_sad *sad, *tmp_sad = NULL;
76         struct rte_hash_parameters hash_params = {0};
77         int ret;
78         uint32_t sa_sum;
79
80         RTE_BUILD_BUG_ON(RTE_IPSEC_SAD_KEY_TYPE_MASK != 3);
81
82         if ((name == NULL) || (conf == NULL) ||
83                         ((conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY] == 0) &&
84                         (conf->max_sa[RTE_IPSEC_SAD_SPI_DIP] == 0) &&
85                         (conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] == 0))) {
86                 rte_errno = EINVAL;
87                 return NULL;
88         }
89
90         ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
91         if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
92                 rte_errno = ENAMETOOLONG;
93                 return NULL;
94         }
95
96         /** Init SAD*/
97         sa_sum = RTE_MAX(MIN_HASH_ENTRIES,
98                 conf->max_sa[RTE_IPSEC_SAD_SPI_ONLY]) +
99                 RTE_MAX(MIN_HASH_ENTRIES,
100                 conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]) +
101                 RTE_MAX(MIN_HASH_ENTRIES,
102                 conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
103         sad = rte_zmalloc_socket(NULL, sizeof(*sad) +
104                 (sizeof(struct hash_cnt) * sa_sum),
105                 RTE_CACHE_LINE_SIZE, conf->socket_id);
106         if (sad == NULL) {
107                 rte_errno = ENOMEM;
108                 return NULL;
109         }
110         memcpy(sad->name, sad_name, sizeof(sad_name));
111
112         hash_params.hash_func = DEFAULT_HASH_FUNC;
113         hash_params.hash_func_init_val = rte_rand();
114         hash_params.socket_id = conf->socket_id;
115         hash_params.name = hash_name;
116         if (conf->flags & RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY)
117                 hash_params.extra_flag = RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY;
118
119         /** Init hash[RTE_IPSEC_SAD_SPI_ONLY] for SPI only */
120         snprintf(hash_name, sizeof(hash_name), "sad_1_%p", sad);
121         hash_params.key_len = sizeof(((struct rte_ipsec_sadv4_key *)0)->spi);
122         hash_params.entries = sa_sum;
123         sad->hash[RTE_IPSEC_SAD_SPI_ONLY] = rte_hash_create(&hash_params);
124         if (sad->hash[RTE_IPSEC_SAD_SPI_ONLY] == NULL) {
125                 rte_ipsec_sad_destroy(sad);
126                 return NULL;
127         }
128
129         /** Init hash[RTE_IPSEC_SAD_SPI_DIP] for SPI + DIP */
130         snprintf(hash_name, sizeof(hash_name), "sad_2_%p", sad);
131         if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
132                 hash_params.key_len +=
133                         sizeof(((struct rte_ipsec_sadv6_key *)0)->dip);
134         else
135                 hash_params.key_len +=
136                         sizeof(((struct rte_ipsec_sadv4_key *)0)->dip);
137         hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
138                         conf->max_sa[RTE_IPSEC_SAD_SPI_DIP]);
139         sad->hash[RTE_IPSEC_SAD_SPI_DIP] = rte_hash_create(&hash_params);
140         if (sad->hash[RTE_IPSEC_SAD_SPI_DIP] == NULL) {
141                 rte_ipsec_sad_destroy(sad);
142                 return NULL;
143         }
144
145         /** Init hash[[RTE_IPSEC_SAD_SPI_DIP_SIP] for SPI + DIP + SIP */
146         snprintf(hash_name, sizeof(hash_name), "sad_3_%p", sad);
147         if (conf->flags & RTE_IPSEC_SAD_FLAG_IPV6)
148                 hash_params.key_len +=
149                         sizeof(((struct rte_ipsec_sadv6_key *)0)->sip);
150         else
151                 hash_params.key_len +=
152                         sizeof(((struct rte_ipsec_sadv4_key *)0)->sip);
153         hash_params.entries = RTE_MAX(MIN_HASH_ENTRIES,
154                         conf->max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP]);
155         sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] = rte_hash_create(&hash_params);
156         if (sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP] == NULL) {
157                 rte_ipsec_sad_destroy(sad);
158                 return NULL;
159         }
160
161         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
162                         rte_ipsec_sad_list);
163         rte_mcfg_tailq_write_lock();
164         /* guarantee there's no existing */
165         TAILQ_FOREACH(te, sad_list, next) {
166                 tmp_sad = (struct rte_ipsec_sad *)te->data;
167                 if (strncmp(sad_name, tmp_sad->name, IPSEC_SAD_NAMESIZE) == 0)
168                         break;
169         }
170         if (te != NULL) {
171                 rte_mcfg_tailq_write_unlock();
172                 rte_errno = EEXIST;
173                 rte_ipsec_sad_destroy(sad);
174                 return NULL;
175         }
176
177         /* allocate tailq entry */
178         te = rte_zmalloc("IPSEC_SAD_TAILQ_ENTRY", sizeof(*te), 0);
179         if (te == NULL) {
180                 rte_mcfg_tailq_write_unlock();
181                 rte_errno = ENOMEM;
182                 rte_ipsec_sad_destroy(sad);
183                 return NULL;
184         }
185
186         te->data = (void *)sad;
187         TAILQ_INSERT_TAIL(sad_list, te, next);
188         rte_mcfg_tailq_write_unlock();
189         return sad;
190 }
191
192 struct rte_ipsec_sad *
193 rte_ipsec_sad_find_existing(const char *name)
194 {
195         char sad_name[IPSEC_SAD_NAMESIZE];
196         struct rte_ipsec_sad *sad = NULL;
197         struct rte_tailq_entry *te;
198         struct rte_ipsec_sad_list *sad_list;
199         int ret;
200
201         ret = snprintf(sad_name, IPSEC_SAD_NAMESIZE, SAD_FORMAT, name);
202         if (ret < 0 || ret >= IPSEC_SAD_NAMESIZE) {
203                 rte_errno = ENAMETOOLONG;
204                 return NULL;
205         }
206
207         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
208                 rte_ipsec_sad_list);
209
210         rte_mcfg_tailq_read_lock();
211         TAILQ_FOREACH(te, sad_list, next) {
212                 sad = (struct rte_ipsec_sad *) te->data;
213                 if (strncmp(sad_name, sad->name, IPSEC_SAD_NAMESIZE) == 0)
214                         break;
215         }
216         rte_mcfg_tailq_read_unlock();
217
218         if (te == NULL) {
219                 rte_errno = ENOENT;
220                 return NULL;
221         }
222
223         return sad;
224 }
225
226 void
227 rte_ipsec_sad_destroy(struct rte_ipsec_sad *sad)
228 {
229         struct rte_tailq_entry *te;
230         struct rte_ipsec_sad_list *sad_list;
231
232         if (sad == NULL)
233                 return;
234
235         sad_list = RTE_TAILQ_CAST(rte_ipsec_sad_tailq.head,
236                         rte_ipsec_sad_list);
237         rte_mcfg_tailq_write_lock();
238         TAILQ_FOREACH(te, sad_list, next) {
239                 if (te->data == (void *)sad)
240                         break;
241         }
242         if (te != NULL)
243                 TAILQ_REMOVE(sad_list, te, next);
244
245         rte_mcfg_tailq_write_unlock();
246
247         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_ONLY]);
248         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP]);
249         rte_hash_free(sad->hash[RTE_IPSEC_SAD_SPI_DIP_SIP]);
250         rte_free(sad);
251         if (te != NULL)
252                 rte_free(te);
253 }
254
255 int
256 rte_ipsec_sad_lookup(__rte_unused const struct rte_ipsec_sad *sad,
257                 __rte_unused const union rte_ipsec_sad_key *keys[],
258                 __rte_unused void *sa[], __rte_unused uint32_t n)
259 {
260         return -ENOTSUP;
261 }