ovn-northd: Allow lport 'addresses' to store multiple ips in each set
[cascardo/ovs.git] / lib / packets.c
index 965754f..daca1b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "unaligned.h"
 
 const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
+const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
+
+struct in6_addr
+flow_tnl_dst(const struct flow_tnl *tnl)
+{
+    return tnl->ip_dst ? in6_addr_mapped_ipv4(tnl->ip_dst) : tnl->ipv6_dst;
+}
+
+struct in6_addr
+flow_tnl_src(const struct flow_tnl *tnl)
+{
+    return tnl->ip_src ? in6_addr_mapped_ipv4(tnl->ip_src) : tnl->ipv6_src;
+}
 
 /* Parses 's' as a 16-digit hexadecimal number representing a datapath ID.  On
  * success stores the dpid into '*dpidp' and returns true, on failure stores 0
@@ -55,7 +68,7 @@ dpid_from_string(const char *s, uint64_t *dpidp)
  * If you change this function's behavior, please update corresponding
  * documentation in vswitch.xml at the same time. */
 bool
-eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
+eth_addr_is_reserved(const struct eth_addr ea)
 {
     struct eth_addr_node {
         struct hmap_node hmap_node;
@@ -128,12 +141,12 @@ eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
 }
 
 bool
-eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
+eth_addr_from_string(const char *s, struct eth_addr *ea)
 {
-    if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
+    if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) {
         return true;
     } else {
-        memset(ea, 0, ETH_ADDR_LEN);
+        *ea = eth_addr_zero;
         return false;
     }
 }
@@ -145,7 +158,7 @@ eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
  * The returned packet has enough headroom to insert an 802.1Q VLAN header if
  * desired. */
 void
-compose_rarp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN])
+compose_rarp(struct dp_packet *b, const struct eth_addr eth_src)
 {
     struct eth_header *eth;
     struct arp_eth_header *arp;
@@ -155,8 +168,8 @@ compose_rarp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN])
                              + ARP_ETH_HEADER_LEN);
     dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
     eth = dp_packet_put_uninit(b, sizeof *eth);
-    memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    eth->eth_dst = eth_addr_broadcast;
+    eth->eth_src = eth_src;
     eth->eth_type = htons(ETH_TYPE_RARP);
 
     arp = dp_packet_put_uninit(b, sizeof *arp);
@@ -165,9 +178,9 @@ compose_rarp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN])
     arp->ar_hln = sizeof arp->ar_sha;
     arp->ar_pln = sizeof arp->ar_spa;
     arp->ar_op = htons(ARP_OP_RARP);
-    memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
+    arp->ar_sha = eth_src;
     put_16aligned_be32(&arp->ar_spa, htonl(0));
-    memcpy(arp->ar_tha, eth_src, ETH_ADDR_LEN);
+    arp->ar_tha = eth_src;
     put_16aligned_be32(&arp->ar_tpa, htonl(0));
 
     dp_packet_reset_offsets(b);
@@ -293,7 +306,7 @@ set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse)
     }
 }
 
-/* Push MPLS label stack entry 'lse' onto 'packet' as the the outermost MPLS
+/* Push MPLS label stack entry 'lse' onto 'packet' as the outermost MPLS
  * header.  If 'packet' does not already have any MPLS labels, then its
  * Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */
 void
@@ -370,24 +383,12 @@ eth_from_hex(const char *hex, struct dp_packet **packetp)
 }
 
 void
-eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
-                  const uint8_t mask[ETH_ADDR_LEN], struct ds *s)
+eth_format_masked(const struct eth_addr eth,
+                  const struct eth_addr *mask, struct ds *s)
 {
     ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth));
-    if (mask && !eth_mask_is_exact(mask)) {
-        ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask));
-    }
-}
-
-void
-eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
-                const uint8_t mask[ETH_ADDR_LEN],
-                uint8_t dst[ETH_ADDR_LEN])
-{
-    int i;
-
-    for (i = 0; i < ETH_ADDR_LEN; i++) {
-        dst[i] = src[i] & mask[i];
+    if (mask && !eth_mask_is_exact(*mask)) {
+        ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(*mask));
     }
 }
 
@@ -415,44 +416,264 @@ ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
     }
 }
 
+/* Parses string 's', which must be an IP address.  Stores the IP address into
+ * '*ip'.  Returns true if successful, otherwise false. */
+bool
+ip_parse(const char *s, ovs_be32 *ip)
+{
+    return inet_pton(AF_INET, s, ip) == 1;
+}
+
+/* Parses string 's', which must be an IP address with an optional netmask or
+ * CIDR prefix length.  Stores the IP address into '*ip', netmask into '*mask',
+ * (255.255.255.255, if 's' lacks a netmask), and number of scanned characters
+ * into '*n'.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_masked_len(const char *s, int *n, ovs_be32 *ip,
+                    ovs_be32 *mask)
+{
+    int prefix;
+
+    if (ovs_scan_len(s, n, IP_SCAN_FMT"/"IP_SCAN_FMT,
+                 IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask))) {
+        /* OK. */
+    } else if (ovs_scan_len(s, n, IP_SCAN_FMT"/%d",
+                            IP_SCAN_ARGS(ip), &prefix)) {
+        if (prefix <= 0 || prefix > 32) {
+            return xasprintf("%s: network prefix bits not between 0 and "
+                             "32", s);
+        }
+        *mask = be32_prefix_mask(prefix);
+    } else if (ovs_scan_len(s, n, IP_SCAN_FMT, IP_SCAN_ARGS(ip))) {
+        *mask = OVS_BE32_MAX;
+    } else {
+        return xasprintf("%s: invalid IP address", s);
+    }
+    return NULL;
+}
+
+/* This function is similar to ip_parse_masked_len(), but doesn't return the
+ * number of scanned characters and expects 's' to end after the ip/(optional)
+ * mask.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
+{
+    int n = 0;
+
+    char *error = ip_parse_masked_len(s, &n, ip, mask);
+    if (!error && s[n]) {
+        return xasprintf("%s: invalid IP address", s);
+    }
+    return error;
+}
+
+/* Similar to ip_parse_masked_len(), but the mask, if present, must be a CIDR
+ * mask and is returned as a prefix len in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_cidr_len(const char *s, int *n, ovs_be32 *ip, unsigned int *plen)
+{
+    ovs_be32 mask;
+    char *error;
+
+    error = ip_parse_masked_len(s, n, ip, &mask);
+    if (error) {
+        return error;
+    }
+
+    if (!ip_is_cidr(mask)) {
+        return xasprintf("%s: CIDR network required", s);
+    }
+    *plen = ip_count_cidr_bits(mask);
+    return NULL;
+}
+
+/* Similar to ip_parse_cidr_len(), but doesn't return the number of scanned
+ * characters and expects 's' to be NULL terminated at the end of the
+ * ip/(optional) cidr. */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
+{
+    int n = 0;
+
+    char *error = ip_parse_cidr_len(s, &n, ip, plen);
+    if (!error && s[n]) {
+        return xasprintf("%s: invalid IP address", s);
+    }
+    return error;
+}
+
+/* Parses string 's', which must be an IPv6 address.  Stores the IPv6 address
+ * into '*ip'.  Returns true if successful, otherwise false. */
+bool
+ipv6_parse(const char *s, struct in6_addr *ip)
+{
+    return inet_pton(AF_INET6, s, ip) == 1;
+}
+
+/* Parses string 's', which must be an IPv6 address with an optional netmask or
+ * CIDR prefix length.  Stores the IPv6 address into '*ip' and the netmask into
+ * '*mask' (if 's' does not contain a netmask, all-one-bits is assumed), and
+ * number of scanned characters into '*n'.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_masked_len(const char *s, int *n, struct in6_addr *ip,
+                      struct in6_addr *mask)
+{
+    char ipv6_s[IPV6_SCAN_LEN + 1];
+    int prefix;
+
+    if (ovs_scan_len(s, n, " "IPV6_SCAN_FMT, ipv6_s)
+        && ipv6_parse(ipv6_s, ip)) {
+        if (ovs_scan_len(s, n, "/%d", &prefix)) {
+            if (prefix <= 0 || prefix > 128) {
+                return xasprintf("%s: IPv6 network prefix bits not between 0 "
+                                 "and 128", s);
+            }
+            *mask = ipv6_create_mask(prefix);
+        } else if (ovs_scan_len(s, n, "/"IPV6_SCAN_FMT, ipv6_s)) {
+             if (!ipv6_parse(ipv6_s, mask)) {
+                 return xasprintf("%s: Invalid IPv6 mask", s);
+             }
+            /* OK. */
+        } else {
+            /* OK. No mask. */
+            *mask = in6addr_exact;
+        }
+        return NULL;
+    }
+    return xasprintf("%s: invalid IPv6 address", s);
+}
+
+/* This function is similar to ipv6_parse_masked_len(), but doesn't return the
+ * number of scanned characters and expects 's' to end following the
+ * ipv6/(optional) mask. */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask)
+{
+    int n = 0;
+
+    char *error = ipv6_parse_masked_len(s, &n, ip, mask);
+    if (!error && s[n]) {
+        return xasprintf("%s: invalid IPv6 address", s);
+    }
+    return error;
+}
+
+/* Similar to ipv6_parse_masked_len(), but the mask, if present, must be a CIDR
+ * mask and is returned as a prefix length in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_cidr_len(const char *s, int *n, struct in6_addr *ip,
+                    unsigned int *plen)
+{
+    struct in6_addr mask;
+    char *error;
+
+    error = ipv6_parse_masked_len(s, n, ip, &mask);
+    if (error) {
+        return error;
+    }
+
+    if (!ipv6_is_cidr(&mask)) {
+        return xasprintf("%s: IPv6 CIDR network required", s);
+    }
+    *plen = ipv6_count_cidr_bits(&mask);
+    return NULL;
+}
+
+/* Similar to ipv6_parse_cidr_len(), but doesn't return the number of scanned
+ * characters and expects 's' to end after the ipv6/(optional) cidr. */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
+{
+    int n = 0;
+
+    char *error = ipv6_parse_cidr_len(s, &n, ip, plen);
+    if (!error && s[n]) {
+        return xasprintf("%s: invalid IPv6 address", s);
+    }
+    return error;
+}
 
 /* Stores the string representation of the IPv6 address 'addr' into the
  * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
  * bytes long. */
 void
