API Reference Manual  1.46.0
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 
52 static inline
53 uint64_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 
76 static inline
77 int 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 
118 static inline
119 char *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 
138 static inline
139 char *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 
152 typedef 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 
161 typedef struct flow_bucket_s {
162  odp_rwlock_t lock;
163  flow_entry_t *next;
164 } flow_bucket_t;
165 
169 typedef 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 
178 static flow_table_t fwd_lookup_cache;
179 
180 static 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 
238 static 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 
254 static inline
255 int 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 
263 static inline
264 flow_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 
278 static inline
279 flow_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 
304 void 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  }
336 out:
337  return;
338 }
339 
341 fwd_db_t *fwd_db;
342 
343 void 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 
366 int 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 
434 void 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 
448 void 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 
459 void 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 
474 fwd_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.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
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.