Merge branch 'master' of ssh://crater.dragonflybsd.org/repository/git/dragonfly
[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.10 2007/04/22 01:13:14 dillon 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 = 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 /*
728  * NOTE: (so) is referenced from soabort*() and netmsg_pru_abort()
729  *       will sofree() it when we return.
730  */
731 static int
732 sctp6_abort(struct socket *so)
733 {
734         struct sctp_inpcb *inp;
735         int error;
736
737         inp = (struct sctp_inpcb *)so->so_pcb;
738         if (inp) {
739                 soisdisconnected(so);
740                 sctp_inpcb_free(inp, 1);
741                 error = 0;
742         } else {
743                 error = EINVAL;
744         }
745
746         return error;
747 }
748
749 static int
750 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
751 sctp6_attach(struct socket *so, int proto, struct thread *p)
752 #elif defined(__DragonFly__)
753 sctp6_attach(struct socket *so, int proto, struct pru_attach_info *ai)
754 #else
755 sctp6_attach(struct socket *so, int proto, struct proc *p)
756 #endif
757 {
758         struct in6pcb *inp6;
759         int error;
760         struct sctp_inpcb *inp;
761
762         inp = (struct sctp_inpcb *)so->so_pcb;
763         if (inp != NULL)
764                 return EINVAL;
765
766         if (so->so_snd.ssb_hiwat == 0 || so->so_rcv.ssb_hiwat == 0) {
767                 error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL);
768                 if (error)
769                         return error;
770         }
771         crit_enter();
772         error = sctp_inpcb_alloc(so);
773         crit_exit();
774         if (error)
775                 return error;
776         inp = (struct sctp_inpcb *)so->so_pcb;
777         inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6;     /* I'm v6! */
778         inp6 = (struct in6pcb *)inp;
779
780 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
781         inp6->inp_vflag |= INP_IPV6;
782 #else
783 #if defined(__OpenBSD__)
784         inp->ip_inp.inp.inp_flags |= INP_IPV6;
785 #else
786         inp->inp_vflag |=  INP_IPV6;
787 #endif
788 #endif
789 #if defined(__NetBSD__)
790         if (ip6_v6only) {
791                 inp6->in6p_flags |= IN6P_IPV6_V6ONLY;
792         }
793         so->so_send = sctp_sosend;
794 #endif
795         inp6->in6p_hops = -1;           /* use kernel default */
796         inp6->in6p_cksum = -1;  /* just to be sure */
797 #ifdef INET
798         /*
799          * XXX: ugly!!
800          * IPv4 TTL initialization is necessary for an IPv6 socket as well,
801          * because the socket may be bound to an IPv6 wildcard address,
802          * which may match an IPv4-mapped IPv6 address.
803          */
804 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
805         inp6->inp_ip_ttl = ip_defttl;
806 #else
807         inp->inp_ip_ttl = ip_defttl;
808 #endif
809 #endif
810         /*
811          * Hmm what about the IPSEC stuff that is missing here but
812          * in sctp_attach()?
813          */
814         return 0;
815 }
816
817 static int
818 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
819 sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
820 {
821 #else
822 #if defined(__FreeBSD__) || defined(__APPLE__)
823 sctp6_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
824 {
825 #else
826 sctp6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
827 {
828         struct sockaddr *addr = nam ?  mtod(nam, struct sockaddr *) : NULL;
829 #endif
830 #endif
831         struct sctp_inpcb *inp;
832         struct in6pcb *inp6;
833         int error;
834
835         inp = (struct sctp_inpcb *)so->so_pcb;
836         if (inp == NULL)
837                 return EINVAL;
838
839         inp6 = (struct in6pcb *)inp;
840 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
841         inp6->inp_vflag &= ~INP_IPV4;
842         inp6->inp_vflag |= INP_IPV6;
843 #else
844 #if defined(__OpenBSD__)
845         inp->ip_inp.inp.inp_flags &= ~INP_IPV4;
846         inp->ip_inp.inp.inp_flags |= INP_IPV6;
847 #else
848         inp->inp_vflag &= ~INP_IPV4;
849         inp->inp_vflag |= INP_IPV6;
850 #endif
851 #endif
852         if (addr != NULL &&
853 #if defined(__OpenBSD__)
854              (0) /* we always do dual bind */
855 #elif defined (__NetBSD__)
856              !(inp6->in6p_flags & IN6P_IPV6_V6ONLY)
857 #else
858              !(inp6->inp_flags & IN6P_IPV6_V6ONLY)
859 #endif
860              ) {
861                 if (addr->sa_family == AF_INET) {
862                         /* binding v4 addr to v6 socket, so reset flags */
863 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
864                         inp6->inp_vflag |= INP_IPV4;
865                         inp6->inp_vflag &= ~INP_IPV6;
866 #else
867 #if defined(__OpenBSD__)
868                         inp->ip_inp.inp.inp_flags |= INP_IPV4;
869                         inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
870 #else
871                         inp->inp_vflag |= INP_IPV4;
872                         inp->inp_vflag &= ~INP_IPV6;
873 #endif
874 #endif
875                 } else {
876                         struct sockaddr_in6 *sin6_p;
877                         sin6_p = (struct sockaddr_in6 *)addr;
878
879                         if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) {
880 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
881                           inp6->inp_vflag |= INP_IPV4;
882 #else
883 #if defined(__OpenBSD__)
884                           inp->ip_inp.inp.inp_flags |= INP_IPV4;
885 #else
886                           inp->inp_vflag |= INP_IPV4;
887 #endif
888 #endif
889                         }
890                         else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
891                                 struct sockaddr_in sin;
892                                 in6_sin6_2_sin(&sin, sin6_p);
893 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
894                                 inp6->inp_vflag |= INP_IPV4;
895                                 inp6->inp_vflag &= ~INP_IPV6;
896 #else
897 #if defined(__OpenBSD__)
898                                 inp->ip_inp.inp.inp_flags |= INP_IPV4;
899                                 inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
900
901 #else
902                                 inp->inp_vflag |= INP_IPV4;
903                                 inp->inp_vflag &= ~INP_IPV6;
904 #endif
905 #endif
906                                 crit_enter();
907                                 error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, p);
908                                 crit_exit();
909                                 return error;
910                         }
911                 }
912         } else if (addr != NULL) {
913                 /* IPV6_V6ONLY socket */
914                 if (addr->sa_family == AF_INET) {
915                         /* can't bind v4 addr to v6 only socket! */
916                         return EINVAL;
917                 } else {
918                         struct sockaddr_in6 *sin6_p;
919                         sin6_p = (struct sockaddr_in6 *)addr;
920
921                         if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr))
922                                 /* can't bind v4-mapped addrs either! */
923                                 /* NOTE: we don't support SIIT */
924                                 return EINVAL;
925                 }
926         }
927         crit_enter();
928         error = sctp_inpcb_bind(so, addr, p);
929         crit_exit();
930         return error;
931 }
932
933 /*This could be made common with sctp_detach() since they are identical */
934 static int
935 sctp6_detach(struct socket *so)
936 {
937         struct sctp_inpcb *inp;
938
939         inp = (struct sctp_inpcb *)so->so_pcb;
940         if (inp == NULL)
941                 return EINVAL;
942         crit_enter();
943         if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
944             (so->so_rcv.ssb_cc > 0))
945                 sctp_inpcb_free(inp, 1);
946         else
947                 sctp_inpcb_free(inp, 0);
948         crit_exit();
949         return 0;
950 }
951
952 static int
953 sctp6_disconnect(struct socket *so)
954 {
955         struct sctp_inpcb *inp;
956
957         crit_enter();
958         inp = (struct sctp_inpcb *)so->so_pcb;
959         if (inp == NULL) {
960                 crit_exit();
961                 return (ENOTCONN);
962         }
963         if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
964                 if (LIST_EMPTY(&inp->sctp_asoc_list)) {
965                         /* No connection */
966                         crit_exit();
967                         return (ENOTCONN);
968                 } else {
969                         int some_on_streamwheel = 0;
970                         struct sctp_association *asoc;
971                         struct sctp_tcb *stcb;
972
973                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
974                         if (stcb == NULL) {
975                                 crit_exit();
976                                 return (EINVAL);
977                         }
978                         asoc = &stcb->asoc;
979                         if (!TAILQ_EMPTY(&asoc->out_wheel)) {
980                                 /* Check to see if some data queued */
981                                 struct sctp_stream_out *outs;
982                                 TAILQ_FOREACH(outs, &asoc->out_wheel,
983                                               next_spoke) {
984                                         if (!TAILQ_EMPTY(&outs->outqueue)) {
985                                                 some_on_streamwheel = 1;
986                                                 break;
987                                         }
988                                 }
989                         }
990
991                         if (TAILQ_EMPTY(&asoc->send_queue) &&
992                             TAILQ_EMPTY(&asoc->sent_queue) &&
993                             (some_on_streamwheel == 0)) {
994                                 /* nothing queued to send, so I'm done... */
995                                 if ((SCTP_GET_STATE(asoc) !=
996                                      SCTP_STATE_SHUTDOWN_SENT) &&
997                                     (SCTP_GET_STATE(asoc) !=
998                                      SCTP_STATE_SHUTDOWN_ACK_SENT)) {
999                                         /* only send SHUTDOWN the first time */
1000 #ifdef SCTP_DEBUG
1001                                         if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
1002                                                 kprintf("%s:%d sends a shutdown\n",
1003                                                        __FILE__,
1004                                                        __LINE__
1005                                                         );
1006                                         }
1007 #endif
1008                                         sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
1009                                         sctp_chunk_output(stcb->sctp_ep, stcb, 1);
1010                                         asoc->state = SCTP_STATE_SHUTDOWN_SENT;
1011                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
1012                                                          stcb->sctp_ep, stcb,
1013                                                          asoc->primary_destination);
1014                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
1015                                                          stcb->sctp_ep, stcb,
1016                                                          asoc->primary_destination);
1017                                 }
1018                         } else {
1019                                 /*
1020                                  * we still got (or just got) data to send,
1021                                  * so set SHUTDOWN_PENDING
1022                                  */
1023                                 /*
1024                                  * XXX sockets draft says that MSG_EOF should
1025                                  * be sent with no data.  currently, we will
1026                                  * allow user data to be sent first and move
1027                                  * to SHUTDOWN-PENDING
1028                                  */
1029                                 asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
1030                         }
1031                         crit_exit();
1032                         return (0);
1033                 }
1034         } else {
1035                 /* UDP model does not support this */
1036                 crit_exit();
1037                 return EOPNOTSUPP;
1038         }
1039 }
1040
1041 int
1042 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1043 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1044           struct mbuf *control, struct thread *p);
1045 #else
1046 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1047           struct mbuf *control, struct proc *p);
1048 #endif
1049
1050
1051 static int
1052 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1053 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1054            struct mbuf *control, struct thread *p)
1055 {
1056 #else
1057 #if defined(__FreeBSD__) || defined(__APPLE__)
1058 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1059            struct mbuf *control, struct proc *p)
1060 {
1061 #else
1062 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam,
1063            struct mbuf *control, struct proc *p)
1064 {
1065         struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *) : NULL;
1066 #endif
1067 #endif
1068         struct sctp_inpcb *inp;
1069         struct inpcb *in_inp;
1070         struct in6pcb *inp6;
1071 #ifdef INET
1072         struct sockaddr_in6 *sin6;
1073 #endif /* INET */
1074         /* No SPL needed since sctp_output does this */
1075
1076         inp = (struct sctp_inpcb *)so->so_pcb;
1077         if (inp == NULL) {
1078                 if (control) {
1079                         m_freem(control);
1080                         control = NULL;
1081                 }
1082                 m_freem(m);
1083                 return EINVAL;
1084         }
1085         in_inp = (struct inpcb *)inp;
1086         inp6 = (struct in6pcb *)inp;
1087         /* For the TCP model we may get a NULL addr, if we
1088          * are a connected socket thats ok.
1089          */
1090         if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) &&
1091             (addr == NULL)) {
1092                 goto connected_type;
1093         }
1094         if (addr == NULL) {
1095                 m_freem(m);
1096                 if (control) {
1097                         m_freem(control);
1098                         control = NULL;
1099                 }
1100                 return (EDESTADDRREQ);
1101         }
1102
1103 #ifdef INET
1104         sin6 = (struct sockaddr_in6 *)addr;
1105         if (
1106
1107 #if defined(__OpenBSD__)
1108              (0) /* we always do dual bind */
1109 #elif defined (__NetBSD__)
1110              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1111 #else
1112              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1113 #endif
1114             ) {
1115                 /*
1116                  * if IPV6_V6ONLY flag, we discard datagrams
1117                  * destined to a v4 addr or v4-mapped addr
1118                  */
1119                 if (addr->sa_family == AF_INET) {
1120                         return EINVAL;
1121                 }
1122                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1123                         return EINVAL;
1124                 }
1125         }
1126
1127         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1128                 if (!ip6_v6only) {
1129                         struct sockaddr_in sin;
1130                         /* convert v4-mapped into v4 addr and send */
1131                         in6_sin6_2_sin(&sin, sin6);
1132                         return sctp_send(so, flags,  m, (struct sockaddr *)&sin,
1133                                            control, p);
1134                 } else {
1135                         /* mapped addresses aren't enabled */
1136                         return EINVAL;
1137                 }
1138         }
1139 #endif /* INET */
1140 connected_type:
1141         /* now what about control */
1142         if (control) {
1143                 if (inp->control) {
1144                         kprintf("huh? control set?\n");
1145                         m_freem(inp->control);
1146                         inp->control = NULL;
1147                 }
1148                 inp->control = control;
1149         }
1150         /* add it in possibly */
1151         if ((inp->pkt) &&
1152             (inp->pkt->m_flags & M_PKTHDR)) {
1153                 struct mbuf *x;
1154                 int c_len;
1155
1156                 c_len = 0;
1157                 /* How big is it */
1158                 for (x=m;x;x = x->m_next) {
1159                         c_len += x->m_len;
1160                 }
1161                 inp->pkt->m_pkthdr.len += c_len;
1162         }
1163         /* Place the data */
1164         if (inp->pkt) {
1165                 inp->pkt_last->m_next = m;
1166                 inp->pkt_last = m;
1167         } else {
1168                 inp->pkt_last = inp->pkt = m;
1169         }
1170         if (
1171 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1172             /* FreeBSD and MacOSX uses a flag passed */
1173             (!(flags & PRUS_MORETOCOME))
1174 #elif defined(__NetBSD__)
1175             /* NetBSD uses the so_state field */
1176             (!(so->so_state & SS_MORETOCOME))
1177 #else
1178             1   /* Open BSD does not have any "more to come" indication */
1179 #endif
1180             ) {
1181                 /*
1182                  * note with the current version this code will only be
1183                  * used by OpenBSD, NetBSD and FreeBSD have methods for
1184                  * re-defining sosend() to use sctp_sosend().  One can
1185                  * optionaly switch back to this code (by changing back
1186                  * the defininitions but this is not advisable.
1187                  */
1188                 int ret;
1189                 ret = sctp_output(inp, inp->pkt , addr, inp->control, p, flags);
1190                 inp->pkt = NULL;
1191                 inp->control = NULL;
1192                 return (ret);
1193         } else {
1194                 return (0);
1195         }
1196 }
1197
1198 static int
1199 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1200 sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
1201 {
1202 #else
1203 #if defined(__FreeBSD__) || defined(__APPLE__)
1204 sctp6_connect(struct socket *so, struct sockaddr *addr, struct proc *p)
1205 {
1206 #else
1207 sctp6_connect(struct socket *so, struct mbuf *nam, struct proc *p)
1208 {
1209         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1210 #endif
1211 #endif
1212         int error = 0;
1213         struct sctp_inpcb *inp;
1214         struct in6pcb *inp6;
1215         struct sctp_tcb *stcb;
1216 #ifdef INET
1217         struct sockaddr_in6 *sin6;
1218         struct sockaddr_storage ss;
1219 #endif /* INET */
1220
1221         crit_enter();
1222         inp6 = (struct in6pcb *)so->so_pcb;
1223         inp = (struct sctp_inpcb *)so->so_pcb;
1224         if (inp == NULL) {
1225                 crit_exit();
1226                 return (ECONNRESET);    /* I made the same as TCP since
1227                                          * we are not setup? */
1228         }
1229         SCTP_ASOC_CREATE_LOCK(inp);
1230         SCTP_INP_RLOCK(inp);
1231         if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
1232             SCTP_PCB_FLAGS_UNBOUND) {
1233                 /* Bind a ephemeral port */
1234                 SCTP_INP_RUNLOCK(inp);
1235                 error = sctp6_bind(so, NULL, p);
1236                 if (error) {
1237                         crit_exit();
1238                         SCTP_ASOC_CREATE_UNLOCK(inp);
1239
1240                         return (error);
1241                 }
1242                 SCTP_INP_RLOCK(inp);
1243         }
1244
1245         if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
1246             (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
1247                 /* We are already connected AND the TCP model */
1248                 crit_exit();
1249                 SCTP_INP_RUNLOCK(inp);
1250                 SCTP_ASOC_CREATE_UNLOCK(inp);
1251                 return (EADDRINUSE);
1252         }
1253
1254 #ifdef INET
1255         sin6 = (struct sockaddr_in6 *)addr;
1256         if (
1257 #if defined(__OpenBSD__)
1258              (0) /* we always do dual bind */
1259 #elif defined (__NetBSD__)
1260              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1261 #else
1262              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1263 #endif
1264             ) {
1265                 /*
1266                  * if IPV6_V6ONLY flag, ignore connections
1267                  * destined to a v4 addr or v4-mapped addr
1268                  */
1269                 if (addr->sa_family == AF_INET) {
1270                         crit_exit();
1271                         SCTP_INP_RUNLOCK(inp);
1272                         SCTP_ASOC_CREATE_UNLOCK(inp);
1273                         return EINVAL;
1274                 }
1275                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1276                         crit_exit();
1277                         SCTP_INP_RUNLOCK(inp);
1278                         SCTP_ASOC_CREATE_UNLOCK(inp);
1279                         return EINVAL;
1280                 }
1281         }
1282
1283         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1284                 if (!ip6_v6only) {
1285                         /* convert v4-mapped into v4 addr */
1286                         in6_sin6_2_sin((struct sockaddr_in *)&ss, sin6);
1287                         addr = (struct sockaddr *)&ss;
1288                 } else {
1289                         /* mapped addresses aren't enabled */
1290                         crit_exit();
1291                         SCTP_INP_RUNLOCK(inp);
1292                         SCTP_ASOC_CREATE_UNLOCK(inp);
1293                         return EINVAL;
1294                 }
1295         } else
1296 #endif /* INET */
1297                 addr = addr;    /* for true v6 address case */
1298
1299         /* Now do we connect? */
1300         if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1301                 stcb = LIST_FIRST(&inp->sctp_asoc_list);
1302                 if (stcb)
1303                         SCTP_TCB_UNLOCK (stcb);
1304                 SCTP_INP_RUNLOCK(inp);
1305         }else {
1306                 SCTP_INP_RUNLOCK(inp);
1307                 SCTP_INP_WLOCK(inp);
1308                 SCTP_INP_INCR_REF(inp);
1309                 SCTP_INP_WUNLOCK(inp);
1310                 stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL);
1311                 if (stcb == NULL) {
1312                         SCTP_INP_WLOCK(inp);
1313                         SCTP_INP_DECR_REF(inp);
1314                         SCTP_INP_WUNLOCK(inp);
1315                 }
1316         }
1317
1318         if (stcb != NULL) {
1319                 /* Already have or am bring up an association */
1320                 SCTP_ASOC_CREATE_UNLOCK(inp);
1321                 SCTP_TCB_UNLOCK (stcb);
1322                 crit_exit();
1323                 return (EALREADY);
1324         }
1325         /* We are GOOD to go */
1326         stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
1327         SCTP_ASOC_CREATE_UNLOCK(inp);
1328         if (stcb == NULL) {
1329                 /* Gak! no memory */
1330                 crit_exit();
1331                 return (error);
1332         }
1333         if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
1334                 stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
1335                 /* Set the connected flag so we can queue data */
1336                 soisconnecting(so);
1337         }
1338         stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
1339         SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
1340         sctp_send_initiate(inp, stcb);
1341         SCTP_TCB_UNLOCK (stcb);
1342         crit_exit();
1343         return error;
1344 }
1345
1346 static int
1347 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1348 sctp6_getaddr(struct socket *so, struct sockaddr **addr)
1349 {
1350         struct sockaddr_in6 *sin6;
1351 #else
1352 sctp6_getaddr(struct socket *so, struct mbuf *nam)
1353 {
1354         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1355 #endif
1356         struct sctp_inpcb *inp;
1357         /*
1358          * Do the malloc first in case it blocks.
1359          */
1360 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1361         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1362                M_WAITOK | M_ZERO);
1363 #else
1364         nam->m_len = sizeof(*sin6);
1365 #endif
1366         bzero(sin6, sizeof(*sin6));
1367         sin6->sin6_family = AF_INET6;
1368         sin6->sin6_len = sizeof(*sin6);
1369
1370         inp = (struct sctp_inpcb *)so->so_pcb;
1371         if (!inp) {
1372 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1373                 FREE(sin6, M_SONAME);
1374 #endif
1375                 return ECONNRESET;
1376         }
1377
1378         sin6->sin6_port = inp->sctp_lport;
1379         if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
1380                 /* For the bound all case you get back 0 */
1381                 if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1382                         struct sctp_tcb *stcb;
1383                         struct sockaddr_in6 *sin_a6;
1384                         struct sctp_nets *net;
1385                         int fnd;
1386
1387                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1388                         if (stcb == NULL) {
1389                                 goto notConn6;
1390                         }
1391                         fnd = 0;
1392                         sin_a6 = NULL;
1393                         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1394                                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1395                                 if (sin_a6->sin6_family == AF_INET6) {
1396                                         fnd = 1;
1397                                         break;
1398                                 }
1399                         }
1400                         if ((!fnd) || (sin_a6 == NULL)) {
1401                                 /* punt */
1402                                 goto notConn6;
1403                         }
1404                         sin6->sin6_addr = sctp_ipv6_source_address_selection(
1405                             inp, stcb, (struct route *)&net->ro, net, 0);
1406
1407                 } else {
1408                         /* For the bound all case you get back 0 */
1409                 notConn6:
1410                         memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
1411                 }
1412         } else {
1413                 /* Take the first IPv6 address in the list */
1414                 struct sctp_laddr *laddr;
1415                 int fnd = 0;
1416                 LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
1417                         if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
1418                                 struct sockaddr_in6 *sin_a;
1419                                 sin_a = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
1420                                 sin6->sin6_addr = sin_a->sin6_addr;
1421                                 fnd = 1;
1422                                 break;
1423                         }
1424                 }
1425                 if (!fnd) {
1426 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1427                         FREE(sin6, M_SONAME);
1428 #endif
1429                         return ENOENT;
1430                 }
1431         }
1432         /* Scoping things for v6 */
1433         if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
1434                 /* skip ifp check below */
1435                 in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1436         else
1437                 sin6->sin6_scope_id = 0;        /*XXX*/
1438 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1439         (*addr) = (struct sockaddr *)sin6;
1440 #endif
1441         return (0);
1442 }
1443
1444 static int
1445 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1446 sctp6_peeraddr(struct socket *so, struct sockaddr **addr)
1447 {
1448         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)*addr;
1449 #else
1450 sctp6_peeraddr(struct socket *so, struct mbuf *nam)
1451 {
1452         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1453 #endif
1454         int fnd;
1455         struct sockaddr_in6 *sin_a6;
1456         struct sctp_inpcb *inp;
1457         struct sctp_tcb *stcb;
1458         struct sctp_nets *net;
1459         /*
1460          * Do the malloc first in case it blocks.
1461          */
1462         inp = (struct sctp_inpcb *)so->so_pcb;
1463         if (!(inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
1464                 /* UDP type and listeners will drop out here */
1465                 return (ENOTCONN);
1466         }
1467 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1468         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1469                M_WAITOK | M_ZERO);
1470 #else
1471         nam->m_len = sizeof(*sin6);
1472 #endif
1473         bzero(sin6, sizeof(*sin6));
1474         sin6->sin6_family = AF_INET6;
1475         sin6->sin6_len = sizeof(*sin6);
1476
1477         /* We must recapture incase we blocked */
1478         inp = (struct sctp_inpcb *)so->so_pcb;
1479         if (!inp) {
1480 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1481                 FREE(sin6, M_SONAME);
1482 #endif
1483                 return ECONNRESET;
1484         }
1485         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1486         if (stcb == NULL) {
1487 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1488                 FREE(sin6, M_SONAME);
1489 #endif
1490                 return ECONNRESET;
1491         }
1492         fnd = 0;
1493         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1494                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1495                 if (sin_a6->sin6_family == AF_INET6) {
1496                         fnd = 1;
1497                         sin6->sin6_port = stcb->rport;
1498                         sin6->sin6_addr = sin_a6->sin6_addr;
1499                         break;
1500                 }
1501         }
1502         if (!fnd) {
1503                 /* No IPv4 address */
1504 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1505                 FREE(sin6, M_SONAME);
1506 #endif
1507                 return ENOENT;
1508         }
1509         in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1510 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1511         *addr = (struct sockaddr *)sin6;
1512 #endif
1513         return (0);
1514 }
1515
1516 static int
1517 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1518 sctp6_in6getaddr(struct socket *so, struct sockaddr **nam)
1519 {
1520         struct sockaddr *addr;
1521 #else
1522 sctp6_in6getaddr(struct socket *so, struct mbuf *nam)
1523 {
1524         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1525 #endif
1526         struct in6pcb *inp6 = sotoin6pcb(so);
1527         int error;
1528
1529         if (inp6 == NULL)
1530                 return EINVAL;
1531
1532         crit_enter();
1533         /* allow v6 addresses precedence */
1534         error = sctp6_getaddr(so, nam);
1535         if (error) {
1536                 /* try v4 next if v6 failed */
1537                 error = sctp_ingetaddr(so, nam);
1538                 if (error) {
1539                         crit_exit();
1540                         return (error);
1541                 }
1542 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1543                 addr = *nam;
1544 #endif
1545                 /* if I'm V6ONLY, convert it to v4-mapped */
1546                 if (
1547 #if defined(__OpenBSD__)
1548              (0) /* we always do dual bind */
1549 #elif defined (__NetBSD__)
1550              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1551 #else
1552              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1553 #endif
1554                     ) {
1555                         struct sockaddr_in6 sin6;
1556                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1557                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1558 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1559                         nam->m_len = sizeof(sin6);
1560 #endif
1561 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1562                 } else {
1563                         nam->m_len = sizeof(struct sockaddr_in);
1564 #endif
1565                 }
1566 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1567         } else {
1568                 nam->m_len = sizeof(struct sockaddr_in6);
1569 #endif
1570         }
1571         crit_exit();
1572         return (error);
1573 }
1574
1575
1576 static int
1577 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1578 sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam)
1579 {
1580         struct sockaddr *addr = *nam;
1581 #else
1582 sctp6_getpeeraddr(struct socket *so, struct mbuf *nam)
1583 {
1584         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1585 #endif
1586         struct in6pcb *inp6 = sotoin6pcb(so);
1587         int error;
1588
1589         if (inp6 == NULL)
1590                 return EINVAL;
1591
1592         crit_enter();
1593         /* allow v6 addresses precedence */
1594         error = sctp6_peeraddr(so, nam);
1595         if (error) {
1596                 /* try v4 next if v6 failed */
1597                 error = sctp_peeraddr(so, nam);
1598                 if (error) {
1599                         crit_exit();
1600                         return (error);
1601                 }
1602                 /* if I'm V6ONLY, convert it to v4-mapped */
1603                 if (
1604 #if defined(__OpenBSD__)
1605              (0) /* we always do dual bind */
1606 #elif defined (__NetBSD__)
1607              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1608 #else
1609              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1610 #endif
1611                     ) {
1612                         struct sockaddr_in6 sin6;
1613                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1614                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1615 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1616                         nam->m_len = sizeof(sin6);
1617 #endif
1618 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1619                 } else {
1620                         nam->m_len = sizeof(struct sockaddr_in);
1621 #endif
1622                 }
1623 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1624         } else {
1625                 nam->m_len = sizeof(struct sockaddr_in6);
1626 #endif
1627         }
1628         crit_exit();
1629         return error;
1630 }
1631
1632 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1633 struct pr_usrreqs sctp6_usrreqs = {
1634         .pru_abort = sctp6_abort,
1635         .pru_accept = sctp_accept,
1636         .pru_attach = sctp6_attach,
1637         .pru_bind = sctp6_bind,
1638         .pru_connect = sctp6_connect,
1639         .pru_connect2 = pru_connect2_notsupp,
1640         .pru_control = in6_control,
1641         .pru_detach = sctp6_detach,
1642         .pru_disconnect = sctp6_disconnect,
1643         .pru_listen = sctp_listen,
1644         .pru_peeraddr = sctp6_getpeeraddr,
1645         .pru_rcvd = sctp_usr_recvd,
1646         .pru_rcvoob = pru_rcvoob_notsupp,
1647         .pru_send = sctp6_send,
1648         .pru_sense = pru_sense_null,
1649         .pru_shutdown = sctp_shutdown,
1650         .pru_sockaddr = sctp6_in6getaddr,
1651         .pru_sosend = sctp_sosend,
1652         .pru_soreceive = soreceive
1653 };
1654
1655 #else
1656
1657 int
1658 sctp6_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
1659              struct mbuf *control, struct proc *p)
1660 {
1661         int s;
1662         int error = 0;
1663         int family;
1664
1665 #if defined(__OpenBSD__)
1666         p = curproc;
1667 #endif
1668         s = splsoftnet();
1669         family = so->so_proto->pr_domain->dom_family;
1670
1671         if (req == PRU_CONTROL) {
1672                 switch (family) {
1673                 case PF_INET:
1674                         error = in_control(so, (long)m, (caddr_t)nam,
1675                             (struct ifnet *)control
1676 #if defined(__NetBSD__)
1677                              , p
1678 #endif
1679                              );
1680 #ifdef INET6
1681                 case PF_INET6:
1682                         error = in6_control(so, (long)m, (caddr_t)nam,
1683                             (struct ifnet *)control, p);
1684 #endif
1685                 default:
1686                         error = EAFNOSUPPORT;
1687                 }
1688                 splx(s);
1689                 return (error);
1690         }
1691 #ifdef __NetBSD__
1692         if (req == PRU_PURGEIF) {
1693                 struct ifnet *ifn;
1694                 struct ifaddr *ifa;
1695                 ifn = (struct ifnet *)control;
1696                 TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
1697                         if (ifa->ifa_addr->sa_family == family) {
1698                                 sctp_delete_ip_address(ifa);
1699                         }
1700                 }
1701                 switch (family) {
1702                 case PF_INET:
1703                         in_purgeif (ifn);
1704                         break;
1705 #ifdef INET6
1706                 case PF_INET6:
1707                         in6_purgeif (ifn);
1708                         break;
1709 #endif
1710                 default:
1711                         splx(s);
1712                         return (EAFNOSUPPORT);
1713                 }
1714                 splx(s);
1715                 return (0);
1716         }
1717 #endif
1718         switch (req) {
1719         case PRU_ATTACH:
1720                 error = sctp6_attach(so, family, p);
1721                 break;
1722         case PRU_DETACH:
1723                 error = sctp6_detach(so);
1724                 break;
1725         case PRU_BIND:
1726                 if (nam == NULL)
1727                         return (EINVAL);
1728                 error = sctp6_bind(so, nam, p);
1729                 break;
1730         case PRU_LISTEN:
1731                 error = sctp_listen(so, p);
1732                 break;
1733         case PRU_CONNECT:
1734                 if (nam == NULL)
1735                         return (EINVAL);
1736                 error = sctp6_connect(so, nam, p);
1737                 break;
1738         case PRU_DISCONNECT:
1739                 error = sctp6_disconnect(so);
1740                 break;
1741         case PRU_ACCEPT:
1742                 if (nam == NULL)
1743                         return (EINVAL);
1744                 error = sctp_accept(so, nam);
1745                 break;
1746         case PRU_SHUTDOWN:
1747                 error = sctp_shutdown(so);
1748                 break;
1749
1750         case PRU_RCVD:
1751                 /*
1752                  * For OpenBSD and NetBSD, this is real ugly. The (mbuf *)
1753                  * nam that is passed (by soreceive()) is the int flags
1754                  * cast as a (mbuf *) yuck!
1755                  */
1756                 error = sctp_usr_recvd(so, (int)((long)nam));
1757                 break;
1758
1759         case PRU_SEND:
1760                 /* Flags are ignored */
1761                 error = sctp6_send(so, 0, m, nam, control, p);
1762                 break;
1763         case PRU_ABORT:
1764                 error = sctp6_abort(so);
1765                 break;
1766
1767         case PRU_SENSE:
1768                 error = 0;
1769                 break;
1770         case PRU_RCVOOB:
1771                 error = EAFNOSUPPORT;
1772                 break;
1773         case PRU_SENDOOB:
1774                 error = EAFNOSUPPORT;
1775                 break;
1776         case PRU_PEERADDR:
1777                 error = sctp6_getpeeraddr(so, nam);
1778                 break;
1779         case PRU_SOCKADDR:
1780                 error = sctp6_in6getaddr(so, nam);
1781                 break;
1782         case PRU_SLOWTIMO:
1783                 error = 0;
1784                 break;
1785         default:
1786                 break;
1787         }
1788         splx(s);
1789         return (error);
1790 }
1791 #endif