diff options
Diffstat (limited to 'net/dccp/ipv6.c')
-rw-r--r-- | net/dccp/ipv6.c | 385 |
1 files changed, 190 insertions, 195 deletions
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 718509dcb24d..ed4a50263802 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c | |||
@@ -36,11 +36,6 @@ | |||
36 | /* Socket used for sending RSTs and ACKs */ | 36 | /* Socket used for sending RSTs and ACKs */ |
37 | static struct socket *dccp_v6_ctl_socket; | 37 | static struct socket *dccp_v6_ctl_socket; |
38 | 38 | ||
39 | static void dccp_v6_ctl_send_reset(struct sk_buff *skb); | ||
40 | static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb); | ||
41 | |||
42 | static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); | ||
43 | |||
44 | static struct inet_connection_sock_af_ops dccp_ipv6_mapped; | 39 | static struct inet_connection_sock_af_ops dccp_ipv6_mapped; |
45 | static struct inet_connection_sock_af_ops dccp_ipv6_af_ops; | 40 | static struct inet_connection_sock_af_ops dccp_ipv6_af_ops; |
46 | 41 | ||
@@ -87,183 +82,6 @@ static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb) | |||
87 | dh->dccph_sport); | 82 | dh->dccph_sport); |
88 | } | 83 | } |
89 | 84 | ||
90 | static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | ||
91 | int addr_len) | ||
92 | { | ||
93 | struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr; | ||
94 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
95 | struct inet_sock *inet = inet_sk(sk); | ||
96 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
97 | struct dccp_sock *dp = dccp_sk(sk); | ||
98 | struct in6_addr *saddr = NULL, *final_p = NULL, final; | ||
99 | struct flowi fl; | ||
100 | struct dst_entry *dst; | ||
101 | int addr_type; | ||
102 | int err; | ||
103 | |||
104 | dp->dccps_role = DCCP_ROLE_CLIENT; | ||
105 | |||
106 | if (addr_len < SIN6_LEN_RFC2133) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (usin->sin6_family != AF_INET6) | ||
110 | return -EAFNOSUPPORT; | ||
111 | |||
112 | memset(&fl, 0, sizeof(fl)); | ||
113 | |||
114 | if (np->sndflow) { | ||
115 | fl.fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; | ||
116 | IP6_ECN_flow_init(fl.fl6_flowlabel); | ||
117 | if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) { | ||
118 | struct ip6_flowlabel *flowlabel; | ||
119 | flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); | ||
120 | if (flowlabel == NULL) | ||
121 | return -EINVAL; | ||
122 | ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst); | ||
123 | fl6_sock_release(flowlabel); | ||
124 | } | ||
125 | } | ||
126 | /* | ||
127 | * connect() to INADDR_ANY means loopback (BSD'ism). | ||
128 | */ | ||
129 | if (ipv6_addr_any(&usin->sin6_addr)) | ||
130 | usin->sin6_addr.s6_addr[15] = 1; | ||
131 | |||
132 | addr_type = ipv6_addr_type(&usin->sin6_addr); | ||
133 | |||
134 | if (addr_type & IPV6_ADDR_MULTICAST) | ||
135 | return -ENETUNREACH; | ||
136 | |||
137 | if (addr_type & IPV6_ADDR_LINKLOCAL) { | ||
138 | if (addr_len >= sizeof(struct sockaddr_in6) && | ||
139 | usin->sin6_scope_id) { | ||
140 | /* If interface is set while binding, indices | ||
141 | * must coincide. | ||
142 | */ | ||
143 | if (sk->sk_bound_dev_if && | ||
144 | sk->sk_bound_dev_if != usin->sin6_scope_id) | ||
145 | return -EINVAL; | ||
146 | |||
147 | sk->sk_bound_dev_if = usin->sin6_scope_id; | ||
148 | } | ||
149 | |||
150 | /* Connect to link-local address requires an interface */ | ||
151 | if (!sk->sk_bound_dev_if) | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
155 | ipv6_addr_copy(&np->daddr, &usin->sin6_addr); | ||
156 | np->flow_label = fl.fl6_flowlabel; | ||
157 | |||
158 | /* | ||
159 | * DCCP over IPv4 | ||
160 | */ | ||
161 | if (addr_type == IPV6_ADDR_MAPPED) { | ||
162 | u32 exthdrlen = icsk->icsk_ext_hdr_len; | ||
163 | struct sockaddr_in sin; | ||
164 | |||
165 | SOCK_DEBUG(sk, "connect: ipv4 mapped\n"); | ||
166 | |||
167 | if (__ipv6_only_sock(sk)) | ||
168 | return -ENETUNREACH; | ||
169 | |||
170 | sin.sin_family = AF_INET; | ||
171 | sin.sin_port = usin->sin6_port; | ||
172 | sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; | ||
173 | |||
174 | icsk->icsk_af_ops = &dccp_ipv6_mapped; | ||
175 | sk->sk_backlog_rcv = dccp_v4_do_rcv; | ||
176 | |||
177 | err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin)); | ||
178 | if (err) { | ||
179 | icsk->icsk_ext_hdr_len = exthdrlen; | ||
180 | icsk->icsk_af_ops = &dccp_ipv6_af_ops; | ||
181 | sk->sk_backlog_rcv = dccp_v6_do_rcv; | ||
182 | goto failure; | ||
183 | } else { | ||
184 | ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF), | ||
185 | inet->saddr); | ||
186 | ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF), | ||
187 | inet->rcv_saddr); | ||
188 | } | ||
189 | |||
190 | return err; | ||
191 | } | ||
192 | |||
193 | if (!ipv6_addr_any(&np->rcv_saddr)) | ||
194 | saddr = &np->rcv_saddr; | ||
195 | |||
196 | fl.proto = IPPROTO_DCCP; | ||
197 | ipv6_addr_copy(&fl.fl6_dst, &np->daddr); | ||
198 | ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr); | ||
199 | fl.oif = sk->sk_bound_dev_if; | ||
200 | fl.fl_ip_dport = usin->sin6_port; | ||
201 | fl.fl_ip_sport = inet->sport; | ||
202 | security_sk_classify_flow(sk, &fl); | ||
203 | |||
204 | if (np->opt != NULL && np->opt->srcrt != NULL) { | ||
205 | const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; | ||
206 | |||
207 | ipv6_addr_copy(&final, &fl.fl6_dst); | ||
208 | ipv6_addr_copy(&fl.fl6_dst, rt0->addr); | ||
209 | final_p = &final; | ||
210 | } | ||
211 | |||
212 | err = ip6_dst_lookup(sk, &dst, &fl); | ||
213 | if (err) | ||
214 | goto failure; | ||
215 | |||
216 | if (final_p) | ||
217 | ipv6_addr_copy(&fl.fl6_dst, final_p); | ||
218 | |||
219 | err = xfrm_lookup(&dst, &fl, sk, 0); | ||
220 | if (err < 0) | ||
221 | goto failure; | ||
222 | |||
223 | if (saddr == NULL) { | ||
224 | saddr = &fl.fl6_src; | ||
225 | ipv6_addr_copy(&np->rcv_saddr, saddr); | ||
226 | } | ||
227 | |||
228 | /* set the source address */ | ||
229 | ipv6_addr_copy(&np->saddr, saddr); | ||
230 | inet->rcv_saddr = LOOPBACK4_IPV6; | ||
231 | |||
232 | __ip6_dst_store(sk, dst, NULL, NULL); | ||
233 | |||
234 | icsk->icsk_ext_hdr_len = 0; | ||
235 | if (np->opt != NULL) | ||
236 | icsk->icsk_ext_hdr_len = (np->opt->opt_flen + | ||
237 | np->opt->opt_nflen); | ||
238 | |||
239 | inet->dport = usin->sin6_port; | ||
240 | |||
241 | dccp_set_state(sk, DCCP_REQUESTING); | ||
242 | err = inet6_hash_connect(&dccp_death_row, sk); | ||
243 | if (err) | ||
244 | goto late_failure; | ||
245 | /* FIXME */ | ||
246 | #if 0 | ||
247 | dp->dccps_gar = secure_dccp_v6_sequence_number(np->saddr.s6_addr32, | ||
248 | np->daddr.s6_addr32, | ||
249 | inet->sport, | ||
250 | inet->dport); | ||
251 | #endif | ||
252 | err = dccp_connect(sk); | ||
253 | if (err) | ||
254 | goto late_failure; | ||
255 | |||
256 | return 0; | ||
257 | |||
258 | late_failure: | ||
259 | dccp_set_state(sk, DCCP_CLOSED); | ||
260 | __sk_dst_reset(sk); | ||
261 | failure: | ||
262 | inet->dport = 0; | ||
263 | sk->sk_route_caps = 0; | ||
264 | return err; | ||
265 | } | ||
266 | |||
267 | static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 85 | static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
268 | int type, int code, int offset, __be32 info) | 86 | int type, int code, int offset, __be32 info) |
269 | { | 87 | { |
@@ -487,19 +305,6 @@ static void dccp_v6_reqsk_destructor(struct request_sock *req) | |||
487 | kfree_skb(inet6_rsk(req)->pktopts); | 305 | kfree_skb(inet6_rsk(req)->pktopts); |
488 | } | 306 | } |
489 | 307 | ||
490 | static struct request_sock_ops dccp6_request_sock_ops = { | ||
491 | .family = AF_INET6, | ||
492 | .obj_size = sizeof(struct dccp6_request_sock), | ||
493 | .rtx_syn_ack = dccp_v6_send_response, | ||
494 | .send_ack = dccp_reqsk_send_ack, | ||
495 | .destructor = dccp_v6_reqsk_destructor, | ||
496 | .send_reset = dccp_v6_ctl_send_reset, | ||
497 | }; | ||
498 | |||
499 | static struct timewait_sock_ops dccp6_timewait_sock_ops = { | ||
500 | .twsk_obj_size = sizeof(struct dccp6_timewait_sock), | ||
501 | }; | ||
502 | |||
503 | static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) | 308 | static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) |
504 | { | 309 | { |
505 | struct ipv6_pinfo *np = inet6_sk(sk); | 310 | struct ipv6_pinfo *np = inet6_sk(sk); |
@@ -580,6 +385,15 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb) | |||
580 | kfree_skb(skb); | 385 | kfree_skb(skb); |
581 | } | 386 | } |
582 | 387 | ||
388 | static struct request_sock_ops dccp6_request_sock_ops = { | ||
389 | .family = AF_INET6, | ||
390 | .obj_size = sizeof(struct dccp6_request_sock), | ||
391 | .rtx_syn_ack = dccp_v6_send_response, | ||
392 | .send_ack = dccp_reqsk_send_ack, | ||
393 | .destructor = dccp_v6_reqsk_destructor, | ||
394 | .send_reset = dccp_v6_ctl_send_reset, | ||
395 | }; | ||
396 | |||
583 | static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) | 397 | static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) |
584 | { | 398 | { |
585 | const struct dccp_hdr *dh = dccp_hdr(skb); | 399 | const struct dccp_hdr *dh = dccp_hdr(skb); |
@@ -1062,6 +876,183 @@ discard_and_relse: | |||
1062 | goto discard_it; | 876 | goto discard_it; |
1063 | } | 877 | } |
1064 | 878 | ||
879 | static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, | ||
880 | int addr_len) | ||
881 | { | ||
882 | struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr; | ||
883 | struct inet_connection_sock *icsk = inet_csk(sk); | ||
884 | struct inet_sock *inet = inet_sk(sk); | ||
885 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
886 | struct dccp_sock *dp = dccp_sk(sk); | ||
887 | struct in6_addr *saddr = NULL, *final_p = NULL, final; | ||
888 | struct flowi fl; | ||
889 | struct dst_entry *dst; | ||
890 | int addr_type; | ||
891 | int err; | ||
892 | |||
893 | dp->dccps_role = DCCP_ROLE_CLIENT; | ||
894 | |||
895 | if (addr_len < SIN6_LEN_RFC2133) | ||
896 | return -EINVAL; | ||
897 | |||
898 | if (usin->sin6_family != AF_INET6) | ||
899 | return -EAFNOSUPPORT; | ||
900 | |||
901 | memset(&fl, 0, sizeof(fl)); | ||
902 | |||
903 | if (np->sndflow) { | ||
904 | fl.fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; | ||
905 | IP6_ECN_flow_init(fl.fl6_flowlabel); | ||
906 | if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) { | ||
907 | struct ip6_flowlabel *flowlabel; | ||
908 | flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel); | ||
909 | if (flowlabel == NULL) | ||
910 | return -EINVAL; | ||
911 | ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst); | ||
912 | fl6_sock_release(flowlabel); | ||
913 | } | ||
914 | } | ||
915 | /* | ||
916 | * connect() to INADDR_ANY means loopback (BSD'ism). | ||
917 | */ | ||
918 | if (ipv6_addr_any(&usin->sin6_addr)) | ||
919 | usin->sin6_addr.s6_addr[15] = 1; | ||
920 | |||
921 | addr_type = ipv6_addr_type(&usin->sin6_addr); | ||
922 | |||
923 | if (addr_type & IPV6_ADDR_MULTICAST) | ||
924 | return -ENETUNREACH; | ||
925 | |||
926 | if (addr_type & IPV6_ADDR_LINKLOCAL) { | ||
927 | if (addr_len >= sizeof(struct sockaddr_in6) && | ||
928 | usin->sin6_scope_id) { | ||
929 | /* If interface is set while binding, indices | ||
930 | * must coincide. | ||
931 | */ | ||
932 | if (sk->sk_bound_dev_if && | ||
933 | sk->sk_bound_dev_if != usin->sin6_scope_id) | ||
934 | return -EINVAL; | ||
935 | |||
936 | sk->sk_bound_dev_if = usin->sin6_scope_id; | ||
937 | } | ||
938 | |||
939 | /* Connect to link-local address requires an interface */ | ||
940 | if (!sk->sk_bound_dev_if) | ||
941 | return -EINVAL; | ||
942 | } | ||
943 | |||
944 | ipv6_addr_copy(&np->daddr, &usin->sin6_addr); | ||
945 | np->flow_label = fl.fl6_flowlabel; | ||
946 | |||
947 | /* | ||
948 | * DCCP over IPv4 | ||
949 | */ | ||
950 | if (addr_type == IPV6_ADDR_MAPPED) { | ||
951 | u32 exthdrlen = icsk->icsk_ext_hdr_len; | ||
952 | struct sockaddr_in sin; | ||
953 | |||
954 | SOCK_DEBUG(sk, "connect: ipv4 mapped\n"); | ||
955 | |||
956 | if (__ipv6_only_sock(sk)) | ||
957 | return -ENETUNREACH; | ||
958 | |||
959 | sin.sin_family = AF_INET; | ||
960 | sin.sin_port = usin->sin6_port; | ||
961 | sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3]; | ||
962 | |||
963 | icsk->icsk_af_ops = &dccp_ipv6_mapped; | ||
964 | sk->sk_backlog_rcv = dccp_v4_do_rcv; | ||
965 | |||
966 | err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin)); | ||
967 | if (err) { | ||
968 | icsk->icsk_ext_hdr_len = exthdrlen; | ||
969 | icsk->icsk_af_ops = &dccp_ipv6_af_ops; | ||
970 | sk->sk_backlog_rcv = dccp_v6_do_rcv; | ||
971 | goto failure; | ||
972 | } else { | ||
973 | ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF), | ||
974 | inet->saddr); | ||
975 | ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF), | ||
976 | inet->rcv_saddr); | ||
977 | } | ||
978 | |||
979 | return err; | ||
980 | } | ||
981 | |||
982 | if (!ipv6_addr_any(&np->rcv_saddr)) | ||
983 | saddr = &np->rcv_saddr; | ||
984 | |||
985 | fl.proto = IPPROTO_DCCP; | ||
986 | ipv6_addr_copy(&fl.fl6_dst, &np->daddr); | ||
987 | ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr); | ||
988 | fl.oif = sk->sk_bound_dev_if; | ||
989 | fl.fl_ip_dport = usin->sin6_port; | ||
990 | fl.fl_ip_sport = inet->sport; | ||
991 | security_sk_classify_flow(sk, &fl); | ||
992 | |||
993 | if (np->opt != NULL && np->opt->srcrt != NULL) { | ||
994 | const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; | ||
995 | |||
996 | ipv6_addr_copy(&final, &fl.fl6_dst); | ||
997 | ipv6_addr_copy(&fl.fl6_dst, rt0->addr); | ||
998 | final_p = &final; | ||
999 | } | ||
1000 | |||
1001 | err = ip6_dst_lookup(sk, &dst, &fl); | ||
1002 | if (err) | ||
1003 | goto failure; | ||
1004 | |||
1005 | if (final_p) | ||
1006 | ipv6_addr_copy(&fl.fl6_dst, final_p); | ||
1007 | |||
1008 | err = xfrm_lookup(&dst, &fl, sk, 0); | ||
1009 | if (err < 0) | ||
1010 | goto failure; | ||
1011 | |||
1012 | if (saddr == NULL) { | ||
1013 | saddr = &fl.fl6_src; | ||
1014 | ipv6_addr_copy(&np->rcv_saddr, saddr); | ||
1015 | } | ||
1016 | |||
1017 | /* set the source address */ | ||
1018 | ipv6_addr_copy(&np->saddr, saddr); | ||
1019 | inet->rcv_saddr = LOOPBACK4_IPV6; | ||
1020 | |||
1021 | __ip6_dst_store(sk, dst, NULL, NULL); | ||
1022 | |||
1023 | icsk->icsk_ext_hdr_len = 0; | ||
1024 | if (np->opt != NULL) | ||
1025 | icsk->icsk_ext_hdr_len = (np->opt->opt_flen + | ||
1026 | np->opt->opt_nflen); | ||
1027 | |||
1028 | inet->dport = usin->sin6_port; | ||
1029 | |||
1030 | dccp_set_state(sk, DCCP_REQUESTING); | ||
1031 | err = inet6_hash_connect(&dccp_death_row, sk); | ||
1032 | if (err) | ||
1033 | goto late_failure; | ||
1034 | /* FIXME */ | ||
1035 | #if 0 | ||
1036 | dp->dccps_gar = secure_dccp_v6_sequence_number(np->saddr.s6_addr32, | ||
1037 | np->daddr.s6_addr32, | ||
1038 | inet->sport, | ||
1039 | inet->dport); | ||
1040 | #endif | ||
1041 | err = dccp_connect(sk); | ||
1042 | if (err) | ||
1043 | goto late_failure; | ||
1044 | |||
1045 | return 0; | ||
1046 | |||
1047 | late_failure: | ||
1048 | dccp_set_state(sk, DCCP_CLOSED); | ||
1049 | __sk_dst_reset(sk); | ||
1050 | failure: | ||
1051 | inet->dport = 0; | ||
1052 | sk->sk_route_caps = 0; | ||
1053 | return err; | ||
1054 | } | ||
1055 | |||
1065 | static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { | 1056 | static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = { |
1066 | .queue_xmit = inet6_csk_xmit, | 1057 | .queue_xmit = inet6_csk_xmit, |
1067 | .send_check = dccp_v6_send_check, | 1058 | .send_check = dccp_v6_send_check, |
@@ -1122,6 +1113,10 @@ static int dccp_v6_destroy_sock(struct sock *sk) | |||
1122 | return inet6_destroy_sock(sk); | 1113 | return inet6_destroy_sock(sk); |
1123 | } | 1114 | } |
1124 | 1115 | ||
1116 | static struct timewait_sock_ops dccp6_timewait_sock_ops = { | ||
1117 | .twsk_obj_size = sizeof(struct dccp6_timewait_sock), | ||
1118 | }; | ||
1119 | |||
1125 | static struct proto dccp_v6_prot = { | 1120 | static struct proto dccp_v6_prot = { |
1126 | .name = "DCCPv6", | 1121 | .name = "DCCPv6", |
1127 | .owner = THIS_MODULE, | 1122 | .owner = THIS_MODULE, |