From 4528f34f93422dde4c8ae4c56869cc4e268159c7 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 15 Aug 2013 11:07:24 -0700 Subject: [PATCH] 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 --- 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 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 e8c22dbbc..826367217 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -164,7 +164,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; @@ -175,10 +175,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; @@ -226,7 +226,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 36b468905..07cc015ad 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -664,7 +664,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; @@ -675,7 +675,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; @@ -711,8 +711,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; @@ -744,7 +744,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; @@ -764,25 +764,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', @@ -829,14 +833,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 32f3b2da6..b77691465 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -536,6 +536,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 -- 2.43.0