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