This fixes the same problem for IPv6 headers treated for other headers in
the previous commit.

Signed-off-by: Ben Pfaff <b...@nicira.com>
---
 lib/csum.c    |    7 ++++---
 lib/csum.h    |    2 +-
 lib/flow.c    |   10 +++++-----
 lib/packets.c |   32 ++++++++++++++++++--------------
 lib/packets.h |   32 ++++++++++++++++++++++++++++++++
 5 files changed, 60 insertions(+), 23 deletions(-)

diff --git a/lib/csum.c b/lib/csum.c
index fb32a53..a9334fe 100644
--- a/lib/csum.c
+++ b/lib/csum.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -116,14 +116,15 @@ recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, 
ovs_be32 new_u32)
  * contained 'old_csum' and in which a field that contained 'old_u32[4]' was
  * changed to contain 'new_u32[4]'. */
 ovs_be16
-recalc_csum128(ovs_be16 old_csum, ovs_be32 old_u32[4],
+recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
                const ovs_be32 new_u32[4])
 {
     ovs_be16 new_csum = old_csum;
     int i;
 
     for (i = 0; i < 4; ++i) {
-        new_csum = recalc_csum32(new_csum, old_u32[i], new_u32[i]);
+        new_csum = recalc_csum32(new_csum,
+                                 get_16aligned_be32(&old_u32[i]), new_u32[i]);
     }
     return new_csum;
 }
diff --git a/lib/csum.h b/lib/csum.h
index 6382d29..df4b19d 100644
--- a/lib/csum.h
+++ b/lib/csum.h
@@ -28,7 +28,7 @@ uint32_t csum_continue(uint32_t partial, const void *, 
size_t);
 ovs_be16 csum_finish(uint32_t partial);
 ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
 ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
-ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_be32 old_u32[4],
+ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
                         const ovs_be32 new_u32[4]);
 
 #endif /* csum.h */
