diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/sctp/ipv6.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r-- | net/sctp/ipv6.c | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c new file mode 100644 index 000000000000..e42c74e3ec1e --- /dev/null +++ b/net/sctp/ipv6.c | |||
@@ -0,0 +1,1013 @@ | |||
1 | /* SCTP kernel reference Implementation | ||
2 | * (C) Copyright IBM Corp. 2002, 2004 | ||
3 | * Copyright (c) 2001 Nokia, Inc. | ||
4 | * Copyright (c) 2001 La Monte H.P. Yarroll | ||
5 | * Copyright (c) 2002-2003 Intel Corp. | ||
6 | * | ||
7 | * This file is part of the SCTP kernel reference Implementation | ||
8 | * | ||
9 | * SCTP over IPv6. | ||
10 | * | ||
11 | * The SCTP reference implementation is free software; | ||
12 | * you can redistribute it and/or modify it under the terms of | ||
13 | * the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2, or (at your option) | ||
15 | * any later version. | ||
16 | * | ||
17 | * The SCTP reference implementation is distributed in the hope that it | ||
18 | * will be useful, but WITHOUT ANY WARRANTY; without even the implied | ||
19 | * ************************ | ||
20 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
21 | * See the GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with GNU CC; see the file COPYING. If not, write to | ||
25 | * the Free Software Foundation, 59 Temple Place - Suite 330, | ||
26 | * Boston, MA 02111-1307, USA. | ||
27 | * | ||
28 | * Please send any bug reports or fixes you make to the | ||
29 | * email address(es): | ||
30 | * lksctp developers <lksctp-developers@lists.sourceforge.net> | ||
31 | * | ||
32 | * Or submit a bug report through the following website: | ||
33 | * http://www.sf.net/projects/lksctp | ||
34 | * | ||
35 | * Written or modified by: | ||
36 | * Le Yanqun <yanqun.le@nokia.com> | ||
37 | * Hui Huang <hui.huang@nokia.com> | ||
38 | * La Monte H.P. Yarroll <piggy@acm.org> | ||
39 | * Sridhar Samudrala <sri@us.ibm.com> | ||
40 | * Jon Grimm <jgrimm@us.ibm.com> | ||
41 | * Ardelle Fan <ardelle.fan@intel.com> | ||
42 | * | ||
43 | * Based on: | ||
44 | * linux/net/ipv6/tcp_ipv6.c | ||
45 | * | ||
46 | * Any bugs reported given to us we will try to fix... any fixes shared will | ||
47 | * be incorporated into the next SCTP release. | ||
48 | */ | ||
49 | |||
50 | #include <linux/module.h> | ||
51 | #include <linux/errno.h> | ||
52 | #include <linux/types.h> | ||
53 | #include <linux/socket.h> | ||
54 | #include <linux/sockios.h> | ||
55 | #include <linux/net.h> | ||
56 | #include <linux/sched.h> | ||
57 | #include <linux/in.h> | ||
58 | #include <linux/in6.h> | ||
59 | #include <linux/netdevice.h> | ||
60 | #include <linux/init.h> | ||
61 | #include <linux/ipsec.h> | ||
62 | |||
63 | #include <linux/ipv6.h> | ||
64 | #include <linux/icmpv6.h> | ||
65 | #include <linux/random.h> | ||
66 | #include <linux/seq_file.h> | ||
67 | |||
68 | #include <net/protocol.h> | ||
69 | #include <net/tcp.h> | ||
70 | #include <net/ndisc.h> | ||
71 | #include <net/ipv6.h> | ||
72 | #include <net/transp_v6.h> | ||
73 | #include <net/addrconf.h> | ||
74 | #include <net/ip6_route.h> | ||
75 | #include <net/inet_common.h> | ||
76 | #include <net/inet_ecn.h> | ||
77 | #include <net/sctp/sctp.h> | ||
78 | |||
79 | #include <asm/uaccess.h> | ||
80 | |||
81 | extern int sctp_inetaddr_event(struct notifier_block *, unsigned long, void *); | ||
82 | static struct notifier_block sctp_inet6addr_notifier = { | ||
83 | .notifier_call = sctp_inetaddr_event, | ||
84 | }; | ||
85 | |||
86 | /* ICMP error handler. */ | ||
87 | SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | ||
88 | int type, int code, int offset, __u32 info) | ||
89 | { | ||
90 | struct inet6_dev *idev; | ||
91 | struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; | ||
92 | struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); | ||
93 | struct sock *sk; | ||
94 | struct sctp_endpoint *ep; | ||
95 | struct sctp_association *asoc; | ||
96 | struct sctp_transport *transport; | ||
97 | struct ipv6_pinfo *np; | ||
98 | char *saveip, *savesctp; | ||
99 | int err; | ||
100 | |||
101 | idev = in6_dev_get(skb->dev); | ||
102 | |||
103 | /* Fix up skb to look at the embedded net header. */ | ||
104 | saveip = skb->nh.raw; | ||
105 | savesctp = skb->h.raw; | ||
106 | skb->nh.ipv6h = iph; | ||
107 | skb->h.raw = (char *)sh; | ||
108 | sk = sctp_err_lookup(AF_INET6, skb, sh, &ep, &asoc, &transport); | ||
109 | /* Put back, the original pointers. */ | ||
110 | skb->nh.raw = saveip; | ||
111 | skb->h.raw = savesctp; | ||
112 | if (!sk) { | ||
113 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); | ||
114 | goto out; | ||
115 | } | ||
116 | |||
117 | /* Warning: The sock lock is held. Remember to call | ||
118 | * sctp_err_finish! | ||
119 | */ | ||
120 | |||
121 | switch (type) { | ||
122 | case ICMPV6_PKT_TOOBIG: | ||
123 | sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); | ||
124 | goto out_unlock; | ||
125 | case ICMPV6_PARAMPROB: | ||
126 | if (ICMPV6_UNK_NEXTHDR == code) { | ||
127 | sctp_icmp_proto_unreachable(sk, ep, asoc, transport); | ||
128 | goto out_unlock; | ||
129 | } | ||
130 | break; | ||
131 | default: | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | np = inet6_sk(sk); | ||
136 | icmpv6_err_convert(type, code, &err); | ||
137 | if (!sock_owned_by_user(sk) && np->recverr) { | ||
138 | sk->sk_err = err; | ||
139 | sk->sk_error_report(sk); | ||
140 | } else { /* Only an error on timeout */ | ||
141 | sk->sk_err_soft = err; | ||
142 | } | ||
143 | |||
144 | out_unlock: | ||
145 | sctp_err_finish(sk, ep, asoc); | ||
146 | out: | ||
147 | if (likely(idev != NULL)) | ||
148 | in6_dev_put(idev); | ||
149 | } | ||
150 | |||
151 | /* Based on tcp_v6_xmit() in tcp_ipv6.c. */ | ||
152 | static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, | ||
153 | int ipfragok) | ||
154 | { | ||
155 | struct sock *sk = skb->sk; | ||
156 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
157 | struct flowi fl; | ||
158 | |||
159 | memset(&fl, 0, sizeof(fl)); | ||
160 | |||
161 | fl.proto = sk->sk_protocol; | ||
162 | |||
163 | /* Fill in the dest address from the route entry passed with the skb | ||
164 | * and the source address from the transport. | ||
165 | */ | ||
166 | ipv6_addr_copy(&fl.fl6_dst, &transport->ipaddr.v6.sin6_addr); | ||
167 | ipv6_addr_copy(&fl.fl6_src, &transport->saddr.v6.sin6_addr); | ||
168 | |||
169 | fl.fl6_flowlabel = np->flow_label; | ||
170 | IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); | ||
171 | if (ipv6_addr_type(&fl.fl6_src) & IPV6_ADDR_LINKLOCAL) | ||
172 | fl.oif = transport->saddr.v6.sin6_scope_id; | ||
173 | else | ||
174 | fl.oif = sk->sk_bound_dev_if; | ||
175 | fl.fl_ip_sport = inet_sk(sk)->sport; | ||
176 | fl.fl_ip_dport = transport->ipaddr.v6.sin6_port; | ||
177 | |||
178 | if (np->opt && np->opt->srcrt) { | ||
179 | struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; | ||
180 | ipv6_addr_copy(&fl.fl6_dst, rt0->addr); | ||
181 | } | ||
182 | |||
183 | SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, " | ||
184 | "src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " | ||
185 | "dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
186 | __FUNCTION__, skb, skb->len, | ||
187 | NIP6(fl.fl6_src), NIP6(fl.fl6_dst)); | ||
188 | |||
189 | SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); | ||
190 | |||
191 | return ip6_xmit(sk, skb, &fl, np->opt, ipfragok); | ||
192 | } | ||
193 | |||
194 | /* Returns the dst cache entry for the given source and destination ip | ||
195 | * addresses. | ||
196 | */ | ||
197 | static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, | ||
198 | union sctp_addr *daddr, | ||
199 | union sctp_addr *saddr) | ||
200 | { | ||
201 | struct dst_entry *dst; | ||
202 | struct flowi fl; | ||
203 | |||
204 | memset(&fl, 0, sizeof(fl)); | ||
205 | ipv6_addr_copy(&fl.fl6_dst, &daddr->v6.sin6_addr); | ||
206 | if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) | ||
207 | fl.oif = daddr->v6.sin6_scope_id; | ||
208 | |||
209 | |||
210 | SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", | ||
211 | __FUNCTION__, NIP6(fl.fl6_dst)); | ||
212 | |||
213 | if (saddr) { | ||
214 | ipv6_addr_copy(&fl.fl6_src, &saddr->v6.sin6_addr); | ||
215 | SCTP_DEBUG_PRINTK( | ||
216 | "SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ", | ||
217 | NIP6(fl.fl6_src)); | ||
218 | } | ||
219 | |||
220 | dst = ip6_route_output(NULL, &fl); | ||
221 | if (dst) { | ||
222 | struct rt6_info *rt; | ||
223 | rt = (struct rt6_info *)dst; | ||
224 | SCTP_DEBUG_PRINTK( | ||
225 | "rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " | ||
226 | "rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
227 | NIP6(rt->rt6i_dst.addr), NIP6(rt->rt6i_src.addr)); | ||
228 | } else { | ||
229 | SCTP_DEBUG_PRINTK("NO ROUTE\n"); | ||
230 | } | ||
231 | |||
232 | return dst; | ||
233 | } | ||
234 | |||
235 | /* Returns the number of consecutive initial bits that match in the 2 ipv6 | ||
236 | * addresses. | ||
237 | */ | ||
238 | static inline int sctp_v6_addr_match_len(union sctp_addr *s1, | ||
239 | union sctp_addr *s2) | ||
240 | { | ||
241 | struct in6_addr *a1 = &s1->v6.sin6_addr; | ||
242 | struct in6_addr *a2 = &s2->v6.sin6_addr; | ||
243 | int i, j; | ||
244 | |||
245 | for (i = 0; i < 4 ; i++) { | ||
246 | __u32 a1xora2; | ||
247 | |||
248 | a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i]; | ||
249 | |||
250 | if ((j = fls(ntohl(a1xora2)))) | ||
251 | return (i * 32 + 32 - j); | ||
252 | } | ||
253 | |||
254 | return (i*32); | ||
255 | } | ||
256 | |||
257 | /* Fills in the source address(saddr) based on the destination address(daddr) | ||
258 | * and asoc's bind address list. | ||
259 | */ | ||
260 | static void sctp_v6_get_saddr(struct sctp_association *asoc, | ||
261 | struct dst_entry *dst, | ||
262 | union sctp_addr *daddr, | ||
263 | union sctp_addr *saddr) | ||
264 | { | ||
265 | struct sctp_bind_addr *bp; | ||
266 | rwlock_t *addr_lock; | ||
267 | struct sctp_sockaddr_entry *laddr; | ||
268 | struct list_head *pos; | ||
269 | sctp_scope_t scope; | ||
270 | union sctp_addr *baddr = NULL; | ||
271 | __u8 matchlen = 0; | ||
272 | __u8 bmatchlen; | ||
273 | |||
274 | SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p " | ||
275 | "daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", | ||
276 | __FUNCTION__, asoc, dst, NIP6(daddr->v6.sin6_addr)); | ||
277 | |||
278 | if (!asoc) { | ||
279 | ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr); | ||
280 | SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " | ||
281 | "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
282 | NIP6(saddr->v6.sin6_addr)); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | scope = sctp_scope(daddr); | ||
287 | |||
288 | bp = &asoc->base.bind_addr; | ||
289 | addr_lock = &asoc->base.addr_lock; | ||
290 | |||
291 | /* Go through the bind address list and find the best source address | ||
292 | * that matches the scope of the destination address. | ||
293 | */ | ||
294 | sctp_read_lock(addr_lock); | ||
295 | list_for_each(pos, &bp->address_list) { | ||
296 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
297 | if ((laddr->a.sa.sa_family == AF_INET6) && | ||
298 | (scope <= sctp_scope(&laddr->a))) { | ||
299 | bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); | ||
300 | if (!baddr || (matchlen < bmatchlen)) { | ||
301 | baddr = &laddr->a; | ||
302 | matchlen = bmatchlen; | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | if (baddr) { | ||
308 | memcpy(saddr, baddr, sizeof(union sctp_addr)); | ||
309 | SCTP_DEBUG_PRINTK("saddr: " | ||
310 | "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
311 | NIP6(saddr->v6.sin6_addr)); | ||
312 | } else { | ||
313 | printk(KERN_ERR "%s: asoc:%p Could not find a valid source " | ||
314 | "address for the " | ||
315 | "dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
316 | __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); | ||
317 | } | ||
318 | |||
319 | sctp_read_unlock(addr_lock); | ||
320 | } | ||
321 | |||
322 | /* Make a copy of all potential local addresses. */ | ||
323 | static void sctp_v6_copy_addrlist(struct list_head *addrlist, | ||
324 | struct net_device *dev) | ||
325 | { | ||
326 | struct inet6_dev *in6_dev; | ||
327 | struct inet6_ifaddr *ifp; | ||
328 | struct sctp_sockaddr_entry *addr; | ||
329 | |||
330 | read_lock(&addrconf_lock); | ||
331 | if ((in6_dev = __in6_dev_get(dev)) == NULL) { | ||
332 | read_unlock(&addrconf_lock); | ||
333 | return; | ||
334 | } | ||
335 | |||
336 | read_lock(&in6_dev->lock); | ||
337 | for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) { | ||
338 | /* Add the address to the local list. */ | ||
339 | addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC); | ||
340 | if (addr) { | ||
341 | addr->a.v6.sin6_family = AF_INET6; | ||
342 | addr->a.v6.sin6_port = 0; | ||
343 | addr->a.v6.sin6_addr = ifp->addr; | ||
344 | addr->a.v6.sin6_scope_id = dev->ifindex; | ||
345 | INIT_LIST_HEAD(&addr->list); | ||
346 | list_add_tail(&addr->list, addrlist); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | read_unlock(&in6_dev->lock); | ||
351 | read_unlock(&addrconf_lock); | ||
352 | } | ||
353 | |||
354 | /* Initialize a sockaddr_storage from in incoming skb. */ | ||
355 | static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb, | ||
356 | int is_saddr) | ||
357 | { | ||
358 | void *from; | ||
359 | __u16 *port; | ||
360 | struct sctphdr *sh; | ||
361 | |||
362 | port = &addr->v6.sin6_port; | ||
363 | addr->v6.sin6_family = AF_INET6; | ||
364 | addr->v6.sin6_flowinfo = 0; /* FIXME */ | ||
365 | addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif; | ||
366 | |||
367 | sh = (struct sctphdr *) skb->h.raw; | ||
368 | if (is_saddr) { | ||
369 | *port = ntohs(sh->source); | ||
370 | from = &skb->nh.ipv6h->saddr; | ||
371 | } else { | ||
372 | *port = ntohs(sh->dest); | ||
373 | from = &skb->nh.ipv6h->daddr; | ||
374 | } | ||
375 | ipv6_addr_copy(&addr->v6.sin6_addr, from); | ||
376 | } | ||
377 | |||
378 | /* Initialize an sctp_addr from a socket. */ | ||
379 | static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk) | ||
380 | { | ||
381 | addr->v6.sin6_family = AF_INET6; | ||
382 | addr->v6.sin6_port = inet_sk(sk)->num; | ||
383 | addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr; | ||
384 | } | ||
385 | |||
386 | /* Initialize sk->sk_rcv_saddr from sctp_addr. */ | ||
387 | static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) | ||
388 | { | ||
389 | if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { | ||
390 | inet6_sk(sk)->rcv_saddr.s6_addr32[0] = 0; | ||
391 | inet6_sk(sk)->rcv_saddr.s6_addr32[1] = 0; | ||
392 | inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); | ||
393 | inet6_sk(sk)->rcv_saddr.s6_addr32[3] = | ||
394 | addr->v4.sin_addr.s_addr; | ||
395 | } else { | ||
396 | inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | /* Initialize sk->sk_daddr from sctp_addr. */ | ||
401 | static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk) | ||
402 | { | ||
403 | if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { | ||
404 | inet6_sk(sk)->daddr.s6_addr32[0] = 0; | ||
405 | inet6_sk(sk)->daddr.s6_addr32[1] = 0; | ||
406 | inet6_sk(sk)->daddr.s6_addr32[2] = htonl(0x0000ffff); | ||
407 | inet6_sk(sk)->daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr; | ||
408 | } else { | ||
409 | inet6_sk(sk)->daddr = addr->v6.sin6_addr; | ||
410 | } | ||
411 | } | ||
412 | |||
413 | /* Initialize a sctp_addr from an address parameter. */ | ||
414 | static void sctp_v6_from_addr_param(union sctp_addr *addr, | ||
415 | union sctp_addr_param *param, | ||
416 | __u16 port, int iif) | ||
417 | { | ||
418 | addr->v6.sin6_family = AF_INET6; | ||
419 | addr->v6.sin6_port = port; | ||
420 | addr->v6.sin6_flowinfo = 0; /* BUG */ | ||
421 | ipv6_addr_copy(&addr->v6.sin6_addr, ¶m->v6.addr); | ||
422 | addr->v6.sin6_scope_id = iif; | ||
423 | } | ||
424 | |||
425 | /* Initialize an address parameter from a sctp_addr and return the length | ||
426 | * of the address parameter. | ||
427 | */ | ||
428 | static int sctp_v6_to_addr_param(const union sctp_addr *addr, | ||
429 | union sctp_addr_param *param) | ||
430 | { | ||
431 | int length = sizeof(sctp_ipv6addr_param_t); | ||
432 | |||
433 | param->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS; | ||
434 | param->v6.param_hdr.length = ntohs(length); | ||
435 | ipv6_addr_copy(¶m->v6.addr, &addr->v6.sin6_addr); | ||
436 | |||
437 | return length; | ||
438 | } | ||
439 | |||
440 | /* Initialize a sctp_addr from a dst_entry. */ | ||
441 | static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, | ||
442 | unsigned short port) | ||
443 | { | ||
444 | struct rt6_info *rt = (struct rt6_info *)dst; | ||
445 | addr->sa.sa_family = AF_INET6; | ||
446 | addr->v6.sin6_port = port; | ||
447 | ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); | ||
448 | } | ||
449 | |||
450 | /* Compare addresses exactly. | ||
451 | * v4-mapped-v6 is also in consideration. | ||
452 | */ | ||
453 | static int sctp_v6_cmp_addr(const union sctp_addr *addr1, | ||
454 | const union sctp_addr *addr2) | ||
455 | { | ||
456 | if (addr1->sa.sa_family != addr2->sa.sa_family) { | ||
457 | if (addr1->sa.sa_family == AF_INET && | ||
458 | addr2->sa.sa_family == AF_INET6 && | ||
459 | IPV6_ADDR_MAPPED == ipv6_addr_type(&addr2->v6.sin6_addr)) { | ||
460 | if (addr2->v6.sin6_port == addr1->v4.sin_port && | ||
461 | addr2->v6.sin6_addr.s6_addr32[3] == | ||
462 | addr1->v4.sin_addr.s_addr) | ||
463 | return 1; | ||
464 | } | ||
465 | if (addr2->sa.sa_family == AF_INET && | ||
466 | addr1->sa.sa_family == AF_INET6 && | ||
467 | IPV6_ADDR_MAPPED == ipv6_addr_type(&addr1->v6.sin6_addr)) { | ||
468 | if (addr1->v6.sin6_port == addr2->v4.sin_port && | ||
469 | addr1->v6.sin6_addr.s6_addr32[3] == | ||
470 | addr2->v4.sin_addr.s_addr) | ||
471 | return 1; | ||
472 | } | ||
473 | return 0; | ||
474 | } | ||
475 | if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) | ||
476 | return 0; | ||
477 | /* If this is a linklocal address, compare the scope_id. */ | ||
478 | if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { | ||
479 | if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id && | ||
480 | (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) { | ||
481 | return 0; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | return 1; | ||
486 | } | ||
487 | |||
488 | /* Initialize addr struct to INADDR_ANY. */ | ||
489 | static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port) | ||
490 | { | ||
491 | memset(addr, 0x00, sizeof(union sctp_addr)); | ||
492 | addr->v6.sin6_family = AF_INET6; | ||
493 | addr->v6.sin6_port = port; | ||
494 | } | ||
495 | |||
496 | /* Is this a wildcard address? */ | ||
497 | static int sctp_v6_is_any(const union sctp_addr *addr) | ||
498 | { | ||
499 | int type; | ||
500 | type = ipv6_addr_type((struct in6_addr *)&addr->v6.sin6_addr); | ||
501 | return IPV6_ADDR_ANY == type; | ||
502 | } | ||
503 | |||
504 | /* Should this be available for binding? */ | ||
505 | static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) | ||
506 | { | ||
507 | int type; | ||
508 | struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; | ||
509 | |||
510 | type = ipv6_addr_type(in6); | ||
511 | if (IPV6_ADDR_ANY == type) | ||
512 | return 1; | ||
513 | if (type == IPV6_ADDR_MAPPED) { | ||
514 | if (sp && !sp->v4mapped) | ||
515 | return 0; | ||
516 | if (sp && ipv6_only_sock(sctp_opt2sk(sp))) | ||
517 | return 0; | ||
518 | sctp_v6_map_v4(addr); | ||
519 | return sctp_get_af_specific(AF_INET)->available(addr, sp); | ||
520 | } | ||
521 | if (!(type & IPV6_ADDR_UNICAST)) | ||
522 | return 0; | ||
523 | |||
524 | return ipv6_chk_addr(in6, NULL, 0); | ||
525 | } | ||
526 | |||
527 | /* This function checks if the address is a valid address to be used for | ||
528 | * SCTP. | ||
529 | * | ||
530 | * Output: | ||
531 | * Return 0 - If the address is a non-unicast or an illegal address. | ||
532 | * Return 1 - If the address is a unicast. | ||
533 | */ | ||
534 | static int sctp_v6_addr_valid(union sctp_addr *addr, struct sctp_sock *sp) | ||
535 | { | ||
536 | int ret = ipv6_addr_type(&addr->v6.sin6_addr); | ||
537 | |||
538 | /* Support v4-mapped-v6 address. */ | ||
539 | if (ret == IPV6_ADDR_MAPPED) { | ||
540 | /* Note: This routine is used in input, so v4-mapped-v6 | ||
541 | * are disallowed here when there is no sctp_sock. | ||
542 | */ | ||
543 | if (!sp || !sp->v4mapped) | ||
544 | return 0; | ||
545 | if (sp && ipv6_only_sock(sctp_opt2sk(sp))) | ||
546 | return 0; | ||
547 | sctp_v6_map_v4(addr); | ||
548 | return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp); | ||
549 | } | ||
550 | |||
551 | /* Is this a non-unicast address */ | ||
552 | if (!(ret & IPV6_ADDR_UNICAST)) | ||
553 | return 0; | ||
554 | |||
555 | return 1; | ||
556 | } | ||
557 | |||
558 | /* What is the scope of 'addr'? */ | ||
559 | static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) | ||
560 | { | ||
561 | int v6scope; | ||
562 | sctp_scope_t retval; | ||
563 | |||
564 | /* The IPv6 scope is really a set of bit fields. | ||
565 | * See IFA_* in <net/if_inet6.h>. Map to a generic SCTP scope. | ||
566 | */ | ||
567 | |||
568 | v6scope = ipv6_addr_scope(&addr->v6.sin6_addr); | ||
569 | switch (v6scope) { | ||
570 | case IFA_HOST: | ||
571 | retval = SCTP_SCOPE_LOOPBACK; | ||
572 | break; | ||
573 | case IFA_LINK: | ||
574 | retval = SCTP_SCOPE_LINK; | ||
575 | break; | ||
576 | case IFA_SITE: | ||
577 | retval = SCTP_SCOPE_PRIVATE; | ||
578 | break; | ||
579 | default: | ||
580 | retval = SCTP_SCOPE_GLOBAL; | ||
581 | break; | ||
582 | }; | ||
583 | |||
584 | return retval; | ||
585 | } | ||
586 | |||
587 | /* Create and initialize a new sk for the socket to be returned by accept(). */ | ||
588 | static struct sock *sctp_v6_create_accept_sk(struct sock *sk, | ||
589 | struct sctp_association *asoc) | ||
590 | { | ||
591 | struct inet_sock *inet = inet_sk(sk); | ||
592 | struct sock *newsk; | ||
593 | struct inet_sock *newinet; | ||
594 | struct ipv6_pinfo *newnp, *np = inet6_sk(sk); | ||
595 | struct sctp6_sock *newsctp6sk; | ||
596 | |||
597 | newsk = sk_alloc(PF_INET6, GFP_KERNEL, sk->sk_prot, 1); | ||
598 | if (!newsk) | ||
599 | goto out; | ||
600 | |||
601 | sock_init_data(NULL, newsk); | ||
602 | |||
603 | newsk->sk_type = SOCK_STREAM; | ||
604 | |||
605 | newsk->sk_prot = sk->sk_prot; | ||
606 | newsk->sk_no_check = sk->sk_no_check; | ||
607 | newsk->sk_reuse = sk->sk_reuse; | ||
608 | |||
609 | newsk->sk_destruct = inet_sock_destruct; | ||
610 | newsk->sk_family = PF_INET6; | ||
611 | newsk->sk_protocol = IPPROTO_SCTP; | ||
612 | newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv; | ||
613 | newsk->sk_shutdown = sk->sk_shutdown; | ||
614 | sock_reset_flag(sk, SOCK_ZAPPED); | ||
615 | |||
616 | newsctp6sk = (struct sctp6_sock *)newsk; | ||
617 | inet_sk(newsk)->pinet6 = &newsctp6sk->inet6; | ||
618 | |||
619 | newinet = inet_sk(newsk); | ||
620 | newnp = inet6_sk(newsk); | ||
621 | |||
622 | memcpy(newnp, np, sizeof(struct ipv6_pinfo)); | ||
623 | |||
624 | /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname() | ||
625 | * and getpeername(). | ||
626 | */ | ||
627 | newinet->sport = inet->sport; | ||
628 | newnp->saddr = np->saddr; | ||
629 | newnp->rcv_saddr = np->rcv_saddr; | ||
630 | newinet->dport = htons(asoc->peer.port); | ||
631 | sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk); | ||
632 | |||
633 | /* Init the ipv4 part of the socket since we can have sockets | ||
634 | * using v6 API for ipv4. | ||
635 | */ | ||
636 | newinet->uc_ttl = -1; | ||
637 | newinet->mc_loop = 1; | ||
638 | newinet->mc_ttl = 1; | ||
639 | newinet->mc_index = 0; | ||
640 | newinet->mc_list = NULL; | ||
641 | |||
642 | if (ipv4_config.no_pmtu_disc) | ||
643 | newinet->pmtudisc = IP_PMTUDISC_DONT; | ||
644 | else | ||
645 | newinet->pmtudisc = IP_PMTUDISC_WANT; | ||
646 | |||
647 | #ifdef INET_REFCNT_DEBUG | ||
648 | atomic_inc(&inet6_sock_nr); | ||
649 | atomic_inc(&inet_sock_nr); | ||
650 | #endif | ||
651 | |||
652 | if (newsk->sk_prot->init(newsk)) { | ||
653 | sk_common_release(newsk); | ||
654 | newsk = NULL; | ||
655 | } | ||
656 | |||
657 | out: | ||
658 | return newsk; | ||
659 | } | ||
660 | |||
661 | /* Map v4 address to mapped v6 address */ | ||
662 | static void sctp_v6_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr) | ||
663 | { | ||
664 | if (sp->v4mapped && AF_INET == addr->sa.sa_family) | ||
665 | sctp_v4_map_v6(addr); | ||
666 | } | ||
667 | |||
668 | /* Where did this skb come from? */ | ||
669 | static int sctp_v6_skb_iif(const struct sk_buff *skb) | ||
670 | { | ||
671 | struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; | ||
672 | return opt->iif; | ||
673 | } | ||
674 | |||
675 | /* Was this packet marked by Explicit Congestion Notification? */ | ||
676 | static int sctp_v6_is_ce(const struct sk_buff *skb) | ||
677 | { | ||
678 | return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20); | ||
679 | } | ||
680 | |||
681 | /* Dump the v6 addr to the seq file. */ | ||
682 | static void sctp_v6_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr) | ||
683 | { | ||
684 | seq_printf(seq, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", | ||
685 | NIP6(addr->v6.sin6_addr)); | ||
686 | } | ||
687 | |||
688 | /* Initialize a PF_INET6 socket msg_name. */ | ||
689 | static void sctp_inet6_msgname(char *msgname, int *addr_len) | ||
690 | { | ||
691 | struct sockaddr_in6 *sin6; | ||
692 | |||
693 | sin6 = (struct sockaddr_in6 *)msgname; | ||
694 | sin6->sin6_family = AF_INET6; | ||
695 | sin6->sin6_flowinfo = 0; | ||
696 | sin6->sin6_scope_id = 0; /*FIXME */ | ||
697 | *addr_len = sizeof(struct sockaddr_in6); | ||
698 | } | ||
699 | |||
700 | /* Initialize a PF_INET msgname from a ulpevent. */ | ||
701 | static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, | ||
702 | char *msgname, int *addrlen) | ||
703 | { | ||
704 | struct sockaddr_in6 *sin6, *sin6from; | ||
705 | |||
706 | if (msgname) { | ||
707 | union sctp_addr *addr; | ||
708 | struct sctp_association *asoc; | ||
709 | |||
710 | asoc = event->asoc; | ||
711 | sctp_inet6_msgname(msgname, addrlen); | ||
712 | sin6 = (struct sockaddr_in6 *)msgname; | ||
713 | sin6->sin6_port = htons(asoc->peer.port); | ||
714 | addr = &asoc->peer.primary_addr; | ||
715 | |||
716 | /* Note: If we go to a common v6 format, this code | ||
717 | * will change. | ||
718 | */ | ||
719 | |||
720 | /* Map ipv4 address into v4-mapped-on-v6 address. */ | ||
721 | if (sctp_sk(asoc->base.sk)->v4mapped && | ||
722 | AF_INET == addr->sa.sa_family) { | ||
723 | sctp_v4_map_v6((union sctp_addr *)sin6); | ||
724 | sin6->sin6_addr.s6_addr32[3] = | ||
725 | addr->v4.sin_addr.s_addr; | ||
726 | return; | ||
727 | } | ||
728 | |||
729 | sin6from = &asoc->peer.primary_addr.v6; | ||
730 | ipv6_addr_copy(&sin6->sin6_addr, &sin6from->sin6_addr); | ||
731 | if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) | ||
732 | sin6->sin6_scope_id = sin6from->sin6_scope_id; | ||
733 | } | ||
734 | } | ||
735 | |||
736 | /* Initialize a msg_name from an inbound skb. */ | ||
737 | static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, | ||
738 | int *addr_len) | ||
739 | { | ||
740 | struct sctphdr *sh; | ||
741 | struct sockaddr_in6 *sin6; | ||
742 | |||
743 | if (msgname) { | ||
744 | sctp_inet6_msgname(msgname, addr_len); | ||
745 | sin6 = (struct sockaddr_in6 *)msgname; | ||
746 | sh = (struct sctphdr *)skb->h.raw; | ||
747 | sin6->sin6_port = sh->source; | ||
748 | |||
749 | /* Map ipv4 address into v4-mapped-on-v6 address. */ | ||
750 | if (sctp_sk(skb->sk)->v4mapped && | ||
751 | skb->nh.iph->version == 4) { | ||
752 | sctp_v4_map_v6((union sctp_addr *)sin6); | ||
753 | sin6->sin6_addr.s6_addr32[3] = skb->nh.iph->saddr; | ||
754 | return; | ||
755 | } | ||
756 | |||
757 | /* Otherwise, just copy the v6 address. */ | ||
758 | ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); | ||
759 | if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { | ||
760 | struct sctp_ulpevent *ev = sctp_skb2event(skb); | ||
761 | sin6->sin6_scope_id = ev->iif; | ||
762 | } | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /* Do we support this AF? */ | ||
767 | static int sctp_inet6_af_supported(sa_family_t family, struct sctp_sock *sp) | ||
768 | { | ||
769 | switch (family) { | ||
770 | case AF_INET6: | ||
771 | return 1; | ||
772 | /* v4-mapped-v6 addresses */ | ||
773 | case AF_INET: | ||
774 | if (!__ipv6_only_sock(sctp_opt2sk(sp)) && sp->v4mapped) | ||
775 | return 1; | ||
776 | default: | ||
777 | return 0; | ||
778 | } | ||
779 | } | ||
780 | |||
781 | /* Address matching with wildcards allowed. This extra level | ||
782 | * of indirection lets us choose whether a PF_INET6 should | ||
783 | * disallow any v4 addresses if we so choose. | ||
784 | */ | ||
785 | static int sctp_inet6_cmp_addr(const union sctp_addr *addr1, | ||
786 | const union sctp_addr *addr2, | ||
787 | struct sctp_sock *opt) | ||
788 | { | ||
789 | struct sctp_af *af1, *af2; | ||
790 | |||
791 | af1 = sctp_get_af_specific(addr1->sa.sa_family); | ||
792 | af2 = sctp_get_af_specific(addr2->sa.sa_family); | ||
793 | |||
794 | if (!af1 || !af2) | ||
795 | return 0; | ||
796 | /* Today, wildcard AF_INET/AF_INET6. */ | ||
797 | if (sctp_is_any(addr1) || sctp_is_any(addr2)) | ||
798 | return 1; | ||
799 | |||
800 | if (addr1->sa.sa_family != addr2->sa.sa_family) | ||
801 | return 0; | ||
802 | |||
803 | return af1->cmp_addr(addr1, addr2); | ||
804 | } | ||
805 | |||
806 | /* Verify that the provided sockaddr looks bindable. Common verification, | ||
807 | * has already been taken care of. | ||
808 | */ | ||
809 | static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr) | ||
810 | { | ||
811 | struct sctp_af *af; | ||
812 | |||
813 | /* ASSERT: address family has already been verified. */ | ||
814 | if (addr->sa.sa_family != AF_INET6) | ||
815 | af = sctp_get_af_specific(addr->sa.sa_family); | ||
816 | else { | ||
817 | struct sock *sk; | ||
818 | int type = ipv6_addr_type(&addr->v6.sin6_addr); | ||
819 | sk = sctp_opt2sk(opt); | ||
820 | if (type & IPV6_ADDR_LINKLOCAL) { | ||
821 | /* Note: Behavior similar to af_inet6.c: | ||
822 | * 1) Overrides previous bound_dev_if | ||
823 | * 2) Destructive even if bind isn't successful. | ||
824 | */ | ||
825 | |||
826 | if (addr->v6.sin6_scope_id) | ||
827 | sk->sk_bound_dev_if = addr->v6.sin6_scope_id; | ||
828 | if (!sk->sk_bound_dev_if) | ||
829 | return 0; | ||
830 | } | ||
831 | af = opt->pf->af; | ||
832 | } | ||
833 | return af->available(addr, opt); | ||
834 | } | ||
835 | |||
836 | /* Verify that the provided sockaddr looks bindable. Common verification, | ||
837 | * has already been taken care of. | ||
838 | */ | ||
839 | static int sctp_inet6_send_verify(struct sctp_sock *opt, union sctp_addr *addr) | ||
840 | { | ||
841 | struct sctp_af *af = NULL; | ||
842 | |||
843 | /* ASSERT: address family has already been verified. */ | ||
844 | if (addr->sa.sa_family != AF_INET6) | ||
845 | af = sctp_get_af_specific(addr->sa.sa_family); | ||
846 | else { | ||
847 | struct sock *sk; | ||
848 | int type = ipv6_addr_type(&addr->v6.sin6_addr); | ||
849 | sk = sctp_opt2sk(opt); | ||
850 | if (type & IPV6_ADDR_LINKLOCAL) { | ||
851 | /* Note: Behavior similar to af_inet6.c: | ||
852 | * 1) Overrides previous bound_dev_if | ||
853 | * 2) Destructive even if bind isn't successful. | ||
854 | */ | ||
855 | |||
856 | if (addr->v6.sin6_scope_id) | ||
857 | sk->sk_bound_dev_if = addr->v6.sin6_scope_id; | ||
858 | if (!sk->sk_bound_dev_if) | ||
859 | return 0; | ||
860 | } | ||
861 | af = opt->pf->af; | ||
862 | } | ||
863 | |||
864 | return af != NULL; | ||
865 | } | ||
866 | |||
867 | /* Fill in Supported Address Type information for INIT and INIT-ACK | ||
868 | * chunks. Note: In the future, we may want to look at sock options | ||
869 | * to determine whether a PF_INET6 socket really wants to have IPV4 | ||
870 | * addresses. | ||
871 | * Returns number of addresses supported. | ||
872 | */ | ||
873 | static int sctp_inet6_supported_addrs(const struct sctp_sock *opt, | ||
874 | __u16 *types) | ||
875 | { | ||
876 | types[0] = SCTP_PARAM_IPV4_ADDRESS; | ||
877 | types[1] = SCTP_PARAM_IPV6_ADDRESS; | ||
878 | return 2; | ||
879 | } | ||
880 | |||
881 | static struct proto_ops inet6_seqpacket_ops = { | ||
882 | .family = PF_INET6, | ||
883 | .owner = THIS_MODULE, | ||
884 | .release = inet6_release, | ||
885 | .bind = inet6_bind, | ||
886 | .connect = inet_dgram_connect, | ||
887 | .socketpair = sock_no_socketpair, | ||
888 | .accept = inet_accept, | ||
889 | .getname = inet6_getname, | ||
890 | .poll = sctp_poll, | ||
891 | .ioctl = inet6_ioctl, | ||
892 | .listen = sctp_inet_listen, | ||
893 | .shutdown = inet_shutdown, | ||
894 | .setsockopt = sock_common_setsockopt, | ||
895 | .getsockopt = sock_common_getsockopt, | ||
896 | .sendmsg = inet_sendmsg, | ||
897 | .recvmsg = sock_common_recvmsg, | ||
898 | .mmap = sock_no_mmap, | ||
899 | }; | ||
900 | |||
901 | static struct inet_protosw sctpv6_seqpacket_protosw = { | ||
902 | .type = SOCK_SEQPACKET, | ||
903 | .protocol = IPPROTO_SCTP, | ||
904 | .prot = &sctpv6_prot, | ||
905 | .ops = &inet6_seqpacket_ops, | ||
906 | .capability = -1, | ||
907 | .no_check = 0, | ||
908 | .flags = SCTP_PROTOSW_FLAG | ||
909 | }; | ||
910 | static struct inet_protosw sctpv6_stream_protosw = { | ||
911 | .type = SOCK_STREAM, | ||
912 | .protocol = IPPROTO_SCTP, | ||
913 | .prot = &sctpv6_prot, | ||
914 | .ops = &inet6_seqpacket_ops, | ||
915 | .capability = -1, | ||
916 | .no_check = 0, | ||
917 | .flags = SCTP_PROTOSW_FLAG, | ||
918 | }; | ||
919 | |||
920 | static int sctp6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) | ||
921 | { | ||
922 | return sctp_rcv(*pskb) ? -1 : 0; | ||
923 | } | ||
924 | |||
925 | static struct inet6_protocol sctpv6_protocol = { | ||
926 | .handler = sctp6_rcv, | ||
927 | .err_handler = sctp_v6_err, | ||
928 | .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL, | ||
929 | }; | ||
930 | |||
931 | static struct sctp_af sctp_ipv6_specific = { | ||
932 | .sctp_xmit = sctp_v6_xmit, | ||
933 | .setsockopt = ipv6_setsockopt, | ||
934 | .getsockopt = ipv6_getsockopt, | ||
935 | .get_dst = sctp_v6_get_dst, | ||
936 | .get_saddr = sctp_v6_get_saddr, | ||
937 | .copy_addrlist = sctp_v6_copy_addrlist, | ||
938 | .from_skb = sctp_v6_from_skb, | ||
939 | .from_sk = sctp_v6_from_sk, | ||
940 | .to_sk_saddr = sctp_v6_to_sk_saddr, | ||
941 | .to_sk_daddr = sctp_v6_to_sk_daddr, | ||
942 | .from_addr_param = sctp_v6_from_addr_param, | ||
943 | .to_addr_param = sctp_v6_to_addr_param, | ||
944 | .dst_saddr = sctp_v6_dst_saddr, | ||
945 | .cmp_addr = sctp_v6_cmp_addr, | ||
946 | .scope = sctp_v6_scope, | ||
947 | .addr_valid = sctp_v6_addr_valid, | ||
948 | .inaddr_any = sctp_v6_inaddr_any, | ||
949 | .is_any = sctp_v6_is_any, | ||
950 | .available = sctp_v6_available, | ||
951 | .skb_iif = sctp_v6_skb_iif, | ||
952 | .is_ce = sctp_v6_is_ce, | ||
953 | .seq_dump_addr = sctp_v6_seq_dump_addr, | ||
954 | .net_header_len = sizeof(struct ipv6hdr), | ||
955 | .sockaddr_len = sizeof(struct sockaddr_in6), | ||
956 | .sa_family = AF_INET6, | ||
957 | }; | ||
958 | |||
959 | static struct sctp_pf sctp_pf_inet6_specific = { | ||
960 | .event_msgname = sctp_inet6_event_msgname, | ||
961 | .skb_msgname = sctp_inet6_skb_msgname, | ||
962 | .af_supported = sctp_inet6_af_supported, | ||
963 | .cmp_addr = sctp_inet6_cmp_addr, | ||
964 | .bind_verify = sctp_inet6_bind_verify, | ||
965 | .send_verify = sctp_inet6_send_verify, | ||
966 | .supported_addrs = sctp_inet6_supported_addrs, | ||
967 | .create_accept_sk = sctp_v6_create_accept_sk, | ||
968 | .addr_v4map = sctp_v6_addr_v4map, | ||
969 | .af = &sctp_ipv6_specific, | ||
970 | }; | ||
971 | |||
972 | /* Initialize IPv6 support and register with inet6 stack. */ | ||
973 | int sctp_v6_init(void) | ||
974 | { | ||
975 | int rc = proto_register(&sctpv6_prot, 1); | ||
976 | |||
977 | if (rc) | ||
978 | goto out; | ||
979 | /* Register inet6 protocol. */ | ||
980 | rc = -EAGAIN; | ||
981 | if (inet6_add_protocol(&sctpv6_protocol, IPPROTO_SCTP) < 0) | ||
982 | goto out_unregister_sctp_proto; | ||
983 | |||
984 | /* Add SCTPv6(UDP and TCP style) to inetsw6 linked list. */ | ||
985 | inet6_register_protosw(&sctpv6_seqpacket_protosw); | ||
986 | inet6_register_protosw(&sctpv6_stream_protosw); | ||
987 | |||
988 | /* Register the SCTP specific PF_INET6 functions. */ | ||
989 | sctp_register_pf(&sctp_pf_inet6_specific, PF_INET6); | ||
990 | |||
991 | /* Register the SCTP specific AF_INET6 functions. */ | ||
992 | sctp_register_af(&sctp_ipv6_specific); | ||
993 | |||
994 | /* Register notifier for inet6 address additions/deletions. */ | ||
995 | register_inet6addr_notifier(&sctp_inet6addr_notifier); | ||
996 | rc = 0; | ||
997 | out: | ||
998 | return rc; | ||
999 | out_unregister_sctp_proto: | ||
1000 | proto_unregister(&sctpv6_prot); | ||
1001 | goto out; | ||
1002 | } | ||
1003 | |||
1004 | /* IPv6 specific exit support. */ | ||
1005 | void sctp_v6_exit(void) | ||
1006 | { | ||
1007 | list_del(&sctp_ipv6_specific.list); | ||
1008 | inet6_del_protocol(&sctpv6_protocol, IPPROTO_SCTP); | ||
1009 | inet6_unregister_protosw(&sctpv6_seqpacket_protosw); | ||
1010 | inet6_unregister_protosw(&sctpv6_stream_protosw); | ||
1011 | unregister_inet6addr_notifier(&sctp_inet6addr_notifier); | ||
1012 | proto_unregister(&sctpv6_prot); | ||
1013 | } | ||