/* // Copyright (c) 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "l3fwd_common.h" #include "interface.h" #include "l2_proto.h" #include "l3fwd_lpm4.h" #include "l3fwd_lpm6.h" #include "lib_arp.h" #include "lib_icmpv6.h" #include /* Declare Global variables */ /* Global for IPV6 */ void *lpm4_table; /**< lpm4_table handler */ /*Hash table for L2 adjacency */ struct rte_hash *l2_adj_hash_handle; /**< l2 adjacency hash table handler */ struct rte_hash *fib_path_hash_handle; /**< fib path hash table handler */ l3_stats_t stats; /**< L3 statistics */ /* Global load balancing hash table for ECMP*/ uint8_t nh_links[MAX_SUPPORTED_FIB_PATHS][HASH_BUCKET_SIZE] = /**< Round Robin Hash entries for ECMP only*/ { /* 1 path, No Load balancing is required */ {0}, /* 2 path */ {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, /* 3 path */ {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, /* 4 path */ {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, /* 5 path */ {0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3}, /* 6 path */ {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3}, /* 7 path */ {0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0}, /* 8 path */ {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7} }; #if 0 #define META_DATA_OFFSET 128 #define RTE_PKTMBUF_HEADROOM 128 /* where is this defined ? */ #define ETHERNET_START (META_DATA_OFFSET + RTE_PKTMBUF_HEADROOM) #define ETH_HDR_SIZE 14 #define IP_START (ETHERNET_START + ETH_HDR_SIZE) #define TCP_START (IP_START + 20) static void print_pkt(struct rte_mbuf *pkt) { int i; int size = 14; uint8_t *rd = RTE_MBUF_METADATA_UINT8_PTR(pkt, ETHERNET_START); printf("Meta-data:\n"); for (i = 0; i < size; i++) { printf("%02x ", rd[i]); if ((i & 3) == 3) printf("\n"); } printf("\n"); printf("IP and TCP/UDP headers:\n"); rd = RTE_MBUF_METADATA_UINT8_PTR(pkt, IP_START); for (i = 0; i < 40; i++) { printf("%02x ", rd[i]); if ((i & 3) == 3) printf("\n"); } } #endif static struct ip_protocol_type *proto_type[2]; int lpm_init(void) { /* Initiliaze LPMv4 params */ struct rte_table_lpm_params lpm_params = { .name = "LPMv4", .n_rules = IPV4_L3FWD_LPM_MAX_RULES, .number_tbl8s = IPV4_L3FWD_LPM_NUMBER_TBL8S, .flags = 0, .entry_unique_size = sizeof(struct fib_info), .offset = 128, }; /* Create LPMv4 tables */ lpm4_table = rte_table_lpm_ops.f_create(&lpm_params, rte_socket_id(), sizeof(struct fib_info)); if (lpm4_table == NULL) { printf("Failed to create LPM IPV4 table\n"); return 0; } /*Initialize L2 ADJ hash params */ struct rte_hash_parameters l2_adj_ipv4_params = { .name = "l2_ADJ_HASH", .entries = 64, .key_len = sizeof(struct l2_adj_key_ipv4), .hash_func = rte_jhash, .hash_func_init_val = 0, }; /* Create IPv4 L2 Adj Hash tables */ l2_adj_hash_handle = rte_hash_create(&l2_adj_ipv4_params); if (l2_adj_hash_handle == NULL) { printf("L2 ADJ rte_hash_create failed\n"); return 0; } else { printf("l2_adj_hash_handle %p\n\n", (void *)l2_adj_hash_handle); } /*Initialize Fib PAth hassh params */ struct rte_hash_parameters fib_path_ipv4_params = { .name = "FIB_PATH_HASH", .entries = 64, .key_len = sizeof(struct fib_path_key_ipv4), .hash_func = rte_jhash, .hash_func_init_val = 0, }; /* Create FIB PATH Hash tables */ fib_path_hash_handle = rte_hash_create(&fib_path_ipv4_params); if (fib_path_hash_handle == NULL) { printf("FIB path rte_hash_create failed\n"); return 0; } return 1; } int lpm4_table_route_add(struct routing_info *data) { struct routing_info *fib = data; struct rte_table_lpm_key lpm_key = { .ip = fib->dst_ip_addr, .depth = fib->depth, }; uint8_t i; static int Total_route_count; struct fib_info entry; entry.dst_ip_addr = rte_bswap32(fib->dst_ip_addr); entry.depth = fib->depth; entry.fib_nh_size = fib->fib_nh_size; /**< For Single Path, greater then 1 for Multipath(ECMP)*/ #if MULTIPATH_FEAT if (entry.fib_nh_size == 0 || entry.fib_nh_size > MAX_FIB_PATHS) #else if (entry.fib_nh_size != 1) /**< For Single FIB_PATH */ #endif { printf("Route can't be configured!!, entry.fib_nh_size = %d\n", entry.fib_nh_size); return 0; } /* Populate L2 adj and precomputes l2 encap string */ #if MULTIPATH_FEAT for (i = 0; i < entry.fib_nh_size; i++) #else for (i = 0; i < 1; i++) #endif { struct fib_path *fib_path_addr = NULL; fib_path_addr = populate_fib_path(fib->nh_ip_addr[i], fib->out_port[i]); if (fib_path_addr) { entry.path[i] = fib_path_addr; printf("Fib info for the Dest IP"); printf(" : %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "/%" PRIu8 " => fib_path Addr: %p, l2_adj Addr: %p\n", (fib->dst_ip_addr & 0xFF000000) >> 24, (fib->dst_ip_addr & 0x00FF0000) >> 16, (fib->dst_ip_addr & 0x0000FF00) >> 8, (fib->dst_ip_addr & 0x000000FF), fib->depth, fib_path_addr, (void *)entry.path[i]->l2_adj_ptr); } else { printf("Fib info for the Dest IP :\ %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "/%" PRIu8 " => fib_path Addr: NULL \n", (fib->dst_ip_addr & 0xFF000000) >> 24, (fib->dst_ip_addr & 0x00FF0000) >> 16, (fib->dst_ip_addr & 0x0000FF00) >> 8, (fib->dst_ip_addr & 0x000000FF), fib->depth); entry.path[i] = NULL; /**< setting all other fib_paths to NULL */ } } int key_found, ret; void *entry_ptr; ret = rte_table_lpm_ops.f_add(lpm4_table, (void *)&lpm_key, &entry, &key_found, &entry_ptr); if (ret != 0) { printf("Failed to Add IP route\n"); return 0; } Total_route_count++; printf("Total Routed Added : %u, Key_found: %d\n", Total_route_count, key_found); printf("Adding Route to LPM table...\n"); printf("Iterate with Cuckoo Hash table\n"); iterate_cuckoo_hash_table(); return 1; } int lpm4_table_route_delete(uint32_t dst_ip, uint8_t depth) { struct rte_table_lpm_key lpm_key = { .ip = dst_ip, .depth = depth, }; int key_found, ret; void *entry = NULL; entry = rte_zmalloc(NULL, 512, RTE_CACHE_LINE_SIZE); /* Deleting a IP route from LPMv4 table */ ret = rte_table_lpm_ops.f_delete(lpm4_table, &lpm_key, &key_found, entry); if (ret) { printf("Failed to Delete IP route from LPMv4 table\n"); return 0; } printf("Deleted route from LPM table (IPv4 Address = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "/%u , key_found = %d\n", (lpm_key.ip & 0xFF000000) >> 24, (lpm_key.ip & 0x00FF0000) >> 16, (lpm_key.ip & 0x0000FF00) >> 8, (lpm_key.ip & 0x000000FF), lpm_key.depth, key_found); /* Deleting a L2 Adj entry if refcount is 1, Else decrement Refcount */ remove_fib_l2_adj_entry(entry); rte_free(entry); printf("Iterate with Cuckoo Hash table\n"); iterate_cuckoo_hash_table(); return 1; } int lpm4_table_lookup(struct rte_mbuf **pkts_burst, uint16_t nb_pkts, uint64_t pkts_mask, l2_phy_interface_t *port_ptr[RTE_PORT_IN_BURST_SIZE_MAX], uint64_t *hit_mask) { struct routing_table_entry *ipv4_entries[RTE_PORT_IN_BURST_SIZE_MAX]; uint64_t lookup_hit_mask_ipv4 = 0; int status; uint64_t pkts_key_mask = pkts_mask; uint64_t lookup_miss_mask_ipv4 = pkts_mask; static uint64_t sent_count; static uint64_t rcvd_count; rcvd_count += nb_pkts; if (L3FWD_DEBUG) { printf (" Received IPv4 nb_pkts: %u, Rcvd_count: %lu\n, pkts_mask: %p\n", nb_pkts, rcvd_count, (void *)pkts_mask); } uint32_t dst_addr_offset = MBUF_HDR_ROOM + ETH_HDR_SIZE + IP_HDR_DST_ADR_OFST; for (; pkts_key_mask;) { /**< Populate key offset in META DATA for all valid pkts */ uint8_t pos = (uint8_t) __builtin_ctzll(pkts_key_mask); uint64_t pkt_mask = 1LLU << pos; pkts_key_mask &= ~pkt_mask; struct rte_mbuf *mbuf = pkts_burst[pos]; uint32_t *lpm_key = NULL; uint32_t *dst_addr = NULL; lpm_key = (uint32_t *) RTE_MBUF_METADATA_UINT8_PTR(mbuf, 128); dst_addr = (uint32_t *) RTE_MBUF_METADATA_UINT8_PTR(mbuf, dst_addr_offset); *lpm_key = *dst_addr; if (L3FWD_DEBUG) { printf("Rcvd Pakt (IPv4 Address = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 ")\n", (rte_cpu_to_be_32(*lpm_key) & 0xFF000000) >> 24, (rte_cpu_to_be_32(*lpm_key) & 0x00FF0000) >> 16, (rte_cpu_to_be_32(*lpm_key) & 0x0000FF00) >> 8, (rte_cpu_to_be_32(*lpm_key) & 0x000000FF)); } } /* Lookup for IP route in LPM table */ if (L3FWD_DEBUG) printf("\nIPV4 Lookup Mask Before = %p\n", (void *)lookup_hit_mask_ipv4); status = rte_table_lpm_ops.f_lookup(lpm4_table, pkts_burst, pkts_mask, &lookup_hit_mask_ipv4, (void **)ipv4_entries); if (status) { printf("LPM Lookup failed for IP route\n"); return 0; } lookup_miss_mask_ipv4 = lookup_miss_mask_ipv4 & (~lookup_hit_mask_ipv4); if (L3FWD_DEBUG) { printf ("AFTER lookup_hit_mask_ipv4 = %p, lookup_miss_mask_ipv4 =%p\n", (void *)lookup_hit_mask_ipv4, (void *)lookup_miss_mask_ipv4); } for (; lookup_miss_mask_ipv4;) { /**< Drop packets for lookup_miss_mask */ uint8_t pos = (uint8_t) __builtin_ctzll(lookup_miss_mask_ipv4); uint64_t pkt_mask = 1LLU << pos; lookup_miss_mask_ipv4 &= ~pkt_mask; rte_pktmbuf_free(pkts_burst[pos]); pkts_burst[pos] = NULL; stats.nb_l3_drop_pkt++; /**< Peg the L3 Drop counter */ if (L3FWD_DEBUG) printf("\n DROP PKT IPV4 Lookup_miss_Mask = %p\n", (void *)lookup_miss_mask_ipv4); } *hit_mask = lookup_hit_mask_ipv4; for (; lookup_hit_mask_ipv4;) { /**< Process the packets for lookup_hit_mask*/ uint8_t pos = (uint8_t) __builtin_ctzll(lookup_hit_mask_ipv4); uint64_t pkt_mask = 1LLU << pos; lookup_hit_mask_ipv4 &= ~pkt_mask; struct rte_mbuf *pkt = pkts_burst[pos]; struct fib_info *entry = (struct fib_info *)ipv4_entries[pos]; #if MULTIPATH_FEAT uint8_t ecmp_path = 0; ecmp_path = ip_hash_load_balance(pkts_burst[pos]); uint8_t selected_path = 0; struct fib_path *fib_path = NULL; if (((entry->fib_nh_size != 0) && (entry->fib_nh_size - 1) < MAX_SUPPORTED_FIB_PATHS) && ((ecmp_path != 0) && (ecmp_path - 1) < HASH_BUCKET_SIZE)) selected_path = nh_links[entry->fib_nh_size - 1][ecmp_path - 1]; if (selected_path < MAX_FIB_PATHS) fib_path = entry->path[selected_path]; if (L3FWD_DEBUG) { printf ("Total supported Path :%u, Hashed ECMP Key : %u, selected Fib_path: %u\n", entry->fib_nh_size, ecmp_path, selected_path); } #else struct fib_path *fib_path = entry->path[0]; #endif if (fib_path == NULL) { rte_pktmbuf_free(pkt); pkts_burst[pos] = NULL; stats.nb_l3_drop_pkt++; /**< Peg the L3 Drop counter */ *hit_mask &= ~pkt_mask; /**< Remove this pkt from port Mask */ if (L3FWD_DEBUG) printf ("Fib_path is NULL, ARP has not resolved, DROPPED UNKNOWN PKT\n"); continue; } if (fib_path->l2_adj_ptr->flags == L2_ADJ_UNRESOLVED) { if (fib_path->l2_adj_ptr->phy_port->ipv4_list != NULL) request_arp(fib_path->l2_adj_ptr->phy_port-> pmdid, fib_path->nh_ip); rte_pktmbuf_free(pkts_burst[pos]); pkts_burst[pos] = NULL; *hit_mask &= ~pkt_mask; /**< Remove this pkt from port Mask */ if (L3FWD_DEBUG) printf ("L2_ADJ_UNRESOLVED, DROPPED UNKNOWN PKT\n"); continue; } /* extract ip headers and MAC */ uint8_t *eth_dest = RTE_MBUF_METADATA_UINT8_PTR(pkt, MBUF_HDR_ROOM); uint8_t *eth_src = RTE_MBUF_METADATA_UINT8_PTR(pkt, MBUF_HDR_ROOM + 6); if (L3FWD_DEBUG) { printf ("MAC BEFORE- DST MAC %02x:%02x:%02x:%02x:%02x:%02x, \ SRC MAC %02x:%02x:%02x:%02x:%02x:%02x \n", eth_dest[0], eth_dest[1], eth_dest[2], eth_dest[3], eth_dest[4], eth_dest[5], eth_src[0], eth_src[1], eth_src[2], eth_src[3], eth_src[4], eth_src[5]); } /* Rewrite the packet with L2 string */ memcpy(eth_dest, fib_path->l2_adj_ptr->l2_string, sizeof(struct ether_addr) * 2); // For MAC if (L3FWD_DEBUG) { int k = 0; for (k = 0; k < 14; k++) { printf("%02x ", fib_path->l2_adj_ptr->l2_string[k]); printf("\n"); } printf ("MAC AFTER DST MAC %02x:%02x:%02x:%02x:%02x:%02x, \ SRC MAC %02x:%02x:%02x:%02x:%02x:%02x\n", eth_dest[0], eth_dest[1], eth_dest[2], eth_dest[3], eth_dest[4], eth_dest[5], eth_src[0], eth_src[1], eth_src[2], eth_src[3], eth_src[4], eth_src[5]); } port_ptr[pos] = fib_path->l2_adj_ptr->phy_port; if (L3FWD_DEBUG) { printf("l3fwd_lookup API!!!!\n"); //print_pkt(pkt); } sent_count++; stats.nb_tx_l3_pkt++; if (L3FWD_DEBUG) printf ("Successfully sent to port %u, sent_count : %lu\n\r", fib_path->out_port, sent_count); } return 1; } int is_valid_ipv4_pkt(struct ipv4_hdr *pkt, uint32_t link_len) { if (link_len < sizeof(struct ipv4_hdr)) return -1; if (((pkt->version_ihl) >> 4) != 4) return -1; if ((pkt->version_ihl & 0xf) < 5) return -1; if (rte_cpu_to_be_16(pkt->total_length) < sizeof(struct ipv4_hdr)) return -1; return 0; } int get_dest_mac_for_nexthop(uint32_t next_hop_ip, uint8_t out_phy_port, struct ether_addr *hw_addr) { struct arp_entry_data *arp_data = NULL; struct arp_key_ipv4 arp_key; arp_key.port_id = out_phy_port; arp_key.ip = next_hop_ip; arp_data = retrieve_arp_entry(arp_key, DYNAMIC_ARP); if (arp_data == NULL) { printf("ARP entry is not found for ip %x, port %d\n", next_hop_ip, out_phy_port); return 0; } ether_addr_copy(&arp_data->eth_addr, hw_addr); return 1; } struct l2_adj_entry *retrieve_l2_adj_entry(struct l2_adj_key_ipv4 l2_adj_key) { struct l2_adj_entry *ret_l2_adj_data = NULL; l2_adj_key.filler1 = 0; l2_adj_key.filler2 = 0; l2_adj_key.filler3 = 0; int ret = rte_hash_lookup_data(l2_adj_hash_handle, &l2_adj_key, (void **)&ret_l2_adj_data); if (ret < 0) { #ifdef L2L3_DEBUG printf ("L2 Adj hash lookup failed ret %d, EINVAL %d, ENOENT %d\n", ret, EINVAL, ENOENT); #endif return NULL; } else { #ifdef L2L3_DEBUG printf ("L2 Adj hash lookup Success, Entry Already Exist ret %d, EINVAL %d, ENOENT %d\n", ret, EINVAL, ENOENT); #endif return ret_l2_adj_data; } } void remove_fib_l2_adj_entry(void *entry) { struct fib_info entry1; memcpy(&entry1, entry, sizeof(struct fib_info)); struct fib_path *fib_path_addr = entry1.path[0]; /**< For Single path */ if (fib_path_addr->refcount > 1) { printf (" BEFORE fib_path entry, nh_ip %x, port %d, refcount %d\n", fib_path_addr->nh_ip, fib_path_addr->out_port, fib_path_addr->refcount); fib_path_addr->refcount--; /**< Just decrement the refcount this entry is still referred*/ printf("AFTER fib_path entry, nh_ip %x, port %d, refcount %d\n", fib_path_addr->nh_ip, fib_path_addr->out_port, fib_path_addr->refcount); } else { /**< Refcount is 1 so delete both fib_path and l2_adj_entry */ struct l2_adj_entry *adj_addr = NULL; adj_addr = fib_path_addr->l2_adj_ptr; if (adj_addr != NULL) { /** < l2_adj_entry is has some entry in hash table*/ struct l2_adj_key_ipv4 l2_adj_key = { .Next_hop_ip = fib_path_addr->nh_ip, .out_port_id = fib_path_addr->out_port, }; #ifdef L3FWD_DEBUG printf (" l2_adj_entry is removed for ip %x, port %d, refcount %d\n", l2_adj_key.Next_hop_ip, l2_adj_key.out_port_id, adj_addr->refcount); #endif rte_hash_del_key(l2_adj_hash_handle, &l2_adj_key); rte_free(adj_addr); /**< free the memory which was allocated for Hash entry */ adj_addr = NULL; } struct fib_path_key_ipv4 path_key = { .nh_ip = fib_path_addr->nh_ip, .out_port = fib_path_addr->out_port, }; printf ("fib_path entry is removed for ip %x, port %d, refcount %d\n", fib_path_addr->nh_ip, fib_path_addr->out_port, fib_path_addr->refcount); rte_hash_del_key(fib_path_hash_handle, &path_key); rte_free(fib_path_addr); /**< Free the memory which was allocated for Hash entry*/ fib_path_addr = NULL; } } struct l2_adj_entry *populate_l2_adj(uint32_t ipaddr, uint8_t portid) { struct l2_adj_key_ipv4 l2_adj_key; l2_adj_key.out_port_id = portid; l2_adj_key.Next_hop_ip = ipaddr; l2_adj_key.filler1 = 0; l2_adj_key.filler2 = 0; l2_adj_key.filler3 = 0; struct ether_addr eth_dst; struct l2_adj_entry *adj_data = NULL; /* Populate L2 adj if the MAC Address is already present in L2 Adj HAsh Table */ adj_data = retrieve_l2_adj_entry(l2_adj_key); if (adj_data) { /**< L2 Adj Entry Exists*/ printf ("l2_adj_entry exists ip%x, port %d, Refcnt :%u Address :%p\n", l2_adj_key.Next_hop_ip, l2_adj_key.out_port_id, adj_data->refcount, adj_data); ether_addr_copy(&adj_data->eth_addr, ð_dst); adj_data->refcount++; printf ("l2_adj_entry UPDATED Refcount for NH ip%x, port %d, Refcnt :%u Address :%p\n", l2_adj_key.Next_hop_ip, l2_adj_key.out_port_id, adj_data->refcount, adj_data); return adj_data; } struct ether_addr eth_src; l2_phy_interface_t *port; //uint16_t ether_type = 0x0800; port = ifm_get_port(portid); if (port != NULL) { memcpy(ð_src, &port->macaddr, sizeof(struct ether_addr)); unsigned char *p = (unsigned char *)eth_src.addr_bytes; printf("S-MAC %x:%x:%x:%x:%x:%x\n\r", p[0], p[1], p[2], p[3], p[4], p[5]); uint32_t size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct l2_adj_entry)); adj_data = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE); if (adj_data == NULL) { printf("L2 Adjacency memory allocation failed !\n"); return NULL; } adj_data->out_port_id = portid; adj_data->Next_hop_ip = ipaddr; adj_data->refcount++; adj_data->phy_port = port; memset(&adj_data->eth_addr, 0, sizeof(struct ether_addr)); memset(&adj_data->l2_string, 0, 256); /**< Store the received MAC Address in L2 Adj HAsh Table */ rte_hash_add_key_data(l2_adj_hash_handle, &l2_adj_key, adj_data); #ifdef L2L3_DEBUG printf ("L2 adj data stored in l2_adj_entry hash table,Addr:%p\n", adj_data); #endif } else { #ifdef L2L3_DEBUG printf("\n PORT %u IS DOWN...\n", portid); #endif return NULL; } /* Query ARP to get L2 Adj */ if (get_dest_mac_for_nexthop(ipaddr, portid, ð_dst)) { unsigned char *p = (unsigned char *)eth_dst.addr_bytes; printf ("ARP resolution success and stored in l2_adj_entry hash table:D-MAC %x:%x:%x:%x:%x:%x\n\r", p[0], p[1], p[2], p[3], p[4], p[5]); memcpy(adj_data->l2_string, ð_dst, sizeof(struct ether_addr)); //** < Precompute the L2 String encap*/ memcpy(&adj_data->l2_string[6], ð_src, sizeof(struct ether_addr)); //memcpy(&adj_data->l2_string[12], ðer_type, 2); ether_addr_copy(ð_dst, &adj_data->eth_addr); adj_data->flags = L2_ADJ_RESOLVED; } else { adj_data->flags = L2_ADJ_UNRESOLVED; printf (" ARP resolution Failed !! , unable to write in l2_adj_entry\n"); } return adj_data; } struct fib_path *populate_fib_path(uint32_t nh_ip, uint8_t portid) { struct fib_path_key_ipv4 path_key; path_key.out_port = portid; path_key.nh_ip = nh_ip; path_key.filler1 = 0; path_key.filler2 = 0; path_key.filler3 = 0; struct fib_path *fib_data = NULL; /* Populate fib_path */ fib_data = retrieve_fib_path_entry(path_key); if (fib_data) {/**< fib_path entry already exists */ /* Already present in FIB_PATH cuckoo HAsh Table */ printf ("fib_path_entry already exists for NextHop ip: %x, port %d\n, Refcount %u Addr:%p\n", fib_data->nh_ip, fib_data->out_port, fib_data->refcount, fib_data); fib_data->refcount++; fib_data->l2_adj_ptr->refcount++; printf ("fib_path Refcount Updated NextHop :%x , port %u, Refcount %u\n\r", fib_data->nh_ip, fib_data->out_port, fib_data->refcount); return fib_data; } else { printf("fib_path entry Doesn't Exists.......\n"); } fib_data = NULL; struct l2_adj_entry *l2_adj_ptr = NULL; l2_adj_ptr = populate_l2_adj(nh_ip, portid); if (l2_adj_ptr) { uint32_t size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct fib_path)); fib_data = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE); fib_data->out_port = portid; fib_data->nh_ip = nh_ip; fib_data->refcount++; fib_data->l2_adj_ptr = l2_adj_ptr; printf("%s: get port details %u %d\n\r", __FUNCTION__, portid, __LINE__); /* Store the received MAC Address in L2 Adj HAsh Table */ int status; status = rte_hash_add_key_data(fib_path_hash_handle, &path_key, fib_data); if (status) { printf ("fib_path entry addition to hash table FAILED!! NextHop :%x , port %u, Refcount %u\n\r", fib_data->nh_ip, fib_data->out_port, fib_data->refcount); rte_free(fib_data); } else { printf ("fib_path entry Added into hash table for the NextHop :%x , port %u, Refcount %u\n\r", fib_data->nh_ip, fib_data->out_port, fib_data->refcount); printf (" l2_adj_entry Addr: %p, Fib_path Addr: %p, FibPath->l2ADJ Addr:%p \n", l2_adj_ptr, fib_data, fib_data->l2_adj_ptr); printf (" ARP resolution success l2_adj_entry Addr: %p, Fib_path Addr: %p \n", l2_adj_ptr, fib_data); return fib_data; } } else { printf (" ARP resolution failed and unable to write fib path in fib_path cuckoo hash\n"); } return NULL; } struct fib_path *retrieve_fib_path_entry(struct fib_path_key_ipv4 path_key) { printf("FIB PATH for NExtHOP IP : %x, port :%u\n", path_key.nh_ip, path_key.out_port); struct fib_path *ret_fib_path_data = NULL; int ret = rte_hash_lookup_data(fib_path_hash_handle, &path_key, (void **)&ret_fib_path_data); if (ret < 0) { printf ("FIB PATH hash lookup Failed!! ret %d, EINVAL %d, ENOENT %d\n", ret, EINVAL, ENOENT); return NULL; } else { printf("FIB PATH ALREADY Exists for NExtHOP IP: %x, port: %u\n", path_key.nh_ip, path_key.out_port); return ret_fib_path_data; } } void iterate_cuckoo_hash_table(void) { const void *next_key; void *next_data; uint32_t iter = 0; printf("\n\t\t\t FIB_path Cache table...."); printf ("\n----------------------------------------------------------------"); printf("\n\tNextHop IP Port Refcount l2_adj_ptr_addrress\n"); printf ("\n----------------------------------------------------------------\n"); while (rte_hash_iterate (fib_path_hash_handle, &next_key, &next_data, &iter) >= 0) { struct fib_path *tmp_data = (struct fib_path *)next_data; struct fib_path_key_ipv4 tmp_key; memcpy(&tmp_key, next_key, sizeof(tmp_key)); printf("\t %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 " \t %u \t %u \t %p\n", (tmp_data->nh_ip & 0xFF000000) >> 24, (tmp_data->nh_ip & 0x00FF0000) >> 16, (tmp_data->nh_ip & 0x0000FF00) >> 8, (tmp_data->nh_ip & 0x000000FF), tmp_data->out_port, tmp_data->refcount, tmp_data->l2_adj_ptr); } iter = 0; printf("\n\t\t\t L2 ADJ Cache table....."); printf ("\n------------------------------------------------------------------------------------"); printf ("\n\tNextHop IP Port \t l2 Encap string \t l2_Phy_interface\n"); printf ("\n------------------------------------------------------------------------------------\n"); while (rte_hash_iterate (l2_adj_hash_handle, &next_key, &next_data, &iter) >= 0) { struct l2_adj_entry *l2_data = (struct l2_adj_entry *)next_data; struct l2_adj_key_ipv4 l2_key; memcpy(&l2_key, next_key, sizeof(l2_key)); printf("\t %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "\t %u \t%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x\t%p\n", (l2_data->Next_hop_ip & 0xFF000000) >> 24, (l2_data->Next_hop_ip & 0x00FF0000) >> 16, (l2_data->Next_hop_ip & 0x0000FF00) >> 8, (l2_data->Next_hop_ip & 0x000000FF), l2_data->out_port_id, l2_data->l2_string[0], l2_data->l2_string[1], l2_data->l2_string[2], l2_data->l2_string[3], l2_data->l2_string[4], l2_data->l2_string[5], l2_data->l2_string[6], l2_data->l2_string[7], l2_data->l2_string[8], l2_data->l2_string[9], l2_data->l2_string[10], l2_data->l2_string[11], l2_data->phy_port); } } void print_l3_stats(void) { printf("==============================================\n"); printf("\t\t L3 STATISTICS \t\n"); printf("==============================================\n"); printf(" Num of Received L3 Pkts : %lu\n", stats.nb_rx_l3_pkt); printf(" Num of Dropped L3 Pkts : %lu\n", stats.nb_l3_drop_pkt); printf(" Num of Transmitted L3 Pkts : %lu\n", stats.nb_tx_l3_pkt); printf(" Num of ICMP Pkts Rcvd at L3 : %lu\n", stats.nb_rx_l3_icmp_pkt); printf(" Num of ICMP Pkts Tx to ICMP : %lu\n", stats.nb_tx_l3_icmp_pkt); stats.total_nb_rx_l3_pkt = stats.nb_rx_l3_icmp_pkt + stats.nb_rx_l3_pkt; stats.total_nb_tx_l3_pkt = stats.nb_tx_l3_icmp_pkt + stats.nb_tx_l3_pkt; printf(" Total Num of Rcvd pkts at L3: %lu\n", stats.total_nb_rx_l3_pkt); printf(" Total Num of Sent pkts at L3: %lu\n", stats.total_nb_tx_l3_pkt); } void ip_local_packets_process(struct rte_mbuf **pkt_burst, uint16_t nb_rx, uint64_t icmp_pkt_mask, l2_phy_interface_t *port) { process_arpicmp_pkt_parse(pkt_burst, nb_rx, icmp_pkt_mask, port); } void ip_forward_deliver(struct rte_mbuf **pkt_burst, uint16_t nb_pkts, uint64_t ipv4_forward_pkts_mask, l2_phy_interface_t *port) { if (L3FWD_DEBUG) { printf ("ip_forward_deliver BEFORE DROP: nb_pkts: %u\n from in_port %u", nb_pkts, port->pmdid); } uint64_t pkts_for_process = ipv4_forward_pkts_mask; struct ipv4_hdr *ipv4_hdr; l2_phy_interface_t *port_ptr[RTE_PORT_IN_BURST_SIZE_MAX]; uint64_t hit_mask = 0; for (; pkts_for_process;) { /**< process only valid packets.*/ uint8_t pos = (uint8_t) __builtin_ctzll(pkts_for_process); uint64_t pkt_mask = 1LLU << pos; /**< bitmask representing only this packet */ pkts_for_process &= ~pkt_mask; /**< remove this packet from the mask */ ipv4_hdr = rte_pktmbuf_mtod_offset(pkt_burst[pos], struct ipv4_hdr *, sizeof(struct ether_hdr)); /* Make sure the IPv4 packet is valid */ if (is_valid_ipv4_pkt(ipv4_hdr, pkt_burst[pos]->pkt_len) < 0) { rte_pktmbuf_free(pkt_burst[pos]); /**< Drop the Unknown IPv4 Packet */ pkt_burst[pos] = NULL; ipv4_forward_pkts_mask &= ~(1LLU << pos); /**< That will clear bit of that position*/ nb_pkts--; stats.nb_l3_drop_pkt++; } } if (L3FWD_DEBUG) { printf ("\nl3fwd_rx_ipv4_packets_received AFTER DROP: nb_pkts: %u, valid_Pkts_mask :%lu\n", nb_pkts, ipv4_forward_pkts_mask); } /* Lookup for IP destination in LPMv4 table */ lpm4_table_lookup(pkt_burst, nb_pkts, ipv4_forward_pkts_mask, port_ptr, &hit_mask); for (; hit_mask;) { /**< process only valid packets.*/ uint8_t pos = (uint8_t) __builtin_ctzll(hit_mask); uint64_t pkt_mask = 1LLU << pos; /**< bitmask representing only this packet */ hit_mask &= ~pkt_mask; /**< remove this packet from the mask */ port_ptr[pos]->transmit_single_pkt(port_ptr[pos], pkt_burst[pos]); } } void l3_protocol_type_add(uint8_t protocol_type, void (*func) (struct rte_mbuf **, uint16_t, uint64_t, l2_phy_interface_t *port)) { switch (protocol_type) { case IPPROTO_ICMP: proto_type[IP_LOCAL] = rte_malloc(NULL, sizeof(struct ip_protocol_type), RTE_CACHE_LINE_SIZE); proto_type[IP_LOCAL]->protocol_type = protocol_type; proto_type[IP_LOCAL]->func = func; break; case IPPROTO_TCP: // Time being treared as Remote forwarding case IPPROTO_UDP: proto_type[IP_REMOTE] = rte_malloc(NULL, sizeof(struct ip_protocol_type), RTE_CACHE_LINE_SIZE); proto_type[IP_REMOTE]->protocol_type = protocol_type; proto_type[IP_REMOTE]->func = func; break; } } void l3fwd_rx_ipv4_packets(struct rte_mbuf **m, uint16_t nb_pkts, uint64_t valid_pkts_mask, l2_phy_interface_t *port) { if (L3FWD_DEBUG) { printf ("l3fwd_rx_ipv4_packets_received BEFORE DROP: nb_pkts: %u\n from in_port %u", nb_pkts, port->pmdid); } uint64_t pkts_for_process = valid_pkts_mask; struct ipv4_hdr *ipv4_hdr; uint32_t configure_port_ip = 0; uint64_t icmp_pkts_mask = RTE_LEN2MASK(nb_pkts, uint64_t); uint64_t ipv4_forward_pkts_mask = RTE_LEN2MASK(nb_pkts, uint64_t); uint16_t nb_icmp_pkt = 0; uint16_t nb_l3_pkt = 0; if (port->ipv4_list != NULL) configure_port_ip = (uint32_t) (((ipv4list_t *) (port->ipv4_list))->ipaddr); for (; pkts_for_process;) { /**< process only valid packets.*/ uint8_t pos = (uint8_t) __builtin_ctzll(pkts_for_process); uint64_t pkt_mask = 1LLU << pos; /**< bitmask representing only this packet */ pkts_for_process &= ~pkt_mask; /**< remove this packet from the mask */ ipv4_hdr = rte_pktmbuf_mtod_offset(m[pos], struct ipv4_hdr *, sizeof(struct ether_hdr)); if ((ipv4_hdr->next_proto_id == IPPROTO_ICMP) && (ipv4_hdr->dst_addr == configure_port_ip)) { ipv4_forward_pkts_mask &= ~pkt_mask; /**< Its ICMP, remove this packet from the ipv4_forward_pkts_mask*/ stats.nb_rx_l3_icmp_pkt++; /**< Increment stats for ICMP PKT */ nb_icmp_pkt++; } else{ // Forward the packet icmp_pkts_mask &= ~pkt_mask; /**< Not ICMP, remove this packet from the icmp_pkts_mask*/ stats.nb_rx_l3_pkt++; nb_l3_pkt++; /**< Increment stats for L3 PKT */ } } if (icmp_pkts_mask) { if (L3FWD_DEBUG) printf ("\n RECEiVED LOCAL ICMP PKT at L3...\n PROCESSING ICMP LOCAL PKT...\n"); proto_type[IP_LOCAL]->func(m, nb_icmp_pkt, icmp_pkts_mask, port); } if (ipv4_forward_pkts_mask) { if (L3FWD_DEBUG) printf ("\n RECEIVED L3 PKT, \n\n FORWARDING L3 PKT....\n"); proto_type[IP_REMOTE]->func(m, nb_l3_pkt, ipv4_forward_pkts_mask, port); } } void resolve_l2_adj(uint32_t nexthop_ip, uint8_t out_port_id, const struct ether_addr *hw_addr) { struct l2_adj_key_ipv4 l2_adj_key = { .Next_hop_ip = nexthop_ip, .out_port_id = out_port_id, }; //uint16_t ether_type = 0x0800; struct l2_adj_entry *adj_data = retrieve_l2_adj_entry(l2_adj_key); if (adj_data) { /**< L2 Adj Entry Exists*/ printf ("l2_adj_entry exists ip%x, port %d, Refcnt :%u Address :%p\n", l2_adj_key.Next_hop_ip, l2_adj_key.out_port_id, adj_data->refcount, adj_data); if (adj_data->flags == L2_ADJ_UNRESOLVED || memcmp(hw_addr, &adj_data->eth_addr, sizeof(struct ether_addr))) { memcpy(adj_data->l2_string, hw_addr, sizeof(struct ether_addr)); //** < Precompute the L2 String encap*/ memcpy(&adj_data->l2_string[6], &adj_data->phy_port->macaddr, sizeof(struct ether_addr)); //memcpy(&adj_data->l2_string[12], ðer_type, 2); ether_addr_copy(hw_addr, &adj_data->eth_addr); adj_data->flags = L2_ADJ_RESOLVED; } return; } l2_phy_interface_t *port; port = ifm_get_port(out_port_id); if (port != NULL) { uint32_t size = RTE_CACHE_LINE_ROUNDUP(sizeof(struct l2_adj_entry)); adj_data = rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE); if (adj_data == NULL) { printf("L2 Adjacency memory allocation failed !\n"); return; } adj_data->out_port_id = out_port_id; adj_data->Next_hop_ip = nexthop_ip; adj_data->phy_port = port; memcpy(adj_data->l2_string, hw_addr, sizeof(struct ether_addr)); //** < Precompute the L2 String encap*/ memcpy(&adj_data->l2_string[6], &adj_data->phy_port->macaddr, sizeof(struct ether_addr)); //memcpy(&adj_data->l2_string[12], ðer_type, 2); ether_addr_copy(hw_addr, &adj_data->eth_addr); adj_data->flags = L2_ADJ_RESOLVED; rte_hash_add_key_data(l2_adj_hash_handle, &l2_adj_key, adj_data); printf ("L2 adj data stored in l2_adj_entry hash table,Addr:%p\n", adj_data); } else printf("PORT:%u IS DOWN...\n", out_port_id); return; } uint8_t ip_hash_load_balance(struct rte_mbuf *mbuf) { uint32_t src_addr_offset = MBUF_HDR_ROOM + ETH_HDR_SIZE + IP_HDR_SRC_ADR_OFST; uint32_t dst_addr_offset = MBUF_HDR_ROOM + ETH_HDR_SIZE + IP_HDR_DST_ADR_OFST; uint32_t *dst_addr = NULL; uint32_t *src_addr = NULL; src_addr = (uint32_t *) RTE_MBUF_METADATA_UINT8_PTR(mbuf, src_addr_offset); dst_addr = (uint32_t *) RTE_MBUF_METADATA_UINT8_PTR(mbuf, dst_addr_offset); uint32_t hash_key1 = *src_addr; /* STORE SRC IP in key1 variable */ uint32_t hash_key2 = *dst_addr; /* STORE DST IP in key variable */ hash_key1 = hash_key1 ^ hash_key2; /* XOR With SRC and DST IP, Result is hask_key1 */ hash_key2 = hash_key1; /* MOVE The result to hask_key2 */ hash_key1 = rotr32(hash_key1, 16); /* Circular Rotate to 16 bit */ hash_key1 = hash_key1 ^ hash_key2; /* XOR With Key1 with Key2 */ hash_key2 = hash_key1; /* MOVE The result to hask_key2 */ hash_key1 = rotr32(hash_key1, 8); /* Circular Rotate to 8 bit */ hash_key1 = hash_key1 ^ hash_key2; /* XOR With Key1 with Key2 */ hash_key1 = hash_key1 & (HASH_BUCKET_SIZE - 1); /* MASK the KEY with BUCKET SIZE */ if (L3FWD_DEBUG) printf("Hash Result_key: %d, \n", hash_key1); return hash_key1; } uint32_t rotr32(uint32_t value, unsigned int count) { const unsigned int mask = (CHAR_BIT * sizeof(value) - 1); count &= mask; return (value >> count) | (value << ((-count) & mask)); } void ip_local_out_deliver(struct rte_mbuf **pkt_burst, uint16_t nb_rx, uint64_t ipv4_pkts_mask, l2_phy_interface_t *port) { ip_forward_deliver(pkt_burst, nb_rx, ipv4_pkts_mask, port); }