From patchwork Tue Aug 2 02:16:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zong Kai LI X-Patchwork-Id: 654476 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (archives.nicira.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 3s3KZN1ykxz9t2R for ; Tue, 2 Aug 2016 12:16:28 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=sK7t3A3K; dkim-atps=neutral Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id 7FBF2109DA; Mon, 1 Aug 2016 19:16:27 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id 8A50A109D8 for ; Mon, 1 Aug 2016 19:16:26 -0700 (PDT) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id 26B6C420062 for ; Mon, 1 Aug 2016 20:16:26 -0600 (MDT) X-ASG-Debug-ID: 1470104185-09eadd39d43f3200001-byXFYA Received: from mx1-pf2.cudamail.com ([192.168.24.2]) by bar5.cudamail.com with ESMTP id PadFRSuNDtHklsHD (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Mon, 01 Aug 2016 20:16:25 -0600 (MDT) X-Barracuda-Envelope-From: zealokii@gmail.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.2 Received: from unknown (HELO mail-it0-f66.google.com) (209.85.214.66) by mx1-pf2.cudamail.com with ESMTPS (AES128-SHA encrypted); 2 Aug 2016 02:16:24 -0000 Received-SPF: pass (mx1-pf2.cudamail.com: SPF record at _netblocks.google.com designates 209.85.214.66 as permitted sender) X-Barracuda-Apparent-Source-IP: 209.85.214.66 X-Barracuda-RBL-IP: 209.85.214.66 Received: by mail-it0-f66.google.com with SMTP id d65so13072898ith.0 for ; Mon, 01 Aug 2016 19:16:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=YXXFFxa3EExnnQcKtzKabdO3rpcW5Qv2h7MMHFWQimc=; b=sK7t3A3KgZLMVQIAqqchNctik3RpSMuKfMajvEV4S3chfEKVK+gPmrVSq5e3AB2ONT xjdRyVk/e2D5vUmqpwSa7w644zmmm9tbwppdHLl28mJyMhOCuUoKTWjpltnRsqHG+qgA Tx5Q7ubyD31XibT+ESxhA55CQXC2zm7gLee3sAODx1m8cj2cGNDleHJUWXUt33QwH22E PMqPxd7TB/fAughsgbc7rnymWlDHkXCCu0Pzv+EydZWn9MZGDc/MyYEyQEsVTvojApd2 hammLA2WMXfm0zY+NV4kXY+AlvXbPLJB2didMKQyfq5XGRHmX9r76+KU22sQKMRCZRkl X++g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=YXXFFxa3EExnnQcKtzKabdO3rpcW5Qv2h7MMHFWQimc=; b=kWhl24P1/x1ESe138M78fxj0m+a/LycJccbUJAg/UldEA3tz0Dp9J4lEUWUAiMIikF 1Q8LPqtaX7ETY1NiVI/9C1p2Q0A/cMwsRHRuLdI4LSEOIeHFz7nenhq74j+gCQkc4jMV FhDLfw3mNA45NsKIToVFJHWdJOpEodt7QyVfe2UBQUnKJo+7pLj3+FV2b1UO6CUUcZ88 drzZOFOYSFKWvI7iknrLcTygZ1aVwq4zTXQ3fih/pVsBoNPxEGkapg4wIWNiTyEcl01C ZikUAa1vL8OPVZBc9gBJueIsoXWNJggoSmJ+e/PyX+hF8/AY2YSVY/1vk0bc6aMGrnwC wLIw== X-Gm-Message-State: AEkoouslnQb7sFEBwA+Cst9nGmMXWseIMuatTY2Ty2pT5XlDLJ0CQbCGAKGbKcwspR9L5A== X-Received: by 10.36.43.88 with SMTP id h85mr17294173ita.57.1470104183492; Mon, 01 Aug 2016 19:16:23 -0700 (PDT) Received: from localhost.localdomain ([106.38.0.71]) by smtp.gmail.com with ESMTPSA id b3sm9136684itg.17.2016.08.01.19.16.18 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 01 Aug 2016 19:16:23 -0700 (PDT) X-CudaMail-Envelope-Sender: zealokii@gmail.com From: Zong Kai LI To: dev@openvswitch.org X-CudaMail-MID: CM-E2-731076805 X-CudaMail-DTE: 080116 X-CudaMail-Originating-IP: 209.85.214.66 Date: Tue, 2 Aug 2016 10:16:13 +0800 X-ASG-Orig-Subj: [##CM-E2-731076805##][ovs-dev] [PATCH RFC v2 1/3] ovn: add SLAAC support for IPv6 Message-Id: <1470104173-32060-1-git-send-email-zealokii@gmail.com> X-Mailer: git-send-email 1.9.1 X-GBUdb-Analysis: 0, 209.85.214.66, Ugly c=0.297854 p=-0.333333 Source Normal X-MessageSniffer-Rules: 0-0-0-32767-c X-Barracuda-Connect: UNKNOWN[192.168.24.2] X-Barracuda-Start-Time: 1470104185 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 0.60 X-Barracuda-Spam-Status: No, SCORE=0.60 using global scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC5_MJ1963, DKIM_SIGNED, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.31684 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.00 DKIM_SIGNED Domain Keys Identified Mail: message has a signature 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Subject: [ovs-dev] [PATCH RFC v2 1/3] ovn: add SLAAC support for IPv6 X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" This patch introduces methods to compose a Router Advertisement (RA) packet, introduces flags for RA, renames ovs_nd_opt to ovs_nd_lla_opt to specify it's Source/Target Link-layer Address option. Signed-off-by: Zong Kai LI Acked-by: Dustin Lundquist --- lib/flow.c | 26 ++++----- lib/odp-execute.c | 20 +++---- lib/packets.c | 168 +++++++++++++++++++++++++++++++++++++++++++++--------- lib/packets.h | 86 ++++++++++++++++++++++++---- 4 files changed, 239 insertions(+), 61 deletions(-) diff --git a/lib/flow.c b/lib/flow.c index ba4f8c7..250dc7d 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -400,8 +400,8 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp, while (*sizep >= 8) { /* The minimum size of an option is 8 bytes, which also is * the size of Ethernet link-layer options. */ - const struct ovs_nd_opt *nd_opt = *datap; - int opt_len = nd_opt->nd_opt_len * ND_OPT_LEN; + const struct ovs_nd_lla_opt *nd_opt = *datap; + int opt_len = nd_opt->length * ND_LLA_OPT_LEN; if (!opt_len || opt_len > *sizep) { return; @@ -410,17 +410,17 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp, /* Store the link layer address if the appropriate option is * provided. It is considered an error if the same link * layer option is specified twice. */ - if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR + if (nd_opt->type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) { if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) { - arp_buf[0] = nd_opt->nd_opt_mac; + arp_buf[0] = nd_opt->lla; } else { goto invalid; } - } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR + } else if (nd_opt->type == ND_OPT_TARGET_LINKADDR && opt_len == 8) { if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) { - arp_buf[1] = nd_opt->nd_opt_mac; + arp_buf[1] = nd_opt->lla; } else { goto invalid; } @@ -2273,7 +2273,7 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow) (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { struct in6_addr *nd_target; - struct ovs_nd_opt *nd_opt; + struct ovs_nd_lla_opt *nd_opt; l4_len += sizeof *nd_target; nd_target = dp_packet_put_zeros(p, sizeof *nd_target); @@ -2282,16 +2282,16 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow) if (!eth_addr_is_zero(flow->arp_sha)) { l4_len += 8; nd_opt = dp_packet_put_zeros(p, 8); - nd_opt->nd_opt_len = 1; - nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd_opt->nd_opt_mac = flow->arp_sha; + nd_opt->length = 1; + nd_opt->type = ND_OPT_SOURCE_LINKADDR; + nd_opt->lla = flow->arp_sha; } if (!eth_addr_is_zero(flow->arp_tha)) { l4_len += 8; nd_opt = dp_packet_put_zeros(p, 8); - nd_opt->nd_opt_len = 1; - nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - nd_opt->nd_opt_mac = flow->arp_tha; + nd_opt->length = 1; + nd_opt->type = ND_OPT_TARGET_LINKADDR; + nd_opt->lla = flow->arp_tha; } } } diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 5a43904..f791a33 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -185,7 +185,7 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key, const struct ovs_key_nd *mask) { const struct ovs_nd_msg *ns = dp_packet_l4(packet); - const struct ovs_nd_opt *nd_opt = dp_packet_get_nd_payload(packet); + const struct ovs_nd_lla_opt *nd_opt = dp_packet_get_nd_payload(packet); if (OVS_LIKELY(ns && nd_opt)) { int bytes_remain = dp_packet_l4_size(packet) - sizeof(*ns); @@ -193,25 +193,25 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key, struct eth_addr sll_buf = eth_addr_zero; struct eth_addr tll_buf = eth_addr_zero; - while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) { - if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR - && nd_opt->nd_opt_len == 1) { - sll_buf = nd_opt->nd_opt_mac; + while (bytes_remain >= ND_LLA_OPT_LEN && nd_opt->length != 0) { + if (nd_opt->type == ND_OPT_SOURCE_LINKADDR + && nd_opt->length == 1) { + sll_buf = nd_opt->lla; ether_addr_copy_masked(&sll_buf, key->nd_sll, mask->nd_sll); /* A packet can only contain one SLL or TLL option */ break; - } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR - && nd_opt->nd_opt_len == 1) { - tll_buf = nd_opt->nd_opt_mac; + } else if (nd_opt->type == ND_OPT_TARGET_LINKADDR + && nd_opt->length == 1) { + tll_buf = nd_opt->lla; ether_addr_copy_masked(&tll_buf, key->nd_tll, mask->nd_tll); /* A packet can only contain one SLL or TLL option */ break; } - nd_opt += nd_opt->nd_opt_len; - bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN; + nd_opt += nd_opt->length; + bytes_remain -= nd_opt->length * ND_LLA_OPT_LEN; } packet_set_nd(packet, diff --git a/lib/packets.c b/lib/packets.c index d1e2a70..53e1008 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -35,6 +35,7 @@ const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT; const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT; +const struct in6_addr in6addr_all_routers = IN6ADDR_ALL_ROUTERS_INIT; struct in6_addr flow_tnl_dst(const struct flow_tnl *tnl) @@ -1139,7 +1140,7 @@ packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4], const struct eth_addr sll, const struct eth_addr tll) { struct ovs_nd_msg *ns; - struct ovs_nd_opt *nd_opt; + struct ovs_nd_lla_opt *nd_opt; int bytes_remain = dp_packet_l4_size(packet); if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) { @@ -1156,33 +1157,33 @@ packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4], target, true); } - while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) { - if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR - && nd_opt->nd_opt_len == 1) { - if (!eth_addr_equals(nd_opt->nd_opt_mac, sll)) { + while (bytes_remain >= ND_LLA_OPT_LEN && nd_opt->length != 0) { + if (nd_opt->type == ND_OPT_SOURCE_LINKADDR + && nd_opt->length == 1) { + if (!eth_addr_equals(nd_opt->lla, sll)) { ovs_be16 *csum = &(ns->icmph.icmp6_cksum); - *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, sll); - nd_opt->nd_opt_mac = sll; + *csum = recalc_csum48(*csum, nd_opt->lla, sll); + nd_opt->lla = sll; } /* A packet can only contain one SLL or TLL option */ break; - } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR - && nd_opt->nd_opt_len == 1) { - if (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) { + } else if (nd_opt->type == ND_OPT_TARGET_LINKADDR + && nd_opt->length == 1) { + if (!eth_addr_equals(nd_opt->lla, tll)) { ovs_be16 *csum = &(ns->icmph.icmp6_cksum); - *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, tll); - nd_opt->nd_opt_mac = tll; + *csum = recalc_csum48(*csum, nd_opt->lla, tll); + nd_opt->lla = tll; } /* A packet can only contain one SLL or TLL option */ break; } - nd_opt += nd_opt->nd_opt_len; - bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN; + nd_opt += nd_opt->length; + bytes_remain -= nd_opt->length * ND_LLA_OPT_LEN; } } @@ -1353,7 +1354,7 @@ compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src, struct in6_addr sn_addr; struct eth_addr eth_dst; struct ovs_nd_msg *ns; - struct ovs_nd_opt *nd_opt; + struct ovs_nd_lla_opt *nd_opt; uint32_t icmp_csum; in6_addr_solicited_node(&sn_addr, ipv6_dst); @@ -1361,15 +1362,15 @@ compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src, eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN); ns = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, &sn_addr, - 0, 0, 255, ND_MSG_LEN + ND_OPT_LEN); + 0, 0, 255, ND_MSG_LEN + ND_LLA_OPT_LEN); ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT; ns->icmph.icmp6_code = 0; put_16aligned_be32(&ns->rso_flags, htonl(0)); nd_opt = &ns->options[0]; - nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd_opt->nd_opt_len = 1; + nd_opt->type = ND_OPT_SOURCE_LINKADDR; + nd_opt->length = 1; /* Copy target address to temp buffer to prevent misaligned access. */ ovs_be32 tbuf[4]; @@ -1378,8 +1379,8 @@ compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src, ns->icmph.icmp6_cksum = 0; icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); - ns->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ns, - ND_MSG_LEN + ND_OPT_LEN)); + ns->icmph.icmp6_cksum = csum_finish( + csum_continue(icmp_csum, ns, ND_MSG_LEN + ND_LLA_OPT_LEN)); } /* Compose an IPv6 Neighbor Discovery Neighbor Advertisement message. */ @@ -1390,20 +1391,20 @@ compose_nd_na(struct dp_packet *b, ovs_be32 rso_flags) { struct ovs_nd_msg *na; - struct ovs_nd_opt *nd_opt; + struct ovs_nd_lla_opt *nd_opt; uint32_t icmp_csum; eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN); na = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst, - 0, 0, 255, ND_MSG_LEN + ND_OPT_LEN); + 0, 0, 255, ND_MSG_LEN + ND_LLA_OPT_LEN); na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT; na->icmph.icmp6_code = 0; put_16aligned_be32(&na->rso_flags, rso_flags); nd_opt = &na->options[0]; - nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; - nd_opt->nd_opt_len = 1; + nd_opt->type = ND_OPT_TARGET_LINKADDR; + nd_opt->length = 1; /* Copy target address to temp buffer to prevent misaligned access. */ ovs_be32 tbuf[4]; @@ -1412,8 +1413,123 @@ compose_nd_na(struct dp_packet *b, na->icmph.icmp6_cksum = 0; icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); - na->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, na, - ND_MSG_LEN + ND_OPT_LEN)); + na->icmph.icmp6_cksum = csum_finish( + csum_continue(icmp_csum, na, ND_MSG_LEN + ND_LLA_OPT_LEN)); +} + +/* Compose an IPv6 Neighbor Discovery Router Advertisement message without + * any IPv6 Neighbor Discovery options. */ +void +compose_nd_ra(struct dp_packet *b, + const struct eth_addr eth_src, const struct eth_addr eth_dst, + const ovs_be32 ipv6_src[4], const ovs_be32 ipv6_dst[4], + uint8_t cur_hop_limit, uint8_t mo_flags, ovs_be16 router_lifetime, + ovs_be32 reachable_time, ovs_be32 retrans_timer) +{ + struct ovs_ra_msg *ra; + uint32_t icmp_csum; + + eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN); + ra = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst, 0, 0, 255, + RA_MSG_LEN); + + ra->icmph.icmp6_type = ND_ROUTER_ADVERT; + ra->icmph.icmp6_code = 0; + ra->cur_hop_limit = cur_hop_limit; + ra->mo_flags = mo_flags; + ra->router_lifetime = router_lifetime; + ra->reachable_time = reachable_time; + ra->retrans_timer = retrans_timer; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, RA_MSG_LEN)); +} + +/* Append an IPv6 Neighbor Discovery Prefix Information option to a + * Router Advertisement message. */ +void +packet_put_ra_prefix_opt(struct dp_packet *b, + uint8_t plen, uint8_t la_flags, ovs_be32 valid_lifetime, + ovs_be32 preferred_lifetime, const ovs_be32 prefix[4]) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_PREFIX_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_prefix_opt *prefix_opt; + uint32_t icmp_csum; + + prefix_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_prefix_opt)); + prefix_opt->type = ND_OPT_PREFIX_INFORMATION; + prefix_opt->length = 4; + prefix_opt->prefix_length = plen; + prefix_opt->la_flags = la_flags; + prefix_opt->valid_lifetime = valid_lifetime; + prefix_opt->preferred_lifetime = preferred_lifetime; + prefix_opt->reserved = 0; + packet_update_csum128(b, IPPROTO_ICMPV6, prefix_opt->prefix.be32, prefix); + memcpy(prefix_opt->prefix.be32, prefix, sizeof(ovs_be32[4])); + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_PREFIX_OPT_LEN)); +} + +/* Append an IPv6 Neighbor Discovery MTU option to a + * Router Advertisement message. */ +void +packet_put_ra_mtu_opt(struct dp_packet *b, ovs_be32 mtu) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_MTU_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_mtu_opt *mtu_opt; + uint32_t icmp_csum; + + mtu_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_mtu_opt)); + mtu_opt->type = ND_OPT_MTU; + mtu_opt->length = 1; + mtu_opt->reserved = 0; + ovs_be16 *csum = &(ra->icmph.icmp6_cksum); + *csum = recalc_csum32(*csum, mtu_opt->mtu, mtu); + mtu_opt->mtu = mtu; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_MTU_OPT_LEN)); +} + +/* Append an IPv6 Neighbor Discovery Source Link-layer Address option to a + * Router Advertisement message. */ +void +packet_put_ra_sll_opt(struct dp_packet *b, const struct eth_addr lla) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_LLA_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_lla_opt *lla_opt; + uint32_t icmp_csum; + + lla_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_lla_opt)); + lla_opt->type = ND_OPT_SOURCE_LINKADDR; + lla_opt->length = 1; + ovs_be16 *csum = &(ra->icmph.icmp6_cksum); + *csum = recalc_csum48(*csum, lla_opt->lla, lla); + lla_opt->lla = lla; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_LLA_OPT_LEN)); } uint32_t diff --git a/lib/packets.h b/lib/packets.h index 9b98b29..9bff2c0 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -161,6 +161,12 @@ static const struct eth_addr eth_addr_lacp OVS_UNUSED static const struct eth_addr eth_addr_bfd OVS_UNUSED = { { { 0x00, 0x23, 0x20, 0x00, 0x00, 0x01 } } }; +static const struct eth_addr eth_addr_ip6_all_hosts OVS_UNUSED + = { { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } } }; + +static const struct eth_addr eth_addr_ip6_all_routers OVS_UNUSED + = { { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x02 } } }; + static inline bool eth_addr_is_broadcast(const struct eth_addr a) { return (a.be16[0] & a.be16[1] & a.be16[2]) == htons(0xffff); @@ -831,15 +837,48 @@ BUILD_ASSERT_DECL(ICMP6_HEADER_LEN == sizeof(struct icmp6_header)); uint32_t packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *); -/* Neighbor Discovery option field. - * ND options are always a multiple of 8 bytes in size. */ -#define ND_OPT_LEN 8 -struct ovs_nd_opt { - uint8_t nd_opt_type; /* Values defined in icmp6.h */ - uint8_t nd_opt_len; /* in units of 8 octets (the size of this struct) */ - struct eth_addr nd_opt_mac; /* Ethernet address in the case of SLL or TLL options */ +/* Neighbor Discovery packet flags. */ +#define ND_RSO_ROUTER 0x80000000 +#define ND_RSO_SOLICITED 0x40000000 +#define ND_RSO_OVERRIDE 0x20000000 +#define ND_PREFIX_ON_LINK 0x80 +#define ND_PREFIX_AUTONOMOUS_ADDRESS 0x40 +#define ND_ROUTER_ADV_MANAGED_ADDRESS 0x80 +#define ND_ROUTER_ADV_OTHER_CONFIG 0x40 + +/* Neighbor Discovery option: Source/Target Link-layer Address. */ +#define ND_LLA_OPT_LEN 8 +struct ovs_nd_lla_opt { + uint8_t type; /* Values defined in icmp6.h */ + uint8_t length; /* in units of 8 octets (the size of this struct) */ + struct eth_addr lla; /* Ethernet address in the case of SLL or TLL options */ +}; +BUILD_ASSERT_DECL(ND_LLA_OPT_LEN == sizeof(struct ovs_nd_lla_opt)); + +/* Neighbor Discovery option: Prefix Information. */ +#define ND_PREFIX_OPT_LEN 32 +struct ovs_nd_prefix_opt { + uint8_t type; /* Values defined in icmp6.h */ + uint8_t length; + uint8_t prefix_length; + /* on-link flag, autonomous address-configuration flag, 6-bit reserved. */ + uint8_t la_flags; + ovs_be32 valid_lifetime; + ovs_be32 preferred_lifetime; + ovs_be32 reserved; + union ovs_16aligned_in6_addr prefix; +}; +BUILD_ASSERT_DECL(ND_PREFIX_OPT_LEN == sizeof(struct ovs_nd_prefix_opt)); + +/* Neighbor Discovery option: MTU. */ +#define ND_MTU_OPT_LEN 8 +struct ovs_nd_mtu_opt { + uint8_t type; /* Values defined in icmp6.h */ + uint8_t length; + ovs_be16 reserved; + ovs_be32 mtu; }; -BUILD_ASSERT_DECL(ND_OPT_LEN == sizeof(struct ovs_nd_opt)); +BUILD_ASSERT_DECL(ND_MTU_OPT_LEN == sizeof(struct ovs_nd_mtu_opt)); /* Like struct nd_msg (from ndisc.h), but whereas that struct requires 32-bit * alignment, this one only requires 16-bit alignment. */ @@ -848,13 +887,20 @@ struct ovs_nd_msg { struct icmp6_header icmph; ovs_16aligned_be32 rso_flags; union ovs_16aligned_in6_addr target; - struct ovs_nd_opt options[0]; + struct ovs_nd_lla_opt options[0]; }; BUILD_ASSERT_DECL(ND_MSG_LEN == sizeof(struct ovs_nd_msg)); -#define ND_RSO_ROUTER 0x80000000 -#define ND_RSO_SOLICITED 0x40000000 -#define ND_RSO_OVERRIDE 0x20000000 +#define RA_MSG_LEN 16 +struct ovs_ra_msg { + struct icmp6_header icmph; + uint8_t cur_hop_limit; + uint8_t mo_flags; + ovs_be16 router_lifetime; + ovs_be32 reachable_time; + ovs_be32 retrans_timer; +}; +BUILD_ASSERT_DECL(RA_MSG_LEN == sizeof(struct ovs_ra_msg)); /* * Use the same struct for MLD and MLD2, naming members as the defined fields in @@ -910,6 +956,10 @@ extern const struct in6_addr in6addr_all_hosts; #define IN6ADDR_ALL_HOSTS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 } } } +extern const struct in6_addr in6addr_all_routers; +#define IN6ADDR_ALL_ROUTERS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 } } } + static inline bool ipv6_addr_equals(const struct in6_addr *a, const struct in6_addr *b) { @@ -1107,6 +1157,18 @@ void compose_nd_na(struct dp_packet *, const struct eth_addr eth_src, const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst, ovs_be32 rso_flags); +void compose_nd_ra(struct dp_packet *, + const struct eth_addr eth_src, const struct eth_addr eth_dst, + const ovs_be32 ipv6_src[4], const ovs_be32 ipv6_dst[4], + uint8_t cur_hop_limit, uint8_t mo_flags, + ovs_be16 router_lifetime, ovs_be32 reachable_time, + ovs_be32 retrans_timer); +void packet_put_ra_sll_opt(struct dp_packet *, const struct eth_addr lla); +void packet_put_ra_mtu_opt(struct dp_packet *, ovs_be32 mtu); +void packet_put_ra_prefix_opt(struct dp_packet *, + uint8_t plen, uint8_t la_flags, + ovs_be32 valid_lifetime, ovs_be32 preferred_lifetime, + const ovs_be32 router_prefix[4]); uint32_t packet_csum_pseudoheader(const struct ip_header *); void IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6);