Rename printf -> kprintf in sys/ and add some defines where necessary
[dragonfly.git] / sys / netinet6 / sctp6_usrreq.c
1 /*      $KAME: sctp6_usrreq.c,v 1.35 2004/08/17 06:28:03 t-momose Exp $ */
2 /*      $DragonFly: src/sys/netinet6/sctp6_usrreq.c,v 1.8 2006/12/22 23:57:53 swildner Exp $    */
3
4 /*
5  * Copyright (c) 2001, 2002, 2003, 2004 Cisco Systems, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by Cisco Systems, Inc.
19  * 4. Neither the name of the project nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY CISCO SYSTEMS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL CISCO SYSTEMS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #if !(defined(__OpenBSD__) || defined(__APPLE__))
36 #include "opt_inet.h"
37 #endif
38 #if defined(__FreeBSD__) || defined(__DragonFly__)
39 #include "opt_inet6.h"
40 #include "opt_inet.h"
41 #endif
42 #ifdef __NetBSD__
43 #include "opt_inet.h"
44 #endif
45 #if !(defined(__OpenBSD__) || defined(__APPLE__))
46 #include "opt_ipsec.h"
47 #endif
48 #ifdef __APPLE__
49 #include <sctp.h>
50 #elif !defined(__OpenBSD__)
51 #include "opt_sctp.h"
52 #endif
53
54 #include <sys/param.h>
55 #include <sys/kernel.h>
56 #include <sys/mbuf.h>
57 #include <sys/domain.h>
58 #include <sys/protosw.h>
59 #include <sys/socket.h>
60 #include <sys/malloc.h>
61 #include <sys/socketvar.h>
62 #include <sys/sysctl.h>
63 #include <sys/errno.h>
64 #include <sys/stat.h>
65 #include <sys/systm.h>
66 #include <sys/syslog.h>
67 #include <sys/proc.h>
68 #include <sys/thread2.h>
69 #include <net/if.h>
70 #include <net/route.h>
71 #include <net/if_types.h>
72 #include <netinet/in.h>
73 #include <netinet/in_systm.h>
74 #include <netinet/ip.h>
75 #include <netinet/in_pcb.h>
76 #include <netinet/in_var.h>
77 #include <netinet/ip_var.h>
78 #include <netinet/sctp_pcb.h>
79 #include <netinet/sctp_header.h>
80 #include <netinet/sctp_var.h>
81 #include <netinet/sctputil.h>
82 #include <netinet/sctp_output.h>
83 #include <netinet/sctp_input.h>
84 #include <netinet/sctp_asconf.h>
85 #include <netinet6/ip6_var.h>
86 #include <netinet/ip6.h>
87 #if !defined(__OpenBSD__)
88 #include <netinet6/in6_pcb.h>
89 #endif
90 #include <netinet/icmp6.h>
91 #include <netinet6/sctp6_var.h>
92 #include <netinet6/ip6protosw.h>
93 #include <netinet6/nd6.h>
94
95 #ifdef IPSEC
96 #ifndef __OpenBSD__
97 #include <netinet6/ipsec6.h>
98 #include <netinet6/ipsec.h>
99 #else
100 #undef IPSEC
101 #endif
102 #endif /*IPSEC*/
103
104 #if defined(NFAITH) && NFAITH > 0
105 #include <net/if_faith.h>
106 #endif
107
108 #include <net/net_osdep.h>
109
110 extern struct protosw inetsw[];
111
112 #if defined(HAVE_NRL_INPCB) || defined(__FreeBSD__) || defined(__DragonFly__)
113 #ifndef in6pcb
114 #define in6pcb          inpcb
115 #endif
116 #ifndef sotoin6pcb
117 #define sotoin6pcb      sotoinpcb
118 #endif
119 #endif
120
121 #ifdef SCTP_DEBUG
122 extern u_int32_t sctp_debug_on;
123 #endif
124
125 static  int sctp6_detach(struct socket *so);
126
127 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
128 extern void in6_sin_2_v4mapsin6 (struct sockaddr_in *sin,
129                                  struct sockaddr_in6 *sin6);
130 extern void in6_sin6_2_sin (struct sockaddr_in *,
131                             struct sockaddr_in6 *sin6);
132 extern void in6_sin6_2_sin_in_sock(struct sockaddr *nam);
133
134 /*
135  * Convert sockaddr_in6 to sockaddr_in.  Original sockaddr_in6 must be
136  * v4 mapped addr or v4 compat addr
137  */
138 void
139 in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
140 {
141         bzero(sin, sizeof(*sin));
142         sin->sin_len = sizeof(struct sockaddr_in);
143         sin->sin_family = AF_INET;
144         sin->sin_port = sin6->sin6_port;
145         sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3];
146 }
147
148 /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */
149 void
150 in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6)
151 {
152         bzero(sin6, sizeof(*sin6));
153         sin6->sin6_len = sizeof(struct sockaddr_in6);
154         sin6->sin6_family = AF_INET6;
155         sin6->sin6_port = sin->sin_port;
156         sin6->sin6_addr.s6_addr32[0] = 0;
157         sin6->sin6_addr.s6_addr32[1] = 0;
158         sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP;
159         sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr;
160 }
161
162 /* Convert sockaddr_in6 into sockaddr_in. */
163 void
164 in6_sin6_2_sin_in_sock(struct sockaddr *nam)
165 {
166         struct sockaddr_in *sin_p;
167         struct sockaddr_in6 sin6;
168
169         /* save original sockaddr_in6 addr and convert it to sockaddr_in  */
170         sin6 = *(struct sockaddr_in6 *)nam;
171         sin_p = (struct sockaddr_in *)nam;
172         in6_sin6_2_sin(sin_p, &sin6);
173 }
174
175 #endif /* !(__FreeBSD__ || __APPLE__) */
176
177 extern int sctp_no_csum_on_loopback;
178
179 int
180 #if defined(__APPLE__)
181 sctp6_input(struct mbuf **mp, int *offp)
182 #else
183 sctp6_input(struct mbuf **mp, int *offp, int proto)
184 #endif
185 {
186         struct mbuf *m = *mp;
187         struct ip6_hdr *ip6;
188         struct sctphdr *sh;
189         struct sctp_inpcb *in6p = NULL;
190         struct sctp_nets *net;
191         int refcount_up = 0;
192         u_int32_t check, calc_check;
193         struct inpcb *in6p_ip;
194         struct sctp_chunkhdr *ch;
195         struct mbuf *opts = NULL;
196         int length, mlen, offset, iphlen;
197         u_int8_t ecn_bits;
198         struct sctp_tcb *stcb = NULL;
199         int off = *offp;
200
201         ip6 = mtod(m, struct ip6_hdr *);
202 #ifndef PULLDOWN_TEST
203         /* If PULLDOWN_TEST off, must be in a single mbuf. */
204         IP6_EXTHDR_CHECK(m, off, (int)(sizeof(*sh) + sizeof(*ch)), IPPROTO_DONE);
205         sh = (struct sctphdr *)((caddr_t)ip6 + off);
206         ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(*sh));
207 #else
208         /* Ensure that (sctphdr + sctp_chunkhdr) in a row. */
209         IP6_EXTHDR_GET(sh, struct sctphdr *, m, off, sizeof(*sh) + sizeof(*ch));
210         if (sh == NULL) {
211                 sctp_pegs[SCTP_HDR_DROPS]++;
212                 return IPPROTO_DONE;
213         }
214         ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr));
215 #endif
216
217         iphlen = off;
218         offset = iphlen + sizeof(*sh) + sizeof(*ch);
219
220 #if defined(NFAITH) && NFAITH > 0
221 #if defined(__FreeBSD_cc_version) && __FreeBSD_cc_version <= 430000
222 #if defined(NFAITH) && 0 < NFAITH
223         if (faithprefix(&ip6h->ip6_dst)) {
224                 /* XXX send icmp6 host/port unreach? */
225                 goto bad;
226         }
227 #endif
228 #else
229
230 #ifdef __FreeBSD__
231         if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) {
232                 /* XXX send icmp6 host/port unreach? */
233                 goto bad;
234         }
235 #else
236         if (faithprefix(&ip6->ip6_dst))
237                 goto bad;
238 #endif
239 #endif /* __FreeBSD_cc_version */
240
241 #endif /* NFAITH defined and > 0 */
242         sctp_pegs[SCTP_INPKTS]++;
243 #ifdef SCTP_DEBUG
244         if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
245                 kprintf("V6 input gets a packet iphlen:%d pktlen:%d\n", iphlen, m->m_pkthdr.len);
246         }
247 #endif
248         if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
249                 /* No multi-cast support in SCTP */
250                 sctp_pegs[SCTP_IN_MCAST]++;
251                 goto bad;
252         }
253         /* destination port of 0 is illegal, based on RFC2960. */
254         if (sh->dest_port == 0)
255                 goto bad;
256         if ((sctp_no_csum_on_loopback == 0) ||
257            (m->m_pkthdr.rcvif == NULL) ||
258            (m->m_pkthdr.rcvif->if_type != IFT_LOOP)) {
259                 /* we do NOT validate things from the loopback if the
260                  * sysctl is set to 1.
261                  */
262                 check = sh->checksum;           /* save incoming checksum */
263                 if ((check == 0) && (sctp_no_csum_on_loopback)) {
264                         /* special hook for where we got a local address
265                          * somehow routed across a non IFT_LOOP type interface
266                          */
267                         if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &ip6->ip6_dst))
268                                 goto sctp_skip_csum;
269                 }
270                 sh->checksum = 0;               /* prepare for calc */
271                 calc_check = sctp_calculate_sum(m, &mlen, iphlen);
272                 if (calc_check != check) {
273 #ifdef SCTP_DEBUG
274                         if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
275                                 kprintf("Bad CSUM on SCTP packet calc_check:%x check:%x  m:%x mlen:%d iphlen:%d\n",
276                                        calc_check, check, (u_int)m,
277                                        mlen, iphlen);
278                         }
279 #endif
280                         stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
281                                                          sh, ch, &in6p, &net);
282                         /* in6p's ref-count increased && stcb locked */
283                         if ((in6p) && (stcb)) {
284                                 sctp_send_packet_dropped(stcb, net, m, iphlen, 1);
285                                 sctp_chunk_output((struct sctp_inpcb *)in6p, stcb, 2);
286                         }  else if ((in6p != NULL) && (stcb == NULL)) {
287                                 refcount_up = 1;
288                         }
289                         sctp_pegs[SCTP_BAD_CSUM]++;
290                         goto bad;
291                 }
292                 sh->checksum = calc_check;
293         } else {
294 sctp_skip_csum:
295                 mlen = m->m_pkthdr.len;
296         }
297         net = NULL;
298         /*
299          * Locate pcb and tcb for datagram
300          * sctp_findassociation_addr() wants IP/SCTP/first chunk header...
301          */
302 #ifdef SCTP_DEBUG
303         if (sctp_debug_on & SCTP_DEBUG_INPUT1) {
304                 kprintf("V6 Find the association\n");
305         }
306 #endif
307         stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch),
308             sh, ch, &in6p, &net);
309         /* in6p's ref-count increased */
310         if (in6p == NULL) {
311                 struct sctp_init_chunk *init_chk, chunk_buf;
312
313                 sctp_pegs[SCTP_NOPORTS]++;
314                 if (ch->chunk_type == SCTP_INITIATION) {
315                         /* we do a trick here to get the INIT tag,
316                          * dig in and get the tag from the INIT and
317                          * put it in the common header.
318                          */
319                         init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
320                             iphlen + sizeof(*sh), sizeof(*init_chk),
321                             (u_int8_t *)&chunk_buf);
322                         sh->v_tag = init_chk->init.initiate_tag;
323                 }
324                 sctp_send_abort(m, iphlen, sh, 0, NULL);
325                 goto bad;
326         } else if (stcb == NULL) {
327                 refcount_up = 1;
328         }
329         in6p_ip = (struct inpcb *)in6p;
330 #ifdef IPSEC
331         /*
332          * Check AH/ESP integrity.
333          */
334 #ifdef __OpenBSD__
335         {
336                 struct inpcb *i_inp;
337                 struct m_tag *mtag;
338                 struct tdb_ident *tdbi;
339                 struct tdb *tdb;
340                 int error;
341
342                 /* Find most recent IPsec tag */
343                 i_inp = (struct inpcb *)in6p;
344                 mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
345                 crit_enter();
346                 if (mtag != NULL) {
347                         tdbi = (struct tdb_ident *)(mtag + 1);
348                         tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto);
349                 } else
350                         tdb = NULL;
351
352                 ipsp_spd_lookup(m, af, iphlen, &error, IPSP_DIRECTION_IN,
353                     tdb, i_inp);
354                 if (error) {
355                         crit_exit();
356                         goto bad;
357                 }
358
359                 /* Latch SA */
360                 if (i_inp->inp_tdb_in != tdb) {
361                         if (tdb) {
362                                 tdb_add_inp(tdb, i_inp, 1);
363                                 if (i_inp->inp_ipo == NULL) {
364                                         i_inp->inp_ipo = ipsec_add_policy(i_inp,
365                                             af, IPSP_DIRECTION_OUT);
366                                         if (i_inp->inp_ipo == NULL) {
367                                                 crit_exit();
368                                                 goto bad;
369                                         }
370                                 }
371                                 if (i_inp->inp_ipo->ipo_dstid == NULL &&
372                                     tdb->tdb_srcid != NULL) {
373                                         i_inp->inp_ipo->ipo_dstid =
374                                             tdb->tdb_srcid;
375                                         tdb->tdb_srcid->ref_count++;
376                                 }
377                                 if (i_inp->inp_ipsec_remotecred == NULL &&
378                                     tdb->tdb_remote_cred != NULL) {
379                                         i_inp->inp_ipsec_remotecred =
380                                             tdb->tdb_remote_cred;
381                                         tdb->tdb_remote_cred->ref_count++;
382                                 }
383                                 if (i_inp->inp_ipsec_remoteauth == NULL &&
384                                     tdb->tdb_remote_auth != NULL) {
385                                         i_inp->inp_ipsec_remoteauth =
386                                             tdb->tdb_remote_auth;
387                                         tdb->tdb_remote_auth->ref_count++;
388                                 }
389                         } else { /* Just reset */
390                                 TAILQ_REMOVE(&i_inp->inp_tdb_in->tdb_inp_in,
391                                     i_inp, binp_tdb_in_next);
392                                 i_inp->inp_tdb_in = NULL;
393                         }
394                 }
395                 crit_exit();
396         }
397 #else
398         if (ipsec6_in_reject_so(m, in6p->sctp_socket)) {
399 /* XXX */
400 #ifndef __APPLE__
401                 /* FIX ME: need to find right stat for __APPLE__ */
402                 ipsec6stat.in_polvio++;
403 #endif
404                 goto bad;
405         }
406 #endif
407 #endif /*IPSEC*/
408
409         /*
410          * Construct sockaddr format source address.
411          * Stuff source address and datagram in user buffer.
412          */
413         if ((in6p->ip_inp.inp.inp_flags & INP_CONTROLOPTS)
414 #ifndef __OpenBSD__
415             || (in6p->sctp_socket->so_options & SO_TIMESTAMP)
416 #endif
417             ) {
418 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
419 #if (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version < 501113) || \
420     defined(__APPLE__) || defined(__DragonFly__)
421                 ip6_savecontrol(in6p_ip, &opts, ip6, m);
422 #elif __FreeBSD_version >= 440000 || (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version >= 501113)
423                 ip6_savecontrol(in6p_ip, m, &opts);
424 #else
425                 ip6_savecontrol(in6p_ip, m, &opts, NULL);
426 #endif
427 #else
428                 ip6_savecontrol((struct in6pcb *)in6p_ip, m, &opts);
429 #endif
430         }
431
432         /*
433          * CONTROL chunk processing
434          */
435         length = ntohs(ip6->ip6_plen) + iphlen;
436         offset -= sizeof(*ch);
437         ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff);
438         crit_enter();
439         sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
440             in6p, stcb, net, ecn_bits);
441         /* inp's ref-count reduced && stcb unlocked */
442         crit_exit();
443         /* XXX this stuff below gets moved to appropriate parts later... */
444         if (m)
445                 m_freem(m);
446         if (opts)
447                 m_freem(opts);
448
449         if ((in6p) && refcount_up){
450                 /* reduce ref-count */
451                 SCTP_INP_WLOCK(in6p);
452                 SCTP_INP_DECR_REF(in6p);
453                 SCTP_INP_WUNLOCK(in6p);
454         }
455
456         return IPPROTO_DONE;
457
458 bad:
459         if (stcb)
460                 SCTP_TCB_UNLOCK(stcb);
461
462         if ((in6p) && refcount_up){
463                 /* reduce ref-count */
464                 SCTP_INP_WLOCK(in6p);
465                 SCTP_INP_DECR_REF(in6p);
466                 SCTP_INP_WUNLOCK(in6p);
467         }
468         if (m)
469                 m_freem(m);
470         if (opts)
471                 m_freem(opts);
472         return IPPROTO_DONE;
473 }
474
475
476 static void
477 sctp6_notify_mbuf(struct sctp_inpcb *inp,
478                   struct icmp6_hdr *icmp6,
479                   struct sctphdr *sh,
480                   struct sctp_tcb *stcb,
481                   struct sctp_nets *net)
482 {
483         unsigned int nxtsz;
484
485         if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
486             (icmp6 == NULL) || (sh == NULL)) {
487                 goto out;
488         }
489
490         /* First do we even look at it? */
491         if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag))
492                 goto out;
493
494         if (icmp6->icmp6_type != ICMP6_PACKET_TOO_BIG) {
495                 /* not PACKET TO BIG */
496                 goto out;
497         }
498         /*
499          * ok we need to look closely. We could even get smarter and
500          * look at anyone that we sent to in case we get a different
501          * ICMP that tells us there is no way to reach a host, but for
502          * this impl, all we care about is MTU discovery.
503          */
504         nxtsz = ntohl(icmp6->icmp6_mtu);
505         /* Stop any PMTU timer */
506         sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
507
508         /* Adjust destination size limit */
509         if (net->mtu > nxtsz) {
510                 net->mtu = nxtsz;
511         }
512         /* now what about the ep? */
513         if (stcb->asoc.smallest_mtu > nxtsz) {
514                 struct sctp_tmit_chunk *chk;
515                 struct sctp_stream_out *strm;
516                 /* Adjust that too */
517                 stcb->asoc.smallest_mtu = nxtsz;
518                 /* now off to subtract IP_DF flag if needed */
519
520                 TAILQ_FOREACH(chk, &stcb->asoc.send_queue, sctp_next) {
521                         if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
522                                 chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
523                         }
524                 }
525                 TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
526                         if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
527                                 /*
528                                  * For this guy we also mark for immediate
529                                  * resend since we sent to big of chunk
530                                  */
531                                 chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
532                                 if (chk->sent != SCTP_DATAGRAM_RESEND)
533                                         stcb->asoc.sent_queue_retran_cnt++;
534                                 chk->sent = SCTP_DATAGRAM_RESEND;
535                                 chk->rec.data.doing_fast_retransmit = 0;
536
537                                 chk->sent = SCTP_DATAGRAM_RESEND;
538                                 /* Clear any time so NO RTT is being done */
539                                 chk->sent_rcv_time.tv_sec = 0;
540                                 chk->sent_rcv_time.tv_usec = 0;
541                                 stcb->asoc.total_flight -= chk->send_size;
542                                 net->flight_size -= chk->send_size;
543                         }
544                 }
545                 TAILQ_FOREACH(strm, &stcb->asoc.out_wheel, next_spoke) {
546                         TAILQ_FOREACH(chk, &strm->outqueue, sctp_next) {
547                                 if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
548                                         chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
549                                 }
550                         }
551                 }
552         }
553         sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
554 out:
555         if (inp) {
556                 /* reduce inp's ref-count */
557                 SCTP_INP_WLOCK(inp);
558                 SCTP_INP_DECR_REF(inp);
559                 SCTP_INP_WUNLOCK(inp);
560         }
561         if (stcb)
562                 SCTP_TCB_UNLOCK(stcb);
563 }
564
565
566 void
567 sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d)
568 {
569         struct sctphdr sh;
570         struct ip6ctlparam *ip6cp = NULL;
571         int cm;
572
573         if (pktdst->sa_family != AF_INET6 ||
574             pktdst->sa_len != sizeof(struct sockaddr_in6))
575                 return;
576
577         if ((unsigned)cmd >= PRC_NCMDS)
578                 return;
579         if (PRC_IS_REDIRECT(cmd)) {
580                 d = NULL;
581         } else if (inet6ctlerrmap[cmd] == 0) {
582                 return;
583         }
584
585         /* if the parameter is from icmp6, decode it. */
586         if (d != NULL) {
587                 ip6cp = (struct ip6ctlparam *)d;
588         } else {
589                 ip6cp = (struct ip6ctlparam *)NULL;
590         }
591
592         if (ip6cp) {
593                 /*
594                  * XXX: We assume that when IPV6 is non NULL,
595                  * M and OFF are valid.
596                  */
597                 /* check if we can safely examine src and dst ports */
598                 struct sctp_inpcb *inp;
599                 struct sctp_tcb *stcb;
600                 struct sctp_nets *net;
601                 struct sockaddr_in6 final;
602
603                 if (ip6cp->ip6c_m == NULL ||
604                     (size_t)ip6cp->ip6c_m->m_pkthdr.len < (ip6cp->ip6c_off + sizeof(sh)))
605                         return;
606
607                 bzero(&sh, sizeof(sh));
608                 bzero(&final, sizeof(final));
609                 inp = NULL;
610                 net = NULL;
611                 m_copydata(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(sh),
612                     (caddr_t)&sh);
613                 ip6cp->ip6c_src->sin6_port = sh.src_port;
614                 final.sin6_len = sizeof(final);
615                 final.sin6_family = AF_INET6;
616 #if defined(__FreeBSD__) && __FreeBSD_cc_version < 440000
617                 final.sin6_addr = *ip6cp->ip6c_finaldst;
618 #else
619                 final.sin6_addr = ((struct sockaddr_in6 *)pktdst)->sin6_addr;
620 #endif /* __FreeBSD_cc_version */
621                 final.sin6_port = sh.dest_port;
622                 crit_enter();
623                 stcb = sctp_findassociation_addr_sa((struct sockaddr *)ip6cp->ip6c_src,
624                                                     (struct sockaddr *)&final,
625                                                     &inp, &net, 1);
626                 /* inp's ref-count increased && stcb locked */
627                 if (stcb != NULL && inp && (inp->sctp_socket != NULL)) {
628                         if (cmd == PRC_MSGSIZE) {
629                                 sctp6_notify_mbuf(inp,
630                                                   ip6cp->ip6c_icmp6,
631                                                   &sh,
632                                                   stcb,
633                                                   net);
634                                 /* inp's ref-count reduced && stcb unlocked */
635                         } else {
636                                 if (cmd == PRC_HOSTDEAD) {
637                                         cm = EHOSTUNREACH;
638                                 } else {
639                                         cm = inet6ctlerrmap[cmd];
640                                 }
641                                 sctp_notify(inp, cm, &sh,
642                                             (struct sockaddr *)&final,
643                                             stcb, net);
644                                 /* inp's ref-count reduced && stcb unlocked */
645                         }
646                 } else {
647                         if (PRC_IS_REDIRECT(cmd) && inp) {
648 #ifdef __OpenBSD__
649                                 in_rtchange((struct inpcb *)inp,
650                                             inetctlerrmap[cmd]);
651 #else
652                                 in6_rtchange((struct in6pcb *)inp,
653                                              inet6ctlerrmap[cmd]);
654 #endif
655                         }
656                         if (inp) {
657                                 /* reduce inp's ref-count */
658                                 SCTP_INP_WLOCK(inp);
659                                 SCTP_INP_DECR_REF(inp);
660                                 SCTP_INP_WUNLOCK(inp);
661                         }
662                         if (stcb)
663                                 SCTP_TCB_UNLOCK(stcb);
664                 }
665                 crit_exit();
666         }
667 }
668
669 /*
670  * this routine can probably be collasped into the one in sctp_userreq.c
671  * since they do the same thing and now we lookup with a sockaddr
672  */
673 #ifdef __FreeBSD__
674 static int
675 sctp6_getcred(SYSCTL_HANDLER_ARGS)
676 {
677         struct sockaddr_in6 addrs[2];
678         struct sctp_inpcb *inp;
679         struct sctp_nets *net;
680         struct sctp_tcb *stcb;
681         int error;
682
683 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
684         error = suser(req->td);
685 #else
686         error = suser(req->p);
687 #endif
688         if (error)
689                 return (error);
690
691         if (req->newlen != sizeof(addrs))
692                 return (EINVAL);
693         if (req->oldlen != sizeof(struct ucred))
694                 return (EINVAL);
695         error = SYSCTL_IN(req, addrs, sizeof(addrs));
696         if (error)
697                 return (error);
698         crit_enter();
699
700         stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]),
701                                            sin6tosa(&addrs[1]),
702                                            &inp, &net, 1);
703         if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
704                 error = ENOENT;
705                 if (inp) {
706                         SCTP_INP_WLOCK(inp);
707                         SCTP_INP_DECR_REF(inp);
708                         SCTP_INP_WUNLOCK(inp);
709                 }
710                 goto out;
711         }
712         error = SYSCTL_OUT(req, inp->sctp_socket->so_cred,
713                            sizeof(struct ucred));
714
715         SCTP_TCB_UNLOCK (stcb);
716 out:
717         crit_exit();
718         return (error);
719 }
720
721 SYSCTL_PROC(_net_inet6_sctp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
722             0, 0,
723             sctp6_getcred, "S,ucred", "Get the ucred of a SCTP6 connection");
724
725 #endif
726
727 /* This is the same as the sctp_abort() could be made common */
728 static int
729 sctp6_abort(struct socket *so)
730 {
731         struct sctp_inpcb *inp;
732
733         inp = (struct sctp_inpcb *)so->so_pcb;
734         if (inp == NULL)
735                 return EINVAL;  /* ??? possible? panic instead? */
736         soisdisconnected(so);
737         crit_enter();
738         sctp_inpcb_free(inp, 1);
739         crit_exit();
740         return 0;
741 }
742
743 static int
744 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
745 sctp6_attach(struct socket *so, int proto, struct thread *p)
746 #elif defined(__DragonFly__)
747 sctp6_attach(struct socket *so, int proto, struct pru_attach_info *ai)
748 #else
749 sctp6_attach(struct socket *so, int proto, struct proc *p)
750 #endif
751 {
752         struct in6pcb *inp6;
753         int error;
754         struct sctp_inpcb *inp;
755
756         inp = (struct sctp_inpcb *)so->so_pcb;
757         if (inp != NULL)
758                 return EINVAL;
759
760         if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
761                 error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL);
762                 if (error)
763                         return error;
764         }
765         crit_enter();
766         error = sctp_inpcb_alloc(so);
767         crit_exit();
768         if (error)
769                 return error;
770         inp = (struct sctp_inpcb *)so->so_pcb;
771         inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6;     /* I'm v6! */
772         inp6 = (struct in6pcb *)inp;
773
774 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
775         inp6->inp_vflag |= INP_IPV6;
776 #else
777 #if defined(__OpenBSD__)
778         inp->ip_inp.inp.inp_flags |= INP_IPV6;
779 #else
780         inp->inp_vflag |=  INP_IPV6;
781 #endif
782 #endif
783 #if defined(__NetBSD__)
784         if (ip6_v6only) {
785                 inp6->in6p_flags |= IN6P_IPV6_V6ONLY;
786         }
787         so->so_send = sctp_sosend;
788 #endif
789         inp6->in6p_hops = -1;           /* use kernel default */
790         inp6->in6p_cksum = -1;  /* just to be sure */
791 #ifdef INET
792         /*
793          * XXX: ugly!!
794          * IPv4 TTL initialization is necessary for an IPv6 socket as well,
795          * because the socket may be bound to an IPv6 wildcard address,
796          * which may match an IPv4-mapped IPv6 address.
797          */
798 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
799         inp6->inp_ip_ttl = ip_defttl;
800 #else
801         inp->inp_ip_ttl = ip_defttl;
802 #endif
803 #endif
804         /*
805          * Hmm what about the IPSEC stuff that is missing here but
806          * in sctp_attach()?
807          */
808         return 0;
809 }
810
811 static int
812 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
813 sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
814 {
815 #else
816 #if defined(__FreeBSD__) || defined(__APPLE__)
817 sctp6_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
818 {
819 #else
820 sctp6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
821 {
822         struct sockaddr *addr = nam ?  mtod(nam, struct sockaddr *) : NULL;
823 #endif
824 #endif
825         struct sctp_inpcb *inp;
826         struct in6pcb *inp6;
827         int error;
828
829         inp = (struct sctp_inpcb *)so->so_pcb;
830         if (inp == NULL)
831                 return EINVAL;
832
833         inp6 = (struct in6pcb *)inp;
834 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
835         inp6->inp_vflag &= ~INP_IPV4;
836         inp6->inp_vflag |= INP_IPV6;
837 #else
838 #if defined(__OpenBSD__)
839         inp->ip_inp.inp.inp_flags &= ~INP_IPV4;
840         inp->ip_inp.inp.inp_flags |= INP_IPV6;
841 #else
842         inp->inp_vflag &= ~INP_IPV4;
843         inp->inp_vflag |= INP_IPV6;
844 #endif
845 #endif
846         if (addr != NULL &&
847 #if defined(__OpenBSD__)
848              (0) /* we always do dual bind */
849 #elif defined (__NetBSD__)
850              !(inp6->in6p_flags & IN6P_IPV6_V6ONLY)
851 #else
852              !(inp6->inp_flags & IN6P_IPV6_V6ONLY)
853 #endif
854              ) {
855                 if (addr->sa_family == AF_INET) {
856                         /* binding v4 addr to v6 socket, so reset flags */
857 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
858                         inp6->inp_vflag |= INP_IPV4;
859                         inp6->inp_vflag &= ~INP_IPV6;
860 #else
861 #if defined(__OpenBSD__)
862                         inp->ip_inp.inp.inp_flags |= INP_IPV4;
863                         inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
864 #else
865                         inp->inp_vflag |= INP_IPV4;
866                         inp->inp_vflag &= ~INP_IPV6;
867 #endif
868 #endif
869                 } else {
870                         struct sockaddr_in6 *sin6_p;
871                         sin6_p = (struct sockaddr_in6 *)addr;
872
873                         if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) {
874 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
875                           inp6->inp_vflag |= INP_IPV4;
876 #else
877 #if defined(__OpenBSD__)
878                           inp->ip_inp.inp.inp_flags |= INP_IPV4;
879 #else
880                           inp->inp_vflag |= INP_IPV4;
881 #endif
882 #endif
883                         }
884                         else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
885                                 struct sockaddr_in sin;
886                                 in6_sin6_2_sin(&sin, sin6_p);
887 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
888                                 inp6->inp_vflag |= INP_IPV4;
889                                 inp6->inp_vflag &= ~INP_IPV6;
890 #else
891 #if defined(__OpenBSD__)
892                                 inp->ip_inp.inp.inp_flags |= INP_IPV4;
893                                 inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
894
895 #else
896                                 inp->inp_vflag |= INP_IPV4;
897                                 inp->inp_vflag &= ~INP_IPV6;
898 #endif
899 #endif
900                                 crit_enter();
901                                 error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, p);
902                                 crit_exit();
903                                 return error;
904                         }
905                 }
906         } else if (addr != NULL) {
907                 /* IPV6_V6ONLY socket */
908                 if (addr->sa_family == AF_INET) {
909                         /* can't bind v4 addr to v6 only socket! */
910                         return EINVAL;
911                 } else {
912                         struct sockaddr_in6 *sin6_p;
913                         sin6_p = (struct sockaddr_in6 *)addr;
914
915                         if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr))
916                                 /* can't bind v4-mapped addrs either! */
917                                 /* NOTE: we don't support SIIT */
918                                 return EINVAL;
919                 }
920         }
921         crit_enter();
922         error = sctp_inpcb_bind(so, addr, p);
923         crit_exit();
924         return error;
925 }
926
927 /*This could be made common with sctp_detach() since they are identical */
928 static int
929 sctp6_detach(struct socket *so)
930 {
931         struct sctp_inpcb *inp;
932
933         inp = (struct sctp_inpcb *)so->so_pcb;
934         if (inp == NULL)
935                 return EINVAL;
936         crit_enter();
937         if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
938             (so->so_rcv.sb_cc > 0))
939                 sctp_inpcb_free(inp, 1);
940         else
941                 sctp_inpcb_free(inp, 0);
942         crit_exit();
943         return 0;
944 }
945
946 static int
947 sctp6_disconnect(struct socket *so)
948 {
949         struct sctp_inpcb *inp;
950
951         crit_enter();
952         inp = (struct sctp_inpcb *)so->so_pcb;
953         if (inp == NULL) {
954                 crit_exit();
955                 return (ENOTCONN);
956         }
957         if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
958                 if (LIST_EMPTY(&inp->sctp_asoc_list)) {
959                         /* No connection */
960                         crit_exit();
961                         return (ENOTCONN);
962                 } else {
963                         int some_on_streamwheel = 0;
964                         struct sctp_association *asoc;
965                         struct sctp_tcb *stcb;
966
967                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
968                         if (stcb == NULL) {
969                                 crit_exit();
970                                 return (EINVAL);
971                         }
972                         asoc = &stcb->asoc;
973                         if (!TAILQ_EMPTY(&asoc->out_wheel)) {
974                                 /* Check to see if some data queued */
975                                 struct sctp_stream_out *outs;
976                                 TAILQ_FOREACH(outs, &asoc->out_wheel,
977                                               next_spoke) {
978                                         if (!TAILQ_EMPTY(&outs->outqueue)) {
979                                                 some_on_streamwheel = 1;
980                                                 break;
981                                         }
982                                 }
983                         }
984
985                         if (TAILQ_EMPTY(&asoc->send_queue) &&
986                             TAILQ_EMPTY(&asoc->sent_queue) &&
987                             (some_on_streamwheel == 0)) {
988                                 /* nothing queued to send, so I'm done... */
989                                 if ((SCTP_GET_STATE(asoc) !=
990                                      SCTP_STATE_SHUTDOWN_SENT) &&
991                                     (SCTP_GET_STATE(asoc) !=
992                                      SCTP_STATE_SHUTDOWN_ACK_SENT)) {
993                                         /* only send SHUTDOWN the first time */
994 #ifdef SCTP_DEBUG
995                                         if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
996                                                 kprintf("%s:%d sends a shutdown\n",
997                                                        __FILE__,
998                                                        __LINE__
999                                                         );
1000                                         }
1001 #endif
1002                                         sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
1003                                         sctp_chunk_output(stcb->sctp_ep, stcb, 1);
1004                                         asoc->state = SCTP_STATE_SHUTDOWN_SENT;
1005                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
1006                                                          stcb->sctp_ep, stcb,
1007                                                          asoc->primary_destination);
1008                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
1009                                                          stcb->sctp_ep, stcb,
1010                                                          asoc->primary_destination);
1011                                 }
1012                         } else {
1013                                 /*
1014                                  * we still got (or just got) data to send,
1015                                  * so set SHUTDOWN_PENDING
1016                                  */
1017                                 /*
1018                                  * XXX sockets draft says that MSG_EOF should
1019                                  * be sent with no data.  currently, we will
1020                                  * allow user data to be sent first and move
1021                                  * to SHUTDOWN-PENDING
1022                                  */
1023                                 asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
1024                         }
1025                         crit_exit();
1026                         return (0);
1027                 }
1028         } else {
1029                 /* UDP model does not support this */
1030                 crit_exit();
1031                 return EOPNOTSUPP;
1032         }
1033 }
1034
1035 int
1036 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1037 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1038           struct mbuf *control, struct thread *p);
1039 #else
1040 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1041           struct mbuf *control, struct proc *p);
1042 #endif
1043
1044
1045 static int
1046 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1047 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1048            struct mbuf *control, struct thread *p)
1049 {
1050 #else
1051 #if defined(__FreeBSD__) || defined(__APPLE__)
1052 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1053            struct mbuf *control, struct proc *p)
1054 {
1055 #else
1056 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam,
1057            struct mbuf *control, struct proc *p)
1058 {
1059         struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *) : NULL;
1060 #endif
1061 #endif
1062         struct sctp_inpcb *inp;
1063         struct inpcb *in_inp;
1064         struct in6pcb *inp6;
1065 #ifdef INET
1066         struct sockaddr_in6 *sin6;
1067 #endif /* INET */
1068         /* No SPL needed since sctp_output does this */
1069
1070         inp = (struct sctp_inpcb *)so->so_pcb;
1071         if (inp == NULL) {
1072                 if (control) {
1073                         m_freem(control);
1074                         control = NULL;
1075                 }
1076                 m_freem(m);
1077                 return EINVAL;
1078         }
1079         in_inp = (struct inpcb *)inp;
1080         inp6 = (struct in6pcb *)inp;
1081         /* For the TCP model we may get a NULL addr, if we
1082          * are a connected socket thats ok.
1083          */
1084         if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) &&
1085             (addr == NULL)) {
1086                 goto connected_type;
1087         }
1088         if (addr == NULL) {
1089                 m_freem(m);
1090                 if (control) {
1091                         m_freem(control);
1092                         control = NULL;
1093                 }
1094                 return (EDESTADDRREQ);
1095         }
1096
1097 #ifdef INET
1098         sin6 = (struct sockaddr_in6 *)addr;
1099         if (
1100
1101 #if defined(__OpenBSD__)
1102              (0) /* we always do dual bind */
1103 #elif defined (__NetBSD__)
1104              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1105 #else
1106              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1107 #endif
1108             ) {
1109                 /*
1110                  * if IPV6_V6ONLY flag, we discard datagrams
1111                  * destined to a v4 addr or v4-mapped addr
1112                  */
1113                 if (addr->sa_family == AF_INET) {
1114                         return EINVAL;
1115                 }
1116                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1117                         return EINVAL;
1118                 }
1119         }
1120
1121         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1122                 if (!ip6_v6only) {
1123                         struct sockaddr_in sin;
1124                         /* convert v4-mapped into v4 addr and send */
1125                         in6_sin6_2_sin(&sin, sin6);
1126                         return sctp_send(so, flags,  m, (struct sockaddr *)&sin,
1127                                            control, p);
1128                 } else {
1129                         /* mapped addresses aren't enabled */
1130                         return EINVAL;
1131                 }
1132         }
1133 #endif /* INET */
1134 connected_type:
1135         /* now what about control */
1136         if (control) {
1137                 if (inp->control) {
1138                         kprintf("huh? control set?\n");
1139                         m_freem(inp->control);
1140                         inp->control = NULL;
1141                 }
1142                 inp->control = control;
1143         }
1144         /* add it in possibly */
1145         if ((inp->pkt) &&
1146             (inp->pkt->m_flags & M_PKTHDR)) {
1147                 struct mbuf *x;
1148                 int c_len;
1149
1150                 c_len = 0;
1151                 /* How big is it */
1152                 for (x=m;x;x = x->m_next) {
1153                         c_len += x->m_len;
1154                 }
1155                 inp->pkt->m_pkthdr.len += c_len;
1156         }
1157         /* Place the data */
1158         if (inp->pkt) {
1159                 inp->pkt_last->m_next = m;
1160                 inp->pkt_last = m;
1161         } else {
1162                 inp->pkt_last = inp->pkt = m;
1163         }
1164         if (
1165 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1166             /* FreeBSD and MacOSX uses a flag passed */
1167             (!(flags & PRUS_MORETOCOME))
1168 #elif defined(__NetBSD__)
1169             /* NetBSD uses the so_state field */
1170             (!(so->so_state & SS_MORETOCOME))
1171 #else
1172             1   /* Open BSD does not have any "more to come" indication */
1173 #endif
1174             ) {
1175                 /*
1176                  * note with the current version this code will only be
1177                  * used by OpenBSD, NetBSD and FreeBSD have methods for
1178                  * re-defining sosend() to use sctp_sosend().  One can
1179                  * optionaly switch back to this code (by changing back
1180                  * the defininitions but this is not advisable.
1181                  */
1182                 int ret;
1183                 ret = sctp_output(inp, inp->pkt , addr, inp->control, p, flags);
1184                 inp->pkt = NULL;
1185                 inp->control = NULL;
1186                 return (ret);
1187         } else {
1188                 return (0);
1189         }
1190 }
1191
1192 static int
1193 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1194 sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
1195 {
1196 #else
1197 #if defined(__FreeBSD__) || defined(__APPLE__)
1198 sctp6_connect(struct socket *so, struct sockaddr *addr, struct proc *p)
1199 {
1200 #else
1201 sctp6_connect(struct socket *so, struct mbuf *nam, struct proc *p)
1202 {
1203         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1204 #endif
1205 #endif
1206         int error = 0;
1207         struct sctp_inpcb *inp;
1208         struct in6pcb *inp6;
1209         struct sctp_tcb *stcb;
1210 #ifdef INET
1211         struct sockaddr_in6 *sin6;
1212         struct sockaddr_storage ss;
1213 #endif /* INET */
1214
1215         crit_enter();
1216         inp6 = (struct in6pcb *)so->so_pcb;
1217         inp = (struct sctp_inpcb *)so->so_pcb;
1218         if (inp == NULL) {
1219                 crit_exit();
1220                 return (ECONNRESET);    /* I made the same as TCP since
1221                                          * we are not setup? */
1222         }
1223         SCTP_ASOC_CREATE_LOCK(inp);
1224         SCTP_INP_RLOCK(inp);
1225         if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
1226             SCTP_PCB_FLAGS_UNBOUND) {
1227                 /* Bind a ephemeral port */
1228                 SCTP_INP_RUNLOCK(inp);
1229                 error = sctp6_bind(so, NULL, p);
1230                 if (error) {
1231                         crit_exit();
1232                         SCTP_ASOC_CREATE_UNLOCK(inp);
1233
1234                         return (error);
1235                 }
1236                 SCTP_INP_RLOCK(inp);
1237         }
1238
1239         if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
1240             (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
1241                 /* We are already connected AND the TCP model */
1242                 crit_exit();
1243                 SCTP_INP_RUNLOCK(inp);
1244                 SCTP_ASOC_CREATE_UNLOCK(inp);
1245                 return (EADDRINUSE);
1246         }
1247
1248 #ifdef INET
1249         sin6 = (struct sockaddr_in6 *)addr;
1250         if (
1251 #if defined(__OpenBSD__)
1252              (0) /* we always do dual bind */
1253 #elif defined (__NetBSD__)
1254              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1255 #else
1256              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1257 #endif
1258             ) {
1259                 /*
1260                  * if IPV6_V6ONLY flag, ignore connections
1261                  * destined to a v4 addr or v4-mapped addr
1262                  */
1263                 if (addr->sa_family == AF_INET) {
1264                         crit_exit();
1265                         SCTP_INP_RUNLOCK(inp);
1266                         SCTP_ASOC_CREATE_UNLOCK(inp);
1267                         return EINVAL;
1268                 }
1269                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1270                         crit_exit();
1271                         SCTP_INP_RUNLOCK(inp);
1272                         SCTP_ASOC_CREATE_UNLOCK(inp);
1273                         return EINVAL;
1274                 }
1275         }
1276
1277         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1278                 if (!ip6_v6only) {
1279                         /* convert v4-mapped into v4 addr */
1280                         in6_sin6_2_sin((struct sockaddr_in *)&ss, sin6);
1281                         addr = (struct sockaddr *)&ss;
1282                 } else {
1283                         /* mapped addresses aren't enabled */
1284                         crit_exit();
1285                         SCTP_INP_RUNLOCK(inp);
1286                         SCTP_ASOC_CREATE_UNLOCK(inp);
1287                         return EINVAL;
1288                 }
1289         } else
1290 #endif /* INET */
1291                 addr = addr;    /* for true v6 address case */
1292
1293         /* Now do we connect? */
1294         if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1295                 stcb = LIST_FIRST(&inp->sctp_asoc_list);
1296                 if (stcb)
1297                         SCTP_TCB_UNLOCK (stcb);
1298                 SCTP_INP_RUNLOCK(inp);
1299         }else {
1300                 SCTP_INP_RUNLOCK(inp);
1301                 SCTP_INP_WLOCK(inp);
1302                 SCTP_INP_INCR_REF(inp);
1303                 SCTP_INP_WUNLOCK(inp);
1304                 stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL);
1305                 if (stcb == NULL) {
1306                         SCTP_INP_WLOCK(inp);
1307                         SCTP_INP_DECR_REF(inp);
1308                         SCTP_INP_WUNLOCK(inp);
1309                 }
1310         }
1311
1312         if (stcb != NULL) {
1313                 /* Already have or am bring up an association */
1314                 SCTP_ASOC_CREATE_UNLOCK(inp);
1315                 SCTP_TCB_UNLOCK (stcb);
1316                 crit_exit();
1317                 return (EALREADY);
1318         }
1319         /* We are GOOD to go */
1320         stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
1321         SCTP_ASOC_CREATE_UNLOCK(inp);
1322         if (stcb == NULL) {
1323                 /* Gak! no memory */
1324                 crit_exit();
1325                 return (error);
1326         }
1327         if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
1328                 stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
1329                 /* Set the connected flag so we can queue data */
1330                 soisconnecting(so);
1331         }
1332         stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
1333         SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
1334         sctp_send_initiate(inp, stcb);
1335         SCTP_TCB_UNLOCK (stcb);
1336         crit_exit();
1337         return error;
1338 }
1339
1340 static int
1341 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1342 sctp6_getaddr(struct socket *so, struct sockaddr **addr)
1343 {
1344         struct sockaddr_in6 *sin6;
1345 #else
1346 sctp6_getaddr(struct socket *so, struct mbuf *nam)
1347 {
1348         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1349 #endif
1350         struct sctp_inpcb *inp;
1351         /*
1352          * Do the malloc first in case it blocks.
1353          */
1354 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1355         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1356                M_WAITOK | M_ZERO);
1357 #else
1358         nam->m_len = sizeof(*sin6);
1359 #endif
1360         bzero(sin6, sizeof(*sin6));
1361         sin6->sin6_family = AF_INET6;
1362         sin6->sin6_len = sizeof(*sin6);
1363
1364         inp = (struct sctp_inpcb *)so->so_pcb;
1365         if (!inp) {
1366 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1367                 FREE(sin6, M_SONAME);
1368 #endif
1369                 return ECONNRESET;
1370         }
1371
1372         sin6->sin6_port = inp->sctp_lport;
1373         if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
1374                 /* For the bound all case you get back 0 */
1375                 if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1376                         struct sctp_tcb *stcb;
1377                         struct sockaddr_in6 *sin_a6;
1378                         struct sctp_nets *net;
1379                         int fnd;
1380
1381                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1382                         if (stcb == NULL) {
1383                                 goto notConn6;
1384                         }
1385                         fnd = 0;
1386                         sin_a6 = NULL;
1387                         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1388                                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1389                                 if (sin_a6->sin6_family == AF_INET6) {
1390                                         fnd = 1;
1391                                         break;
1392                                 }
1393                         }
1394                         if ((!fnd) || (sin_a6 == NULL)) {
1395                                 /* punt */
1396                                 goto notConn6;
1397                         }
1398                         sin6->sin6_addr = sctp_ipv6_source_address_selection(
1399                             inp, stcb, (struct route *)&net->ro, net, 0);
1400
1401                 } else {
1402                         /* For the bound all case you get back 0 */
1403                 notConn6:
1404                         memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
1405                 }
1406         } else {
1407                 /* Take the first IPv6 address in the list */
1408                 struct sctp_laddr *laddr;
1409                 int fnd = 0;
1410                 LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
1411                         if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
1412                                 struct sockaddr_in6 *sin_a;
1413                                 sin_a = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
1414                                 sin6->sin6_addr = sin_a->sin6_addr;
1415                                 fnd = 1;
1416                                 break;
1417                         }
1418                 }
1419                 if (!fnd) {
1420 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1421                         FREE(sin6, M_SONAME);
1422 #endif
1423                         return ENOENT;
1424                 }
1425         }
1426         /* Scoping things for v6 */
1427         if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
1428                 /* skip ifp check below */
1429                 in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1430         else
1431                 sin6->sin6_scope_id = 0;        /*XXX*/
1432 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1433         (*addr) = (struct sockaddr *)sin6;
1434 #endif
1435         return (0);
1436 }
1437
1438 static int
1439 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1440 sctp6_peeraddr(struct socket *so, struct sockaddr **addr)
1441 {
1442         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)*addr;
1443 #else
1444 sctp6_peeraddr(struct socket *so, struct mbuf *nam)
1445 {
1446         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1447 #endif
1448         int fnd;
1449         struct sockaddr_in6 *sin_a6;
1450         struct sctp_inpcb *inp;
1451         struct sctp_tcb *stcb;
1452         struct sctp_nets *net;
1453         /*
1454          * Do the malloc first in case it blocks.
1455          */
1456         inp = (struct sctp_inpcb *)so->so_pcb;
1457         if (!(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
1458                 /* UDP type and listeners will drop out here */
1459                 return (ENOTCONN);
1460         }
1461 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1462         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1463                M_WAITOK | M_ZERO);
1464 #else
1465         nam->m_len = sizeof(*sin6);
1466 #endif
1467         bzero(sin6, sizeof(*sin6));
1468         sin6->sin6_family = AF_INET6;
1469         sin6->sin6_len = sizeof(*sin6);
1470
1471         /* We must recapture incase we blocked */
1472         inp = (struct sctp_inpcb *)so->so_pcb;
1473         if (!inp) {
1474 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1475                 FREE(sin6, M_SONAME);
1476 #endif
1477                 return ECONNRESET;
1478         }
1479         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1480         if (stcb == NULL) {
1481 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1482                 FREE(sin6, M_SONAME);
1483 #endif
1484                 return ECONNRESET;
1485         }
1486         fnd = 0;
1487         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1488                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1489                 if (sin_a6->sin6_family == AF_INET6) {
1490                         fnd = 1;
1491                         sin6->sin6_port = stcb->rport;
1492                         sin6->sin6_addr = sin_a6->sin6_addr;
1493                         break;
1494                 }
1495         }
1496         if (!fnd) {
1497                 /* No IPv4 address */
1498 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1499                 FREE(sin6, M_SONAME);
1500 #endif
1501                 return ENOENT;
1502         }
1503         in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1504 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1505         *addr = (struct sockaddr *)sin6;
1506 #endif
1507         return (0);
1508 }
1509
1510 static int
1511 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1512 sctp6_in6getaddr(struct socket *so, struct sockaddr **nam)
1513 {
1514         struct sockaddr *addr;
1515 #else
1516 sctp6_in6getaddr(struct socket *so, struct mbuf *nam)
1517 {
1518         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1519 #endif
1520         struct in6pcb *inp6 = sotoin6pcb(so);
1521         int error;
1522
1523         if (inp6 == NULL)
1524                 return EINVAL;
1525
1526         crit_enter();
1527         /* allow v6 addresses precedence */
1528         error = sctp6_getaddr(so, nam);
1529         if (error) {
1530                 /* try v4 next if v6 failed */
1531                 error = sctp_ingetaddr(so, nam);
1532                 if (error) {
1533                         crit_exit();
1534                         return (error);
1535                 }
1536 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1537                 addr = *nam;
1538 #endif
1539                 /* if I'm V6ONLY, convert it to v4-mapped */
1540                 if (
1541 #if defined(__OpenBSD__)
1542              (0) /* we always do dual bind */
1543 #elif defined (__NetBSD__)
1544              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1545 #else
1546              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1547 #endif
1548                     ) {
1549                         struct sockaddr_in6 sin6;
1550                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1551                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1552 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1553                         nam->m_len = sizeof(sin6);
1554 #endif
1555 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1556                 } else {
1557                         nam->m_len = sizeof(struct sockaddr_in);
1558 #endif
1559                 }
1560 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1561         } else {
1562                 nam->m_len = sizeof(struct sockaddr_in6);
1563 #endif
1564         }
1565         crit_exit();
1566         return (error);
1567 }
1568
1569
1570 static int
1571 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1572 sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam)
1573 {
1574         struct sockaddr *addr = *nam;
1575 #else
1576 sctp6_getpeeraddr(struct socket *so, struct mbuf *nam)
1577 {
1578         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1579 #endif
1580         struct in6pcb *inp6 = sotoin6pcb(so);
1581         int error;
1582
1583         if (inp6 == NULL)
1584                 return EINVAL;
1585
1586         crit_enter();
1587         /* allow v6 addresses precedence */
1588         error = sctp6_peeraddr(so, nam);
1589         if (error) {
1590                 /* try v4 next if v6 failed */
1591                 error = sctp_peeraddr(so, nam);
1592                 if (error) {
1593                         crit_exit();
1594                         return (error);
1595                 }
1596                 /* if I'm V6ONLY, convert it to v4-mapped */
1597                 if (
1598 #if defined(__OpenBSD__)
1599              (0) /* we always do dual bind */
1600 #elif defined (__NetBSD__)
1601              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1602 #else
1603              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1604 #endif
1605                     ) {
1606                         struct sockaddr_in6 sin6;
1607                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1608                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1609 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1610                         nam->m_len = sizeof(sin6);
1611 #endif
1612 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1613                 } else {
1614                         nam->m_len = sizeof(struct sockaddr_in);
1615 #endif
1616                 }
1617 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1618         } else {
1619                 nam->m_len = sizeof(struct sockaddr_in6);
1620 #endif
1621         }
1622         crit_exit();
1623         return error;
1624 }
1625
1626 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1627 struct pr_usrreqs sctp6_usrreqs = {
1628         sctp6_abort, sctp_accept, sctp6_attach, sctp6_bind,
1629         sctp6_connect, pru_connect2_notsupp, in6_control,
1630         sctp6_detach, sctp6_disconnect, sctp_listen, sctp6_getpeeraddr,
1631         sctp_usr_recvd, pru_rcvoob_notsupp, sctp6_send, pru_sense_null,
1632         sctp_shutdown, sctp6_in6getaddr, sctp_sosend, soreceive, sopoll
1633 };
1634
1635 #else
1636
1637 int
1638 sctp6_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
1639              struct mbuf *control, struct proc *p)
1640 {
1641         int s;
1642         int error = 0;
1643         int family;
1644
1645 #if defined(__OpenBSD__)
1646         p = curproc;
1647 #endif
1648         s = splsoftnet();
1649         family = so->so_proto->pr_domain->dom_family;
1650
1651         if (req == PRU_CONTROL) {
1652                 switch (family) {
1653                 case PF_INET:
1654                         error = in_control(so, (long)m, (caddr_t)nam,
1655                             (struct ifnet *)control
1656 #if defined(__NetBSD__)
1657                              , p
1658 #endif
1659                              );
1660 #ifdef INET6
1661                 case PF_INET6:
1662                         error = in6_control(so, (long)m, (caddr_t)nam,
1663                             (struct ifnet *)control, p);
1664 #endif
1665                 default:
1666                         error = EAFNOSUPPORT;
1667                 }
1668                 splx(s);
1669                 return (error);
1670         }
1671 #ifdef __NetBSD__
1672         if (req == PRU_PURGEIF) {
1673                 struct ifnet *ifn;
1674                 struct ifaddr *ifa;
1675                 ifn = (struct ifnet *)control;
1676                 TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
1677                         if (ifa->ifa_addr->sa_family == family) {
1678                                 sctp_delete_ip_address(ifa);
1679                         }
1680                 }
1681                 switch (family) {
1682                 case PF_INET:
1683                         in_purgeif (ifn);
1684                         break;
1685 #ifdef INET6
1686                 case PF_INET6:
1687                         in6_purgeif (ifn);
1688                         break;
1689 #endif
1690                 default:
1691                         splx(s);
1692                         return (EAFNOSUPPORT);
1693                 }
1694                 splx(s);
1695                 return (0);
1696         }
1697 #endif
1698         switch (req) {
1699         case PRU_ATTACH:
1700                 error = sctp6_attach(so, family, p);
1701                 break;
1702         case PRU_DETACH:
1703                 error = sctp6_detach(so);
1704                 break;
1705         case PRU_BIND:
1706                 if (nam == NULL)
1707                         return (EINVAL);
1708                 error = sctp6_bind(so, nam, p);
1709                 break;
1710         case PRU_LISTEN:
1711                 error = sctp_listen(so, p);
1712                 break;
1713         case PRU_CONNECT:
1714                 if (nam == NULL)
1715                         return (EINVAL);
1716                 error = sctp6_connect(so, nam, p);
1717                 break;
1718         case PRU_DISCONNECT:
1719                 error = sctp6_disconnect(so);
1720                 break;
1721         case PRU_ACCEPT:
1722                 if (nam == NULL)
1723                         return (EINVAL);
1724                 error = sctp_accept(so, nam);
1725                 break;
1726         case PRU_SHUTDOWN:
1727                 error = sctp_shutdown(so);
1728                 break;
1729
1730         case PRU_RCVD:
1731                 /*
1732                  * For OpenBSD and NetBSD, this is real ugly. The (mbuf *)
1733                  * nam that is passed (by soreceive()) is the int flags
1734                  * cast as a (mbuf *) yuck!
1735                  */
1736                 error = sctp_usr_recvd(so, (int)((long)nam));
1737                 break;
1738
1739         case PRU_SEND:
1740                 /* Flags are ignored */
1741                 error = sctp6_send(so, 0, m, nam, control, p);
1742                 break;
1743         case PRU_ABORT:
1744                 error = sctp6_abort(so);
1745                 break;
1746
1747         case PRU_SENSE:
1748                 error = 0;
1749                 break;
1750         case PRU_RCVOOB:
1751                 error = EAFNOSUPPORT;
1752                 break;
1753         case PRU_SENDOOB:
1754                 error = EAFNOSUPPORT;
1755                 break;
1756         case PRU_PEERADDR:
1757                 error = sctp6_getpeeraddr(so, nam);
1758                 break;
1759         case PRU_SOCKADDR:
1760                 error = sctp6_in6getaddr(so, nam);
1761                 break;
1762         case PRU_SLOWTIMO:
1763                 error = 0;
1764                 break;
1765         default:
1766                 break;
1767         }
1768         splx(s);
1769         return (error);
1770 }
1771 #endif