1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018 Vladimir Medvedkin <[email protected]>
3 * Copyright(c) 2019 Intel Corporation
4 */
5
6 #include <stdint.h>
7 #include <string.h>
8
9 #include <rte_eal_memconfig.h>
10 #include <rte_tailq.h>
11 #include <rte_errno.h>
12 #include <rte_malloc.h>
13 #include <rte_string_fns.h>
14
15 #include <rte_rib6.h>
16 #include <rte_fib6.h>
17
18 #include "trie.h"
19
20 TAILQ_HEAD(rte_fib6_list, rte_tailq_entry);
21 static struct rte_tailq_elem rte_fib6_tailq = {
22 .name = "RTE_FIB6",
23 };
24 EAL_REGISTER_TAILQ(rte_fib6_tailq)
25
26 /* Maximum length of a FIB name. */
27 #define FIB6_NAMESIZE 64
28
29 #if defined(RTE_LIBRTE_FIB_DEBUG)
30 #define FIB6_RETURN_IF_TRUE(cond, retval) do { \
31 if (cond) \
32 return retval; \
33 } while (0)
34 #else
35 #define FIB6_RETURN_IF_TRUE(cond, retval)
36 #endif
37
38 struct rte_fib6 {
39 char name[FIB6_NAMESIZE];
40 enum rte_fib6_type type; /**< Type of FIB struct */
41 struct rte_rib6 *rib; /**< RIB helper datastructure */
42 void *dp; /**< pointer to the dataplane struct*/
43 rte_fib6_lookup_fn_t lookup; /**< FIB lookup function */
44 rte_fib6_modify_fn_t modify; /**< modify FIB datastructure */
45 uint64_t def_nh;
46 };
47
48 static void
dummy_lookup(void * fib_p,uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],uint64_t * next_hops,const unsigned int n)49 dummy_lookup(void *fib_p, uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
50 uint64_t *next_hops, const unsigned int n)
51 {
52 unsigned int i;
53 struct rte_fib6 *fib = fib_p;
54 struct rte_rib6_node *node;
55
56 for (i = 0; i < n; i++) {
57 node = rte_rib6_lookup(fib->rib, ips[i]);
58 if (node != NULL)
59 rte_rib6_get_nh(node, &next_hops[i]);
60 else
61 next_hops[i] = fib->def_nh;
62 }
63 }
64
65 static int
dummy_modify(struct rte_fib6 * fib,const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],uint8_t depth,uint64_t next_hop,int op)66 dummy_modify(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
67 uint8_t depth, uint64_t next_hop, int op)
68 {
69 struct rte_rib6_node *node;
70 if ((fib == NULL) || (depth > RTE_FIB6_MAXDEPTH))
71 return -EINVAL;
72
73 node = rte_rib6_lookup_exact(fib->rib, ip, depth);
74
75 switch (op) {
76 case RTE_FIB6_ADD:
77 if (node == NULL)
78 node = rte_rib6_insert(fib->rib, ip, depth);
79 if (node == NULL)
80 return -rte_errno;
81 return rte_rib6_set_nh(node, next_hop);
82 case RTE_FIB6_DEL:
83 if (node == NULL)
84 return -ENOENT;
85 rte_rib6_remove(fib->rib, ip, depth);
86 return 0;
87 }
88 return -EINVAL;
89 }
90
91 static int
init_dataplane(struct rte_fib6 * fib,__rte_unused int socket_id,struct rte_fib6_conf * conf)92 init_dataplane(struct rte_fib6 *fib, __rte_unused int socket_id,
93 struct rte_fib6_conf *conf)
94 {
95 char dp_name[sizeof(void *)];
96
97 snprintf(dp_name, sizeof(dp_name), "%p", fib);
98 switch (conf->type) {
99 case RTE_FIB6_DUMMY:
100 fib->dp = fib;
101 fib->lookup = dummy_lookup;
102 fib->modify = dummy_modify;
103 return 0;
104 case RTE_FIB6_TRIE:
105 fib->dp = trie_create(dp_name, socket_id, conf);
106 if (fib->dp == NULL)
107 return -rte_errno;
108 fib->lookup = trie_get_lookup_fn(fib->dp, RTE_FIB6_LOOKUP_DEFAULT);
109 fib->modify = trie_modify;
110 return 0;
111 default:
112 return -EINVAL;
113 }
114 return 0;
115 }
116
117 int
rte_fib6_add(struct rte_fib6 * fib,const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],uint8_t depth,uint64_t next_hop)118 rte_fib6_add(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
119 uint8_t depth, uint64_t next_hop)
120 {
121 if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
122 (depth > RTE_FIB6_MAXDEPTH))
123 return -EINVAL;
124 return fib->modify(fib, ip, depth, next_hop, RTE_FIB6_ADD);
125 }
126
127 int
rte_fib6_delete(struct rte_fib6 * fib,const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],uint8_t depth)128 rte_fib6_delete(struct rte_fib6 *fib, const uint8_t ip[RTE_FIB6_IPV6_ADDR_SIZE],
129 uint8_t depth)
130 {
131 if ((fib == NULL) || (ip == NULL) || (fib->modify == NULL) ||
132 (depth > RTE_FIB6_MAXDEPTH))
133 return -EINVAL;
134 return fib->modify(fib, ip, depth, 0, RTE_FIB6_DEL);
135 }
136
137 int
rte_fib6_lookup_bulk(struct rte_fib6 * fib,uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],uint64_t * next_hops,int n)138 rte_fib6_lookup_bulk(struct rte_fib6 *fib,
139 uint8_t ips[][RTE_FIB6_IPV6_ADDR_SIZE],
140 uint64_t *next_hops, int n)
141 {
142 FIB6_RETURN_IF_TRUE((fib == NULL) || (ips == NULL) ||
143 (next_hops == NULL) || (fib->lookup == NULL), -EINVAL);
144 fib->lookup(fib->dp, ips, next_hops, n);
145 return 0;
146 }
147
148 struct rte_fib6 *
rte_fib6_create(const char * name,int socket_id,struct rte_fib6_conf * conf)149 rte_fib6_create(const char *name, int socket_id, struct rte_fib6_conf *conf)
150 {
151 char mem_name[FIB6_NAMESIZE];
152 int ret;
153 struct rte_fib6 *fib = NULL;
154 struct rte_rib6 *rib = NULL;
155 struct rte_tailq_entry *te;
156 struct rte_fib6_list *fib_list;
157 struct rte_rib6_conf rib_conf;
158
159 /* Check user arguments. */
160 if ((name == NULL) || (conf == NULL) || (conf->max_routes < 0) ||
161 (conf->type > RTE_FIB6_TRIE)) {
162 rte_errno = EINVAL;
163 return NULL;
164 }
165
166 rib_conf.ext_sz = conf->rib_ext_sz;
167 rib_conf.max_nodes = conf->max_routes * 2;
168
169 rib = rte_rib6_create(name, socket_id, &rib_conf);
170 if (rib == NULL) {
171 RTE_LOG(ERR, LPM,
172 "Can not allocate RIB %s\n", name);
173 return NULL;
174 }
175
176 snprintf(mem_name, sizeof(mem_name), "FIB6_%s", name);
177 fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
178
179 rte_mcfg_tailq_write_lock();
180
181 /* guarantee there's no existing */
182 TAILQ_FOREACH(te, fib_list, next) {
183 fib = (struct rte_fib6 *)te->data;
184 if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
185 break;
186 }
187 fib = NULL;
188 if (te != NULL) {
189 rte_errno = EEXIST;
190 goto exit;
191 }
192
193 /* allocate tailq entry */
194 te = rte_zmalloc("FIB_TAILQ_ENTRY", sizeof(*te), 0);
195 if (te == NULL) {
196 RTE_LOG(ERR, LPM,
197 "Can not allocate tailq entry for FIB %s\n", name);
198 rte_errno = ENOMEM;
199 goto exit;
200 }
201
202 /* Allocate memory to store the FIB data structures. */
203 fib = rte_zmalloc_socket(mem_name,
204 sizeof(struct rte_fib6), RTE_CACHE_LINE_SIZE, socket_id);
205 if (fib == NULL) {
206 RTE_LOG(ERR, LPM, "FIB %s memory allocation failed\n", name);
207 rte_errno = ENOMEM;
208 goto free_te;
209 }
210
211 rte_strlcpy(fib->name, name, sizeof(fib->name));
212 fib->rib = rib;
213 fib->type = conf->type;
214 fib->def_nh = conf->default_nh;
215 ret = init_dataplane(fib, socket_id, conf);
216 if (ret < 0) {
217 RTE_LOG(ERR, LPM,
218 "FIB dataplane struct %s memory allocation failed\n",
219 name);
220 rte_errno = -ret;
221 goto free_fib;
222 }
223
224 te->data = (void *)fib;
225 TAILQ_INSERT_TAIL(fib_list, te, next);
226
227 rte_mcfg_tailq_write_unlock();
228
229 return fib;
230
231 free_fib:
232 rte_free(fib);
233 free_te:
234 rte_free(te);
235 exit:
236 rte_mcfg_tailq_write_unlock();
237 rte_rib6_free(rib);
238
239 return NULL;
240 }
241
242 struct rte_fib6 *
rte_fib6_find_existing(const char * name)243 rte_fib6_find_existing(const char *name)
244 {
245 struct rte_fib6 *fib = NULL;
246 struct rte_tailq_entry *te;
247 struct rte_fib6_list *fib_list;
248
249 fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
250
251 rte_mcfg_tailq_read_lock();
252 TAILQ_FOREACH(te, fib_list, next) {
253 fib = (struct rte_fib6 *) te->data;
254 if (strncmp(name, fib->name, FIB6_NAMESIZE) == 0)
255 break;
256 }
257 rte_mcfg_tailq_read_unlock();
258
259 if (te == NULL) {
260 rte_errno = ENOENT;
261 return NULL;
262 }
263
264 return fib;
265 }
266
267 static void
free_dataplane(struct rte_fib6 * fib)268 free_dataplane(struct rte_fib6 *fib)
269 {
270 switch (fib->type) {
271 case RTE_FIB6_DUMMY:
272 return;
273 case RTE_FIB6_TRIE:
274 trie_free(fib->dp);
275 default:
276 return;
277 }
278 }
279
280 void
rte_fib6_free(struct rte_fib6 * fib)281 rte_fib6_free(struct rte_fib6 *fib)
282 {
283 struct rte_tailq_entry *te;
284 struct rte_fib6_list *fib_list;
285
286 if (fib == NULL)
287 return;
288
289 fib_list = RTE_TAILQ_CAST(rte_fib6_tailq.head, rte_fib6_list);
290
291 rte_mcfg_tailq_write_lock();
292
293 /* find our tailq entry */
294 TAILQ_FOREACH(te, fib_list, next) {
295 if (te->data == (void *)fib)
296 break;
297 }
298 if (te != NULL)
299 TAILQ_REMOVE(fib_list, te, next);
300
301 rte_mcfg_tailq_write_unlock();
302
303 free_dataplane(fib);
304 rte_rib6_free(fib->rib);
305 rte_free(fib);
306 rte_free(te);
307 }
308
309 void *
rte_fib6_get_dp(struct rte_fib6 * fib)310 rte_fib6_get_dp(struct rte_fib6 *fib)
311 {
312 return (fib == NULL) ? NULL : fib->dp;
313 }
314
315 struct rte_rib6 *
rte_fib6_get_rib(struct rte_fib6 * fib)316 rte_fib6_get_rib(struct rte_fib6 *fib)
317 {
318 return (fib == NULL) ? NULL : fib->rib;
319 }
320
321 int
rte_fib6_select_lookup(struct rte_fib6 * fib,enum rte_fib6_lookup_type type)322 rte_fib6_select_lookup(struct rte_fib6 *fib,
323 enum rte_fib6_lookup_type type)
324 {
325 rte_fib6_lookup_fn_t fn;
326
327 switch (fib->type) {
328 case RTE_FIB6_TRIE:
329 fn = trie_get_lookup_fn(fib->dp, type);
330 if (fn == NULL)
331 return -EINVAL;
332 fib->lookup = fn;
333 return 0;
334 default:
335 return -EINVAL;
336 }
337 }
338