diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2014-10-30 14:27:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-30 20:01:18 -0400 |
commit | 5188cd44c55db3e92cd9e77a40b5baa7ed4340f7 (patch) | |
tree | d4f4a4497056c8bbf9dd7435356e45f9bcbf9796 | |
parent | 3d0ad09412ffe00c9afa201d01effdb6023d09b4 (diff) |
drivers/net, ipv6: Select IPv6 fragment idents for virtio UFO packets
UFO is now disabled on all drivers that work with virtio net headers,
but userland may try to send UFO/IPv6 packets anyway. Instead of
sending with ID=0, we should select identifiers on their behalf (as we
used to).
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Fixes: 916e4cf46d02 ("ipv6: reuse ip6_frag_id from ip6_ufo_append_data")
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/macvtap.c | 3 | ||||
-rw-r--r-- | drivers/net/tun.c | 6 | ||||
-rw-r--r-- | include/net/ipv6.h | 2 | ||||
-rw-r--r-- | net/ipv6/output_core.c | 34 |
4 files changed, 44 insertions, 1 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 2aeaa61ece09..6f226de655a4 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/idr.h> | 16 | #include <linux/idr.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | 18 | ||
19 | #include <net/ipv6.h> | ||
19 | #include <net/net_namespace.h> | 20 | #include <net/net_namespace.h> |
20 | #include <net/rtnetlink.h> | 21 | #include <net/rtnetlink.h> |
21 | #include <net/sock.h> | 22 | #include <net/sock.h> |
@@ -572,6 +573,8 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, | |||
572 | pr_warn_once("macvtap: %s: using disabled UFO feature; please fix this program\n", | 573 | pr_warn_once("macvtap: %s: using disabled UFO feature; please fix this program\n", |
573 | current->comm); | 574 | current->comm); |
574 | gso_type = SKB_GSO_UDP; | 575 | gso_type = SKB_GSO_UDP; |
576 | if (skb->protocol == htons(ETH_P_IPV6)) | ||
577 | ipv6_proxy_select_ident(skb); | ||
575 | break; | 578 | break; |
576 | default: | 579 | default: |
577 | return -EINVAL; | 580 | return -EINVAL; |
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 280d3d2a9792..7302398f0b1f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include <linux/nsproxy.h> | 65 | #include <linux/nsproxy.h> |
66 | #include <linux/virtio_net.h> | 66 | #include <linux/virtio_net.h> |
67 | #include <linux/rcupdate.h> | 67 | #include <linux/rcupdate.h> |
68 | #include <net/ipv6.h> | ||
68 | #include <net/net_namespace.h> | 69 | #include <net/net_namespace.h> |
69 | #include <net/netns/generic.h> | 70 | #include <net/netns/generic.h> |
70 | #include <net/rtnetlink.h> | 71 | #include <net/rtnetlink.h> |
@@ -1139,6 +1140,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1139 | break; | 1140 | break; |
1140 | } | 1141 | } |
1141 | 1142 | ||
1143 | skb_reset_network_header(skb); | ||
1144 | |||
1142 | if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { | 1145 | if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { |
1143 | pr_debug("GSO!\n"); | 1146 | pr_debug("GSO!\n"); |
1144 | switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { | 1147 | switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { |
@@ -1159,6 +1162,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1159 | current->comm); | 1162 | current->comm); |
1160 | } | 1163 | } |
1161 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; | 1164 | skb_shinfo(skb)->gso_type = SKB_GSO_UDP; |
1165 | if (skb->protocol == htons(ETH_P_IPV6)) | ||
1166 | ipv6_proxy_select_ident(skb); | ||
1162 | break; | 1167 | break; |
1163 | } | 1168 | } |
1164 | default: | 1169 | default: |
@@ -1189,7 +1194,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, | |||
1189 | skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; | 1194 | skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; |
1190 | } | 1195 | } |
1191 | 1196 | ||
1192 | skb_reset_network_header(skb); | ||
1193 | skb_probe_transport_header(skb, 0); | 1197 | skb_probe_transport_header(skb, 0); |
1194 | 1198 | ||
1195 | rxhash = skb_get_hash(skb); | 1199 | rxhash = skb_get_hash(skb); |
diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 97f472012438..4292929392b0 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h | |||
@@ -671,6 +671,8 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add | |||
671 | return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); | 671 | return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); |
672 | } | 672 | } |
673 | 673 | ||
674 | void ipv6_proxy_select_ident(struct sk_buff *skb); | ||
675 | |||
674 | int ip6_dst_hoplimit(struct dst_entry *dst); | 676 | int ip6_dst_hoplimit(struct dst_entry *dst); |
675 | 677 | ||
676 | static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, | 678 | static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, |
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index fc24c390af05..97f41a3e68d9 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c | |||
@@ -3,11 +3,45 @@ | |||
3 | * not configured or static. These functions are needed by GSO/GRO implementation. | 3 | * not configured or static. These functions are needed by GSO/GRO implementation. |
4 | */ | 4 | */ |
5 | #include <linux/export.h> | 5 | #include <linux/export.h> |
6 | #include <net/ip.h> | ||
6 | #include <net/ipv6.h> | 7 | #include <net/ipv6.h> |
7 | #include <net/ip6_fib.h> | 8 | #include <net/ip6_fib.h> |
8 | #include <net/addrconf.h> | 9 | #include <net/addrconf.h> |
9 | #include <net/secure_seq.h> | 10 | #include <net/secure_seq.h> |
10 | 11 | ||
12 | /* This function exists only for tap drivers that must support broken | ||
13 | * clients requesting UFO without specifying an IPv6 fragment ID. | ||
14 | * | ||
15 | * This is similar to ipv6_select_ident() but we use an independent hash | ||
16 | * seed to limit information leakage. | ||
17 | * | ||
18 | * The network header must be set before calling this. | ||
19 | */ | ||
20 | void ipv6_proxy_select_ident(struct sk_buff *skb) | ||
21 | { | ||
22 | static u32 ip6_proxy_idents_hashrnd __read_mostly; | ||
23 | struct in6_addr buf[2]; | ||
24 | struct in6_addr *addrs; | ||
25 | u32 hash, id; | ||
26 | |||
27 | addrs = skb_header_pointer(skb, | ||
28 | skb_network_offset(skb) + | ||
29 | offsetof(struct ipv6hdr, saddr), | ||
30 | sizeof(buf), buf); | ||
31 | if (!addrs) | ||
32 | return; | ||
33 | |||
34 | net_get_random_once(&ip6_proxy_idents_hashrnd, | ||
35 | sizeof(ip6_proxy_idents_hashrnd)); | ||
36 | |||
37 | hash = __ipv6_addr_jhash(&addrs[1], ip6_proxy_idents_hashrnd); | ||
38 | hash = __ipv6_addr_jhash(&addrs[0], hash); | ||
39 | |||
40 | id = ip_idents_reserve(hash, 1); | ||
41 | skb_shinfo(skb)->ip6_frag_id = htonl(id); | ||
42 | } | ||
43 | EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); | ||
44 | |||
11 | int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) | 45 | int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) |
12 | { | 46 | { |
13 | u16 offset = sizeof(struct ipv6hdr); | 47 | u16 offset = sizeof(struct ipv6hdr); |