-format_ipv6_addr(char *addr_str, const struct in6_addr *addr)
+ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
 {
-    inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+    char *dst;
+
+    ds_reserve(s, s->length + INET6_ADDRSTRLEN);
+
+    dst = s->string + s->length;
+    inet_ntop(AF_INET6, addr, dst, INET6_ADDRSTRLEN);
+    s->length += strlen(dst);
 }
 
+/* Same as print_ipv6_addr, but optionally encloses the address in square
+ * brackets. */
 void
-print_ipv6_addr(struct ds *string, const struct in6_addr *addr)
+ipv6_format_addr_bracket(const struct in6_addr *addr, struct ds *s,
+                         bool bracket)
 {
-    char *dst;
-
-    ds_reserve(string, string->length + INET6_ADDRSTRLEN);
+    if (bracket) {
+        ds_put_char(s, '[');
+    }
+    ipv6_format_addr(addr, s);
+    if (bracket) {
+        ds_put_char(s, ']');
+    }
+}
 
-    dst = string->string + string->length;
-    format_ipv6_addr(dst, addr);
-    string->length += strlen(dst);
+void
+ipv6_format_mapped(const struct in6_addr *addr, struct ds *s)
+{
+    if (IN6_IS_ADDR_V4MAPPED(addr)) {
+        ds_put_format(s, IP_FMT, addr->s6_addr[12], addr->s6_addr[13],
+                                 addr->s6_addr[14], addr->s6_addr[15]);
+    } else {
+        ipv6_format_addr(addr, s);
+    }
 }
 
 void
