summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/prox_ipv6.c
diff options
context:
space:
mode:
authorXavier Simonart <xavier.simonart@intel.com>2020-05-11 21:00:33 +0200
committerXavier Simonart <xavier.simonart@intel.com>2020-05-29 23:45:09 +0200
commitc871c361f9d69a93429ae385e7dbf21a46aa6857 (patch)
tree957ec8a53d2cd0a9b5676e163c22e8a3ae8f6f3e /VNFs/DPPD-PROX/prox_ipv6.c
parent7c4601f23c526b14a67674782b303663dfaa95af (diff)
Added initial support for NDP (IPv6)
Following messages are now handled by PROX - router_solicitation - neighbour_solicitation - router_advertisement - neighbour_advertisement The following parameters are supported (through the PROX config file) - sub mode=ndp This will enable handling of router and neighbour solicitation and advertisement. - local ipv6=xxxx:xxxx:xxxxx:xxxx:xxxx:xxxx:xxxx:xxxx This will configure the local IPv6 address of the port. This parameter is optional. If not specified, the local IPv6 will be calculated from the EUI. - global ipv6=xxxx:xxxx:xxxxx:xxxx:xxxx:xxxx:xxxx:xxxx This will configure the global IPv6 address of the port. This parameter is optional. If not specified, the global IPv6 will be calculated from the EUI and the router prefix received from the router. - ipv6 router=yes This will cause the core to behave as an IPv6 router i.e. it will generate Router Advertisement messages This is only useful in back to back cases, when no real IPv6 router is present in the setup. - router prefix=xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx The router prefix usedin the router advertisement The prefix will be used by the node to build an IPv6 global address in cases none were configured. "Unsollicited NA" parameter has been added within the core/task section. If set to yes (Unsollicited NA=yes), then an unsollicited neighbour Advertisement is sent at startup A same core/task cannot support both l3 and ndp mode. Those messages will be generated or handled when submode is set to "ndp": - neighbour sollicitation - neighbour advertisement - router sollicitation - router advertisement An example configuration is provided: config/ipv6.cfg in which port 0 / core 1 plays the role of the generator and port 1 / core 2 plays the role of the swap. Change-Id: Id0ab32d384448b4cf767fb4a1c486fc023f4f395 Signed-off-by: Xavier Simonart <xavier.simonart@intel.com>
Diffstat (limited to 'VNFs/DPPD-PROX/prox_ipv6.c')
-rw-r--r--VNFs/DPPD-PROX/prox_ipv6.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/prox_ipv6.c b/VNFs/DPPD-PROX/prox_ipv6.c
new file mode 100644
index 00000000..9425f4a0
--- /dev/null
+++ b/VNFs/DPPD-PROX/prox_ipv6.c
@@ -0,0 +1,302 @@
+/*
+// Copyright (c) 2020 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 "task_base.h"
+#include "handle_master.h"
+#include "prox_cfg.h"
+#include "prox_ipv6.h"
+
+struct ipv6_addr null_addr = {{0}};
+char ip6_str[40]; // 8 blocks of 2 bytes (4 char) + 1x ":" between blocks
+
+void set_mcast_mac_from_ipv6(prox_rte_ether_addr *mac, struct ipv6_addr *ipv6_addr)
+{
+ mac->addr_bytes[0] = 0x33;
+ mac->addr_bytes[1] = 0x33;
+ memcpy(((uint32_t *)&mac->addr_bytes[2]), (uint32_t *)(&ipv6_addr->bytes[12]), sizeof(uint32_t));
+}
+
+// Note that this function is not Mthread safe and would result in garbage if called simultaneously from multiple threads
+// This function is however only used for debugging, printing errors...
+char *IP6_Canonical(struct ipv6_addr *addr)
+{
+ uint8_t *a = (uint8_t *)addr;
+ char *ptr = ip6_str;
+ int field = -1, len = 0, stored_field = 0, stored_len = 0;
+
+ // Find longest run of consecutive 16-bit 0 fields
+ for (int i = 0; i < 8; i++) {
+ if (((int)a[i * 2] == 0) && ((int)a[i * 2 + 1] == 0)) {
+ len++;
+ if (field == -1)
+ field = i; // Store where the first 0 field started
+ } else {
+ if (len > stored_len) {
+ // the longest run of consecutive 16-bit 0 fields MUST be shortened
+ stored_len = len;
+ stored_field = field;
+ }
+ len = 0;
+ field = -1;
+ }
+ }
+ if (len > stored_len) {
+ // the longest run of consecutive 16-bit 0 fields MUST be shortened
+ stored_len = len;
+ stored_field = field;
+ }
+ if (stored_len <= 1) {
+ // The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
+ stored_len = 0;
+ stored_field = -1;
+ }
+ for (int i = 0; i < 8; i++) {
+ if (i == stored_field) {
+ sprintf(ptr, ":");
+ ptr++;
+ if (i == 0) {
+ sprintf(ptr, ":");
+ ptr++;
+ }
+ i +=stored_len - 1; // ++ done in for loop
+ continue;
+ }
+ if ((int)a[i * 2] & 0xF0) {
+ sprintf(ptr, "%02x%02x", (int)a[i * 2], (int)a[i * 2 + 1]);
+ ptr+=4;
+ } else if ((int)a[i * 2] & 0x0F) {
+ sprintf(ptr, "%x%02x", (int)a[i * 2] >> 4, (int)a[i * 2] + 1);
+ ptr+=3;
+ } else if ((int)a[i * 2 + 1] & 0xF0) {
+ sprintf(ptr, "%02x", (int)a[i * 2 + 1]);
+ ptr+=2;
+ } else {
+ sprintf(ptr, "%x", ((int)a[i * 2 + 1]) & 0xF);
+ ptr++;
+ }
+ if (i != 7) {
+ sprintf(ptr, ":");
+ ptr++;
+ }
+ }
+ return ip6_str;
+}
+
+void set_link_local(struct ipv6_addr *ipv6_addr)
+{
+ ipv6_addr->bytes[0] = 0xfe;
+ ipv6_addr->bytes[1] = 0x80;
+}
+
+// Create Extended Unique Identifier (RFC 2373)
+// Store it in LSB of IPv6 address
+void set_EUI(struct ipv6_addr *ipv6_addr, prox_rte_ether_addr *mac)
+{
+ memcpy(&ipv6_addr->bytes[8], mac, 3); // Copy first 3 bytes of MAC
+ ipv6_addr->bytes[8] = ipv6_addr->bytes[8] ^ 0x02; // Invert Universal/local bit
+ ipv6_addr->bytes[11] = 0xff; // Next 2 bytes are 0xfffe
+ ipv6_addr->bytes[12] = 0xfe;
+ memcpy(&ipv6_addr->bytes[13], &mac->addr_bytes[3], 3); // Copy last 3 bytes
+ // plog_info("mac = "MAC_BYTES_FMT", eui = "IPv6_BYTES_FMT"\n", MAC_BYTES(mac->addr_bytes), IPv6_BYTES(ipv6_addr->bytes));
+}
+
+void create_mac_from_EUI(struct ipv6_addr *ipv6_addr, prox_rte_ether_addr *mac)
+{
+ memcpy(mac, &ipv6_addr->bytes[8], 3);
+ mac->addr_bytes[0] = mac->addr_bytes[0] ^ 0x02;
+ memcpy(&mac->addr_bytes[3], &ipv6_addr->bytes[13], 3);
+}
+void build_router_advertisement(struct rte_mbuf *mbuf, prox_rte_ether_addr *s_addr, struct ipv6_addr *ipv6_s_addr, struct ipv6_addr *router_prefix)
+{
+ prox_rte_ether_hdr *peth = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ init_mbuf_seg(mbuf);
+ mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM); // Software calculates the checksum
+
+ memcpy(peth->d_addr.addr_bytes, &prox_cfg.all_nodes_mac_addr, sizeof(prox_rte_ether_addr));
+ memcpy(peth->s_addr.addr_bytes, s_addr, sizeof(prox_rte_ether_addr));
+ peth->ether_type = ETYPE_IPv6;
+
+ prox_rte_ipv6_hdr *ipv6_hdr = (prox_rte_ipv6_hdr *)(peth + 1);
+ ipv6_hdr->vtc_flow = 0x00000060;
+ ipv6_hdr->payload_len = rte_cpu_to_be_16(sizeof(struct icmpv6_RA) + sizeof(struct icmpv6_prefix_option));
+ ipv6_hdr->proto = ICMPv6;
+ ipv6_hdr->hop_limits = 255;
+ memcpy(ipv6_hdr->src_addr, ipv6_s_addr, sizeof(struct ipv6_addr)); // 0 = "Unspecified address" if unknown
+ memcpy(ipv6_hdr->dst_addr, &prox_cfg.all_nodes_ipv6_mcast_addr, sizeof(struct ipv6_addr));
+
+ struct icmpv6_RA *router_advertisement = (struct icmpv6_RA *)(ipv6_hdr + 1);
+ router_advertisement->type = ICMPv6_RA;
+ router_advertisement->code = 0;
+ router_advertisement->hop_limit = 255;
+ router_advertisement->bits = 0; // M and O bits set to 0 => no dhcpv6
+ router_advertisement->router_lifespan = rte_cpu_to_be_16(9000); // 9000 sec
+ router_advertisement->reachable_timeout = rte_cpu_to_be_32(30000); // 1 sec
+ router_advertisement->retrans_timeout = rte_cpu_to_be_32(1000); // 30 sec
+
+ struct icmpv6_option *option = &router_advertisement->options;
+ option->type = ICMPv6_source_link_layer_address;
+ option->length = 1; // 8 bytes
+ memcpy(&option->data, s_addr, sizeof(prox_rte_ether_addr));
+
+ struct icmpv6_prefix_option *prefix_option = (struct icmpv6_prefix_option *)(option + 1);
+ prefix_option->type = ICMPv6_prefix_information;
+ prefix_option->length = 4; // 32 bytes
+ prefix_option->prefix_length = 64; // 64 bits in prefix
+ prefix_option->flag = 0xc0; // on-link flag & autonamous address-configuration flag are set
+ prefix_option->valid_lifetime = rte_cpu_to_be_32(86400); // 1 day
+ prefix_option->preferred_lifetime = rte_cpu_to_be_32(43200); // 12 hours
+ prefix_option->reserved = 0;
+ memcpy(&prefix_option->prefix, router_prefix, sizeof(struct ipv6_addr));
+ // Could Add MTU Option
+ router_advertisement->checksum = 0;
+ router_advertisement->checksum = rte_ipv6_udptcp_cksum(ipv6_hdr, router_advertisement);
+
+ uint16_t pktlen = rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr);
+ rte_pktmbuf_pkt_len(mbuf) = pktlen;
+ rte_pktmbuf_data_len(mbuf) = pktlen;
+}
+
+void build_router_sollicitation(struct rte_mbuf *mbuf, prox_rte_ether_addr *s_addr, struct ipv6_addr *ipv6_s_addr)
+{
+ prox_rte_ether_hdr *peth = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+
+ init_mbuf_seg(mbuf);
+ mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM); // Software calculates the checksum
+
+ memcpy(peth->d_addr.addr_bytes, &prox_cfg.all_routers_mac_addr, sizeof(prox_rte_ether_addr));
+ memcpy(peth->s_addr.addr_bytes, s_addr, sizeof(prox_rte_ether_addr));
+ peth->ether_type = ETYPE_IPv6;
+
+ prox_rte_ipv6_hdr *ipv6_hdr = (prox_rte_ipv6_hdr *)(peth + 1);
+ ipv6_hdr->vtc_flow = 0x00000060;
+ ipv6_hdr->payload_len = rte_cpu_to_be_16(sizeof(struct icmpv6_RS));
+ ipv6_hdr->proto = ICMPv6;
+ ipv6_hdr->hop_limits = 255;
+ memcpy(ipv6_hdr->src_addr, ipv6_s_addr, sizeof(struct ipv6_addr)); // 0 = "Unspecified address" if unknown
+ memcpy(ipv6_hdr->dst_addr, &prox_cfg.all_routers_ipv6_mcast_addr, sizeof(struct ipv6_addr));
+
+ struct icmpv6_RS *router_sollicitation = (struct icmpv6_RS *)(ipv6_hdr + 1);
+ router_sollicitation->type = ICMPv6_RS;
+ router_sollicitation->code = 0;
+ router_sollicitation->options.type = ICMPv6_source_link_layer_address;
+ router_sollicitation->options.length = 1; // 8 bytes
+ memcpy(&router_sollicitation->options.data, s_addr, sizeof(prox_rte_ether_addr));
+
+ router_sollicitation->checksum = 0;
+ router_sollicitation->checksum = rte_ipv6_udptcp_cksum(ipv6_hdr, router_sollicitation);
+ uint16_t pktlen = rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr);
+ rte_pktmbuf_pkt_len(mbuf) = pktlen;
+ rte_pktmbuf_data_len(mbuf) = pktlen;
+}
+
+void build_neighbour_sollicitation(struct rte_mbuf *mbuf, prox_rte_ether_addr *s_addr, struct ipv6_addr *dst, struct ipv6_addr *src)
+{
+ prox_rte_ether_hdr *peth = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ prox_rte_ether_addr mac_dst;
+ set_mcast_mac_from_ipv6(&mac_dst, dst);
+
+ init_mbuf_seg(mbuf);
+ mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM); // Software calculates the checksum
+
+ memcpy(peth->d_addr.addr_bytes, &mac_dst, sizeof(prox_rte_ether_addr));
+ memcpy(peth->s_addr.addr_bytes, s_addr, sizeof(prox_rte_ether_addr));
+ peth->ether_type = ETYPE_IPv6;
+
+ prox_rte_ipv6_hdr *ipv6_hdr = (prox_rte_ipv6_hdr *)(peth + 1);
+ ipv6_hdr->vtc_flow = 0x00000060;
+ ipv6_hdr->payload_len = rte_cpu_to_be_16(sizeof(struct icmpv6_NS));
+ ipv6_hdr->proto = ICMPv6;
+ ipv6_hdr->hop_limits = 255;
+ memcpy(ipv6_hdr->src_addr, src, 16);
+ memcpy(ipv6_hdr->dst_addr, dst, 16);
+
+ struct icmpv6_NS *neighbour_sollicitation = (struct icmpv6_NS *)(ipv6_hdr + 1);
+ neighbour_sollicitation->type = ICMPv6_NS;
+ neighbour_sollicitation->code = 0;
+ neighbour_sollicitation->reserved = 0;
+ memcpy(&neighbour_sollicitation->target_address, dst, sizeof(struct ipv6_addr));
+ neighbour_sollicitation->options.type = ICMPv6_source_link_layer_address;
+ neighbour_sollicitation->options.length = 1; // 8 bytes
+ memcpy(&neighbour_sollicitation->options.data, s_addr, sizeof(prox_rte_ether_addr));
+ neighbour_sollicitation->checksum = 0;
+ neighbour_sollicitation->checksum = rte_ipv6_udptcp_cksum(ipv6_hdr, neighbour_sollicitation);
+
+ uint16_t pktlen = rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr);
+ rte_pktmbuf_pkt_len(mbuf) = pktlen;
+ rte_pktmbuf_data_len(mbuf) = pktlen;
+}
+
+void build_neighbour_advertisement(struct task_base *tbase, struct rte_mbuf *mbuf, prox_rte_ether_addr *target, struct ipv6_addr *src_ipv6_addr, int sollicited)
+{
+ struct task_master *task = (struct task_master *)tbase;
+ prox_rte_ether_hdr *peth = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+ prox_rte_ipv6_hdr *ipv6_hdr = (prox_rte_ipv6_hdr *)(peth + 1);
+
+ uint8_t port_id = get_port(mbuf);
+
+ init_mbuf_seg(mbuf);
+ mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM); // Software calculates the checksum
+
+ // If source mac is null, use all_nodes_mac_addr.
+ if ((!sollicited) || (memcmp(peth->s_addr.addr_bytes, &null_addr, sizeof(struct ipv6_addr)) == 0)) {
+ memcpy(peth->d_addr.addr_bytes, &prox_cfg.all_nodes_mac_addr, sizeof(prox_rte_ether_addr));
+ memcpy(ipv6_hdr->dst_addr, &prox_cfg.all_nodes_ipv6_mcast_addr, sizeof(struct ipv6_addr));
+ } else {
+ memcpy(peth->d_addr.addr_bytes, peth->s_addr.addr_bytes, sizeof(prox_rte_ether_addr));
+ memcpy(ipv6_hdr->dst_addr, ipv6_hdr->src_addr, sizeof(struct ipv6_addr));
+ }
+
+ memcpy(peth->s_addr.addr_bytes, &task->internal_port_table[port_id].mac, sizeof(prox_rte_ether_addr));
+ peth->ether_type = ETYPE_IPv6;
+
+ ipv6_hdr->vtc_flow = 0x00000060;
+ ipv6_hdr->payload_len = rte_cpu_to_be_16(sizeof(struct icmpv6_NA));
+ ipv6_hdr->proto = ICMPv6;
+ ipv6_hdr->hop_limits = 255;
+ memcpy(ipv6_hdr->src_addr, src_ipv6_addr, sizeof(struct ipv6_addr));
+
+ struct icmpv6_NA *neighbour_advertisement = (struct icmpv6_NA *)(ipv6_hdr + 1);
+ neighbour_advertisement->type = ICMPv6_NA;
+ neighbour_advertisement->code = 0;
+ neighbour_advertisement->reserved = 0;
+ if (task->internal_port_table[port_id].flags & IPV6_ROUTER)
+ neighbour_advertisement->bits = 0xC0; // R+S bit set
+ else
+ neighbour_advertisement->bits = 0x40; // S bit set
+ if (!sollicited) {
+ memcpy(&neighbour_advertisement->destination_address, src_ipv6_addr, sizeof(struct ipv6_addr));
+ neighbour_advertisement->bits &= 0xBF; // Clear S bit
+ neighbour_advertisement->bits |= 0x20; // Overide bit
+ }
+ // else neighbour_advertisement->destination_address is already set to neighbour_sollicitation->target_address
+
+ struct icmpv6_option *option = &neighbour_advertisement->options;
+ // Do not think this is necessary
+ // option->type = ICMPv6_source_link_layer_address;
+ // option->length = 1; // 8 bytes
+ // memcpy(&option->data, &task->internal_port_table[port_id].mac, sizeof(prox_rte_ether_addr));
+
+ // option = option + 1;
+ option->type = ICMPv6_target_link_layer_address;
+ option->length = 1; // 8 bytes
+ memcpy(&option->data, target, sizeof(prox_rte_ether_addr));
+
+ neighbour_advertisement->checksum = 0;
+ neighbour_advertisement->checksum = rte_ipv6_udptcp_cksum(ipv6_hdr, neighbour_advertisement);
+ uint16_t pktlen = rte_be_to_cpu_16(ipv6_hdr->payload_len) + sizeof(prox_rte_ipv6_hdr) + sizeof(prox_rte_ether_hdr);
+ rte_pktmbuf_pkt_len(mbuf) = pktlen;
+ rte_pktmbuf_data_len(mbuf) = pktlen;
+}