]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/packets.c
dpif-netdev: Fix few comments.
[mirror_ovs.git] / lib / packets.c
index 5ad16b502ed50b9b6064fbacb11e0a891f47fa50..7a9071c0c354212aa79d175cc7ae2d9fa05515b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,8 +26,8 @@
 #include "csum.h"
 #include "crc32c.h"
 #include "flow.h"
-#include "hmap.h"
-#include "dynamic-string.h"
+#include "openvswitch/hmap.h"
+#include "openvswitch/dynamic-string.h"
 #include "ovs-thread.h"
 #include "odp-util.h"
 #include "dp-packet.h"
@@ -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)
@@ -140,10 +141,15 @@ eth_addr_is_reserved(const struct eth_addr ea)
     return false;
 }
 
+/* Attempts to parse 's' as an Ethernet address.  If successful, stores the
+ * address in 'ea' and returns true, otherwise zeros 'ea' and returns
+ * false.  This function checks trailing characters. */
 bool
 eth_addr_from_string(const char *s, struct eth_addr *ea)
 {
-    if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) {
+    int n = 0;
+    if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*ea), &n)
+        && !s[n]) {
         return true;
     } else {
         *ea = eth_addr_zero;
@@ -185,6 +191,7 @@ compose_rarp(struct dp_packet *b, const struct eth_addr eth_src)
 
     dp_packet_reset_offsets(b);
     dp_packet_set_l3(b, arp);
+    b->packet_type = htonl(PT_ETH);
 }
 
 /* Insert VLAN header according to given TCI. Packet passed must be Ethernet
@@ -210,7 +217,7 @@ eth_push_vlan(struct dp_packet *packet, ovs_be16 tpid, ovs_be16 tci)
 void
 eth_pop_vlan(struct dp_packet *packet)
 {
-    struct vlan_eth_header *veh = dp_packet_l2(packet);
+    struct vlan_eth_header *veh = dp_packet_eth(packet);
 
     if (veh && dp_packet_size(packet) >= sizeof *veh
         && eth_type_vlan(veh->veth_type)) {
@@ -220,11 +227,52 @@ eth_pop_vlan(struct dp_packet *packet)
     }
 }
 
+/* Push Ethernet header onto 'packet' assuming it is layer 3 */
+void
+push_eth(struct dp_packet *packet, const struct eth_addr *dst,
+         const struct eth_addr *src)
+{
+    struct eth_header *eh;
+
+    ovs_assert(packet->packet_type != htonl(PT_ETH));
+    eh = dp_packet_resize_l2(packet, ETH_HEADER_LEN);
+    eh->eth_dst = *dst;
+    eh->eth_src = *src;
+    eh->eth_type = pt_ns_type_be(packet->packet_type);
+    packet->packet_type = htonl(PT_ETH);
+}
+
+/* Removes Ethernet header, including VLAN header, from 'packet'.
+ *
+ * Previous to calling this function, 'ofpbuf_l3(packet)' must not be NULL */
+void
+pop_eth(struct dp_packet *packet)
+{
+    char *l2_5 = dp_packet_l2_5(packet);
+    char *l3 = dp_packet_l3(packet);
+    ovs_be16 ethertype;
+    int increment;
+
+    ovs_assert(packet->packet_type == htonl(PT_ETH));
+    ovs_assert(l3 != NULL);
+
+    if (l2_5) {
+        increment = packet->l2_5_ofs;
+        ethertype = *(ALIGNED_CAST(ovs_be16 *, (l2_5 - 2)));
+    } else {
+        increment = packet->l3_ofs;
+        ethertype = *(ALIGNED_CAST(ovs_be16 *, (l3 - 2)));
+    }
+
+    dp_packet_resize_l2(packet, -increment);
+    packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, ntohs(ethertype));
+}
+
 /* Set ethertype of the packet. */
 static void
 set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
 {
-    struct eth_header *eh = dp_packet_l2(packet);
+    struct eth_header *eh = dp_packet_eth(packet);
 
     if (!eh) {
         return;
@@ -416,30 +464,56 @@ 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 a port number
+ * with ":" as a separator (e.g.: 192.168.1.2:80).
+ * Stores the IP address into '*ip' and port number to '*port'.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_port(const char *s, ovs_be32 *ip, ovs_be16 *port)
+{
+    int n = 0;
+    if (ovs_scan(s, IP_PORT_SCAN_FMT"%n", IP_PORT_SCAN_ARGS(ip, port), &n)
+        && !s[n]) {
+        return NULL;
+    }
+
+    return xasprintf("%s: invalid IP address or port number", s);
+}
+
 /* Parses string 's', which must be an IP address with an optional netmask or
- * CIDR prefix length.  Stores the IP address into '*ip' and the netmask into
- * '*mask'.  (If 's' does not contain a netmask, 255.255.255.255 is
- * assumed.)
+ * 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(const char *s, ovs_be32 *ip, ovs_be32 *mask)
+ip_parse_masked_len(const char *s, int *n, ovs_be32 *ip,
+                    ovs_be32 *mask)
 {
     int prefix;
-    int n;
 
-    if (ovs_scan(s, IP_SCAN_FMT"/"IP_SCAN_FMT"%n",
-                 IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask), &n) && !s[n]) {
+    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(s, IP_SCAN_FMT"/%d%n", IP_SCAN_ARGS(ip), &prefix, &n)
-               && !s[n]) {
-        if (prefix <= 0 || prefix > 32) {
-            return xasprintf("%s: network prefix bits not between 0 and "
-                             "32", s);
+    } else if (ovs_scan_len(s, n, IP_SCAN_FMT"/%d",
+                            IP_SCAN_ARGS(ip), &prefix)) {
+        if (prefix < 0 || prefix > 32) {
+            return xasprintf("%s: IPv4 network prefix bits not between 0 and "
+                              "32, inclusive", s);
         }
         *mask = be32_prefix_mask(prefix);
-    } else if (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(ip), &n) && !s[n]) {
+    } 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);
@@ -447,6 +521,156 @@ ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
     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, inclusive", 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
 ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
 {
@@ -516,23 +740,43 @@ ipv6_string_mapped(char *addr_str, const struct in6_addr *addr)
     }
 }
 
-struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
-                                 const struct in6_addr *b)
-{
-    int i;
-    struct in6_addr dst;
-
 #ifdef s6_addr32
-    for (i=0; i<4; i++) {
-        dst.s6_addr32[i] = a->s6_addr32[i] & b->s6_addr32[i];
-    }
+#define s6_addrX s6_addr32
+#define IPV6_FOR_EACH(VAR) for (int VAR = 0; VAR < 4; VAR++)
 #else
-    for (i=0; i<16; i++) {
-        dst.s6_addr[i] = a->s6_addr[i] & b->s6_addr[i];
-    }
+#define s6_addrX s6_addr
+#define IPV6_FOR_EACH(VAR) for (int VAR = 0; VAR < 16; VAR++)
 #endif
 
-    return dst;
+struct in6_addr
+ipv6_addr_bitand(const struct in6_addr *a, const struct in6_addr *b)
+{
+   struct in6_addr dst;
+   IPV6_FOR_EACH (i) {
+       dst.s6_addrX[i] = a->s6_addrX[i] & b->s6_addrX[i];
+   }
+   return dst;
+}
+
+struct in6_addr
+ipv6_addr_bitxor(const struct in6_addr *a, const struct in6_addr *b)
+{
+   struct in6_addr dst;
+   IPV6_FOR_EACH (i) {
+       dst.s6_addrX[i] = a->s6_addrX[i] ^ b->s6_addrX[i];
+   }
+   return dst;
+}
+
+bool
+ipv6_is_zero(const struct in6_addr *a)
+{
+   IPV6_FOR_EACH (i) {
+       if (a->s6_addrX[i]) {
+           return false;
+       }
+   }
+   return true;
 }
 
 /* Returns an in6_addr consisting of 'mask' high-order 1-bits and 128-N
@@ -612,43 +856,6 @@ ipv6_is_cidr(const struct in6_addr *netmask)
     return true;
 }
 
-/* Parses string 's', which must be an IPv6 address with an optional
- * CIDR prefix length.  Stores the IP address into '*ipv6' and the CIDR
- * prefix in '*prefix'.  (If 's' does not contain a CIDR length, all-ones
- * is assumed.)
- *
- * Returns NULL if successful, otherwise an error message that the caller must
- * free(). */
-char * OVS_WARN_UNUSED_RESULT
-ipv6_parse_masked(const char *s, struct in6_addr *ipv6, struct in6_addr *mask)
-{
-    char ipv6_s[IPV6_SCAN_LEN + 1];
-    char mask_s[IPV6_SCAN_LEN + 1];
-    int prefix;
-    int n;
-
-    if (ovs_scan(s, IPV6_SCAN_FMT"/"IPV6_SCAN_FMT"%n", ipv6_s, mask_s, &n)
-        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-        && inet_pton(AF_INET6, mask_s, mask) == 1
-        && !s[n]) {
-        /* OK. */
-    } else if (ovs_scan(s, IPV6_SCAN_FMT"/%d%n", ipv6_s, &prefix, &n)
-        && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-        && !s[n]) {
-        if (prefix <= 0 || prefix > 128) {
-            return xasprintf("%s: prefix bits not between 0 and 128", s);
-        }
-        *mask = ipv6_create_mask(prefix);
-    } else if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
-               && inet_pton(AF_INET6, ipv6_s, ipv6) == 1
-               && !s[n]) {
-        *mask = in6addr_exact;
-    } else {
-        return xasprintf("%s: invalid IP address", s);
-    }
-    return NULL;
-}
-
 /* Populates 'b' with an Ethernet II packet headed with the given 'eth_dst',
  * 'eth_src' and 'eth_type' parameters.  A payload of 'size' bytes is allocated
  * in 'b' and returned.  This payload may be populated with appropriate
@@ -673,19 +880,20 @@ eth_compose(struct dp_packet *b, const struct eth_addr eth_dst,
     dp_packet_prealloc_tailroom(b, 2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + size);
     dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
     eth = dp_packet_put_uninit(b, ETH_HEADER_LEN);
-    data = dp_packet_put_uninit(b, size);
+    data = dp_packet_put_zeros(b, size);
 
     eth->eth_dst = eth_dst;
     eth->eth_src = eth_src;
     eth->eth_type = htons(eth_type);
 
+    b->packet_type = htonl(PT_ETH);
     dp_packet_reset_offsets(b);
     dp_packet_set_l3(b, data);
 
     return data;
 }
 
-static void
+void
 packet_set_ipv4_addr(struct dp_packet *packet,
                      ovs_16aligned_be32 *addr, ovs_be32 new_addr)
 {
@@ -716,30 +924,28 @@ packet_set_ipv4_addr(struct dp_packet *packet,
  *
  * This function assumes that L3 and L4 offsets are set in the packet. */
 static bool
-packet_rh_present(struct dp_packet *packet)
+packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr)
 {
     const struct ovs_16aligned_ip6_hdr *nh;
-    int nexthdr;
     size_t len;
     size_t remaining;
     uint8_t *data = dp_packet_l3(packet);
 
     remaining = packet->l4_ofs - packet->l3_ofs;
-
     if (remaining < sizeof *nh) {
         return false;
     }
     nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
     data += sizeof *nh;
     remaining -= sizeof *nh;
-    nexthdr = nh->ip6_nxt;
+    *nexthdr = nh->ip6_nxt;
 
     while (1) {
-        if ((nexthdr != IPPROTO_HOPOPTS)
-                && (nexthdr != IPPROTO_ROUTING)
-                && (nexthdr != IPPROTO_DSTOPTS)
-                && (nexthdr != IPPROTO_AH)
-                && (nexthdr != IPPROTO_FRAGMENT)) {
+        if ((*nexthdr != IPPROTO_HOPOPTS)
+                && (*nexthdr != IPPROTO_ROUTING)
+                && (*nexthdr != IPPROTO_DSTOPTS)
+                && (*nexthdr != IPPROTO_AH)
+                && (*nexthdr != IPPROTO_FRAGMENT)) {
             /* It's either a terminal header (e.g., TCP, UDP) or one we
              * don't understand.  In either case, we're done with the
              * packet, so use it to fill in 'nw_proto'. */
@@ -755,34 +961,34 @@ packet_rh_present(struct dp_packet *packet)
             return false;
         }
 
-        if (nexthdr == IPPROTO_AH) {
+        if (*nexthdr == IPPROTO_AH) {
             /* A standard AH definition isn't available, but the fields
              * we care about are in the same location as the generic
              * option header--only the header length is calculated
              * differently. */
             const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
 
-            nexthdr = ext_hdr->ip6e_nxt;
+            *nexthdr = ext_hdr->ip6e_nxt;
             len = (ext_hdr->ip6e_len + 2) * 4;
-        } else if (nexthdr == IPPROTO_FRAGMENT) {
+        } else if (*nexthdr == IPPROTO_FRAGMENT) {
             const struct ovs_16aligned_ip6_frag *frag_hdr
                 = ALIGNED_CAST(struct ovs_16aligned_ip6_frag *, data);
 
-            nexthdr = frag_hdr->ip6f_nxt;
+            *nexthdr = frag_hdr->ip6f_nxt;
             len = sizeof *frag_hdr;
-        } else if (nexthdr == IPPROTO_ROUTING) {
+        } else if (*nexthdr == IPPROTO_ROUTING) {
             const struct ip6_rthdr *rh = (struct ip6_rthdr *)data;
 
             if (rh->ip6r_segleft > 0) {
                 return true;
             }
 
-            nexthdr = rh->ip6r_nxt;
+            *nexthdr = rh->ip6r_nxt;
             len = (rh->ip6r_len + 1) * 8;
         } else {
             const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
 
-            nexthdr = ext_hdr->ip6e_nxt;
+            *nexthdr = ext_hdr->ip6e_nxt;
             len = (ext_hdr->ip6e_len + 1) * 8;
         }
 
@@ -798,7 +1004,8 @@ packet_rh_present(struct dp_packet *packet)
 
 static void
 packet_update_csum128(struct dp_packet *packet, uint8_t proto,
-                     ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4])
+                      ovs_16aligned_be32 addr[4],
+                      const struct in6_addr *new_addr)
 {
     size_t l4_size = dp_packet_l4_size(packet);
 
@@ -823,9 +1030,10 @@ packet_update_csum128(struct dp_packet *packet, uint8_t proto,
     }
 }
 
-static void
+void
 packet_set_ipv6_addr(struct dp_packet *packet, uint8_t proto,
-                     ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4],
+                     ovs_16aligned_be32 addr[4],
+                     const struct in6_addr *new_addr,
                      bool recalculate_csum)
 {
     if (recalculate_csum) {
@@ -890,11 +1098,15 @@ packet_set_ipv4(struct dp_packet *packet, ovs_be32 src, ovs_be32 dst,
  * appropriate. 'packet' must contain a valid IPv6 packet with correctly
  * populated l[34] offsets. */
 void
-packet_set_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
-                const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+packet_set_ipv6(struct dp_packet *packet, const struct in6_addr *src,
+                const struct in6_addr *dst, uint8_t key_tc, ovs_be32 key_fl,
                 uint8_t key_hl)
 {
     struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+    uint8_t proto = 0;
+    bool rh_present;
+
+    rh_present = packet_rh_present(packet, &proto);
 
     if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
         packet_set_ipv6_addr(packet, proto, nh->ip6_src.be32, src, true);
@@ -902,13 +1114,11 @@ packet_set_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
 
     if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) {
         packet_set_ipv6_addr(packet, proto, nh->ip6_dst.be32, dst,
-                             !packet_rh_present(packet));
+                             !rh_present);
     }
 
     packet_set_ipv6_tc(&nh->ip6_flow, key_tc);
-
     packet_set_ipv6_flow_label(&nh->ip6_flow, key_fl);
-
     nh->ip6_hlim = key_hl;
 }
 
@@ -994,10 +1204,11 @@ packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
 }
 
 void
-packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
-              const struct eth_addr sll, const struct eth_addr tll) {
+packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
+              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 *opt;
     int bytes_remain = dp_packet_l4_size(packet);
 
     if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
@@ -1005,42 +1216,39 @@ packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
     }
 
     ns = dp_packet_l4(packet);