-print_ipv6_masked(struct ds *s, const struct in6_addr *addr,
-                  const struct in6_addr *mask)
+ipv6_format_masked(const struct in6_addr *addr, const struct in6_addr *mask,
+                   struct ds *s)
 {
-    print_ipv6_addr(s, addr);
+    ipv6_format_addr(addr, s);
     if (mask && !ipv6_mask_is_exact(mask)) {
         if (ipv6_is_cidr(mask)) {
             int cidr_bits = ipv6_count_cidr_bits(mask);
             ds_put_format(s, "/%d", cidr_bits);
         } else {
             ds_put_char(s, '/');
-            print_ipv6_addr(s, mask);
+            ipv6_format_addr(mask, s);
         }
     }
 }
 
+/* Stores the string representation of the IPv6 address 'addr' into the
+ * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
+ * bytes long. If addr is IPv4-mapped, store an IPv4 dotted-decimal string. */
+const char *
+ipv6_string_mapped(char *addr_str, const struct in6_addr *addr)
+{
+    ovs_be32 ip;
+    ip = in6_addr_get_mapped_ipv4(addr);
+    if (ip) {
+        return inet_ntop(AF_INET, &ip, addr_str, INET6_ADDRSTRLEN);
+    } else {
+        return inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+    }
+}
+
 struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
                                  const struct in6_addr *b)
 {
@@ -559,8 +780,8 @@ ipv6_is_cidr(const struct in6_addr *netmask)
  * The returned packet has enough headroom to insert an 802.1Q VLAN header if
  * desired. */
 void *
-eth_compose(struct dp_packet *b, const uint8_t eth_dst[ETH_ADDR_LEN],
-            const uint8_t eth_src[ETH_ADDR_LEN], uint16_t eth_type,
+eth_compose(struct dp_packet *b, const struct eth_addr eth_dst,
+            const struct eth_addr eth_src, uint16_t eth_type,
             size_t size)
 {
     void *data;
@@ -575,8 +796,8 @@ eth_compose(struct dp_packet *b, const uint8_t eth_dst[ETH_ADDR_LEN],
     eth = dp_packet_put_uninit(b, ETH_HEADER_LEN);
     data = dp_packet_put_uninit(b, size);
 
-    memcpy(eth->eth_dst, eth_dst, ETH_ADDR_LEN);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    eth->eth_dst = eth_dst;
+    eth->eth_src = eth_src;
     eth->eth_type = htons(eth_type);
 
     dp_packet_reset_offsets(b);
@@ -875,10 +1096,27 @@ packet_set_sctp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst)
     put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
 }
 
+/* Sets the ICMP type and code of the ICMP header contained in 'packet'.
+ * 'packet' must be a valid ICMP packet with its l4 offset properly
+ * populated. */
+void
+packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
+{
+    struct icmp_header *ih = dp_packet_l4(packet);
+    ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
+    ovs_be16 new_tc = htons(type << 8 | code);
+
+    if (orig_tc != new_tc) {
+        ih->icmp_type = type;
+        ih->icmp_code = code;
+
+        ih->icmp_csum = recalc_csum16(ih->icmp_csum, orig_tc, new_tc);
+    }
+}
+
 void
 packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
-              const uint8_t sll[ETH_ADDR_LEN],
-              const uint8_t tll[ETH_ADDR_LEN]) {
+              const struct eth_addr sll, const struct eth_addr tll) {
     struct ovs_nd_msg *ns;
     struct ovs_nd_opt *nd_opt;
     int bytes_remain = dp_packet_l4_size(packet);
@@ -900,22 +1138,22 @@ packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
     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 (memcmp(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN)) {
+            if (!eth_addr_equals(nd_opt->nd_opt_mac, sll)) {
                 ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
 
-                *csum = recalc_csum48(*csum, nd_opt->nd_opt_data, sll);
-                memcpy(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN);
+                *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, sll);
+                nd_opt->nd_opt_mac = 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 (memcmp(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN)) {
+            if (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) {
                 ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
 
-                *csum = recalc_csum48(*csum, nd_opt->nd_opt_data, tll);
-                memcpy(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN);
+                *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, tll);
+                nd_opt->nd_opt_mac = tll;
             }
 
             /* A packet can only contain one SLL or TLL option */
@@ -1012,9 +1250,15 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
 #define ARP_PACKET_SIZE  (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \
                           ARP_ETH_HEADER_LEN)
 
+/* Clears 'b' and replaces its contents by an ARP frame with the specified
+ * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'.  The outer
+ * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination
+ * 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if
+ * 'broadcast' is true. */
 void
-compose_arp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN],
-            ovs_be32 ip_src, ovs_be32 ip_dst)
+compose_arp(struct dp_packet *b, uint16_t arp_op,
+            const struct eth_addr arp_sha, const struct eth_addr arp_tha,
+            bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa)
 {
     struct eth_header *eth;
     struct arp_eth_header *arp;
@@ -1024,8 +1268,8 @@ compose_arp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN],
     dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
 
     eth = dp_packet_put_uninit(b, sizeof *eth);
