From 48f8c3d212644a33dd0abaaa1a0c71d4decaafdf Mon Sep 17 00:00:00 2001 From: Xavier Simonart Date: Mon, 24 Jun 2019 17:01:54 +0200 Subject: Add support for igmp and multicast Multicast can be enabled through configuration or through command line - Through configuration Add multicast=mcast_address (e.g. multicast=01:00:5e:01:02:03) in the port section - Through command line run enable multicast port_id mcast_address (e.g. enable multicast 1 01:00:5e:01:02:03) IGMP join message is sent unsollicited through command line: join igmp core_id task_id ip (e.g. join igmp 1 0 224.1.1.3) To enable swap answering IGMP Query (w/ IGMP Join) - Through configuration Add igmp ipv4=ip_address within the core/task section - Through command line join igmp core_id task_id ip (e.g. join igmp 1 0 224.1.1.3) (this will 1st initiate an unsollicated join, then answer any subsequent query) UDP/TCP packets received on a multicast address (224.0.0.0 => 239.255.255.255) are discarded To stop sending responses to IGMP query: leave igmp core_id task_id Change-Id: I3808ccabf3b38b5a1e10e1b044db63aa05bcd7b5 Signed-off-by: Xavier Simonart --- VNFs/DPPD-PROX/handle_swap.c | 204 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 162 insertions(+), 42 deletions(-) (limited to 'VNFs/DPPD-PROX/handle_swap.c') diff --git a/VNFs/DPPD-PROX/handle_swap.c b/VNFs/DPPD-PROX/handle_swap.c index 63c4dbde..39131013 100644 --- a/VNFs/DPPD-PROX/handle_swap.c +++ b/VNFs/DPPD-PROX/handle_swap.c @@ -21,20 +21,28 @@ #include "task_base.h" #include "lconf.h" #include "log.h" -#include "arp.h" -#include "handle_swap.h" #include "prox_port_cfg.h" #include "mpls.h" #include "qinq.h" #include "gre.h" #include "prefetch.h" +#include "igmp.h" +#include "prox_cksum.h" struct task_swap { struct task_base base; - uint8_t src_dst_mac[12]; + struct rte_mempool *igmp_pool; uint32_t runtime_flags; + uint32_t igmp_address; + uint8_t src_dst_mac[12]; + uint32_t local_ipv4; + int offload_crc; }; +#define NB_IGMP_MBUF 1024 +#define IGMP_MBUF_SIZE 2048 +#define NB_CACHE_IGMP_MBUF 256 + static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf) { struct ether_hdr *hdr; @@ -63,29 +71,46 @@ static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf) } } } -static inline int handle_arp_request(struct task_swap *task, struct ether_hdr_arp *hdr_arp, struct ether_addr *s_addr, uint32_t ip) + +static inline void build_mcast_mac(uint32_t ip, struct ether_addr *dst_mac) { - if ((hdr_arp->arp.data.tpa == ip) || (ip == 0)) { - build_arp_reply(hdr_arp, s_addr); - return 0; - } else if (task->runtime_flags & TASK_MULTIPLE_MAC) { - struct ether_addr tmp_s_addr; - create_mac(hdr_arp, &tmp_s_addr); - build_arp_reply(hdr_arp, &tmp_s_addr); - return 0; - } else { - plogx_dbg("Received ARP on unexpected IP %x, expecting %x\n", rte_be_to_cpu_32(hdr_arp->arp.data.tpa), rte_be_to_cpu_32(ip)); - return OUT_DISCARD; - } + // MAC address is 01:00:5e followed by 23 LSB of IP address + uint64_t mac = 0x0000005e0001L | ((ip & 0xFFFF7F00L) << 16); + memcpy(dst_mac, &mac, sizeof(struct ether_addr)); } -/* - * swap mode does not send arp requests, so does not expect arp replies - * Need to understand later whether we must send arp requests - */ -static inline int handle_arp_replies(struct task_swap *task, struct ether_hdr_arp *hdr_arp) +static inline void build_igmp_message(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t ip, uint8_t igmp_message) { - return OUT_DISCARD; + struct task_swap *task = (struct task_swap *)tbase; + struct ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, struct ether_hdr *); + struct ether_addr dst_mac; + build_mcast_mac(ip, &dst_mac); + + rte_pktmbuf_pkt_len(mbuf) = 46; + rte_pktmbuf_data_len(mbuf) = 46; + init_mbuf_seg(mbuf); + + ether_addr_copy(&dst_mac, &hdr->d_addr); + ether_addr_copy((struct ether_addr *)&task->src_dst_mac[6], &hdr->s_addr); + hdr->ether_type = ETYPE_IPv4; + + struct ipv4_hdr *ip_hdr = (struct ipv4_hdr *)(hdr + 1); + ip_hdr->version_ihl = 0x45; /**< version and header length */ + ip_hdr->type_of_service = 0; /**< type of service */ + ip_hdr->total_length = rte_cpu_to_be_16(32); /**< length of packet */ + ip_hdr->packet_id = 0; /**< packet ID */ + ip_hdr->fragment_offset = 0; /**< fragmentation offset */ + ip_hdr->time_to_live = 1; /**< time to live */ + ip_hdr->next_proto_id = IPPROTO_IGMP; /**< protocol ID */ + ip_hdr->hdr_checksum = 0; /**< header checksum */ + ip_hdr->src_addr = task->local_ipv4; /**< source address */ + ip_hdr->dst_addr = ip; /**< destination address */ + struct igmpv2_hdr *pigmp = (struct igmpv2_hdr *)(ip_hdr + 1); + pigmp->type = igmp_message; + pigmp->max_resp_time = 0; + pigmp->checksum = 0; + pigmp->group_address = ip; + prox_ip_udp_cksum(mbuf, ip_hdr, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc); } static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) @@ -95,6 +120,8 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui struct ether_addr mac; struct ipv4_hdr *ip_hdr; struct udp_hdr *udp_hdr; + struct gre_hdr *pgre; + struct ipv4_hdr *inner_ip_hdr; uint32_t ip; uint16_t port; uint8_t out[64] = {0}; @@ -102,8 +129,9 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui uint32_t mpls_len = 0; struct qinq_hdr *qinq; struct vlan_hdr *vlan; - struct ether_hdr_arp *hdr_arp; uint16_t j; + struct igmpv2_hdr *pigmp; + uint8_t type; for (j = 0; j < n_pkts; ++j) { PREFETCH0(mbufs[j]); @@ -112,12 +140,14 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void *)); } + // TODO 1: check packet is long enough for Ethernet + IP + UDP = 42 bytes for (uint16_t j = 0; j < n_pkts; ++j) { hdr = rte_pktmbuf_mtod(mbufs[j], struct ether_hdr *); switch (hdr->ether_type) { case ETYPE_MPLSU: mpls = (struct mpls_hdr *)(hdr + 1); while (!(mpls->bytes & 0x00010000)) { + // TODO: verify pcket length mpls++; mpls_len += sizeof(struct mpls_hdr); } @@ -173,30 +203,111 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui out[j] = OUT_DISCARD; continue; } - udp_hdr = (struct udp_hdr *)(ip_hdr + 1); + // TODO 2 : check packet is long enough for Ethernet + IP + UDP + extra header (VLAN, MPLS, ...) ip = ip_hdr->dst_addr; - ip_hdr->dst_addr = ip_hdr->src_addr; - ip_hdr->src_addr = ip; - if (ip_hdr->next_proto_id == IPPROTO_GRE) { - struct gre_hdr *pgre = (struct gre_hdr *)(ip_hdr + 1); - struct ipv4_hdr *inner_ip_hdr = ((struct ipv4_hdr *)(pgre + 1)); + + switch (ip_hdr->next_proto_id) { + case IPPROTO_GRE: + ip_hdr->dst_addr = ip_hdr->src_addr; + ip_hdr->src_addr = ip; + + pgre = (struct gre_hdr *)(ip_hdr + 1); + inner_ip_hdr = ((struct ipv4_hdr *)(pgre + 1)); ip = inner_ip_hdr->dst_addr; inner_ip_hdr->dst_addr = inner_ip_hdr->src_addr; inner_ip_hdr->src_addr = ip; + udp_hdr = (struct udp_hdr *)(inner_ip_hdr + 1); + // TODO 3.1 : verify proto is UPD or TCP port = udp_hdr->dst_port; udp_hdr->dst_port = udp_hdr->src_port; udp_hdr->src_port = port; - } else { + write_src_and_dst_mac(task, mbufs[j]); + break; + case IPPROTO_UDP: + case IPPROTO_TCP: + if (task->igmp_address && IS_IPV4_MCAST(rte_be_to_cpu_32(ip))) { + out[j] = OUT_DISCARD; + continue; + } + udp_hdr = (struct udp_hdr *)(ip_hdr + 1); + ip_hdr->dst_addr = ip_hdr->src_addr; + ip_hdr->src_addr = ip; + port = udp_hdr->dst_port; udp_hdr->dst_port = udp_hdr->src_port; udp_hdr->src_port = port; + write_src_and_dst_mac(task, mbufs[j]); + break; + case IPPROTO_IGMP: + pigmp = (struct igmpv2_hdr *)(ip_hdr + 1); + // TODO: check packet len + type = pigmp->type; + if (type == IGMP_MEMBERSHIP_QUERY) { + if (task->igmp_address) { + // We have an address registered + if ((task->igmp_address == pigmp->group_address) || (pigmp->group_address == 0)) { + // We get a request for the registered address, or to 0.0.0.0 + build_igmp_message(tbase, mbufs[j], task->igmp_address, IGMP_MEMBERSHIP_REPORT); // replace Membership query packet with a response + } else { + // Discard as either we are not registered or this is a query for a different group + out[j] = OUT_DISCARD; + continue; + } + } else { + // Discard as either we are not registered + out[j] = OUT_DISCARD; + continue; + } + } else { + // Do not forward other IGMP packets back + out[j] = OUT_DISCARD; + continue; + } + break; + default: + plog_warn("Unsupported IP protocol 0x%x\n", ip_hdr->next_proto_id); + out[j] = OUT_DISCARD; + continue; } - write_src_and_dst_mac(task, mbufs[j]); } return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); } +void igmp_join_group(struct task_base *tbase, uint32_t igmp_address) +{ + struct task_swap *task = (struct task_swap *)tbase; + struct rte_mbuf *igmp_mbuf; + uint8_t out[64] = {0}; + int ret; + + task->igmp_address = igmp_address; + ret = rte_mempool_get(task->igmp_pool, (void **)&igmp_mbuf); + if (ret != 0) { + plog_err("Unable to allocate igmp mbuf\n"); + return; + } + build_igmp_message(tbase, igmp_mbuf, task->igmp_address, IGMP_MEMBERSHIP_REPORT); + task->base.tx_pkt(&task->base, &igmp_mbuf, 1, out); +} + +void igmp_leave_group(struct task_base *tbase) +{ + struct task_swap *task = (struct task_swap *)tbase; + struct rte_mbuf *igmp_mbuf; + uint8_t out[64] = {0}; + int ret; + + task->igmp_address = 0; + ret = rte_mempool_get(task->igmp_pool, (void **)&igmp_mbuf); + if (ret != 0) { + plog_err("Unable to allocate igmp mbuf\n"); + return; + } + build_igmp_message(tbase, igmp_mbuf, task->igmp_address, IGMP_LEAVE_GROUP); + task->base.tx_pkt(&task->base, &igmp_mbuf, 1, out); +} + static void init_task_swap(struct task_base *tbase, struct task_args *targ) { struct task_swap *task = (struct task_swap *)tbase; @@ -240,27 +351,36 @@ static void init_task_swap(struct task_base *tbase, struct task_args *targ) } } task->runtime_flags = targ->flags; + task->igmp_address = rte_cpu_to_be_32(targ->igmp_address); + if (task->igmp_pool == NULL) { + static char name[] = "igmp0_pool"; + name[4]++; + struct rte_mempool *ret = rte_mempool_create(name, NB_IGMP_MBUF, IGMP_MBUF_SIZE, NB_CACHE_IGMP_MBUF, + sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, 0, + rte_socket_id(), 0); + PROX_PANIC(ret == NULL, "Failed to allocate IGMP memory pool on socket %u with %u elements\n", + rte_socket_id(), NB_IGMP_MBUF); + plog_info("\t\tMempool %p (%s) size = %u * %u cache %u, socket %d\n", ret, name, NB_IGMP_MBUF, + IGMP_MBUF_SIZE, NB_CACHE_IGMP_MBUF, rte_socket_id()); + task->igmp_pool = ret; + } + task->local_ipv4 = rte_cpu_to_be_32(targ->local_ipv4); + + struct prox_port_cfg *port = find_reachable_port(targ); + if (port) { + task->offload_crc = port->requested_tx_offload & (DEV_TX_OFFLOAD_IPV4_CKSUM | DEV_TX_OFFLOAD_UDP_CKSUM); + } } static struct task_init task_init_swap = { .mode_str = "swap", .init = init_task_swap, .handle = handle_swap_bulk, - .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, - .size = sizeof(struct task_swap), -}; - -static struct task_init task_init_swap_arp = { - .mode_str = "swap", - .sub_mode_str = "l3", - .init = init_task_swap, - .handle = handle_swap_bulk, - .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS, + .flag_features = 0, .size = sizeof(struct task_swap), }; __attribute__((constructor)) static void reg_task_swap(void) { reg_task(&task_init_swap); - reg_task(&task_init_swap_arp); } -- cgit 1.2.3-korg