-    nd_opt = &ns->options[0];
+    opt = &ns->options[0];
     bytes_remain -= sizeof(*ns);
 
     if (memcmp(&ns->target, target, sizeof(ovs_be32[4]))) {
-        packet_set_ipv6_addr(packet, IPPROTO_ICMPV6,
-                             ns->target.be32,
-                             target, true);
+        packet_set_ipv6_addr(packet, IPPROTO_ICMPV6, ns->target.be32, 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 && opt->len != 0) {
+        if (opt->type == ND_OPT_SOURCE_LINKADDR && opt->len == 1) {
+            if (!eth_addr_equals(opt->mac, 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, opt->mac, sll);
+                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 (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) {
+        } else if (opt->type == ND_OPT_TARGET_LINKADDR && opt->len == 1) {
+            if (!eth_addr_equals(opt->mac, 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, opt->mac, tll);
+                opt->mac = 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;
+        opt += opt->len;
+        bytes_remain -= opt->len * ND_LLA_OPT_LEN;
     }
 }
 
@@ -1133,38 +1341,216 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
  * '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. */
+ * 'broadcast' is true.  Points the L3 header to the ARP header. */
 void
 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;
+    compose_arp__(b);
+
+    struct eth_header *eth = dp_packet_eth(b);
+    eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
+    eth->eth_src = arp_sha;
+
+    struct arp_eth_header *arp = dp_packet_l3(b);
+    arp->ar_op = htons(arp_op);
+    arp->ar_sha = arp_sha;
+    arp->ar_tha = arp_tha;
+    put_16aligned_be32(&arp->ar_spa, arp_spa);
+    put_16aligned_be32(&arp->ar_tpa, arp_tpa);
+}
 
+/* Clears 'b' and replaces its contents by an ARP frame.  Sets the fields in
+ * the Ethernet and ARP headers that are fixed for ARP frames to those fixed
+ * values, and zeroes the other fields.  Points the L3 header to the ARP
+ * header. */
+void
+compose_arp__(struct dp_packet *b)
+{
     dp_packet_clear(b);
     dp_packet_prealloc_tailroom(b, ARP_PACKET_SIZE);
     dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
 
-    eth = dp_packet_put_uninit(b, sizeof *eth);
-    eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
-    eth->eth_src = arp_sha;
+    struct eth_header *eth = dp_packet_put_zeros(b, sizeof *eth);
     eth->eth_type = htons(ETH_TYPE_ARP);
 
-    arp = dp_packet_put_uninit(b, sizeof *arp);
+    struct arp_eth_header *arp = dp_packet_put_zeros(b, sizeof *arp);
     arp->ar_hrd = htons(ARP_HRD_ETHERNET);
     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);
-    arp->ar_sha = arp_sha;
-    arp->ar_tha = arp_tha;
-
-    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);
+
+    b->packet_type = htonl(PT_ETH);
+}
+
+/* This function expects packet with ethernet header with correct
+ * l3 pointer set. */
+static void *
+compose_ipv6(struct dp_packet *packet, uint8_t proto,
+             const struct in6_addr *src, const struct in6_addr *dst,
+             uint8_t key_tc, ovs_be32 key_fl, uint8_t key_hl, int size)
+{
+    struct ip6_hdr *nh;
+    void *data;
+
+    nh = dp_packet_l3(packet);
+    nh->ip6_vfc = 0x60;
+    nh->ip6_nxt = proto;
+    nh->ip6_plen = htons(size);
+    data = dp_packet_put_zeros(packet, size);
+    dp_packet_set_l4(packet, data);
+    packet_set_ipv6(packet, src, dst, key_tc, key_fl, key_hl);
+    return data;
+}
+
+/* Compose an IPv6 Neighbor Discovery Neighbor Solicitation message. */
+void
+compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
+              const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst)
+{
+    struct in6_addr sn_addr;
+    struct eth_addr eth_dst;
+    struct ovs_nd_msg *ns;
+    struct ovs_nd_lla_opt *lla_opt;
+    uint32_t icmp_csum;
+
+    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);
+    ns = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, &sn_addr,
+                      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));
+
+    lla_opt = &ns->options[0];
+    lla_opt->type = ND_OPT_SOURCE_LINKADDR;
+    lla_opt->len = 1;
+
+    packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
+
+    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_LLA_OPT_LEN));
+}
+
+/* Compose an IPv6 Neighbor Discovery Neighbor Advertisement message. */
+void
+compose_nd_na(struct dp_packet *b,
+              const struct eth_addr eth_src, const struct eth_addr eth_dst,
+              const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst,
+              ovs_be32 rso_flags)
+{
+    struct ovs_nd_msg *na;
+    struct ovs_nd_lla_opt *lla_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_LLA_OPT_LEN);
+
+    na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
+    na->icmph.icmp6_code = 0;
+    put_16aligned_be32(&na->rso_flags, rso_flags);
+
+    lla_opt = &na->options[0];
+    lla_opt->type = ND_OPT_TARGET_LINKADDR;
+    lla_opt->len = 1;
+
+    packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
+
+    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_LLA_OPT_LEN));
+}
+
+/* Compose an IPv6 Neighbor Discovery Router Advertisement message with
+ * Source Link-layer Address Option and MTU Option.
+ * Caller can call packet_put_ra_prefix_opt to append Prefix Information
+ * Options to composed messags in 'b'. */
+void
+compose_nd_ra(struct dp_packet *b,
+              const struct eth_addr eth_src, const struct eth_addr eth_dst,
+              const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst,
+              uint8_t cur_hop_limit, uint8_t mo_flags,
+              ovs_be16 router_lt, ovs_be32 reachable_time,
+              ovs_be32 retrans_timer, ovs_be32 mtu)
+{
+    /* Don't compose Router Advertisement packet with MTU Option if mtu
+     * value is 0. */
+    bool with_mtu = mtu != 0;
+    size_t mtu_opt_len = with_mtu ? ND_MTU_OPT_LEN : 0;
+
+    eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+
+    struct ovs_ra_msg *ra = compose_ipv6(
+        b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst, 0, 0, 255,
+        RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_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_lt;
+    ra->reachable_time = reachable_time;
+    ra->retrans_timer = retrans_timer;
+
+    struct ovs_nd_lla_opt *lla_opt = ra->options;
+    lla_opt->type = ND_OPT_SOURCE_LINKADDR;
+    lla_opt->len = 1;
+    lla_opt->mac = eth_src;
+
+    if (with_mtu) {
+        /* ovs_nd_mtu_opt has the same size with ovs_nd_lla_opt. */
+        struct ovs_nd_mtu_opt *mtu_opt
+            = (struct ovs_nd_mtu_opt *)(lla_opt + 1);
+        mtu_opt->type = ND_OPT_MTU;
+        mtu_opt->len = 1;
+        mtu_opt->reserved = 0;
+        put_16aligned_be32(&mtu_opt->mtu, mtu);
+    }
+
+    ra->icmph.icmp6_cksum = 0;
+    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    ra->icmph.icmp6_cksum = csum_finish(csum_continue(
+        icmp_csum, ra, RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_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_be128 prefix)
+{
+    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 =
+        dp_packet_put_uninit(b, sizeof *prefix_opt);
+    prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
+    prefix_opt->len = 4;
+    prefix_opt->prefix_len = plen;
+    prefix_opt->la_flags = la_flags;
+    put_16aligned_be32(&prefix_opt->valid_lifetime, valid_lifetime);
+    put_16aligned_be32(&prefix_opt->preferred_lifetime, preferred_lifetime);
+    put_16aligned_be32(&prefix_opt->reserved, 0);
+    memcpy(prefix_opt->prefix.be32, prefix.be32, sizeof(ovs_be32[4]));
+
+    ra->icmph.icmp6_cksum = 0;
+    uint32_t 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));
 }
 
 uint32_t
@@ -1181,3 +1567,58 @@ 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_continue(partial, &ip6->ip6_src, sizeof ip6->ip6_src);
+    partial = csum_continue(partial, &ip6->ip6_dst, sizeof ip6->ip6_dst);
+    partial = csum_add16(partial, htons(ip6->ip6_nxt));
+    partial = csum_add16(partial, ip6->ip6_plen);
+
+    return partial;
+}
+
+/* Calculate the IPv6 upper layer checksum according to RFC2460. We pass the
+   ip6_nxt and ip6_plen values, so it will also work if extension headers
+   are present. */
+uint16_t
+packet_csum_upperlayer6(const struct ovs_16aligned_ip6_hdr *ip6,
+                        const void *data, uint8_t l4_protocol,
+                        uint16_t l4_size)
+{
+    uint32_t partial = 0;
+
+    partial = csum_continue(partial, &ip6->ip6_src, sizeof ip6->ip6_src);
+    partial = csum_continue(partial, &ip6->ip6_dst, sizeof ip6->ip6_dst);
+    partial = csum_add16(partial, htons(l4_protocol));
+    partial = csum_add16(partial, htons(l4_size));
+
+    partial = csum_continue(partial, data, l4_size);
+
+    return csum_finish(partial);
+}
+#endif
+
+void
+IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6)
+{
+    if (is_ipv6) {
+        ovs_16aligned_be32 *ip6 = dp_packet_l3(pkt);
+
+        put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
+                                htonl(IP_ECN_CE << 20));
+    } else {
+        struct ip_header *nh = dp_packet_l3(pkt);
+        uint8_t tos = nh->ip_tos;
+
+        tos |= IP_ECN_CE;
+        if (nh->ip_tos != tos) {
+            nh->ip_csum = recalc_csum16(nh->ip_csum, htons(nh->ip_tos),
+                                        htons((uint16_t) tos));
+            nh->ip_tos = tos;
+        }
+    }
+}