diff options
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
| -rw-r--r-- | net/ipv6/ipv6_sockglue.c | 122 |
1 files changed, 119 insertions, 3 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 4c20eeb3d568..de6b91981b30 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
| @@ -27,7 +27,6 @@ | |||
| 27 | 27 | ||
| 28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
| 29 | #include <linux/capability.h> | 29 | #include <linux/capability.h> |
| 30 | #include <linux/config.h> | ||
| 31 | #include <linux/errno.h> | 30 | #include <linux/errno.h> |
| 32 | #include <linux/types.h> | 31 | #include <linux/types.h> |
| 33 | #include <linux/socket.h> | 32 | #include <linux/socket.h> |
| @@ -58,9 +57,116 @@ | |||
| 58 | 57 | ||
| 59 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; | 58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; |
| 60 | 59 | ||
| 60 | static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, | ||
| 61 | int proto) | ||
| 62 | { | ||
| 63 | struct inet6_protocol *ops = NULL; | ||
| 64 | |||
| 65 | for (;;) { | ||
| 66 | struct ipv6_opt_hdr *opth; | ||
| 67 | int len; | ||
| 68 | |||
| 69 | if (proto != NEXTHDR_HOP) { | ||
| 70 | ops = rcu_dereference(inet6_protos[proto]); | ||
| 71 | |||
| 72 | if (unlikely(!ops)) | ||
| 73 | break; | ||
| 74 | |||
| 75 | if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) | ||
| 76 | break; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (unlikely(!pskb_may_pull(skb, 8))) | ||
| 80 | break; | ||
| 81 | |||
| 82 | opth = (void *)skb->data; | ||
| 83 | len = opth->hdrlen * 8 + 8; | ||
| 84 | |||
| 85 | if (unlikely(!pskb_may_pull(skb, len))) | ||
| 86 | break; | ||
| 87 | |||
| 88 | proto = opth->nexthdr; | ||
| 89 | __skb_pull(skb, len); | ||
| 90 | } | ||
| 91 | |||
| 92 | return ops; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int ipv6_gso_send_check(struct sk_buff *skb) | ||
| 96 | { | ||
| 97 | struct ipv6hdr *ipv6h; | ||
| 98 | struct inet6_protocol *ops; | ||
| 99 | int err = -EINVAL; | ||
| 100 | |||
| 101 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
| 102 | goto out; | ||
| 103 | |||
| 104 | ipv6h = skb->nh.ipv6h; | ||
| 105 | __skb_pull(skb, sizeof(*ipv6h)); | ||
| 106 | err = -EPROTONOSUPPORT; | ||
| 107 | |||
| 108 | rcu_read_lock(); | ||
| 109 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
| 110 | if (likely(ops && ops->gso_send_check)) { | ||
| 111 | skb->h.raw = skb->data; | ||
| 112 | err = ops->gso_send_check(skb); | ||
| 113 | } | ||
| 114 | rcu_read_unlock(); | ||
| 115 | |||
| 116 | out: | ||
| 117 | return err; | ||
| 118 | } | ||
| 119 | |||
| 120 | static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | ||
| 121 | { | ||
| 122 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
| 123 | struct ipv6hdr *ipv6h; | ||
| 124 | struct inet6_protocol *ops; | ||
| 125 | |||
| 126 | if (!(features & NETIF_F_HW_CSUM)) | ||
| 127 | features &= ~NETIF_F_SG; | ||
| 128 | |||
| 129 | if (unlikely(skb_shinfo(skb)->gso_type & | ||
| 130 | ~(SKB_GSO_UDP | | ||
| 131 | SKB_GSO_DODGY | | ||
| 132 | SKB_GSO_TCP_ECN | | ||
| 133 | SKB_GSO_TCPV6 | | ||
| 134 | 0))) | ||
| 135 | goto out; | ||
| 136 | |||
| 137 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
| 138 | goto out; | ||
| 139 | |||
| 140 | ipv6h = skb->nh.ipv6h; | ||
| 141 | __skb_pull(skb, sizeof(*ipv6h)); | ||
| 142 | segs = ERR_PTR(-EPROTONOSUPPORT); | ||
| 143 | |||
| 144 | rcu_read_lock(); | ||
| 145 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
| 146 | if (likely(ops && ops->gso_segment)) { | ||
| 147 | skb->h.raw = skb->data; | ||
| 148 | segs = ops->gso_segment(skb, features); | ||
| 149 | } | ||
| 150 | rcu_read_unlock(); | ||
| 151 | |||
| 152 | if (unlikely(IS_ERR(segs))) | ||
| 153 | goto out; | ||
| 154 | |||
| 155 | for (skb = segs; skb; skb = skb->next) { | ||
| 156 | ipv6h = skb->nh.ipv6h; | ||
| 157 | ipv6h->payload_len = htons(skb->len - skb->mac_len - | ||
| 158 | sizeof(*ipv6h)); | ||
| 159 | } | ||
| 160 | |||
| 161 | out: | ||
| 162 | return segs; | ||
| 163 | } | ||
| 164 | |||
| 61 | static struct packet_type ipv6_packet_type = { | 165 | static struct packet_type ipv6_packet_type = { |
| 62 | .type = __constant_htons(ETH_P_IPV6), | 166 | .type = __constant_htons(ETH_P_IPV6), |
| 63 | .func = ipv6_rcv, | 167 | .func = ipv6_rcv, |
| 168 | .gso_send_check = ipv6_gso_send_check, | ||
| 169 | .gso_segment = ipv6_gso_segment, | ||
| 64 | }; | 170 | }; |
| 65 | 171 | ||
| 66 | struct ip6_ra_chain *ip6_ra_chain; | 172 | struct ip6_ra_chain *ip6_ra_chain; |
| @@ -259,7 +365,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
| 259 | break; | 365 | break; |
| 260 | 366 | ||
| 261 | case IPV6_TCLASS: | 367 | case IPV6_TCLASS: |
| 262 | if (val < 0 || val > 0xff) | 368 | if (val < -1 || val > 0xff) |
| 263 | goto e_inval; | 369 | goto e_inval; |
| 264 | np->tclass = val; | 370 | np->tclass = val; |
| 265 | retv = 0; | 371 | retv = 0; |
| @@ -304,8 +410,16 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
| 304 | /* routing header option needs extra check */ | 410 | /* routing header option needs extra check */ |
| 305 | if (optname == IPV6_RTHDR && opt->srcrt) { | 411 | if (optname == IPV6_RTHDR && opt->srcrt) { |
| 306 | struct ipv6_rt_hdr *rthdr = opt->srcrt; | 412 | struct ipv6_rt_hdr *rthdr = opt->srcrt; |
| 307 | if (rthdr->type) | 413 | switch (rthdr->type) { |
| 414 | case IPV6_SRCRT_TYPE_0: | ||
| 415 | #ifdef CONFIG_IPV6_MIP6 | ||
| 416 | case IPV6_SRCRT_TYPE_2: | ||
| 417 | #endif | ||
| 418 | break; | ||
| 419 | default: | ||
| 308 | goto sticky_done; | 420 | goto sticky_done; |
| 421 | } | ||
| 422 | |||
| 309 | if ((rthdr->hdrlen & 1) || | 423 | if ((rthdr->hdrlen & 1) || |
| 310 | (rthdr->hdrlen >> 1) != rthdr->segments_left) | 424 | (rthdr->hdrlen >> 1) != rthdr->segments_left) |
| 311 | goto sticky_done; | 425 | goto sticky_done; |
| @@ -844,6 +958,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
| 844 | 958 | ||
| 845 | case IPV6_TCLASS: | 959 | case IPV6_TCLASS: |
| 846 | val = np->tclass; | 960 | val = np->tclass; |
| 961 | if (val < 0) | ||
| 962 | val = 0; | ||
| 847 | break; | 963 | break; |
| 848 | 964 | ||
| 849 | case IPV6_RECVTCLASS: | 965 | case IPV6_RECVTCLASS: |
