aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorAlexander Duyck <alexander.h.duyck@redhat.com>2014-10-10 15:09:12 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-10 15:33:47 -0400
commit5af7fb6e3e92c2797ee30d66138cf6aa6b29240d (patch)
treed5e29d77ae2ec4e10af7220cd29d06f045ddbda2 /net/core
parent8ea6e345a6123fa831e42cd8747f55892a58abff (diff)
flow-dissector: Fix alignment issue in __skb_flow_get_ports
This patch addresses a kernel unaligned access bug seen on a sparc64 system with an igb adapter. Specifically the __skb_flow_get_ports was returning a be32 pointer which was then having the value directly returned. In order to prevent this it is actually easier to simply not populate the ports or address values when an skb is not present. In this case the assumption is that the data isn't needed and rather than slow down the faster aligned accesses by making them have to assume the unaligned path on architectures that don't support efficent unaligned access it makes more sense to simply switch off the bits that were copying the source and destination address/port for the case where we only care about the protocol types and lengths which are normally 16 bit fields anyway. Reported-by: David S. Miller <davem@davemloft.net> Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/flow_dissector.c36
1 files changed, 23 insertions, 13 deletions
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 8560dea58803..45084938c403 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -100,6 +100,13 @@ ip:
100 if (ip_is_fragment(iph)) 100 if (ip_is_fragment(iph))
101 ip_proto = 0; 101 ip_proto = 0;
102 102
103 /* skip the address processing if skb is NULL. The assumption
104 * here is that if there is no skb we are not looking for flow
105 * info but lengths and protocols.
106 */
107 if (!skb)
108 break;
109
103 iph_to_flow_copy_addrs(flow, iph); 110 iph_to_flow_copy_addrs(flow, iph);
104 break; 111 break;
105 } 112 }
@@ -114,17 +121,15 @@ ipv6:
114 return false; 121 return false;
115 122
116 ip_proto = iph->nexthdr; 123 ip_proto = iph->nexthdr;
117 flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
118 flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
119 nhoff += sizeof(struct ipv6hdr); 124 nhoff += sizeof(struct ipv6hdr);
120 125
121 /* skip the flow label processing if skb is NULL. The 126 /* see comment above in IPv4 section */
122 * assumption here is that if there is no skb we are not
123 * looking for flow info as much as we are length.
124 */
125 if (!skb) 127 if (!skb)
126 break; 128 break;
127 129
130 flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
131 flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
132
128 flow_label = ip6_flowlabel(iph); 133 flow_label = ip6_flowlabel(iph);
129 if (flow_label) { 134 if (flow_label) {
130 /* Awesome, IPv6 packet has a flow label so we can 135 /* Awesome, IPv6 packet has a flow label so we can
@@ -231,9 +236,13 @@ ipv6:
231 236
232 flow->n_proto = proto; 237 flow->n_proto = proto;
233 flow->ip_proto = ip_proto; 238 flow->ip_proto = ip_proto;
234 flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto, data, hlen);
235 flow->thoff = (u16) nhoff; 239 flow->thoff = (u16) nhoff;
236 240
241 /* unless skb is set we don't need to record port info */
242 if (skb)
243 flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
244 data, hlen);
245
237 return true; 246 return true;
238} 247}
239EXPORT_SYMBOL(__skb_flow_dissect); 248EXPORT_SYMBOL(__skb_flow_dissect);
@@ -334,15 +343,16 @@ u32 __skb_get_poff(const struct sk_buff *skb, void *data,
334 343
335 switch (keys->ip_proto) { 344 switch (keys->ip_proto) {
336 case IPPROTO_TCP: { 345 case IPPROTO_TCP: {
337 const struct tcphdr *tcph; 346 /* access doff as u8 to avoid unaligned access */
338 struct tcphdr _tcph; 347 const u8 *doff;
348 u8 _doff;
339 349
340 tcph = __skb_header_pointer(skb, poff, sizeof(_tcph), 350 doff = __skb_header_pointer(skb, poff + 12, sizeof(_doff),
341 data, hlen, &_tcph); 351 data, hlen, &_doff);
342 if (!tcph) 352 if (!doff)
343 return poff; 353 return poff;
344 354
345 poff += max_t(u32, sizeof(struct tcphdr), tcph->doff * 4); 355 poff += max_t(u32, sizeof(struct tcphdr), (*doff & 0xF0) >> 2);
346 break; 356 break;
347 } 357 }
348 case IPPROTO_UDP: 358 case IPPROTO_UDP: