+static inline void build_icmp_reply_message(struct task_base *tbase, struct rte_mbuf *mbuf)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ struct ip_port key;
+ key.port = mbuf->port;
+ prox_rte_ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ prox_rte_ether_addr dst_mac;
+ prox_rte_ether_addr_copy(&hdr->s_addr, &dst_mac);
+ prox_rte_ether_addr_copy(&hdr->d_addr, &hdr->s_addr);
+ prox_rte_ether_addr_copy(&dst_mac, &hdr->d_addr);
+ prox_rte_ipv4_hdr *ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
+ key.ip = ip_hdr->dst_addr;
+ ip_hdr->dst_addr = ip_hdr->src_addr;
+ ip_hdr->src_addr = key.ip;
+ prox_rte_icmp_hdr *picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
+ picmp->icmp_type = PROX_RTE_IP_ICMP_ECHO_REPLY;
+
+ int ret = rte_hash_lookup(task->internal_ip_hash, (const void *)&key);
+ if (unlikely(ret < 0)) {
+ // entry not found for this IP.
+ plogx_dbg("Master ignoring ICMP received on un-registered IP "IPv4_BYTES_FMT" on port %d\n", IP4(key.ip), mbuf->port);
+ tx_drop(mbuf);
+ } else {
+ struct rte_ring *ring = task->internal_ip_table[ret].ring;
+ mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM);
+ tx_ring(tbase, ring, SEND_ICMP_FROM_MASTER, mbuf);
+ }
+}
+
+static inline void handle_icmp(struct task_base *tbase, struct rte_mbuf *mbuf)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ uint8_t port_id = get_port(mbuf);
+ struct port_table *port = &task->internal_port_table[port_id];
+ prox_rte_ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ if (hdr->ether_type != ETYPE_IPv4) {
+ tx_drop(mbuf);
+ return;
+ }
+ prox_rte_ipv4_hdr *ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
+ if (ip_hdr->next_proto_id != IPPROTO_ICMP) {
+ tx_drop(mbuf);
+ return;
+ }
+ if (ip_hdr->dst_addr != port->ip) {
+ tx_drop(mbuf);
+ return;
+ }
+
+ prox_rte_icmp_hdr *picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
+ uint8_t type = picmp->icmp_type;
+ if (type == PROX_RTE_IP_ICMP_ECHO_REQUEST) {
+ port->n_echo_req++;
+ if (rte_rdtsc() - port->last_echo_req_rcvd_tsc > rte_get_tsc_hz()) {
+ plog_dbg("Received %u Echo Request on IP "IPv4_BYTES_FMT" (last received from IP "IPv4_BYTES_FMT")\n", port->n_echo_req, IPv4_BYTES(((uint8_t*)&ip_hdr->dst_addr)), IPv4_BYTES(((uint8_t*)&ip_hdr->src_addr)));
+ port->n_echo_req = 0;
+ port->last_echo_req_rcvd_tsc = rte_rdtsc();
+ }
+ return build_icmp_reply_message(tbase, mbuf);
+ } else if (type == PROX_RTE_IP_ICMP_ECHO_REPLY) {
+ port->n_echo_rep++;
+ if (rte_rdtsc() - port->last_echo_rep_rcvd_tsc > rte_get_tsc_hz()) {
+ plog_info("Received %u Echo Reply on IP "IPv4_BYTES_FMT" (last received from IP "IPv4_BYTES_FMT")\n", port->n_echo_rep, IPv4_BYTES(((uint8_t*)&ip_hdr->dst_addr)), IPv4_BYTES(((uint8_t*)&ip_hdr->src_addr)));
+ port->n_echo_rep = 0;
+ port->last_echo_rep_rcvd_tsc = rte_rdtsc();
+ }
+ }
+ tx_drop(mbuf);
+ return;
+}
+
+static inline void handle_unknown_ip6(struct task_base *tbase, struct rte_mbuf *mbuf)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ struct ether_hdr_arp *hdr_arp = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
+ uint8_t port_id = get_port(mbuf);
+ struct ipv6_addr *ip_dst = ctrl_ring_get_ipv6_addr(mbuf);
+ uint16_t vlan = ctrl_ring_get_vlan(mbuf);
+ int ret1, ret2, i;
+
+ plogx_dbg("\tMaster trying to find MAC of external IP "IPv6_BYTES_FMT" for port %d\n", IPv6_BYTES(ip_dst->bytes), port_id);
+ if (unlikely(port_id >= PROX_MAX_PORTS)) {
+ plogx_dbg("Port %d not found", port_id);
+ tx_drop(mbuf);
+ return;
+ }
+ struct ipv6_addr *local_ip_src = &task->internal_port_table[port_id].local_ipv6_addr;
+ struct ipv6_addr *global_ip_src = &task->internal_port_table[port_id].global_ipv6_addr;
+ struct ipv6_addr *ip_src;
+ if (memcmp(local_ip_src, ip_dst, prox_port_cfg[port_id].v6_mask_length) == 0)
+ ip_src = local_ip_src;
+ else if (memcmp(global_ip_src, &null_addr, 16))
+ ip_src = global_ip_src;
+ else {
+ plogx_dbg("Unable to find a src ip for dst ip "IPv6_BYTES_FMT"\n", IPv6_BYTES(ip_dst->bytes));
+ tx_drop(mbuf);
+ return;
+ }
+ struct rte_ring *ring = task->ctrl_tx_rings[get_core(mbuf) * MAX_TASKS_PER_CORE + get_task(mbuf)];
+
+ if (ring == NULL) {
+ plogx_dbg("Port %d not registered", port_id);
+ tx_drop(mbuf);
+ return;
+ }
+
+ ret2 = rte_hash_add_key(task->external_ip6_hash, (const void *)ip_dst);
+ if (unlikely(ret2 < 0)) {
+ plogx_dbg("Unable to add IP "IPv6_BYTES_FMT" in external_ip6_hash\n", IPv6_BYTES(ip_dst->bytes));
+ tx_drop(mbuf);
+ return;
+ }
+
+ // If multiple tasks requesting the same info, we will need to send a reply to all of them
+ // However if one task sends multiple requests to the same IP (e.g. because it is not answering)
+ // then we should not send multiple replies to the same task
+ if (task->external_ip6_table[ret2].nb_requests >= PROX_MAX_ARP_REQUESTS) {
+ // This can only happen if really many tasks requests the same IP
+ plogx_dbg("Unable to add request for IP "IPv6_BYTES_FMT" in external_ip6_table\n", IPv6_BYTES(ip_dst->bytes));
+ tx_drop(mbuf);
+ return;
+ }
+ for (i = 0; i < task->external_ip6_table[ret2].nb_requests; i++) {
+ if (task->external_ip6_table[ret2].rings[i] == ring)
+ break;
+ }
+ if (i >= task->external_ip6_table[ret2].nb_requests) {
+ // If this is a new request i.e. a new task requesting a new IP
+ task->external_ip6_table[ret2].rings[task->external_ip6_table[ret2].nb_requests] = ring;
+ task->external_ip6_table[ret2].nb_requests++;
+ // Only needed for first request - but avoid test and copy the same 6 bytes
+ // In most cases we will only have one request per IP.
+ //memcpy(&task->external_ip6_table[ret2].mac, &task->internal_port_table[port_id].mac, sizeof(prox_rte_ether_addr));
+ }
+
+ // As timers are not handled by master, we might send an NS request even if one was just sent
+ // (and not yet answered) by another task
+ build_neighbour_sollicitation(mbuf, &task->internal_port_table[port_id].mac, ip_dst, ip_src, vlan);
+ tx_ring(tbase, ring, SEND_NDP_FROM_MASTER, mbuf);
+}
+
+static inline void handle_rs(struct task_base *tbase, struct rte_mbuf *mbuf, prox_rte_ipv6_hdr *ipv6_hdr, uint16_t vlan)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ int i, ret;
+ uint8_t port = get_port(mbuf);
+
+ if (task->internal_port_table[port].flags & IPV6_ROUTER) {
+ plogx_dbg("\tMaster handling Router Solicitation from ip "IPv6_BYTES_FMT" on port %d\n", IPv6_BYTES(ipv6_hdr->src_addr), port);
+ struct rte_ring *ring = task->internal_port_table[port].ring;
+ build_router_advertisement(mbuf, &prox_port_cfg[port].eth_addr, &task->internal_port_table[port].local_ipv6_addr, &task->internal_port_table[port].router_prefix, vlan);
+ tx_ring(tbase, ring, SEND_NDP_FROM_MASTER, mbuf);
+ return;
+ }
+}
+
+static inline void handle_ra(struct task_base *tbase, struct rte_mbuf *mbuf, prox_rte_ipv6_hdr *ipv6_hdr, uint16_t vlan)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ int i, ret, send = 0;
+ uint8_t port = get_port(mbuf);
+ struct rte_ring *ring = task->internal_port_table[port].ring;
+
+ plog_dbg("Master handling Router Advertisement from ip "IPv6_BYTES_FMT" on port %d - len = %d; payload_len = %d\n", IPv6_BYTES(ipv6_hdr->src_addr), port, rte_pktmbuf_pkt_len(mbuf), rte_be_to_cpu_16(ipv6_hdr->payload_len));
+ if (rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr) > rte_pktmbuf_pkt_len(mbuf)) {
+ plog_err("Unexpected length received: pkt_len = %d, ipv6 hdr length = %ld, ipv6 payload len = %d\n", rte_pktmbuf_pkt_len(mbuf), sizeof(prox_rte_ipv6_hdr), rte_be_to_cpu_16(ipv6_hdr->payload_len));
+ tx_drop(mbuf);
+ return;
+ }
+ if (ring == NULL) {
+ plog_info("TX side not initialized yet => dropping\n");
+ tx_drop(mbuf);
+ return;
+ }
+ int16_t option_len = rte_be_to_cpu_16(ipv6_hdr->payload_len) - sizeof(struct icmpv6_RA) + sizeof(struct icmpv6_option);
+ struct icmpv6_RA *router_advertisement = (struct icmpv6_RA *)(ipv6_hdr + 1);
+ struct icmpv6_option *option = (struct icmpv6_option *)&router_advertisement->options;
+ struct icmpv6_prefix_option *prefix_option;
+ while(option_len > 0) {
+ uint8_t type = option->type;
+ switch(type) {
+ case ICMPv6_source_link_layer_address:
+ plog_dbg("\tOption %d = Source Link Layer Address\n", type);
+ break;
+ case ICMPv6_prefix_information:
+ prefix_option = (struct icmpv6_prefix_option *)option;
+ plog_dbg("\tOption %d = Prefix Information = %s\n", type, IP6_Canonical(&prefix_option->prefix));
+ send = 1;
+ break;
+ case ICMPv6_mtu:
+ plog_dbg("\tOption %d = MTU\n", type);
+ break;
+ default:
+ plog_dbg("\tOption %d = Unknown Option\n", type);
+ break;
+ }
+ if ((option->length == 0) || (option->length *8 > option_len)) {
+ plog_err("Unexpected option length (%d) received in option %d: %d\n", option->length, option->type, option->length);
+ send = 0;
+ break;
+ }
+ option_len -=option->length * 8;
+ option = (struct icmpv6_option *)(((uint8_t *)option) + option->length * 8);
+ }
+ if (send) {
+ struct ipv6_addr global_ipv6;
+ memcpy(&global_ipv6, &prefix_option->prefix, sizeof(struct ipv6_addr));
+ set_EUI(&global_ipv6, &task->internal_port_table[port].mac);
+ tx_ring_ip6(tbase, ring, IPV6_INFO_FROM_MASTER, mbuf, &global_ipv6);
+ } else
+ tx_drop(mbuf);
+}
+
+static inline void handle_ns(struct task_base *tbase, struct rte_mbuf *mbuf, prox_rte_ipv6_hdr *ipv6_hdr, uint16_t vlan)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ struct icmpv6_NS *neighbour_sollicitation = (struct icmpv6_NS *)(ipv6_hdr + 1);
+ int i, ret;
+ uint8_t port = get_port(mbuf);
+ struct rte_ring *ring = task->internal_port_table[port].ring;
+
+ plog_dbg("Master handling Neighbour Sollicitation for ip "IPv6_BYTES_FMT" on port %d - len = %d; payload_len = %d\n", IPv6_BYTES(neighbour_sollicitation->target_address.bytes), port, rte_pktmbuf_pkt_len(mbuf), rte_be_to_cpu_16(ipv6_hdr->payload_len));
+ if (rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr) > rte_pktmbuf_pkt_len(mbuf)) {
+ plog_err("Unexpected length received: pkt_len = %d, ipv6 hdr length = %ld, ipv6 payload len = %d\n", rte_pktmbuf_pkt_len(mbuf), sizeof(prox_rte_ipv6_hdr), rte_be_to_cpu_16(ipv6_hdr->payload_len));
+ tx_drop(mbuf);
+ return;
+ }
+ int16_t option_len = rte_be_to_cpu_16(ipv6_hdr->payload_len) - sizeof(struct icmpv6_NS) + sizeof(struct icmpv6_option);
+ struct icmpv6_option *option = (struct icmpv6_option *)&neighbour_sollicitation->options;
+ while(option_len > 0) {
+ uint8_t type = option->type;
+ switch(type) {
+ case ICMPv6_source_link_layer_address:
+ plog_dbg("Option %d = Source Link Layer Address\n", type);
+ break;
+ default:
+ plog_dbg("Option %d = Unknown Option\n", type);
+ break;
+ }
+ if ((option->length == 0) || (option->length *8 > option_len)) {
+ plog_err("Unexpected option length (%d) received in option %d: %d\n", option->length, option->type, option->length);
+ tx_drop(mbuf);
+ return;
+ }
+ option_len -=option->length * 8;
+ option = (struct icmpv6_option *)(((uint8_t *)option) + option->length * 8);
+ }
+ struct ip6_port key;
+ memcpy(&key.ip6, &neighbour_sollicitation->target_address, sizeof(struct ipv6_addr));
+ key.port = port;
+
+ if (memcmp(&neighbour_sollicitation->target_address, &task->internal_port_table[port].local_ipv6_addr, 8) == 0) {
+ // Local IP
+ if (task->internal_port_table[port].flags & HANDLE_RANDOM_LOCAL_IP_FLAG) {
+ prox_rte_ether_addr mac;
+ plogx_dbg("\tMaster handling NS request for ip "IPv6_BYTES_FMT" on port %d which supports random ip\n", IPv6_BYTES(key.ip6.bytes), key.port);
+ struct rte_ring *ring = task->internal_port_table[port].ring;
+ create_mac_from_EUI(&key.ip6, &mac);
+ build_neighbour_advertisement(tbase, mbuf, &mac, &task->internal_port_table[port].local_ipv6_addr, PROX_SOLLICITED, vlan);
+ tx_ring(tbase, ring, SEND_NDP_FROM_MASTER, mbuf);
+ return;
+ }
+ } else {
+ if (task->internal_port_table[port].flags & HANDLE_RANDOM_GLOBAL_IP_FLAG) {
+ prox_rte_ether_addr mac;
+ plogx_dbg("\tMaster handling NS request for ip "IPv6_BYTES_FMT" on port %d which supports random ip\n", IPv6_BYTES(key.ip6.bytes), key.port);
+ struct rte_ring *ring = task->internal_port_table[port].ring;
+ create_mac_from_EUI(&key.ip6, &mac);
+ build_neighbour_advertisement(tbase, mbuf, &mac, &task->internal_port_table[port].global_ipv6_addr, PROX_SOLLICITED, vlan);
+ tx_ring(tbase, ring, SEND_NDP_FROM_MASTER, mbuf);
+ return;
+ }
+ }
+
+ ret = rte_hash_lookup(task->internal_ip6_hash, (const void *)&key);
+ if (unlikely(ret < 0)) {
+ // entry not found for this IP.
+ plogx_dbg("Master ignoring Neighbour Sollicitation received on un-registered IP "IPv6_BYTES_FMT" on port %d\n", IPv6_BYTES(key.ip6.bytes), port);
+ tx_drop(mbuf);
+ } else {
+ struct rte_ring *ring = task->internal_ip6_table[ret].ring;
+ if (ring == NULL) return;
+ build_neighbour_advertisement(tbase, mbuf, &task->internal_ip6_table[ret].mac, &key.ip6, PROX_SOLLICITED, vlan);
+ tx_ring(tbase, ring, SEND_NDP_FROM_MASTER, mbuf);
+ }
+}
+
+static inline void handle_na(struct task_base *tbase, struct rte_mbuf *mbuf, prox_rte_ipv6_hdr *ipv6_hdr, uint16_t vlan)