API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_l3fwd_db.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2016-2018 Linaro Limited
3 */
4
7#ifndef _GNU_SOURCE
8#define _GNU_SOURCE
9#endif
10
11#include <stdlib.h>
12#include <string.h>
13
14#include <odp_api.h>
15#include <odp/helper/odph_api.h>
16
17#include <odp_l3fwd_db.h>
18
37#define JHASH_GOLDEN_RATIO 0x9e3779b9
38#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
39#define FWD_BJ3_MIX(a, b, c) \
40{ \
41 a -= c; a ^= rot(c, 4); c += b; \
42 b -= a; b ^= rot(a, 6); a += c; \
43 c -= b; c ^= rot(b, 8); b += a; \
44 a -= c; a ^= rot(c, 16); c += b; \
45 b -= a; b ^= rot(a, 19); a += c; \
46 c -= b; c ^= rot(b, 4); b += a; \
47}
48
52static inline
53uint64_t l3fwd_calc_hash(ipv4_tuple5_t *key)
54{
55 uint64_t l4_ports = 0;
56 uint32_t dst_ip, src_ip;
57
58 src_ip = key->src_ip;
59 dst_ip = key->dst_ip + JHASH_GOLDEN_RATIO;
60 FWD_BJ3_MIX(src_ip, dst_ip, l4_ports);
61
62 return l4_ports;
63}
64
76static inline
77int parse_ipv4_string(char *ipaddress, uint32_t *addr, uint32_t *depth)
78{
79 int b[4];
80 int qualifier = 32;
81 int converted;
82 uint32_t addr_le;
83
84 if (strchr(ipaddress, '/')) {
85 converted = sscanf(ipaddress, "%d.%d.%d.%d/%d",
86 &b[3], &b[2], &b[1], &b[0],
87 &qualifier);
88 if (5 != converted)
89 return -1;
90 } else {
91 converted = sscanf(ipaddress, "%d.%d.%d.%d",
92 &b[3], &b[2], &b[1], &b[0]);
93 if (4 != converted)
94 return -1;
95 }
96
97 if ((b[0] > 255) || (b[1] > 255) || (b[2] > 255) || (b[3] > 255))
98 return -1;
99 if (!qualifier || (qualifier > 32))
100 return -1;
101
102 addr_le = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
103 *addr = odp_le_to_cpu_32(addr_le);
104 *depth = qualifier;
105
106 return 0;
107}
108
118static inline
119char *ipv4_subnet_str(char *b, ip_addr_range_t *range)
120{
121 sprintf(b, "%d.%d.%d.%d/%d",
122 0xFF & ((range->addr) >> 24),
123 0xFF & ((range->addr) >> 16),
124 0xFF & ((range->addr) >> 8),
125 0xFF & ((range->addr) >> 0),
126 range->depth);
127 return b;
128}
129
138static inline
139char *mac_addr_str(char *b, odph_ethaddr_t *mac)
140{
141 uint8_t *byte;
142
143 byte = mac->addr;
144 sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X",
145 byte[0], byte[1], byte[2], byte[3], byte[4], byte[5]);
146 return b;
147}
148
152typedef struct flow_entry_s {
153 ipv4_tuple5_t key;
154 struct flow_entry_s *next;
155 fwd_db_entry_t *fwd_entry;
156} flow_entry_t;
157
161typedef struct flow_bucket_s {
162 odp_rwlock_t lock;
163 flow_entry_t *next;
164} flow_bucket_t;
165
169typedef struct flow_table_s {
170 odp_rwlock_t flow_lock;
171 flow_entry_t *flows;
172 flow_bucket_t *bucket;
173 uint32_t bkt_cnt;
174 uint32_t flow_cnt;
175 uint32_t next_flow;
176} flow_table_t;
177
178static flow_table_t fwd_lookup_cache;
179
180static void create_fwd_hash_cache(void)
181{
182 odp_shm_t hash_shm;
183 flow_bucket_t *bucket = NULL;
184 flow_entry_t *flows;
185 uint32_t bucket_count, flow_count, size;
186 uint32_t i;
187
188 flow_count = FWD_MAX_FLOW_COUNT;
189 bucket_count = flow_count / FWD_DEF_BUCKET_ENTRIES;
190
191 /*Reserve memory for Routing hash table*/
192 size = sizeof(flow_bucket_t) * bucket_count +
193 sizeof(flow_entry_t) * flow_count;
194 hash_shm = odp_shm_reserve("flow_table", size, ODP_CACHE_LINE_SIZE, 0);
195 if (hash_shm != ODP_SHM_INVALID)
196 bucket = odp_shm_addr(hash_shm);
197
198 if (!bucket) {
199 /* Try the second time with small request */
200 flow_count /= 4;
201 bucket_count = flow_count / FWD_DEF_BUCKET_ENTRIES;
202 size = sizeof(flow_bucket_t) * bucket_count +
203 sizeof(flow_entry_t) * flow_count;
204 hash_shm = odp_shm_reserve("flow_table", size,
205 ODP_CACHE_LINE_SIZE, 0);
206 if (hash_shm == ODP_SHM_INVALID) {
207 ODPH_ERR("Error: shared mem reserve failed.\n");
208 exit(EXIT_FAILURE);
209 }
210
211 bucket = odp_shm_addr(hash_shm);
212 if (!bucket) {
213 ODPH_ERR("Error: shared mem alloc failed.\n");
214 exit(-1);
215 }
216 }
217
218 size = sizeof(flow_bucket_t) * bucket_count;
219 flows = (flow_entry_t *)(void *)((char *)bucket + size);
220
221 fwd_lookup_cache.bucket = bucket;
222 fwd_lookup_cache.bkt_cnt = bucket_count;
223 fwd_lookup_cache.flows = flows;
224 fwd_lookup_cache.flow_cnt = flow_count;
225
226 /*Initialize bucket locks*/
227 for (i = 0; i < bucket_count; i++) {
228 bucket = &fwd_lookup_cache.bucket[i];
229 odp_rwlock_init(&bucket->lock);
230 bucket->next = NULL;
231 }
232
233 memset(flows, 0, sizeof(flow_entry_t) * flow_count);
234 odp_rwlock_init(&fwd_lookup_cache.flow_lock);
235 fwd_lookup_cache.next_flow = 0;
236}
237
238static inline flow_entry_t *get_new_flow(void)
239{
240 uint32_t next;
241 flow_entry_t *flow = NULL;
242
243 odp_rwlock_write_lock(&fwd_lookup_cache.flow_lock);
244 next = fwd_lookup_cache.next_flow;
245 if (next < fwd_lookup_cache.flow_cnt) {
246 flow = &fwd_lookup_cache.flows[next];
247 fwd_lookup_cache.next_flow++;
248 }
249 odp_rwlock_write_unlock(&fwd_lookup_cache.flow_lock);
250
251 return flow;
252}
253
254static inline
255int match_key_flow(ipv4_tuple5_t *key, flow_entry_t *flow)
256{
257 if (key->hi64 == flow->key.hi64 && key->lo64 == flow->key.lo64)
258 return 1;
259
260 return 0;
261}
262
263static inline
264flow_entry_t *lookup_fwd_cache(ipv4_tuple5_t *key, flow_bucket_t *bucket)
265{
266 flow_entry_t *rst;
267
268 odp_rwlock_read_lock(&bucket->lock);
269 for (rst = bucket->next; rst != NULL; rst = rst->next) {
270 if (match_key_flow(key, rst))
271 break;
272 }
273 odp_rwlock_read_unlock(&bucket->lock);
274
275 return rst;
276}
277
278static inline
279flow_entry_t *insert_fwd_cache(ipv4_tuple5_t *key,
280 flow_bucket_t *bucket,
281 fwd_db_entry_t *entry)
282{
283 flow_entry_t *flow;
284
285 if (!entry)
286 return NULL;
287
288 flow = get_new_flow();
289 if (!flow)
290 return NULL;
291
292 flow->key = *key;
293 flow->fwd_entry = entry;
294
295 odp_rwlock_write_lock(&bucket->lock);
296 if (bucket->next)
297 flow->next = bucket->next;
298 bucket->next = flow;
299 odp_rwlock_write_unlock(&bucket->lock);
300
301 return flow;
302}
303
304void init_fwd_hash_cache(void)
305{
306 fwd_db_entry_t *entry;
307 flow_entry_t *flow;
308 flow_bucket_t *bucket;
309 uint64_t hash;
310 uint32_t i, nb_hosts;
311 ipv4_tuple5_t key;
312
313 create_fwd_hash_cache();
314
319 memset(&key, 0, sizeof(key));
320 for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
321 nb_hosts = 1 << (32 - entry->subnet.depth);
322 for (i = 0; i < nb_hosts; i++) {
323 key.dst_ip = entry->subnet.addr + i;
324 hash = l3fwd_calc_hash(&key);
325 hash &= fwd_lookup_cache.bkt_cnt - 1;
326 bucket = &fwd_lookup_cache.bucket[hash];
327 flow = lookup_fwd_cache(&key, bucket);
328 if (flow)
329 return;
330
331 flow = insert_fwd_cache(&key, bucket, entry);
332 if (!flow)
333 goto out;
334 }
335 }
336out:
337 return;
338}
339
341fwd_db_t *fwd_db;
342
343void init_fwd_db(void)
344{
345 odp_shm_t shm;
346
347 shm = odp_shm_reserve("shm_fwd_db",
348 sizeof(fwd_db_t),
349 ODP_CACHE_LINE_SIZE,
350 0);
351
352 if (shm == ODP_SHM_INVALID) {
353 ODPH_ERR("Error: shared mem reserve failed.\n");
354 exit(EXIT_FAILURE);
355 }
356
357 fwd_db = odp_shm_addr(shm);
358
359 if (fwd_db == NULL) {
360 ODPH_ERR("Error: shared mem alloc failed.\n");
361 exit(EXIT_FAILURE);
362 }
363 memset(fwd_db, 0, sizeof(*fwd_db));
364}
365
366int create_fwd_db_entry(char *input, char **oif, uint8_t **dst_mac)
367{
368 int pos = 0;
369 char *local;
370 char *str;
371 char *save;
372 char *token;
373 fwd_db_entry_t *entry = &fwd_db->array[fwd_db->index];
374
375 *oif = NULL;
376 *dst_mac = NULL;
377
378 /* Verify we haven't run out of space */
379 if (MAX_DB <= fwd_db->index)
380 return -1;
381
382 /* Make a local copy */
383 local = malloc(strlen(input) + 1);
384 if (NULL == local)
385 return -1;
386 strcpy(local, input);
387
388 /* Setup for using "strtok_r" to search input string */
389 str = local;
390 save = NULL;
391
392 /* Parse tokens separated by ',' */
393 while (NULL != (token = strtok_r(str, ",", &save))) {
394 str = NULL; /* reset str for subsequent strtok_r calls */
395
396 /* Parse token based on its position */
397 switch (pos) {
398 case 0:
399 parse_ipv4_string(token,
400 &entry->subnet.addr,
401 &entry->subnet.depth);
402 break;
403 case 1:
404 odph_strcpy(entry->oif, token, OIF_LEN);
405 *oif = entry->oif;
406 break;
407 case 2:
408 if (odph_eth_addr_parse(&entry->dst_mac, token) < 0) {
409 free(local);
410 return -1;
411 }
412 *dst_mac = entry->dst_mac.addr;
413 break;
414
415 default:
416 printf("ERROR: extra token \"%s\" at position %d\n",
417 token, pos);
418 break;
419 }
420
421 /* Advance to next position */
422 pos++;
423 }
424
425 /* Add route to the list */
426 fwd_db->index++;
427 entry->next = fwd_db->list;
428 fwd_db->list = entry;
429
430 free(local);
431 return 0;
432}
433
434void resolve_fwd_db(char *intf, int portid, uint8_t *mac)
435{
436 fwd_db_entry_t *entry;
437
438 /* Walk the list and attempt to set output and MAC */
439 for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
440 if (strcmp(intf, entry->oif))
441 continue;
442
443 entry->oif_id = portid;
444 memcpy(entry->src_mac.addr, mac, ODPH_ETHADDR_LEN);
445 }
446}
447
448void dump_fwd_db_entry(fwd_db_entry_t *entry)
449{
450 char subnet_str[MAX_STRING];
451 char mac_str[MAX_STRING];
452
453 mac_addr_str(mac_str, &entry->dst_mac);
454 printf("%-32s%-32s%-16s\n",
455 ipv4_subnet_str(subnet_str, &entry->subnet),
456 entry->oif, mac_str);
457}
458
459void dump_fwd_db(void)
460{
461 fwd_db_entry_t *entry;
462
463 printf("Routing table\n"
464 "-----------------\n"
465 "%-32s%-32s%-16s\n",
466 "subnet", "next_hop", "dest_mac");
467
468 for (entry = fwd_db->list; NULL != entry; entry = entry->next)
469 dump_fwd_db_entry(entry);
470
471 printf("\n");
472}
473
474fwd_db_entry_t *find_fwd_db_entry(ipv4_tuple5_t *key)
475{
476 fwd_db_entry_t *entry;
477 flow_entry_t *flow;
478 flow_bucket_t *bucket;
479 uint64_t hash;
480 ipv4_tuple5_t newkey;
481
482 newkey.hi64 = 0;
483 newkey.lo64 = 0;
484 newkey.dst_ip = key->dst_ip;
485 key = &newkey;
486
487 /* first find in cache */
488 hash = l3fwd_calc_hash(key);
489 hash &= fwd_lookup_cache.bkt_cnt - 1;
490 bucket = &fwd_lookup_cache.bucket[hash];
491 flow = lookup_fwd_cache(key, bucket);
492 if (flow)
493 return flow->fwd_entry;
494
495 for (entry = fwd_db->list; NULL != entry; entry = entry->next) {
496 uint32_t mask;
497
498 mask = ((1u << entry->subnet.depth) - 1) <<
499 (32 - entry->subnet.depth);
500
501 if (entry->subnet.addr == (key->dst_ip & mask))
502 break;
503 }
504
505 insert_fwd_cache(key, bucket, entry);
506
507 return entry;
508}
uint32_t odp_le_to_cpu_32(odp_u32le_t le32)
Convert 32bit little endian to cpu native uint32_t.
void odp_rwlock_read_lock(odp_rwlock_t *rwlock)
Acquire read permission on a reader/writer lock.
void odp_rwlock_read_unlock(odp_rwlock_t *rwlock)
Release read permission on a reader/writer lock.
void odp_rwlock_write_unlock(odp_rwlock_t *rwlock)
Release write permission on a reader/writer lock.
void odp_rwlock_write_lock(odp_rwlock_t *rwlock)
Acquire write permission on a reader/writer lock.
void odp_rwlock_init(odp_rwlock_t *rwlock)
Initialize a reader/writer lock.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
The OpenDataPlane API.