From: Ben Pfaff Date: Thu, 15 Aug 2013 18:07:24 +0000 (-0700) Subject: packets: Introduce IPv6 headers not aligned on a 32-bit boundary. X-Git-Tag: v1.9.3~3 X-Git-Url: http://git.cascardo.eti.br/?p=cascardo%2Fovs.git;a=commitdiff_plain;h=5ff82adea7592fd723925e35a6f1d4d8653e001c packets: Introduce IPv6 headers not aligned on a 32-bit boundary. This fixes the same problem for IPv6 headers treated for other headers in the previous commit. Signed-off-by: Ben Pfaff --- diff --git a/lib/csum.c b/lib/csum.c index fb32a530d..a9334fec8 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 6382d298c..df4b19d4e 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 549ab1c16..1d8008832 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -138,7 +138,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; @@ -149,10 +149,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; @@ -200,7 +200,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 9198ff2db..f1644172f 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -483,7 +483,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; @@ -494,7 +494,7 @@ packet_rh_present(struct ofpbuf *packet) if (remaining < sizeof *nh) { return false; } - nh = (struct ip6_hdr *)data; + nh = (struct ovs_16aligned_ip6_hdr *)data; data += sizeof *nh; remaining -= sizeof *nh; nexthdr = nh->ip6_nxt; @@ -530,7 +530,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 = (struct ip6_frag *)data; + const struct ovs_16aligned_ip6_frag *frag_hdr + = (struct ovs_16aligned_ip6_frag *)data; nexthdr = frag_hdr->ip6f_nxt; len = sizeof *frag_hdr; @@ -562,7 +563,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; @@ -582,25 +583,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', @@ -647,14 +652,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 0dfe443a1..c5ad71c1d 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -431,6 +431,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