Add a forth argument to soreserve(). Pass just a NULL for now.
[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.3 2005/07/15 15:52:00 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 #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 <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__) || defined(__DragonFly__)
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__) || defined(__DragonFly__))
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__) || defined(__DragonFly__)
423 #if (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version < 501113) || \
424     defined(__APPLE__) || defined(__DragonFly__)
425                 ip6_savecontrol(in6p_ip, &opts, ip6, m);
426 #elif __FreeBSD_version >= 440000 || (defined(SCTP_BASE_FREEBSD) && __FreeBSD_version >= 501113)
427                 ip6_savecontrol(in6p_ip, m, &opts);
428 #else
429                 ip6_savecontrol(in6p_ip, m, &opts, NULL);
430 #endif
431 #else
432                 ip6_savecontrol((struct in6pcb *)in6p_ip, m, &opts);
433 #endif
434         }
435
436         /*
437          * CONTROL chunk processing
438          */
439         length = ntohs(ip6->ip6_plen) + iphlen;
440         offset -= sizeof(*ch);
441         ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff);
442 #if defined(__NetBSD__) || defined(__OpenBSD__)
443         s = splsoftnet();
444 #else
445         s = splnet();
446 #endif
447         (void)sctp_common_input_processing(&m, iphlen, offset, length, sh, ch,
448             in6p, stcb, net, ecn_bits);
449         /* inp's ref-count reduced && stcb unlocked */
450         splx(s);
451         /* XXX this stuff below gets moved to appropriate parts later... */
452         if (m)
453                 m_freem(m);
454         if (opts)
455                 m_freem(opts);
456
457         if ((in6p) && refcount_up){
458                 /* reduce ref-count */
459                 SCTP_INP_WLOCK(in6p);
460                 SCTP_INP_DECR_REF(in6p);
461                 SCTP_INP_WUNLOCK(in6p);
462         }
463
464         return IPPROTO_DONE;
465
466 bad:
467         if (stcb)
468                 SCTP_TCB_UNLOCK(stcb);
469
470         if ((in6p) && refcount_up){
471                 /* reduce ref-count */
472                 SCTP_INP_WLOCK(in6p);
473                 SCTP_INP_DECR_REF(in6p);
474                 SCTP_INP_WUNLOCK(in6p);
475         }
476         if (m)
477                 m_freem(m);
478         if (opts)
479                 m_freem(opts);
480         return IPPROTO_DONE;
481 }
482
483
484 static void
485 sctp6_notify_mbuf(struct sctp_inpcb *inp,
486                   struct icmp6_hdr *icmp6,
487                   struct sctphdr *sh,
488                   struct sctp_tcb *stcb,
489                   struct sctp_nets *net)
490 {
491         unsigned int nxtsz;
492
493         if ((inp == NULL) || (stcb == NULL) || (net == NULL) ||
494             (icmp6 == NULL) || (sh == NULL)) {
495                 goto out;
496         }
497
498         /* First do we even look at it? */
499         if (ntohl(sh->v_tag) != (stcb->asoc.peer_vtag))
500                 goto out;
501
502         if (icmp6->icmp6_type != ICMP6_PACKET_TOO_BIG) {
503                 /* not PACKET TO BIG */
504                 goto out;
505         }
506         /*
507          * ok we need to look closely. We could even get smarter and
508          * look at anyone that we sent to in case we get a different
509          * ICMP that tells us there is no way to reach a host, but for
510          * this impl, all we care about is MTU discovery.
511          */
512         nxtsz = ntohl(icmp6->icmp6_mtu);
513         /* Stop any PMTU timer */
514         sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
515
516         /* Adjust destination size limit */
517         if (net->mtu > nxtsz) {
518                 net->mtu = nxtsz;
519         }
520         /* now what about the ep? */
521         if (stcb->asoc.smallest_mtu > nxtsz) {
522                 struct sctp_tmit_chunk *chk;
523                 struct sctp_stream_out *strm;
524                 /* Adjust that too */
525                 stcb->asoc.smallest_mtu = nxtsz;
526                 /* now off to subtract IP_DF flag if needed */
527
528                 TAILQ_FOREACH(chk, &stcb->asoc.send_queue, sctp_next) {
529                         if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
530                                 chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
531                         }
532                 }
533                 TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
534                         if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
535                                 /*
536                                  * For this guy we also mark for immediate
537                                  * resend since we sent to big of chunk
538                                  */
539                                 chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
540                                 if (chk->sent != SCTP_DATAGRAM_RESEND)
541                                         stcb->asoc.sent_queue_retran_cnt++;
542                                 chk->sent = SCTP_DATAGRAM_RESEND;
543                                 chk->rec.data.doing_fast_retransmit = 0;
544
545                                 chk->sent = SCTP_DATAGRAM_RESEND;
546                                 /* Clear any time so NO RTT is being done */
547                                 chk->sent_rcv_time.tv_sec = 0;
548                                 chk->sent_rcv_time.tv_usec = 0;
549                                 stcb->asoc.total_flight -= chk->send_size;
550                                 net->flight_size -= chk->send_size;
551                         }
552                 }
553                 TAILQ_FOREACH(strm, &stcb->asoc.out_wheel, next_spoke) {
554                         TAILQ_FOREACH(chk, &strm->outqueue, sctp_next) {
555                                 if ((chk->send_size+IP_HDR_SIZE) > nxtsz) {
556                                         chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
557                                 }
558                         }
559                 }
560         }
561         sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, NULL);
562 out:
563         if (inp) {
564                 /* reduce inp's ref-count */
565                 SCTP_INP_WLOCK(inp);
566                 SCTP_INP_DECR_REF(inp);
567                 SCTP_INP_WUNLOCK(inp);
568         }
569         if (stcb)
570                 SCTP_TCB_UNLOCK(stcb);
571 }
572
573
574 void
575 sctp6_ctlinput(cmd, pktdst, d)
576      int cmd;
577      struct sockaddr *pktdst;
578      void *d;
579 {
580         struct sctphdr sh;
581         struct ip6ctlparam *ip6cp = NULL;
582         int s, cm;
583
584         if (pktdst->sa_family != AF_INET6 ||
585             pktdst->sa_len != sizeof(struct sockaddr_in6))
586                 return;
587
588         if ((unsigned)cmd >= PRC_NCMDS)
589                 return;
590         if (PRC_IS_REDIRECT(cmd)) {
591                 d = NULL;
592         } else if (inet6ctlerrmap[cmd] == 0) {
593                 return;
594         }
595
596         /* if the parameter is from icmp6, decode it. */
597         if (d != NULL) {
598                 ip6cp = (struct ip6ctlparam *)d;
599         } else {
600                 ip6cp = (struct ip6ctlparam *)NULL;
601         }
602
603         if (ip6cp) {
604                 /*
605                  * XXX: We assume that when IPV6 is non NULL,
606                  * M and OFF are valid.
607                  */
608                 /* check if we can safely examine src and dst ports */
609                 struct sctp_inpcb *inp;
610                 struct sctp_tcb *stcb;
611                 struct sctp_nets *net;
612                 struct sockaddr_in6 final;
613
614                 if (ip6cp->ip6c_m == NULL ||
615                     (size_t)ip6cp->ip6c_m->m_pkthdr.len < (ip6cp->ip6c_off + sizeof(sh)))
616                         return;
617
618                 bzero(&sh, sizeof(sh));
619                 bzero(&final, sizeof(final));
620                 inp = NULL;
621                 net = NULL;
622                 m_copydata(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(sh),
623                     (caddr_t)&sh);
624                 ip6cp->ip6c_src->sin6_port = sh.src_port;
625                 final.sin6_len = sizeof(final);
626                 final.sin6_family = AF_INET6;
627 #if defined(__FreeBSD__) && __FreeBSD_cc_version < 440000
628                 final.sin6_addr = *ip6cp->ip6c_finaldst;
629 #else
630                 final.sin6_addr = ((struct sockaddr_in6 *)pktdst)->sin6_addr;
631 #endif /* __FreeBSD_cc_version */
632                 final.sin6_port = sh.dest_port;
633 #if defined(__NetBSD__) || defined(__OpenBSD__)
634                 s = splsoftnet();
635 #else
636                 s = splnet();
637 #endif
638                 stcb = sctp_findassociation_addr_sa((struct sockaddr *)ip6cp->ip6c_src,
639                                                     (struct sockaddr *)&final,
640                                                     &inp, &net, 1);
641                 /* inp's ref-count increased && stcb locked */
642                 if (stcb != NULL && inp && (inp->sctp_socket != NULL)) {
643                         if (cmd == PRC_MSGSIZE) {
644                                 sctp6_notify_mbuf(inp,
645                                                   ip6cp->ip6c_icmp6,
646                                                   &sh,
647                                                   stcb,
648                                                   net);
649                                 /* inp's ref-count reduced && stcb unlocked */
650                         } else {
651                                 if (cmd == PRC_HOSTDEAD) {
652                                         cm = EHOSTUNREACH;
653                                 } else {
654                                         cm = inet6ctlerrmap[cmd];
655                                 }
656                                 sctp_notify(inp, cm, &sh,
657                                             (struct sockaddr *)&final,
658                                             stcb, net);
659                                 /* inp's ref-count reduced && stcb unlocked */
660                         }
661                 } else {
662                         if (PRC_IS_REDIRECT(cmd) && inp) {
663 #ifdef __OpenBSD__
664                                 in_rtchange((struct inpcb *)inp,
665                                             inetctlerrmap[cmd]);
666 #else
667                                 in6_rtchange((struct in6pcb *)inp,
668                                              inet6ctlerrmap[cmd]);
669 #endif
670                         }
671                         if (inp) {
672                                 /* reduce inp's ref-count */
673                                 SCTP_INP_WLOCK(inp);
674                                 SCTP_INP_DECR_REF(inp);
675                                 SCTP_INP_WUNLOCK(inp);
676                         }
677                         if (stcb)
678                                 SCTP_TCB_UNLOCK(stcb);
679                 }
680                 splx(s);
681         }
682 }
683
684 /*
685  * this routine can probably be collasped into the one in sctp_userreq.c
686  * since they do the same thing and now we lookup with a sockaddr
687  */
688 #ifdef __FreeBSD__
689 static int
690 sctp6_getcred(SYSCTL_HANDLER_ARGS)
691 {
692         struct sockaddr_in6 addrs[2];
693         struct sctp_inpcb *inp;
694         struct sctp_nets *net;
695         struct sctp_tcb *stcb;
696         int error, s;
697
698 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
699         error = suser(req->td);
700 #else
701         error = suser(req->p);
702 #endif
703         if (error)
704                 return (error);
705
706         if (req->newlen != sizeof(addrs))
707                 return (EINVAL);
708         if (req->oldlen != sizeof(struct ucred))
709                 return (EINVAL);
710         error = SYSCTL_IN(req, addrs, sizeof(addrs));
711         if (error)
712                 return (error);
713 #if defined(__NetBSD__) || defined(__OpenBSD__)
714         s = splsoftnet();
715 #else
716         s = splnet();
717 #endif
718
719         stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]),
720                                            sin6tosa(&addrs[1]),
721                                            &inp, &net, 1);
722         if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
723                 error = ENOENT;
724                 if (inp) {
725                         SCTP_INP_WLOCK(inp);
726                         SCTP_INP_DECR_REF(inp);
727                         SCTP_INP_WUNLOCK(inp);
728                 }
729                 goto out;
730         }
731         error = SYSCTL_OUT(req, inp->sctp_socket->so_cred,
732                            sizeof(struct ucred));
733
734         SCTP_TCB_UNLOCK (stcb);
735  out:
736         splx(s);
737         return (error);
738 }
739
740 SYSCTL_PROC(_net_inet6_sctp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW,
741             0, 0,
742             sctp6_getcred, "S,ucred", "Get the ucred of a SCTP6 connection");
743
744 #endif
745
746 /* This is the same as the sctp_abort() could be made common */
747 static int
748 sctp6_abort(struct socket *so)
749 {
750         struct sctp_inpcb *inp;
751         int s;
752
753         inp = (struct sctp_inpcb *)so->so_pcb;
754         if (inp == 0)
755                 return EINVAL;  /* ??? possible? panic instead? */
756         soisdisconnected(so);
757 #if defined(__NetBSD__) || defined(__OpenBSD__)
758         s = splsoftnet();
759 #else
760         s = splnet();
761 #endif
762         sctp_inpcb_free(inp, 1);
763         splx(s);
764         return 0;
765 }
766
767 static int
768 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
769 sctp6_attach(struct socket *so, int proto, struct thread *p)
770 #elif defined(__DragonFly__)
771 sctp6_attach(struct socket *so, int proto, struct pru_attach_info *ai)
772 #else
773 sctp6_attach(struct socket *so, int proto, struct proc *p)
774 #endif
775 {
776         struct in6pcb *inp6;
777         int s, error;
778         struct sctp_inpcb *inp;
779
780         inp = (struct sctp_inpcb *)so->so_pcb;
781         if (inp != NULL)
782                 return EINVAL;
783
784         if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
785                 error = soreserve(so, sctp_sendspace, sctp_recvspace, NULL);
786                 if (error)
787                         return error;
788         }
789 #if defined(__NetBSD__) || defined(__OpenBSD__)
790         s = splsoftnet();
791 #else
792         s = splnet();
793 #endif
794         error = sctp_inpcb_alloc(so);
795         splx(s);
796         if (error)
797                 return error;
798         inp = (struct sctp_inpcb *)so->so_pcb;
799         inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6;     /* I'm v6! */
800         inp6 = (struct in6pcb *)inp;
801
802 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
803         inp6->inp_vflag |= INP_IPV6;
804 #else
805 #if defined(__OpenBSD__)
806         inp->ip_inp.inp.inp_flags |= INP_IPV6;
807 #else
808         inp->inp_vflag |=  INP_IPV6;
809 #endif
810 #endif
811 #if defined(__NetBSD__)
812         if (ip6_v6only) {
813                 inp6->in6p_flags |= IN6P_IPV6_V6ONLY;
814         }
815         so->so_send = sctp_sosend;
816 #endif
817         inp6->in6p_hops = -1;           /* use kernel default */
818         inp6->in6p_cksum = -1;  /* just to be sure */
819 #ifdef INET
820         /*
821          * XXX: ugly!!
822          * IPv4 TTL initialization is necessary for an IPv6 socket as well,
823          * because the socket may be bound to an IPv6 wildcard address,
824          * which may match an IPv4-mapped IPv6 address.
825          */
826 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
827         inp6->inp_ip_ttl = ip_defttl;
828 #else
829         inp->inp_ip_ttl = ip_defttl;
830 #endif
831 #endif
832         /*
833          * Hmm what about the IPSEC stuff that is missing here but
834          * in sctp_attach()?
835          */
836         return 0;
837 }
838
839 static int
840 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
841 sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
842 {
843 #else
844 #if defined(__FreeBSD__) || defined(__APPLE__)
845 sctp6_bind(struct socket *so, struct sockaddr *addr, struct proc *p)
846 {
847 #else
848 sctp6_bind(struct socket *so, struct mbuf *nam, struct proc *p)
849 {
850         struct sockaddr *addr = nam ?  mtod(nam, struct sockaddr *) : NULL;
851 #endif
852 #endif
853         struct sctp_inpcb *inp;
854         struct in6pcb *inp6;
855         int s, error;
856
857         inp = (struct sctp_inpcb *)so->so_pcb;
858         if (inp == 0)
859                 return EINVAL;
860
861         inp6 = (struct in6pcb *)inp;
862 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
863         inp6->inp_vflag &= ~INP_IPV4;
864         inp6->inp_vflag |= INP_IPV6;
865 #else
866 #if defined(__OpenBSD__)
867         inp->ip_inp.inp.inp_flags &= ~INP_IPV4;
868         inp->ip_inp.inp.inp_flags |= INP_IPV6;
869 #else
870         inp->inp_vflag &= ~INP_IPV4;
871         inp->inp_vflag |= INP_IPV6;
872 #endif
873 #endif
874         if (addr != NULL &&
875 #if defined(__OpenBSD__)
876              (0) /* we always do dual bind */
877 #elif defined (__NetBSD__)
878              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
879 #else
880              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
881 #endif
882              == 0) {
883                 if (addr->sa_family == AF_INET) {
884                         /* binding v4 addr to v6 socket, so reset flags */
885 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
886                         inp6->inp_vflag |= INP_IPV4;
887                         inp6->inp_vflag &= ~INP_IPV6;
888 #else
889 #if defined(__OpenBSD__)
890                         inp->ip_inp.inp.inp_flags |= INP_IPV4;
891                         inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
892 #else
893                         inp->inp_vflag |= INP_IPV4;
894                         inp->inp_vflag &= ~INP_IPV6;
895 #endif
896 #endif
897                 } else {
898                         struct sockaddr_in6 *sin6_p;
899                         sin6_p = (struct sockaddr_in6 *)addr;
900
901                         if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) {
902 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
903                           inp6->inp_vflag |= INP_IPV4;
904 #else
905 #if defined(__OpenBSD__)
906                           inp->ip_inp.inp.inp_flags |= INP_IPV4;
907 #else
908                           inp->inp_vflag |= INP_IPV4;
909 #endif
910 #endif
911                         }
912                         else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) {
913                                 struct sockaddr_in sin;
914                                 in6_sin6_2_sin(&sin, sin6_p);
915 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
916                                 inp6->inp_vflag |= INP_IPV4;
917                                 inp6->inp_vflag &= ~INP_IPV6;
918 #else
919 #if defined(__OpenBSD__)
920                                 inp->ip_inp.inp.inp_flags |= INP_IPV4;
921                                 inp->ip_inp.inp.inp_flags &= ~INP_IPV6;
922
923 #else
924                                 inp->inp_vflag |= INP_IPV4;
925                                 inp->inp_vflag &= ~INP_IPV6;
926 #endif
927 #endif
928 #if defined(__NetBSD__) || defined(__OpenBSD__)
929                                 s = splsoftnet();
930 #else
931                                 s = splnet();
932 #endif
933                                 error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, p);
934                                 splx(s);
935                                 return error;
936                         }
937                 }
938         } else if (addr != NULL) {
939                 /* IPV6_V6ONLY socket */
940                 if (addr->sa_family == AF_INET) {
941                         /* can't bind v4 addr to v6 only socket! */
942                         return EINVAL;
943                 } else {
944                         struct sockaddr_in6 *sin6_p;
945                         sin6_p = (struct sockaddr_in6 *)addr;
946
947                         if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr))
948                                 /* can't bind v4-mapped addrs either! */
949                                 /* NOTE: we don't support SIIT */
950                                 return EINVAL;
951                 }
952         }
953 #if defined(__NetBSD__) || defined(__OpenBSD__)
954         s = splsoftnet();
955 #else
956         s = splnet();
957 #endif
958         error = sctp_inpcb_bind(so, addr, p);
959         splx(s);
960         return error;
961 }
962
963 /*This could be made common with sctp_detach() since they are identical */
964 static int
965 sctp6_detach(struct socket *so)
966 {
967         struct sctp_inpcb *inp;
968         int s;
969
970         inp = (struct sctp_inpcb *)so->so_pcb;
971         if (inp == 0)
972                 return EINVAL;
973 #if defined(__NetBSD__) || defined(__OpenBSD__)
974         s = splsoftnet();
975 #else
976         s = splnet();
977 #endif
978         if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
979             (so->so_rcv.sb_cc > 0))
980                 sctp_inpcb_free(inp, 1);
981         else
982                 sctp_inpcb_free(inp, 0);
983         splx(s);
984         return 0;
985 }
986
987 static int
988 sctp6_disconnect(struct socket *so)
989 {
990         struct sctp_inpcb *inp;
991         int s;
992
993 #if defined(__NetBSD__) || defined(__OpenBSD__)
994         s = splsoftnet();
995 #else
996         s = splnet();           /* XXX */
997 #endif
998         inp = (struct sctp_inpcb *)so->so_pcb;
999         if (inp == NULL) {
1000                 splx(s);
1001                 return (ENOTCONN);
1002         }
1003         if (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
1004                 if (LIST_EMPTY(&inp->sctp_asoc_list)) {
1005                         /* No connection */
1006                         splx(s);
1007                         return (ENOTCONN);
1008                 } else {
1009                         int some_on_streamwheel = 0;
1010                         struct sctp_association *asoc;
1011                         struct sctp_tcb *stcb;
1012
1013                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1014                         if (stcb == NULL) {
1015                                 splx(s);
1016                                 return (EINVAL);
1017                         }
1018                         asoc = &stcb->asoc;
1019                         if (!TAILQ_EMPTY(&asoc->out_wheel)) {
1020                                 /* Check to see if some data queued */
1021                                 struct sctp_stream_out *outs;
1022                                 TAILQ_FOREACH(outs, &asoc->out_wheel,
1023                                               next_spoke) {
1024                                         if (!TAILQ_EMPTY(&outs->outqueue)) {
1025                                                 some_on_streamwheel = 1;
1026                                                 break;
1027                                         }
1028                                 }
1029                         }
1030
1031                         if (TAILQ_EMPTY(&asoc->send_queue) &&
1032                             TAILQ_EMPTY(&asoc->sent_queue) &&
1033                             (some_on_streamwheel == 0)) {
1034                                 /* nothing queued to send, so I'm done... */
1035                                 if ((SCTP_GET_STATE(asoc) !=
1036                                      SCTP_STATE_SHUTDOWN_SENT) &&
1037                                     (SCTP_GET_STATE(asoc) !=
1038                                      SCTP_STATE_SHUTDOWN_ACK_SENT)) {
1039                                         /* only send SHUTDOWN the first time */
1040 #ifdef SCTP_DEBUG
1041                                         if (sctp_debug_on & SCTP_DEBUG_OUTPUT4) {
1042                                                 printf("%s:%d sends a shutdown\n",
1043                                                        __FILE__,
1044                                                        __LINE__
1045                                                         );
1046                                         }
1047 #endif
1048                                         sctp_send_shutdown(stcb, stcb->asoc.primary_destination);
1049                                         sctp_chunk_output(stcb->sctp_ep, stcb, 1);
1050                                         asoc->state = SCTP_STATE_SHUTDOWN_SENT;
1051                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
1052                                                          stcb->sctp_ep, stcb,
1053                                                          asoc->primary_destination);
1054                                         sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
1055                                                          stcb->sctp_ep, stcb,
1056                                                          asoc->primary_destination);
1057                                 }
1058                         } else {
1059                                 /*
1060                                  * we still got (or just got) data to send,
1061                                  * so set SHUTDOWN_PENDING
1062                                  */
1063                                 /*
1064                                  * XXX sockets draft says that MSG_EOF should
1065                                  * be sent with no data.  currently, we will
1066                                  * allow user data to be sent first and move
1067                                  * to SHUTDOWN-PENDING
1068                                  */
1069                                 asoc->state |= SCTP_STATE_SHUTDOWN_PENDING;
1070                         }
1071                         splx(s);
1072                         return (0);
1073                 }
1074         } else {
1075                 /* UDP model does not support this */
1076                 splx(s);
1077                 return EOPNOTSUPP;
1078         }
1079 }
1080
1081 int
1082 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1083 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1084           struct mbuf *control, struct thread *p);
1085 #else
1086 sctp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1087           struct mbuf *control, struct proc *p);
1088 #endif
1089
1090
1091 static int
1092 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1093 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1094            struct mbuf *control, struct thread *p)
1095 {
1096 #else
1097 #if defined(__FreeBSD__) || defined(__APPLE__)
1098 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
1099            struct mbuf *control, struct proc *p)
1100 {
1101 #else
1102 sctp6_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam,
1103            struct mbuf *control, struct proc *p)
1104 {
1105         struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *) : NULL;
1106 #endif
1107 #endif
1108         struct sctp_inpcb *inp;
1109         struct inpcb *in_inp;
1110         struct in6pcb *inp6;
1111 #ifdef INET
1112         struct sockaddr_in6 *sin6;
1113 #endif /* INET */
1114         /* No SPL needed since sctp_output does this */
1115
1116         inp = (struct sctp_inpcb *)so->so_pcb;
1117         if (inp == NULL) {
1118                 if (control) {
1119                         m_freem(control);
1120                         control = NULL;
1121                 }
1122                 m_freem(m);
1123                 return EINVAL;
1124         }
1125         in_inp = (struct inpcb *)inp;
1126         inp6 = (struct in6pcb *)inp;
1127         /* For the TCP model we may get a NULL addr, if we
1128          * are a connected socket thats ok.
1129          */
1130         if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) &&
1131             (addr == NULL)) {
1132                 goto connected_type;
1133         }
1134         if (addr == NULL) {
1135                 m_freem(m);
1136                 if (control) {
1137                         m_freem(control);
1138                         control = NULL;
1139                 }
1140                 return (EDESTADDRREQ);
1141         }
1142
1143 #ifdef INET
1144         sin6 = (struct sockaddr_in6 *)addr;
1145         if (
1146
1147 #if defined(__OpenBSD__)
1148              (0) /* we always do dual bind */
1149 #elif defined (__NetBSD__)
1150              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1151 #else
1152              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1153 #endif
1154             ) {
1155                 /*
1156                  * if IPV6_V6ONLY flag, we discard datagrams
1157                  * destined to a v4 addr or v4-mapped addr
1158                  */
1159                 if (addr->sa_family == AF_INET) {
1160                         return EINVAL;
1161                 }
1162                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1163                         return EINVAL;
1164                 }
1165         }
1166
1167         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1168                 if (!ip6_v6only) {
1169                         struct sockaddr_in sin;
1170                         /* convert v4-mapped into v4 addr and send */
1171                         in6_sin6_2_sin(&sin, sin6);
1172                         return sctp_send(so, flags,  m, (struct sockaddr *)&sin,
1173                                            control, p);
1174                 } else {
1175                         /* mapped addresses aren't enabled */
1176                         return EINVAL;
1177                 }
1178         }
1179 #endif /* INET */
1180  connected_type:
1181         /* now what about control */
1182         if (control) {
1183                 if (inp->control) {
1184                         printf("huh? control set?\n");
1185                         m_freem(inp->control);
1186                         inp->control = NULL;
1187                 }
1188                 inp->control = control;
1189         }
1190         /* add it in possibly */
1191         if ((inp->pkt) &&
1192             (inp->pkt->m_flags & M_PKTHDR)) {
1193                 struct mbuf *x;
1194                 int c_len;
1195
1196                 c_len = 0;
1197                 /* How big is it */
1198                 for (x=m;x;x = x->m_next) {
1199                         c_len += x->m_len;
1200                 }
1201                 inp->pkt->m_pkthdr.len += c_len;
1202         }
1203         /* Place the data */
1204         if (inp->pkt) {
1205                 inp->pkt_last->m_next = m;
1206                 inp->pkt_last = m;
1207         } else {
1208                 inp->pkt_last = inp->pkt = m;
1209         }
1210         if (
1211 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1212             /* FreeBSD and MacOSX uses a flag passed */
1213             ((flags & PRUS_MORETOCOME) == 0)
1214 #elif defined(__NetBSD__)
1215             /* NetBSD uses the so_state field */
1216             ((so->so_state & SS_MORETOCOME) == 0)
1217 #else
1218             1   /* Open BSD does not have any "more to come" indication */
1219 #endif
1220             ) {
1221                 /*
1222                  * note with the current version this code will only be
1223                  * used by OpenBSD, NetBSD and FreeBSD have methods for
1224                  * re-defining sosend() to use sctp_sosend().  One can
1225                  * optionaly switch back to this code (by changing back
1226                  * the defininitions but this is not advisable.
1227                  */
1228                 int ret;
1229                 ret = sctp_output(inp, inp->pkt , addr, inp->control, p, flags);
1230                 inp->pkt = NULL;
1231                 inp->control = NULL;
1232                 return (ret);
1233         } else {
1234                 return (0);
1235         }
1236 }
1237
1238 static int
1239 #if (defined(__FreeBSD__) && __FreeBSD_version >= 500000) || defined(__DragonFly__)
1240 sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p)
1241 {
1242 #else
1243 #if defined(__FreeBSD__) || defined(__APPLE__)
1244 sctp6_connect(struct socket *so, struct sockaddr *addr, struct proc *p)
1245 {
1246 #else
1247 sctp6_connect(struct socket *so, struct mbuf *nam, struct proc *p)
1248 {
1249         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1250 #endif
1251 #endif
1252 #if defined(__NetBSD__) || defined(__OpenBSD__)
1253         int s = splsoftnet();
1254 #else
1255         int s = splnet();
1256 #endif
1257         int error = 0;
1258         struct sctp_inpcb *inp;
1259         struct in6pcb *inp6;
1260         struct sctp_tcb *stcb;
1261 #ifdef INET
1262         struct sockaddr_in6 *sin6;
1263         struct sockaddr_storage ss;
1264 #endif /* INET */
1265
1266         inp6 = (struct in6pcb *)so->so_pcb;
1267         inp = (struct sctp_inpcb *)so->so_pcb;
1268         if (inp == 0) {
1269                 splx(s);
1270                 return (ECONNRESET);    /* I made the same as TCP since
1271                                          * we are not setup? */
1272         }
1273         SCTP_ASOC_CREATE_LOCK(inp);
1274         SCTP_INP_RLOCK(inp);
1275         if ((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) ==
1276             SCTP_PCB_FLAGS_UNBOUND) {
1277                 /* Bind a ephemeral port */
1278                 SCTP_INP_RUNLOCK(inp);
1279                 error = sctp6_bind(so, NULL, p);
1280                 if (error) {
1281                         splx(s);
1282                         SCTP_ASOC_CREATE_UNLOCK(inp);
1283
1284                         return (error);
1285                 }
1286                 SCTP_INP_RLOCK(inp);
1287         }
1288
1289         if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
1290             (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
1291                 /* We are already connected AND the TCP model */
1292                 splx(s);
1293                 SCTP_INP_RUNLOCK(inp);
1294                 SCTP_ASOC_CREATE_UNLOCK(inp);
1295                 return (EADDRINUSE);
1296         }
1297
1298 #ifdef INET
1299         sin6 = (struct sockaddr_in6 *)addr;
1300         if (
1301 #if defined(__OpenBSD__)
1302              (0) /* we always do dual bind */
1303 #elif defined (__NetBSD__)
1304              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1305 #else
1306              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1307 #endif
1308             ) {
1309                 /*
1310                  * if IPV6_V6ONLY flag, ignore connections
1311                  * destined to a v4 addr or v4-mapped addr
1312                  */
1313                 if (addr->sa_family == AF_INET) {
1314                         splx(s);
1315                         SCTP_INP_RUNLOCK(inp);
1316                         SCTP_ASOC_CREATE_UNLOCK(inp);
1317                         return EINVAL;
1318                 }
1319                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1320                         splx(s);
1321                         SCTP_INP_RUNLOCK(inp);
1322                         SCTP_ASOC_CREATE_UNLOCK(inp);
1323                         return EINVAL;
1324                 }
1325         }
1326
1327         if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
1328                 if (!ip6_v6only) {
1329                         /* convert v4-mapped into v4 addr */
1330                         in6_sin6_2_sin((struct sockaddr_in *)&ss, sin6);
1331                         addr = (struct sockaddr *)&ss;
1332                 } else {
1333                         /* mapped addresses aren't enabled */
1334                         splx(s);
1335                         SCTP_INP_RUNLOCK(inp);
1336                         SCTP_ASOC_CREATE_UNLOCK(inp);
1337                         return EINVAL;
1338                 }
1339         } else
1340 #endif /* INET */
1341                 addr = addr;    /* for true v6 address case */
1342
1343         /* Now do we connect? */
1344         if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1345                 stcb = LIST_FIRST(&inp->sctp_asoc_list);
1346                 if (stcb)
1347                         SCTP_TCB_UNLOCK (stcb);
1348                 SCTP_INP_RUNLOCK(inp);
1349         }else {
1350                 SCTP_INP_RUNLOCK(inp);
1351                 SCTP_INP_WLOCK(inp);
1352                 SCTP_INP_INCR_REF(inp);
1353                 SCTP_INP_WUNLOCK(inp);
1354                 stcb = sctp_findassociation_ep_addr(&inp, addr, NULL, NULL, NULL);
1355                 if (stcb == NULL) {
1356                         SCTP_INP_WLOCK(inp);
1357                         SCTP_INP_DECR_REF(inp);
1358                         SCTP_INP_WUNLOCK(inp);
1359                 }
1360         }
1361
1362         if (stcb != NULL) {
1363                 /* Already have or am bring up an association */
1364                 SCTP_ASOC_CREATE_UNLOCK(inp);
1365                 SCTP_TCB_UNLOCK (stcb);
1366                 splx(s);
1367                 return (EALREADY);
1368         }
1369         /* We are GOOD to go */
1370         stcb = sctp_aloc_assoc(inp, addr, 1, &error, 0);
1371         SCTP_ASOC_CREATE_UNLOCK(inp);
1372         if (stcb == NULL) {
1373                 /* Gak! no memory */
1374                 splx(s);
1375                 return (error);
1376         }
1377         if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) {
1378                 stcb->sctp_ep->sctp_flags |= SCTP_PCB_FLAGS_CONNECTED;
1379                 /* Set the connected flag so we can queue data */
1380                 soisconnecting(so);
1381         }
1382         stcb->asoc.state = SCTP_STATE_COOKIE_WAIT;
1383         SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
1384         sctp_send_initiate(inp, stcb);
1385         SCTP_TCB_UNLOCK (stcb);
1386         splx(s);
1387         return error;
1388 }
1389
1390 static int
1391 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1392 sctp6_getaddr(struct socket *so, struct sockaddr **addr)
1393 {
1394         struct sockaddr_in6 *sin6;
1395 #else
1396 sctp6_getaddr(struct socket *so, struct mbuf *nam)
1397 {
1398         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1399 #endif
1400         struct sctp_inpcb *inp;
1401         /*
1402          * Do the malloc first in case it blocks.
1403          */
1404 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1405         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1406                M_WAITOK | M_ZERO);
1407 #else
1408         nam->m_len = sizeof(*sin6);
1409 #endif
1410         bzero(sin6, sizeof(*sin6));
1411         sin6->sin6_family = AF_INET6;
1412         sin6->sin6_len = sizeof(*sin6);
1413
1414         inp = (struct sctp_inpcb *)so->so_pcb;
1415         if (!inp) {
1416 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1417                 FREE(sin6, M_SONAME);
1418 #endif
1419                 return ECONNRESET;
1420         }
1421
1422         sin6->sin6_port = inp->sctp_lport;
1423         if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
1424                 /* For the bound all case you get back 0 */
1425                 if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
1426                         struct sctp_tcb *stcb;
1427                         struct sockaddr_in6 *sin_a6;
1428                         struct sctp_nets *net;
1429                         int fnd;
1430
1431                         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1432                         if (stcb == NULL) {
1433                                 goto notConn6;
1434                         }
1435                         fnd = 0;
1436                         sin_a6 = NULL;
1437                         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1438                                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1439                                 if (sin_a6->sin6_family == AF_INET6) {
1440                                         fnd = 1;
1441                                         break;
1442                                 }
1443                         }
1444                         if ((!fnd) || (sin_a6 == NULL)) {
1445                                 /* punt */
1446                                 goto notConn6;
1447                         }
1448                         sin6->sin6_addr = sctp_ipv6_source_address_selection(
1449                             inp, stcb, (struct route *)&net->ro, net, 0);
1450
1451                 } else {
1452                         /* For the bound all case you get back 0 */
1453                 notConn6:
1454                         memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
1455                 }
1456         } else {
1457                 /* Take the first IPv6 address in the list */
1458                 struct sctp_laddr *laddr;
1459                 int fnd = 0;
1460                 LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
1461                         if (laddr->ifa->ifa_addr->sa_family == AF_INET6) {
1462                                 struct sockaddr_in6 *sin_a;
1463                                 sin_a = (struct sockaddr_in6 *)laddr->ifa->ifa_addr;
1464                                 sin6->sin6_addr = sin_a->sin6_addr;
1465                                 fnd = 1;
1466                                 break;
1467                         }
1468                 }
1469                 if (!fnd) {
1470 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1471                         FREE(sin6, M_SONAME);
1472 #endif
1473                         return ENOENT;
1474                 }
1475         }
1476         /* Scoping things for v6 */
1477         if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
1478                 /* skip ifp check below */
1479                 in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1480         else
1481                 sin6->sin6_scope_id = 0;        /*XXX*/
1482 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1483         (*addr) = (struct sockaddr *)sin6;
1484 #endif
1485         return (0);
1486 }
1487
1488 static int
1489 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1490 sctp6_peeraddr(struct socket *so, struct sockaddr **addr)
1491 {
1492         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)*addr;
1493 #else
1494 sctp6_peeraddr(struct socket *so, struct mbuf *nam)
1495 {
1496         struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *);
1497 #endif
1498         int fnd;
1499         struct sockaddr_in6 *sin_a6;
1500         struct sctp_inpcb *inp;
1501         struct sctp_tcb *stcb;
1502         struct sctp_nets *net;
1503         /*
1504          * Do the malloc first in case it blocks.
1505          */
1506         inp = (struct sctp_inpcb *)so->so_pcb;
1507         if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) {
1508                 /* UDP type and listeners will drop out here */
1509                 return (ENOTCONN);
1510         }
1511 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1512         MALLOC(sin6, struct sockaddr_in6 *, sizeof *sin6, M_SONAME,
1513                M_WAITOK | M_ZERO);
1514 #else
1515         nam->m_len = sizeof(*sin6);
1516 #endif
1517         bzero(sin6, sizeof(*sin6));
1518         sin6->sin6_family = AF_INET6;
1519         sin6->sin6_len = sizeof(*sin6);
1520
1521         /* We must recapture incase we blocked */
1522         inp = (struct sctp_inpcb *)so->so_pcb;
1523         if (!inp) {
1524 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1525                 FREE(sin6, M_SONAME);
1526 #endif
1527                 return ECONNRESET;
1528         }
1529         stcb = LIST_FIRST(&inp->sctp_asoc_list);
1530         if (stcb == NULL) {
1531 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1532                 FREE(sin6, M_SONAME);
1533 #endif
1534                 return ECONNRESET;
1535         }
1536         fnd = 0;
1537         TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
1538                 sin_a6 = (struct sockaddr_in6 *)&net->ro._l_addr;
1539                 if (sin_a6->sin6_family == AF_INET6) {
1540                         fnd = 1;
1541                         sin6->sin6_port = stcb->rport;
1542                         sin6->sin6_addr = sin_a6->sin6_addr;
1543                         break;
1544                 }
1545         }
1546         if (!fnd) {
1547                 /* No IPv4 address */
1548 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1549                 FREE(sin6, M_SONAME);
1550 #endif
1551                 return ENOENT;
1552         }
1553         in6_recoverscope(sin6, &sin6->sin6_addr, NULL);
1554 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1555         *addr = (struct sockaddr *)sin6;
1556 #endif
1557         return (0);
1558 }
1559
1560 static int
1561 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1562 sctp6_in6getaddr(struct socket *so, struct sockaddr **nam)
1563 {
1564         struct sockaddr *addr;
1565 #else
1566 sctp6_in6getaddr(struct socket *so, struct mbuf *nam)
1567 {
1568         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1569 #endif
1570         struct in6pcb *inp6 = sotoin6pcb(so);
1571         int     error, s;
1572
1573         if (inp6 == NULL)
1574                 return EINVAL;
1575
1576 #if defined(__NetBSD__) || defined(__OpenBSD__)
1577         s = splsoftnet();
1578 #else
1579         s = splnet();
1580 #endif
1581         /* allow v6 addresses precedence */
1582         error = sctp6_getaddr(so, nam);
1583         if (error) {
1584                 /* try v4 next if v6 failed */
1585                 error = sctp_ingetaddr(so, nam);
1586                 if (error) {
1587                         splx(s);
1588                         return (error);
1589                 }
1590 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1591                 addr = *nam;
1592 #endif
1593                 /* if I'm V6ONLY, convert it to v4-mapped */
1594                 if (
1595 #if defined(__OpenBSD__)
1596              (0) /* we always do dual bind */
1597 #elif defined (__NetBSD__)
1598              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1599 #else
1600              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1601 #endif
1602                     ) {
1603                         struct sockaddr_in6 sin6;
1604                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1605                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1606 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1607                         nam->m_len = sizeof(sin6);
1608 #endif
1609 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1610                 } else {
1611                         nam->m_len = sizeof(struct sockaddr_in);
1612 #endif
1613                 }
1614 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1615         } else {
1616                 nam->m_len = sizeof(struct sockaddr_in6);
1617 #endif
1618         }
1619         splx(s);
1620         return (error);
1621 }
1622
1623
1624 static int
1625 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1626 sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam)
1627 {
1628         struct sockaddr *addr = *nam;
1629 #else
1630 sctp6_getpeeraddr(struct socket *so, struct mbuf *nam)
1631 {
1632         struct sockaddr *addr = mtod(nam, struct sockaddr *);
1633 #endif
1634         struct in6pcb *inp6 = sotoin6pcb(so);
1635         int     error, s;
1636
1637         if (inp6 == NULL)
1638                 return EINVAL;
1639
1640 #if defined(__NetBSD__) || defined(__OpenBSD__)
1641         s = splsoftnet();
1642 #else
1643         s = splnet();
1644 #endif
1645         /* allow v6 addresses precedence */
1646         error = sctp6_peeraddr(so, nam);
1647         if (error) {
1648                 /* try v4 next if v6 failed */
1649                 error = sctp_peeraddr(so, nam);
1650                 if (error) {
1651                         splx(s);
1652                         return (error);
1653                 }
1654                 /* if I'm V6ONLY, convert it to v4-mapped */
1655                 if (
1656 #if defined(__OpenBSD__)
1657              (0) /* we always do dual bind */
1658 #elif defined (__NetBSD__)
1659              (inp6->in6p_flags & IN6P_IPV6_V6ONLY)
1660 #else
1661              (inp6->inp_flags & IN6P_IPV6_V6ONLY)
1662 #endif
1663                     ) {
1664                         struct sockaddr_in6 sin6;
1665                         in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6);
1666                         memcpy(addr, &sin6, sizeof(struct sockaddr_in6));
1667 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1668                         nam->m_len = sizeof(sin6);
1669 #endif
1670 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1671                 } else {
1672                         nam->m_len = sizeof(struct sockaddr_in);
1673 #endif
1674                 }
1675 #if !(defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__))
1676         } else {
1677                 nam->m_len = sizeof(struct sockaddr_in6);
1678 #endif
1679         }
1680         splx(s);
1681         return error;
1682 }
1683
1684 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
1685 struct pr_usrreqs sctp6_usrreqs = {
1686         sctp6_abort, sctp_accept, sctp6_attach, sctp6_bind,
1687         sctp6_connect, pru_connect2_notsupp, in6_control,
1688         sctp6_detach, sctp6_disconnect, sctp_listen, sctp6_getpeeraddr,
1689         sctp_usr_recvd, pru_rcvoob_notsupp, sctp6_send, pru_sense_null,
1690         sctp_shutdown, sctp6_in6getaddr, sctp_sosend, soreceive, sopoll
1691 };
1692
1693 #else
1694
1695 int
1696 sctp6_usrreq(so, req, m, nam, control, p)
1697      struct socket *so;
1698      int req;
1699      struct mbuf *m, *nam, *control;
1700      struct proc *p;
1701 {
1702         int s;
1703         int error = 0;
1704         int family;
1705
1706 #if defined(__OpenBSD__)
1707         p = curproc;
1708 #endif
1709         s = splsoftnet();
1710         family = so->so_proto->pr_domain->dom_family;
1711
1712         if (req == PRU_CONTROL) {
1713                 switch (family) {
1714                 case PF_INET:
1715                         error = in_control(so, (long)m, (caddr_t)nam,
1716                             (struct ifnet *)control
1717 #if defined(__NetBSD__)
1718                              , p
1719 #endif
1720                              );
1721 #ifdef INET6
1722                 case PF_INET6:
1723                         error = in6_control(so, (long)m, (caddr_t)nam,
1724                             (struct ifnet *)control, p);
1725 #endif
1726                 default:
1727                         error = EAFNOSUPPORT;
1728                 }
1729                 splx(s);
1730                 return (error);
1731         }
1732 #ifdef __NetBSD__
1733         if (req == PRU_PURGEIF) {
1734                 struct ifnet *ifn;
1735                 struct ifaddr *ifa;
1736                 ifn = (struct ifnet *)control;
1737                 TAILQ_FOREACH(ifa, &ifn->if_addrlist, ifa_list) {
1738                         if (ifa->ifa_addr->sa_family == family) {
1739                                 sctp_delete_ip_address(ifa);
1740                         }
1741                 }
1742                 switch (family) {
1743                 case PF_INET:
1744                         in_purgeif (ifn);
1745                         break;
1746 #ifdef INET6
1747                 case PF_INET6:
1748                         in6_purgeif (ifn);
1749                         break;
1750 #endif
1751                 default:
1752                         splx(s);
1753                         return (EAFNOSUPPORT);
1754                 }
1755                 splx(s);
1756                 return (0);
1757         }
1758 #endif
1759         switch (req) {
1760         case PRU_ATTACH:
1761                 error = sctp6_attach(so, family, p);
1762                 break;
1763         case PRU_DETACH:
1764                 error = sctp6_detach(so);
1765                 break;
1766         case PRU_BIND:
1767                 if (nam == NULL)
1768                         return (EINVAL);
1769                 error = sctp6_bind(so, nam, p);
1770                 break;
1771         case PRU_LISTEN:
1772                 error = sctp_listen(so, p);
1773                 break;
1774         case PRU_CONNECT:
1775                 if (nam == NULL)
1776                         return (EINVAL);
1777                 error = sctp6_connect(so, nam, p);
1778                 break;
1779         case PRU_DISCONNECT:
1780                 error = sctp6_disconnect(so);
1781                 break;
1782         case PRU_ACCEPT:
1783                 if (nam == NULL)
1784                         return (EINVAL);
1785                 error = sctp_accept(so, nam);
1786                 break;
1787         case PRU_SHUTDOWN:
1788                 error = sctp_shutdown(so);
1789                 break;
1790
1791         case PRU_RCVD:
1792                 /*
1793                  * For OpenBSD and NetBSD, this is real ugly. The (mbuf *)
1794                  * nam that is passed (by soreceive()) is the int flags
1795                  * cast as a (mbuf *) yuck!
1796                  */
1797                 error = sctp_usr_recvd(so, (int)((long)nam));
1798                 break;
1799
1800         case PRU_SEND:
1801                 /* Flags are ignored */
1802                 error = sctp6_send(so, 0, m, nam, control, p);
1803                 break;
1804         case PRU_ABORT:
1805                 error = sctp6_abort(so);
1806                 break;
1807
1808         case PRU_SENSE:
1809                 error = 0;
1810                 break;
1811         case PRU_RCVOOB:
1812                 error = EAFNOSUPPORT;
1813                 break;
1814         case PRU_SENDOOB:
1815                 error = EAFNOSUPPORT;
1816                 break;
1817         case PRU_PEERADDR:
1818                 error = sctp6_getpeeraddr(so, nam);
1819                 break;
1820         case PRU_SOCKADDR:
1821                 error = sctp6_in6getaddr(so, nam);
1822                 break;
1823         case PRU_SLOWTIMO:
1824                 error = 0;
1825                 break;
1826         default:
1827                 break;
1828         }
1829         splx(s);
1830         return (error);
1831 }
1832 #endif