diff --git a/lib/flow.c b/lib/flow.c
index b6a7946..b72d3f2 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -158,7 +158,7 @@ parse_ethertype(struct ofpbuf *b)
 static int
 parse_ipv6(struct ofpbuf *packet, struct flow *flow)
 {
-    const struct ip6_hdr *nh;
+    const struct ovs_16aligned_ip6_hdr *nh;
     ovs_be32 tc_flow;
     int nexthdr;
 
@@ -169,10 +169,10 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
 
     nexthdr = nh->ip6_nxt;
 
-    flow->ipv6_src = nh->ip6_src;
-    flow->ipv6_dst = nh->ip6_dst;
+    memcpy(&flow->ipv6_src, &nh->ip6_src, sizeof flow->ipv6_src);
+    memcpy(&flow->ipv6_dst, &nh->ip6_dst, sizeof flow->ipv6_dst);
 
-    tc_flow = get_unaligned_be32(&nh->ip6_flow);
+    tc_flow = get_16aligned_be32(&nh->ip6_flow);
     flow->nw_tos = ntohl(tc_flow) >> 20;
     flow->ipv6_label = tc_flow & htonl(IPV6_LABEL_MASK);
     flow->nw_ttl = nh->ip6_hlim;
@@ -220,7 +220,7 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
                return EINVAL;
             }
         } else if (nexthdr == IPPROTO_FRAGMENT) {
-            const struct ip6_frag *frag_hdr = packet->data;
+            const struct ovs_16aligned_ip6_frag *frag_hdr = packet->data;
 
             nexthdr = frag_hdr->ip6f_nxt;
             if (!ofpbuf_try_pull(packet, sizeof *frag_hdr)) {
diff --git a/lib/packets.c b/lib/packets.c
index 4e52353..61914ee 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -663,7 +663,7 @@ packet_set_ipv4_addr(struct ofpbuf *packet,
 static bool
 packet_rh_present(struct ofpbuf *packet)
 {
-    const struct ip6_hdr *nh;
+    const struct ovs_16aligned_ip6_hdr *nh;
     int nexthdr;
     size_t len;
     size_t remaining;
@@ -674,7 +674,7 @@ packet_rh_present(struct ofpbuf *packet)
     if (remaining < sizeof *nh) {
         return false;
     }
-    nh = ALIGNED_CAST(struct ip6_hdr *, data);
+    nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
     data += sizeof *nh;
     remaining -= sizeof *nh;
     nexthdr = nh->ip6_nxt;
@@ -710,8 +710,8 @@ packet_rh_present(struct ofpbuf *packet)
             nexthdr = ext_hdr->ip6e_nxt;
             len = (ext_hdr->ip6e_len + 2) * 4;
         } else if (nexthdr == IPPROTO_FRAGMENT) {
-            const struct ip6_frag *frag_hdr = ALIGNED_CAST(struct ip6_frag *,
-                                                           data);
+            const struct ovs_16aligned_ip6_frag *frag_hdr
+                = ALIGNED_CAST(struct ovs_16aligned_ip6_frag *, data);
 
             nexthdr = frag_hdr->ip6f_nxt;
             len = sizeof *frag_hdr;
@@ -743,7 +743,7 @@ packet_rh_present(struct ofpbuf *packet)
 
 static void
 packet_update_csum128(struct ofpbuf *packet, uint8_t proto,
-                     ovs_be32 addr[4], const ovs_be32 new_addr[4])
+                     ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4])
 {
     if (proto == IPPROTO_TCP && packet->l7) {
         struct tcp_header *th = packet->l4;
@@ -763,25 +763,29 @@ packet_update_csum128(struct ofpbuf *packet, uint8_t 
proto,
 
 static void
 packet_set_ipv6_addr(struct ofpbuf *packet, uint8_t proto,
-                     struct in6_addr *addr, const ovs_be32 new_addr[4],
+                     ovs_16aligned_be32 *addr, const ovs_be32 new_addr[4],
                      bool recalculate_csum)
 {
     if (recalculate_csum) {
-        packet_update_csum128(packet, proto, (ovs_be32 *)addr, new_addr);
+        packet_update_csum128(packet, proto, addr, new_addr);
     }
     memcpy(addr, new_addr, sizeof(*addr));
 }
 
 static void
-packet_set_ipv6_flow_label(ovs_be32 *flow_label, ovs_be32 flow_key)
+packet_set_ipv6_flow_label(ovs_16aligned_be32 *flow_label, ovs_be32 flow_key)
 {
-    *flow_label = (*flow_label & htonl(~IPV6_LABEL_MASK)) | flow_key;
+    ovs_be32 old_label = get_16aligned_be32(flow_label);
+    ovs_be32 new_label = (old_label & htonl(~IPV6_LABEL_MASK)) | flow_key;
+    put_16aligned_be32(flow_label, new_label);
 }
 
 static void
-packet_set_ipv6_tc(ovs_be32 *flow_label, uint8_t tc)
+packet_set_ipv6_tc(ovs_16aligned_be32 *flow_label, uint8_t tc)
 {
-    *flow_label = (*flow_label & htonl(0xF00FFFFF)) | htonl(tc << 20);
+    ovs_be32 old_label = get_16aligned_be32(flow_label);
+    ovs_be32 new_label = (old_label & htonl(0xF00FFFFF)) | htonl(tc << 20);
+    put_16aligned_be32(flow_label, new_label);
 }
 
 /* Modifies the IPv4 header fields of 'packet' to be consistent with 'src',
@@ -828,14 +832,14 @@ packet_set_ipv6(struct ofpbuf *packet, uint8_t proto, 
const ovs_be32 src[4],
                 const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
                 uint8_t key_hl)
 {
-    struct ip6_hdr *nh = packet->l3;
+    struct ovs_16aligned_ip6_hdr *nh = packet->l3;
 
     if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
-        packet_set_ipv6_addr(packet, proto, &nh->ip6_src, src, true);
+        packet_set_ipv6_addr(packet, proto, nh->ip6_src.be32, src, true);
     }
 
     if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) {
-        packet_set_ipv6_addr(packet, proto, &nh->ip6_dst, dst,
+        packet_set_ipv6_addr(packet, proto, nh->ip6_dst.be32, dst,
                              !packet_rh_present(packet));
     }
 
diff --git a/lib/packets.h b/lib/packets.h
index f71c6e9..2f94bfb 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -527,6 +527,38 @@ struct arp_eth_header {
 };
 BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header));
 
+/* Like struct in6_addr, but whereas that struct requires 32-bit alignment on
+ * most implementations, this one only requires 16-bit alignment. */
+union ovs_16aligned_in6_addr {
+    ovs_be16 be16[8];
+    ovs_16aligned_be32 be32[4];
+};
+
+/* Like struct in6_hdr, but whereas that struct requires 32-bit alignment, this
+ * one only requires 16-bit alignment. */
+struct ovs_16aligned_ip6_hdr {
+    union {
+        struct ovs_16aligned_ip6_hdrctl {
+            ovs_16aligned_be32 ip6_un1_flow;
+            ovs_be16 ip6_un1_plen;
+            uint8_t ip6_un1_nxt;
+            uint8_t ip6_un1_hlim;
+        } ip6_un1;
+        uint8_t ip6_un2_vfc;
+    } ip6_ctlun;
+    union ovs_16aligned_in6_addr ip6_src;
+    union ovs_16aligned_in6_addr ip6_dst;
+};
+
+/* Like struct in6_frag, but whereas that struct requires 32-bit alignment,
+ * this one only requires 16-bit alignment. */
+struct ovs_16aligned_ip6_frag {
+    uint8_t ip6f_nxt;
+    uint8_t ip6f_reserved;
+    ovs_be16 ip6f_offlg;
+    ovs_16aligned_be32 ip6f_ident;
+};
+
 /* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
 #define IPV6_LABEL_MASK 0x000fffff
 
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to