Added support for VLAN in IPv6
[samplevnf.git] / VNFs / DPPD-PROX / packet_utils.c
index 466dd48..7be978d 100644 (file)
@@ -36,7 +36,7 @@
 #include "prox_ipv6.h"
 #include "tx_pkt.h"
 
-static inline int find_ip(struct ether_hdr_arp *pkt, uint16_t len, uint32_t *ip_dst)
+static inline int find_ip(struct ether_hdr_arp *pkt, uint16_t len, uint32_t *ip_dst, uint16_t *vlan)
 {
        prox_rte_vlan_hdr *vlan_hdr;
        prox_rte_ether_hdr *eth_hdr = (prox_rte_ether_hdr*)pkt;
@@ -44,11 +44,13 @@ static inline int find_ip(struct ether_hdr_arp *pkt, uint16_t len, uint32_t *ip_
        uint16_t ether_type = eth_hdr->ether_type;
        uint16_t l2_len = sizeof(prox_rte_ether_hdr);
 
+       *vlan = 0;
        // Unstack VLAN tags
        while (((ether_type == ETYPE_8021ad) || (ether_type == ETYPE_VLAN)) && (l2_len + sizeof(prox_rte_vlan_hdr) < len)) {
                vlan_hdr = (prox_rte_vlan_hdr *)((uint8_t *)pkt + l2_len);
                l2_len +=4;
                ether_type = vlan_hdr->eth_proto;
+               *vlan = rte_be_to_cpu_16(vlan_hdr->vlan_tci & 0xFF0F);  // Store VLAN, or CVLAN if QinQ
        }
 
        switch (ether_type) {
@@ -79,18 +81,37 @@ static inline int find_ip(struct ether_hdr_arp *pkt, uint16_t len, uint32_t *ip_
        return -1;
 }
 
-static inline struct ipv6_addr *find_ip6(prox_rte_ether_hdr *pkt, uint16_t len, struct ipv6_addr *ip_dst)
+static inline void find_vlan(struct ether_hdr_arp *pkt, uint16_t len, uint16_t *vlan)
+{
+       prox_rte_vlan_hdr *vlan_hdr;
+       prox_rte_ether_hdr *eth_hdr = (prox_rte_ether_hdr*)pkt;
+       uint16_t ether_type = eth_hdr->ether_type;
+       uint16_t l2_len = sizeof(prox_rte_ether_hdr);
+
+       *vlan = 0;
+       // Unstack VLAN tags
+       while (((ether_type == ETYPE_8021ad) || (ether_type == ETYPE_VLAN)) && (l2_len + sizeof(prox_rte_vlan_hdr) < len)) {
+               vlan_hdr = (prox_rte_vlan_hdr *)((uint8_t *)pkt + l2_len);
+               l2_len +=4;
+               ether_type = vlan_hdr->eth_proto;
+               *vlan = rte_be_to_cpu_16(vlan_hdr->vlan_tci & 0xFF0F);  // Store VLAN, or CVLAN if QinQ
+       }
+}
+
+static inline struct ipv6_addr *find_ip6(prox_rte_ether_hdr *pkt, uint16_t len, struct ipv6_addr *ip_dst, uint16_t *vlan)
 {
        prox_rte_vlan_hdr *vlan_hdr;
        prox_rte_ipv6_hdr *ip;
        uint16_t ether_type = pkt->ether_type;
        uint16_t l2_len = sizeof(prox_rte_ether_hdr);
 
+       *vlan = 0;
        // Unstack VLAN tags
        while (((ether_type == ETYPE_8021ad) || (ether_type == ETYPE_VLAN)) && (l2_len + sizeof(prox_rte_vlan_hdr) < len)) {
                vlan_hdr = (prox_rte_vlan_hdr *)((uint8_t *)pkt + l2_len);
                l2_len +=4;
                ether_type = vlan_hdr->eth_proto;
+               *vlan = rte_be_to_cpu_16(vlan_hdr->vlan_tci & 0xFF0F);  // Store VLAN, or CVLAN if QinQ
        }
 
        switch (ether_type) {
@@ -121,20 +142,38 @@ static inline struct ipv6_addr *find_ip6(prox_rte_ether_hdr *pkt, uint16_t len,
        return NULL;
 }
 
-static void send_unsollicited_neighbour_advertisement(struct task_base *tbase, struct task_args *targ)
+void send_unsollicited_neighbour_advertisement(struct task_base *tbase)
 {
        int ret;
        uint8_t out = 0, port_id = tbase->l3.reachable_port_id;
-       struct rte_mbuf *mbuf;
-
-       ret = rte_mempool_get(tbase->l3.arp_nd_pool, (void **)&mbuf);
-       if (likely(ret == 0)) {
-               mbuf->port = port_id;
-               build_neighbour_advertisement(tbase->l3.tmaster, mbuf, &prox_port_cfg[port_id].eth_addr, &targ->local_ipv6, PROX_UNSOLLICITED);
-               tbase->aux->tx_ctrlplane_pkt(tbase, &mbuf, 1, &out);
-               TASK_STATS_ADD_TX_NON_DP(&tbase->aux->stats, 1);
-       } else {
-               plog_err("Failed to get a mbuf from arp/ndp mempool\n");
+       struct rte_mbuf *mbuf = NULL;
+
+       if (memcmp(&tbase->l3.local_ipv6, &null_addr, 16) != 0) {
+               ret = rte_mempool_get(tbase->l3.arp_nd_pool, (void **)&mbuf);
+               if (likely(ret == 0)) {
+                       mbuf->port = port_id;
+                       build_neighbour_advertisement(tbase->l3.tmaster, mbuf, &prox_port_cfg[port_id].eth_addr, &tbase->l3.local_ipv6, PROX_UNSOLLICITED, prox_port_cfg[port_id].vlan_tag);
+                       tbase->aux->tx_ctrlplane_pkt(tbase, &mbuf, 1, &out);
+                       TASK_STATS_ADD_TX_NON_DP(&tbase->aux->stats, 1);
+               } else {
+                       plog_err("Failed to get a mbuf from arp/ndp mempool\n");
+                       return;
+               }
+       }
+       if (memcmp(&tbase->l3.global_ipv6, &null_addr, 16) != 0) {
+               ret = rte_mempool_get(tbase->l3.arp_nd_pool, (void **)&mbuf);
+               if (likely(ret == 0)) {
+                       mbuf->port = port_id;
+                       build_neighbour_advertisement(tbase->l3.tmaster, mbuf, &prox_port_cfg[port_id].eth_addr, &tbase->l3.global_ipv6, PROX_UNSOLLICITED, prox_port_cfg[port_id].vlan_tag);
+                       tbase->aux->tx_ctrlplane_pkt(tbase, &mbuf, 1, &out);
+                       TASK_STATS_ADD_TX_NON_DP(&tbase->aux->stats, 1);
+               } else {
+                       plog_err("Failed to get a mbuf from arp/ndp mempool\n");
+                       return;
+               }
+       }
+       if (mbuf == NULL) {
+               plog_err("No neighbor advertisement sent as no local or global ipv6\n");
        }
 }
 
@@ -147,7 +186,7 @@ static void send_router_sollicitation(struct task_base *tbase, struct task_args
        ret = rte_mempool_get(tbase->l3.arp_nd_pool, (void **)&mbuf);
        if (likely(ret == 0)) {
                mbuf->port = port_id;
-               build_router_sollicitation(mbuf, &prox_port_cfg[port_id].eth_addr, &targ->local_ipv6);
+               build_router_sollicitation(mbuf, &prox_port_cfg[port_id].eth_addr, &targ->local_ipv6, prox_port_cfg[port_id].vlan_tag);
                tbase->aux->tx_ctrlplane_pkt(tbase, &mbuf, 1, &out);
                TASK_STATS_ADD_TX_NON_DP(&tbase->aux->stats, 1);
        } else {
@@ -199,7 +238,7 @@ static inline int update_mac_and_send_mbuf(struct arp_table *entry, prox_rte_eth
        return DROP_MBUF;
 }
 
-int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_dst, uint64_t **time, uint64_t tsc)
+int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_dst, uint16_t *vlan, uint64_t **time, uint64_t tsc)
 {
        const uint64_t hz = rte_get_tsc_hz();
        struct ether_hdr_arp *packet = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
@@ -215,7 +254,7 @@ int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_d
                // If a gw (gateway_ipv4) is also specified, it is used as default gw only i.e. lowest priority (shortest prefix)
                // This is implemented automatically through lpm
                uint16_t len = rte_pktmbuf_pkt_len(mbuf);
-               if (find_ip(packet, len, ip_dst) != 0) {
+               if (find_ip(packet, len, ip_dst, vlan) != 0) {
                        // Unable to find IP address => non IP packet => send it as it
                        return SEND_MBUF;
                }
@@ -223,7 +262,7 @@ int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_d
                        // Prevent printing too many messages
                        n_no_route++;
                        if (tsc > last_tsc + rte_get_tsc_hz()) {
-                               plog_err("No route to IP "IPv4_BYTES_FMT" (%ld times)\n", IP4(*ip_dst), n_no_route);
+                               plogx_err("No route to IP "IPv4_BYTES_FMT" (%ld times)\n", IP4(*ip_dst), n_no_route);
                                last_tsc = tsc;
                                n_no_route = 0;
                        }
@@ -249,7 +288,10 @@ int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_d
        }
        // No Routing table specified: only a local ip and maybe a gateway
        // Old default behavior: if a gw is specified, ALL packets go to this gateway (even those we could send w/o the gw
+
+       uint16_t len = rte_pktmbuf_pkt_len(mbuf);
        if (l3->gw.ip) {
+               find_vlan(packet, len, vlan);
                if (likely((l3->flags & FLAG_DST_MAC_KNOWN) && (tsc < l3->gw.arp_ndp_retransmit_timeout) && (tsc < l3->gw.reachable_timeout))) {
                        memcpy(mac, &l3->gw.mac, sizeof(prox_rte_ether_addr));
                        return SEND_MBUF;
@@ -274,8 +316,7 @@ int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_d
                }
        }
 
-       uint16_t len = rte_pktmbuf_pkt_len(mbuf);
-       if (find_ip(packet, len, ip_dst) != 0) {
+       if (find_ip(packet, len, ip_dst, vlan) != 0) {
                // Unable to find IP address => non IP packet => send it as it
                return SEND_MBUF;
        }
@@ -324,7 +365,7 @@ int write_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t *ip_d
        return DROP_MBUF;
 }
 
-int write_ip6_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, struct ipv6_addr *ip_dst)
+int write_ip6_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, struct ipv6_addr *ip_dst, uint16_t *vlan)
 {
        const uint64_t hz = rte_get_tsc_hz();
        prox_rte_ether_hdr *packet = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
@@ -335,23 +376,32 @@ int write_ip6_dst_mac(struct task_base *tbase, struct rte_mbuf *mbuf, struct ipv
        uint16_t len = rte_pktmbuf_pkt_len(mbuf);
 
        struct ipv6_addr *pkt_src_ip6;
-       if ((pkt_src_ip6 = find_ip6(packet, len, ip_dst)) == NULL) {
+       if ((pkt_src_ip6 = find_ip6(packet, len, ip_dst, vlan)) == NULL) {
                // Unable to find IP address => non IP packet => send it as it
                return SEND_MBUF;
        }
        struct l3_base *l3 = &(tbase->l3);
+
+       // Configure source IP
        if (memcmp(&l3->local_ipv6, ip_dst, 8) == 0) {
                // Same prefix as local -> use local
                used_ip_src = &l3->local_ipv6;
-       } else if (memcmp(&l3->global_ipv6 , &null_addr, 16) != 0) {
+       } else if (memcmp(&l3->global_ipv6 , ip_dst, 8) == 0) {
+               // Same prefix as global -> use global
+               used_ip_src = &l3->global_ipv6;
+       } else if (memcmp(&l3->gw.ip6 , &null_addr, sizeof(struct ipv6_addr)) != 0) {
+               used_ip_src = &l3->global_ipv6;
+               memcpy(ip_dst, &l3->gw.ip6, sizeof(struct ipv6_addr));
+       } else if (memcmp(&l3->global_ipv6 , &null_addr, sizeof(struct ipv6_addr)) != 0) {
                // Global IP is defined -> use it
                used_ip_src = &l3->global_ipv6;
        } else {
                plog_info("Error as trying to send a packet to "IPv6_BYTES_FMT" using "IPv6_BYTES_FMT" (local)\n", IPv6_BYTES(ip_dst->bytes), IPv6_BYTES(l3->local_ipv6.bytes));
                return DROP_MBUF;
        }
-
        memcpy(pkt_src_ip6, used_ip_src, sizeof(struct ipv6_addr));
+
+       // Configure dst mac
        if (likely(l3->n_pkts < 4)) {
                for (unsigned int idx = 0; idx < l3->n_pkts; idx++) {
                        if (memcmp(ip_dst, &l3->optimized_arp_table[idx].ip6, sizeof(struct ipv6_addr)) == 0) {
@@ -462,25 +512,26 @@ void task_init_l3(struct task_base *tbase, struct task_args *targ)
                .hash_func_init_val = 0,
        };
        if (targ->flags & TASK_ARG_L3) {
-               plog_info("\tInitializing L3 (IPv4)\n");
+               plog_info("\t\tInitializing L3 (IPv4)\n");
                tbase->l3.ip_hash = rte_hash_create(&hash_params);
                PROX_PANIC(tbase->l3.ip_hash == NULL, "Failed to set up ip hash table\n");
                hash_name[0]++;
        }
 
        if (targ->flags & TASK_ARG_NDP) {
-               plog_info("\tInitializing NDP (IPv6)\n");
+               plog_info("\t\tInitializing NDP (IPv6)\n");
                hash_params.key_len = sizeof(struct ipv6_addr);
                tbase->l3.ip6_hash = rte_hash_create(&hash_params);
                PROX_PANIC(tbase->l3.ip6_hash == NULL, "Failed to set up ip hash table\n");
        }
        tbase->l3.arp_table = (struct arp_table *)prox_zmalloc(n_entries * sizeof(struct arp_table), socket_id);
        PROX_PANIC(tbase->l3.arp_table == NULL, "Failed to allocate memory for %u entries in arp/ndp table\n", n_entries);
-       plog_info("\tarp/ndp table, with %d entries of size %ld\n", n_entries, sizeof(struct l3_base));
+       plog_info("\t\tarp/ndp table, with %d entries of size %ld\n", n_entries, sizeof(struct l3_base));
 
        targ->lconf->ctrl_func_p[targ->task] = handle_ctrl_plane_pkts;
        targ->lconf->ctrl_timeout = freq_to_tsc(targ->ctrl_freq);
        tbase->l3.gw.ip = rte_cpu_to_be_32(targ->gateway_ipv4);
+       memcpy(&tbase->l3.gw.ip6, &targ->gateway_ipv6, sizeof(struct ipv6_addr));
        tbase->flags |= TASK_L3;
        tbase->l3.core_id = targ->lconf->id;
        tbase->l3.task_id = targ->id;
@@ -507,6 +558,17 @@ void task_start_l3(struct task_base *tbase, struct task_args *targ)
         if (port && (tbase->l3.arp_nd_pool == NULL)) {
                static char name[] = "arp0_pool";
                 tbase->l3.reachable_port_id = port - prox_port_cfg;
+               if ((targ->local_ipv4 && port->ip) && (targ->local_ipv4 != port->ip)) {
+                       PROX_PANIC(1, "local_ipv4 in core section ("IPv4_BYTES_FMT") differs from port section ("IPv4_BYTES_FMT")\n", IP4(rte_be_to_cpu_32(targ->local_ipv4)), IP4(rte_be_to_cpu_32(port->ip)));
+               }
+               if ((targ->local_ipv4 && port->ip) && (targ->local_prefix != port->prefix)) {
+                       PROX_PANIC(1, "local_ipv4 prefix in core section (%d) differs from port section (%d)\n", targ->local_prefix, port->prefix);
+               }
+               if (!targ->local_ipv4) {
+                       targ->local_ipv4 = port->ip;
+                       targ->local_prefix = port->prefix;
+                       plog_info("Setting core local_ipv4 from port %d local_ipv4 to "IPv4_BYTES_FMT"\n", tbase->l3.reachable_port_id, IP4(rte_be_to_cpu_32(port->ip)));
+               }
                if (targ->local_ipv4) {
                        tbase->l3.local_ipv4 = rte_be_to_cpu_32(targ->local_ipv4);
                        register_ip_to_ctrl_plane(tbase->l3.tmaster, tbase->l3.local_ipv4, tbase->l3.reachable_port_id, targ->lconf->id, targ->id);
@@ -515,7 +577,7 @@ void task_start_l3(struct task_base *tbase, struct task_args *targ)
                        struct lpm4 *lpm;
                        int ret;
 
-                       PROX_PANIC(tbase->l3.local_ipv4 == 0, "missing local_ipv4 will route table is specified in L3 mode\n");
+                       PROX_PANIC(tbase->l3.local_ipv4 == 0, "missing local_ipv4 while route table is specified in L3 mode\n");
 
                        // LPM might be modified runtime => do not share with other cores
                        ret = lua_to_lpm4(prox_lua(), GLOBAL, targ->route_table, socket_id, &lpm);
@@ -554,7 +616,7 @@ void task_start_l3(struct task_base *tbase, struct task_args *targ)
 
                // Create IPv6 addr if none were configured
                if (targ->flags & TASK_ARG_NDP) {
-                       if (!memcmp(&targ->local_ipv6, &null_addr, 16)) {
+                       if (!memcmp(&targ->local_ipv6, &null_addr, sizeof(struct ipv6_addr))) {
                                set_link_local(&targ->local_ipv6);
                                set_EUI(&targ->local_ipv6, &port->eth_addr);
                        }
@@ -592,7 +654,7 @@ void task_start_l3(struct task_base *tbase, struct task_args *targ)
                }
                if ((targ->flags & TASK_ARG_NDP) && (targ->flags & TASK_ARG_SEND_NA_AT_STARTUP)) {
                        plog_info("Sending unsollicited Neighbour Advertisement\n");
-                       send_unsollicited_neighbour_advertisement(tbase, targ);
+                       send_unsollicited_neighbour_advertisement(tbase);
 
                }
        }