-    memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
-    memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+    eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
+    eth->eth_src = arp_sha;
     eth->eth_type = htons(ETH_TYPE_ARP);
 
     arp = dp_packet_put_uninit(b, sizeof *arp);
@@ -1033,17 +1277,47 @@ compose_arp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN],
     arp->ar_pro = htons(ARP_PRO_IP);
     arp->ar_hln = sizeof arp->ar_sha;
     arp->ar_pln = sizeof arp->ar_spa;
-    arp->ar_op = htons(ARP_OP_REQUEST);
-    memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
-    memset(arp->ar_tha, 0, ETH_ADDR_LEN);
+    arp->ar_op = htons(arp_op);
+    arp->ar_sha = arp_sha;
+    arp->ar_tha = arp_tha;
 
-    put_16aligned_be32(&arp->ar_spa, ip_src);
-    put_16aligned_be32(&arp->ar_tpa, ip_dst);
+    put_16aligned_be32(&arp->ar_spa, arp_spa);
+    put_16aligned_be32(&arp->ar_tpa, arp_tpa);
 
     dp_packet_reset_offsets(b);
     dp_packet_set_l3(b, arp);
 }
 
+void
+compose_nd(struct dp_packet *b, const struct eth_addr eth_src,
+           struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst)
+{
+    struct in6_addr sn_addr;
+    struct eth_addr eth_dst;
+    struct ovs_nd_msg *ns;
+    struct ovs_nd_opt *nd_opt;
+
+    in6_addr_solicited_node(&sn_addr, ipv6_dst);
+    ipv6_multicast_to_ethernet(&eth_dst, &sn_addr);
+
+    eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6,
+                IPV6_HEADER_LEN + ICMP6_HEADER_LEN + ND_OPT_LEN);
+    packet_set_ipv6(b, IPPROTO_ICMPV6,
+                    ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                    ALIGNED_CAST(ovs_be32 *, sn_addr.s6_addr),
+                    0, 0, 255);
+
+    ns = dp_packet_l4(b);
+    nd_opt = &ns->options[0];
+
+    ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT;
+    ns->icmph.icmp6_code = 0;
+
+    nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+    packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
+                  eth_src, eth_addr_zero);
+}
+
 uint32_t
 packet_csum_pseudoheader(const struct ip_header *ip)
 {
@@ -1057,3 +1331,27 @@ packet_csum_pseudoheader(const struct ip_header *ip)
 
     return partial;
 }
+
+#ifndef __CHECKER__
+uint32_t
+packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *ip6)
+{
+    uint32_t partial = 0;
+
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[0])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[1])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[2])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[3])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[0])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[1])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[2])));
+    partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[3])));
+
+    partial = csum_add16(partial, 0);
+    partial = csum_add16(partial, ip6->ip6_plen);
+    partial = csum_add16(partial, 0);
+    partial = csum_add16(partial, ip6->ip6_nxt);
+
+    return partial;
+}
+#endif