diff options
Diffstat (limited to 'net/ipv6/ipv6_sockglue.c')
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 207 |
1 files changed, 81 insertions, 126 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index bf2a686aa13d..d3d93d752e10 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -57,118 +57,6 @@ | |||
57 | 57 | ||
58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; | 58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; |
59 | 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 = ipv6_hdr(skb); | ||
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_reset_transport_header(skb); | ||
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_V6_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 = ipv6_hdr(skb); | ||
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_reset_transport_header(skb); | ||
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 = ipv6_hdr(skb); | ||
157 | ipv6h->payload_len = htons(skb->len - skb->mac_len - | ||
158 | sizeof(*ipv6h)); | ||
159 | } | ||
160 | |||
161 | out: | ||
162 | return segs; | ||
163 | } | ||
164 | |||
165 | static struct packet_type ipv6_packet_type = { | ||
166 | .type = __constant_htons(ETH_P_IPV6), | ||
167 | .func = ipv6_rcv, | ||
168 | .gso_send_check = ipv6_gso_send_check, | ||
169 | .gso_segment = ipv6_gso_segment, | ||
170 | }; | ||
171 | |||
172 | struct ip6_ra_chain *ip6_ra_chain; | 60 | struct ip6_ra_chain *ip6_ra_chain; |
173 | DEFINE_RWLOCK(ip6_ra_lock); | 61 | DEFINE_RWLOCK(ip6_ra_lock); |
174 | 62 | ||
@@ -219,6 +107,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
219 | char __user *optval, int optlen) | 107 | char __user *optval, int optlen) |
220 | { | 108 | { |
221 | struct ipv6_pinfo *np = inet6_sk(sk); | 109 | struct ipv6_pinfo *np = inet6_sk(sk); |
110 | struct net *net = sock_net(sk); | ||
222 | int val, valbool; | 111 | int val, valbool; |
223 | int retv = -ENOPROTOOPT; | 112 | int retv = -ENOPROTOOPT; |
224 | 113 | ||
@@ -544,7 +433,7 @@ done: | |||
544 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val) | 433 | if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val) |
545 | goto e_inval; | 434 | goto e_inval; |
546 | 435 | ||
547 | if (__dev_get_by_index(&init_net, val) == NULL) { | 436 | if (__dev_get_by_index(net, val) == NULL) { |
548 | retv = -ENODEV; | 437 | retv = -ENODEV; |
549 | break; | 438 | break; |
550 | } | 439 | } |
@@ -728,7 +617,67 @@ done: | |||
728 | retv = xfrm_user_policy(sk, optname, optval, optlen); | 617 | retv = xfrm_user_policy(sk, optname, optval, optlen); |
729 | break; | 618 | break; |
730 | 619 | ||
620 | case IPV6_ADDR_PREFERENCES: | ||
621 | { | ||
622 | unsigned int pref = 0; | ||
623 | unsigned int prefmask = ~0; | ||
624 | |||
625 | retv = -EINVAL; | ||
626 | |||
627 | /* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */ | ||
628 | switch (val & (IPV6_PREFER_SRC_PUBLIC| | ||
629 | IPV6_PREFER_SRC_TMP| | ||
630 | IPV6_PREFER_SRC_PUBTMP_DEFAULT)) { | ||
631 | case IPV6_PREFER_SRC_PUBLIC: | ||
632 | pref |= IPV6_PREFER_SRC_PUBLIC; | ||
633 | break; | ||
634 | case IPV6_PREFER_SRC_TMP: | ||
635 | pref |= IPV6_PREFER_SRC_TMP; | ||
636 | break; | ||
637 | case IPV6_PREFER_SRC_PUBTMP_DEFAULT: | ||
638 | break; | ||
639 | case 0: | ||
640 | goto pref_skip_pubtmp; | ||
641 | default: | ||
642 | goto e_inval; | ||
643 | } | ||
644 | |||
645 | prefmask &= ~(IPV6_PREFER_SRC_PUBLIC| | ||
646 | IPV6_PREFER_SRC_TMP); | ||
647 | pref_skip_pubtmp: | ||
648 | |||
649 | /* check HOME/COA conflicts */ | ||
650 | switch (val & (IPV6_PREFER_SRC_HOME|IPV6_PREFER_SRC_COA)) { | ||
651 | case IPV6_PREFER_SRC_HOME: | ||
652 | break; | ||
653 | case IPV6_PREFER_SRC_COA: | ||
654 | pref |= IPV6_PREFER_SRC_COA; | ||
655 | case 0: | ||
656 | goto pref_skip_coa; | ||
657 | default: | ||
658 | goto e_inval; | ||
659 | } | ||
660 | |||
661 | prefmask &= ~IPV6_PREFER_SRC_COA; | ||
662 | pref_skip_coa: | ||
663 | |||
664 | /* check CGA/NONCGA conflicts */ | ||
665 | switch (val & (IPV6_PREFER_SRC_CGA|IPV6_PREFER_SRC_NONCGA)) { | ||
666 | case IPV6_PREFER_SRC_CGA: | ||
667 | case IPV6_PREFER_SRC_NONCGA: | ||
668 | case 0: | ||
669 | break; | ||
670 | default: | ||
671 | goto e_inval; | ||
672 | } | ||
673 | |||
674 | np->srcprefs = (np->srcprefs & prefmask) | pref; | ||
675 | retv = 0; | ||
676 | |||
677 | break; | ||
678 | } | ||
731 | } | 679 | } |
680 | |||
732 | release_sock(sk); | 681 | release_sock(sk); |
733 | 682 | ||
734 | return retv; | 683 | return retv; |
@@ -1015,9 +964,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1015 | dst = sk_dst_get(sk); | 964 | dst = sk_dst_get(sk); |
1016 | if (dst) { | 965 | if (dst) { |
1017 | if (val < 0) | 966 | if (val < 0) |
1018 | val = dst_metric(dst, RTAX_HOPLIMIT); | 967 | val = ip6_dst_hoplimit(dst); |
1019 | if (val < 0) | ||
1020 | val = ipv6_get_hoplimit(dst->dev); | ||
1021 | dst_release(dst); | 968 | dst_release(dst); |
1022 | } | 969 | } |
1023 | if (val < 0) | 970 | if (val < 0) |
@@ -1045,6 +992,24 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1045 | val = np->sndflow; | 992 | val = np->sndflow; |
1046 | break; | 993 | break; |
1047 | 994 | ||
995 | case IPV6_ADDR_PREFERENCES: | ||
996 | val = 0; | ||
997 | |||
998 | if (np->srcprefs & IPV6_PREFER_SRC_TMP) | ||
999 | val |= IPV6_PREFER_SRC_TMP; | ||
1000 | else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC) | ||
1001 | val |= IPV6_PREFER_SRC_PUBLIC; | ||
1002 | else { | ||
1003 | /* XXX: should we return system default? */ | ||
1004 | val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT; | ||
1005 | } | ||
1006 | |||
1007 | if (np->srcprefs & IPV6_PREFER_SRC_COA) | ||
1008 | val |= IPV6_PREFER_SRC_COA; | ||
1009 | else | ||
1010 | val |= IPV6_PREFER_SRC_HOME; | ||
1011 | break; | ||
1012 | |||
1048 | default: | 1013 | default: |
1049 | return -ENOPROTOOPT; | 1014 | return -ENOPROTOOPT; |
1050 | } | 1015 | } |
@@ -1128,13 +1093,3 @@ int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, | |||
1128 | EXPORT_SYMBOL(compat_ipv6_getsockopt); | 1093 | EXPORT_SYMBOL(compat_ipv6_getsockopt); |
1129 | #endif | 1094 | #endif |
1130 | 1095 | ||
1131 | int __init ipv6_packet_init(void) | ||
1132 | { | ||
1133 | dev_add_pack(&ipv6_packet_type); | ||
1134 | return 0; | ||
1135 | } | ||
1136 | |||
1137 | void ipv6_packet_cleanup(void) | ||
1138 | { | ||
1139 | dev_remove_pack(&ipv6_packet_type); | ||
1140 | } | ||