10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51676Sjpk * Common Development and Distribution License (the "License").
61676Sjpk * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
211735Skcpoon
220Sstevel@tonic-gate /*
2312869SKacheong.Poon@Sun.COM * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/systm.h>
280Sstevel@tonic-gate #include <sys/stream.h>
290Sstevel@tonic-gate #include <sys/cmn_err.h>
300Sstevel@tonic-gate #include <sys/ddi.h>
310Sstevel@tonic-gate #include <sys/strsubr.h>
321676Sjpk #include <sys/tsol/tnet.h>
330Sstevel@tonic-gate
340Sstevel@tonic-gate #include <netinet/in.h>
350Sstevel@tonic-gate #include <netinet/ip6.h>
360Sstevel@tonic-gate
3711042SErik.Nordmark@Sun.COM #include <inet/ipsec_impl.h>
380Sstevel@tonic-gate #include <inet/common.h>
390Sstevel@tonic-gate #include <inet/ip.h>
400Sstevel@tonic-gate #include <inet/ip6.h>
4111042SErik.Nordmark@Sun.COM #include <inet/ipsec_impl.h>
420Sstevel@tonic-gate #include <inet/mib2.h>
430Sstevel@tonic-gate #include <inet/sctp_ip.h>
440Sstevel@tonic-gate #include <inet/ipclassifier.h>
450Sstevel@tonic-gate #include <inet/ip_ire.h>
460Sstevel@tonic-gate #include "sctp_impl.h"
470Sstevel@tonic-gate #include "sctp_asconf.h"
480Sstevel@tonic-gate
490Sstevel@tonic-gate ssize_t
sctp_link_abort(mblk_t * mp,uint16_t serror,char * details,size_t len,int iserror,boolean_t tbit)500Sstevel@tonic-gate sctp_link_abort(mblk_t *mp, uint16_t serror, char *details, size_t len,
510Sstevel@tonic-gate int iserror, boolean_t tbit)
520Sstevel@tonic-gate {
530Sstevel@tonic-gate size_t alen;
540Sstevel@tonic-gate mblk_t *amp;
550Sstevel@tonic-gate sctp_chunk_hdr_t *acp;
560Sstevel@tonic-gate sctp_parm_hdr_t *eph;
570Sstevel@tonic-gate
580Sstevel@tonic-gate ASSERT(mp != NULL && mp->b_cont == NULL);
590Sstevel@tonic-gate
600Sstevel@tonic-gate alen = sizeof (*acp) + (serror != 0 ? (sizeof (*eph) + len) : 0);
610Sstevel@tonic-gate
620Sstevel@tonic-gate amp = allocb(alen, BPRI_MED);
630Sstevel@tonic-gate if (amp == NULL) {
640Sstevel@tonic-gate return (-1);
650Sstevel@tonic-gate }
660Sstevel@tonic-gate
670Sstevel@tonic-gate amp->b_wptr = amp->b_rptr + alen;
680Sstevel@tonic-gate
690Sstevel@tonic-gate /* Chunk header */
700Sstevel@tonic-gate acp = (sctp_chunk_hdr_t *)amp->b_rptr;
710Sstevel@tonic-gate acp->sch_id = iserror ? CHUNK_ERROR : CHUNK_ABORT;
720Sstevel@tonic-gate acp->sch_flags = 0;
730Sstevel@tonic-gate acp->sch_len = htons(alen);
740Sstevel@tonic-gate if (tbit)
750Sstevel@tonic-gate SCTP_SET_TBIT(acp);
760Sstevel@tonic-gate
770Sstevel@tonic-gate linkb(mp, amp);
780Sstevel@tonic-gate
790Sstevel@tonic-gate if (serror == 0) {
800Sstevel@tonic-gate return (alen);
810Sstevel@tonic-gate }
820Sstevel@tonic-gate
830Sstevel@tonic-gate eph = (sctp_parm_hdr_t *)(acp + 1);
840Sstevel@tonic-gate eph->sph_type = htons(serror);
850Sstevel@tonic-gate eph->sph_len = htons(len + sizeof (*eph));
860Sstevel@tonic-gate
870Sstevel@tonic-gate if (len > 0) {
880Sstevel@tonic-gate bcopy(details, eph + 1, len);
890Sstevel@tonic-gate }
900Sstevel@tonic-gate
910Sstevel@tonic-gate /* XXX pad */
920Sstevel@tonic-gate
930Sstevel@tonic-gate return (alen);
940Sstevel@tonic-gate }
950Sstevel@tonic-gate
960Sstevel@tonic-gate void
sctp_user_abort(sctp_t * sctp,mblk_t * data)977480SKacheong.Poon@Sun.COM sctp_user_abort(sctp_t *sctp, mblk_t *data)
980Sstevel@tonic-gate {
990Sstevel@tonic-gate mblk_t *mp;
1000Sstevel@tonic-gate int len, hdrlen;
1010Sstevel@tonic-gate char *cause;
1020Sstevel@tonic-gate sctp_faddr_t *fp = sctp->sctp_current;
103*13009SChandrasekar.Marimuthu@Sun.COM ip_xmit_attr_t *ixa = fp->sf_ixa;
1043448Sdh155122 sctp_stack_t *sctps = sctp->sctp_sctps;
1050Sstevel@tonic-gate
1069509Sanil.udupa@sun.com /*
1079509Sanil.udupa@sun.com * Don't need notification if connection is not yet setup,
1089509Sanil.udupa@sun.com * call sctp_clean_death() to reclaim resources.
1099509Sanil.udupa@sun.com * Any pending connect call(s) will error out.
1109509Sanil.udupa@sun.com */
1119509Sanil.udupa@sun.com if (sctp->sctp_state < SCTPS_COOKIE_WAIT) {
1129509Sanil.udupa@sun.com sctp_clean_death(sctp, ECONNABORTED);
1139509Sanil.udupa@sun.com return;
1149509Sanil.udupa@sun.com }
1159509Sanil.udupa@sun.com
1160Sstevel@tonic-gate mp = sctp_make_mp(sctp, fp, 0);
1171735Skcpoon if (mp == NULL) {
1183448Sdh155122 SCTP_KSTAT(sctps, sctp_send_user_abort_failed);
1190Sstevel@tonic-gate return;
1201735Skcpoon }
1211735Skcpoon
1220Sstevel@tonic-gate /*
1230Sstevel@tonic-gate * Create abort chunk.
1240Sstevel@tonic-gate */
1250Sstevel@tonic-gate if (data) {
126*13009SChandrasekar.Marimuthu@Sun.COM if (fp->sf_isv4) {
1270Sstevel@tonic-gate hdrlen = sctp->sctp_hdr_len;
1280Sstevel@tonic-gate } else {
1290Sstevel@tonic-gate hdrlen = sctp->sctp_hdr6_len;
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate hdrlen += sizeof (sctp_chunk_hdr_t) + sizeof (sctp_parm_hdr_t);
1320Sstevel@tonic-gate cause = (char *)data->b_rptr;
1330Sstevel@tonic-gate len = data->b_wptr - data->b_rptr;
1340Sstevel@tonic-gate
135*13009SChandrasekar.Marimuthu@Sun.COM if (len + hdrlen > fp->sf_pmss) {
136*13009SChandrasekar.Marimuthu@Sun.COM len = fp->sf_pmss - hdrlen;
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate } else {
1390Sstevel@tonic-gate cause = NULL;
1400Sstevel@tonic-gate len = 0;
1410Sstevel@tonic-gate }
1427480SKacheong.Poon@Sun.COM /*
1437480SKacheong.Poon@Sun.COM * Since it is a user abort, we should have the sctp_t and hence
1447480SKacheong.Poon@Sun.COM * the correct verification tag. So we should not set the T-bit
1457480SKacheong.Poon@Sun.COM * in the ABORT.
1467480SKacheong.Poon@Sun.COM */
1470Sstevel@tonic-gate if ((len = sctp_link_abort(mp, SCTP_ERR_USER_ABORT, cause, len, 0,
1487480SKacheong.Poon@Sun.COM B_FALSE)) < 0) {
1490Sstevel@tonic-gate freemsg(mp);
1500Sstevel@tonic-gate return;
1510Sstevel@tonic-gate }
15212869SKacheong.Poon@Sun.COM SCTPS_BUMP_MIB(sctps, sctpAborted);
1530Sstevel@tonic-gate BUMP_LOCAL(sctp->sctp_opkts);
1540Sstevel@tonic-gate BUMP_LOCAL(sctp->sctp_obchunks);
1550Sstevel@tonic-gate
15611042SErik.Nordmark@Sun.COM sctp_set_iplen(sctp, mp, ixa);
15711042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_ire != NULL);
15811042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_cred != NULL);
15911042SErik.Nordmark@Sun.COM
16011042SErik.Nordmark@Sun.COM (void) conn_ip_output(mp, ixa);
1617480SKacheong.Poon@Sun.COM
1627480SKacheong.Poon@Sun.COM sctp_assoc_event(sctp, SCTP_COMM_LOST, 0, NULL);
1637480SKacheong.Poon@Sun.COM sctp_clean_death(sctp, ECONNABORTED);
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate * If iserror == 0, sends an abort. If iserror != 0, sends an error.
1680Sstevel@tonic-gate */
1690Sstevel@tonic-gate void
sctp_send_abort(sctp_t * sctp,uint32_t vtag,uint16_t serror,char * details,size_t len,mblk_t * inmp,int iserror,boolean_t tbit,ip_recv_attr_t * ira)1700Sstevel@tonic-gate sctp_send_abort(sctp_t *sctp, uint32_t vtag, uint16_t serror, char *details,
17111042SErik.Nordmark@Sun.COM size_t len, mblk_t *inmp, int iserror, boolean_t tbit, ip_recv_attr_t *ira)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate mblk_t *hmp;
1750Sstevel@tonic-gate uint32_t ip_hdr_len;
1760Sstevel@tonic-gate ipha_t *iniph;
17711042SErik.Nordmark@Sun.COM ipha_t *ahiph = NULL;
1780Sstevel@tonic-gate ip6_t *inip6h;
17911042SErik.Nordmark@Sun.COM ip6_t *ahip6h = NULL;
1800Sstevel@tonic-gate sctp_hdr_t *sh;
1810Sstevel@tonic-gate sctp_hdr_t *insh;
1820Sstevel@tonic-gate size_t ahlen;
1830Sstevel@tonic-gate uchar_t *p;
1840Sstevel@tonic-gate ssize_t alen;
1850Sstevel@tonic-gate int isv4;
18611042SErik.Nordmark@Sun.COM conn_t *connp = sctp->sctp_connp;
1873448Sdh155122 sctp_stack_t *sctps = sctp->sctp_sctps;
18811042SErik.Nordmark@Sun.COM ip_xmit_attr_t *ixa;
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
1910Sstevel@tonic-gate if (isv4) {
1920Sstevel@tonic-gate ahlen = sctp->sctp_hdr_len;
1930Sstevel@tonic-gate } else {
1940Sstevel@tonic-gate ahlen = sctp->sctp_hdr6_len;
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate
1971676Sjpk /*
1981676Sjpk * If this is a labeled system, then check to see if we're allowed to
1991676Sjpk * send a response to this particular sender. If not, then just drop.
2001676Sjpk */
20111042SErik.Nordmark@Sun.COM if (is_system_labeled() && !tsol_can_reply_error(inmp, ira))
2021676Sjpk return;
2031676Sjpk
20411042SErik.Nordmark@Sun.COM hmp = allocb(sctps->sctps_wroff_xtra + ahlen, BPRI_MED);
2050Sstevel@tonic-gate if (hmp == NULL) {
2060Sstevel@tonic-gate /* XXX no resources */
2070Sstevel@tonic-gate return;
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate
2100Sstevel@tonic-gate /* copy in the IP / SCTP header */
2113448Sdh155122 p = hmp->b_rptr + sctps->sctps_wroff_xtra;
2120Sstevel@tonic-gate hmp->b_rptr = p;
2130Sstevel@tonic-gate hmp->b_wptr = p + ahlen;
2140Sstevel@tonic-gate if (isv4) {
2150Sstevel@tonic-gate bcopy(sctp->sctp_iphc, p, sctp->sctp_hdr_len);
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate * Composite is likely incomplete at this point, so pull
2180Sstevel@tonic-gate * info from the incoming IP / SCTP headers.
2190Sstevel@tonic-gate */
2200Sstevel@tonic-gate ahiph = (ipha_t *)p;
2210Sstevel@tonic-gate iniph = (ipha_t *)inmp->b_rptr;
2220Sstevel@tonic-gate ip_hdr_len = IPH_HDR_LENGTH(inmp->b_rptr);
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr_len);
2250Sstevel@tonic-gate ASSERT(OK_32PTR(sh));
2260Sstevel@tonic-gate
2270Sstevel@tonic-gate insh = (sctp_hdr_t *)((uchar_t *)iniph + ip_hdr_len);
2280Sstevel@tonic-gate ASSERT(OK_32PTR(insh));
2290Sstevel@tonic-gate
2300Sstevel@tonic-gate /* Copy in the peer's IP addr */
2310Sstevel@tonic-gate ahiph->ipha_dst = iniph->ipha_src;
2320Sstevel@tonic-gate ahiph->ipha_src = iniph->ipha_dst;
2330Sstevel@tonic-gate } else {
2340Sstevel@tonic-gate bcopy(sctp->sctp_iphc6, p, sctp->sctp_hdr6_len);
2350Sstevel@tonic-gate ahip6h = (ip6_t *)p;
2360Sstevel@tonic-gate inip6h = (ip6_t *)inmp->b_rptr;
2377480SKacheong.Poon@Sun.COM ip_hdr_len = ip_hdr_length_v6(inmp, inip6h);
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr6_len);
2400Sstevel@tonic-gate ASSERT(OK_32PTR(sh));
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate insh = (sctp_hdr_t *)((uchar_t *)inip6h + ip_hdr_len);
2430Sstevel@tonic-gate ASSERT(OK_32PTR(insh));
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate /* Copy in the peer's IP addr */
2460Sstevel@tonic-gate ahip6h->ip6_dst = inip6h->ip6_src;
2470Sstevel@tonic-gate ahip6h->ip6_src = inip6h->ip6_dst;
2480Sstevel@tonic-gate }
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate /* Fill in the holes in the SCTP common header */
2510Sstevel@tonic-gate sh->sh_sport = insh->sh_dport;
2520Sstevel@tonic-gate sh->sh_dport = insh->sh_sport;
2530Sstevel@tonic-gate sh->sh_verf = vtag;
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate /* Link in the abort chunk */
2560Sstevel@tonic-gate if ((alen = sctp_link_abort(hmp, serror, details, len, iserror, tbit))
2570Sstevel@tonic-gate < 0) {
2580Sstevel@tonic-gate freemsg(hmp);
2590Sstevel@tonic-gate return;
2600Sstevel@tonic-gate }
2610Sstevel@tonic-gate
26211042SErik.Nordmark@Sun.COM /*
26311042SErik.Nordmark@Sun.COM * Base the transmission on any routing-related socket options
26411042SErik.Nordmark@Sun.COM * that have been set on the listener/connection.
26511042SErik.Nordmark@Sun.COM */
26611042SErik.Nordmark@Sun.COM ixa = conn_get_ixa_exclusive(connp);
26711042SErik.Nordmark@Sun.COM if (ixa == NULL) {
26811042SErik.Nordmark@Sun.COM freemsg(hmp);
26911042SErik.Nordmark@Sun.COM return;
27011042SErik.Nordmark@Sun.COM }
27111042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_VERIFY_PMTU;
27211042SErik.Nordmark@Sun.COM
27311042SErik.Nordmark@Sun.COM ixa->ixa_pktlen = ahlen + alen;
2740Sstevel@tonic-gate if (isv4) {
27511042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_IS_IPV4;
27611042SErik.Nordmark@Sun.COM ahiph->ipha_length = htons(ixa->ixa_pktlen);
27711042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr_len;
2780Sstevel@tonic-gate } else {
27911042SErik.Nordmark@Sun.COM ixa->ixa_flags &= ~IXAF_IS_IPV4;
28011042SErik.Nordmark@Sun.COM ahip6h->ip6_plen = htons(ixa->ixa_pktlen - IPV6_HDR_LEN);
28111042SErik.Nordmark@Sun.COM ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr6_len;
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate
28412869SKacheong.Poon@Sun.COM SCTPS_BUMP_MIB(sctps, sctpAborted);
2850Sstevel@tonic-gate BUMP_LOCAL(sctp->sctp_obchunks);
2860Sstevel@tonic-gate
28711042SErik.Nordmark@Sun.COM if (is_system_labeled() && ixa->ixa_tsl != NULL) {
28811042SErik.Nordmark@Sun.COM ASSERT(ira->ira_tsl != NULL);
28911042SErik.Nordmark@Sun.COM
29011042SErik.Nordmark@Sun.COM ixa->ixa_tsl = ira->ira_tsl; /* A multi-level responder */
29111042SErik.Nordmark@Sun.COM }
2921676Sjpk
29311042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_IPSEC_SECURE) {
29411042SErik.Nordmark@Sun.COM /*
29511042SErik.Nordmark@Sun.COM * Apply IPsec based on how IPsec was applied to
29611042SErik.Nordmark@Sun.COM * the packet that caused the abort.
29711042SErik.Nordmark@Sun.COM */
29811042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, ixa, hmp, ahiph, ahip6h)) {
29911042SErik.Nordmark@Sun.COM ip_stack_t *ipst = sctps->sctps_netstack->netstack_ip;
30011042SErik.Nordmark@Sun.COM
30111042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
30211042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */
30311042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
3041676Sjpk return;
3051676Sjpk }
30611042SErik.Nordmark@Sun.COM } else {
30711042SErik.Nordmark@Sun.COM ixa->ixa_flags |= IXAF_NO_IPSEC;
3081676Sjpk }
3091676Sjpk
31011042SErik.Nordmark@Sun.COM BUMP_LOCAL(sctp->sctp_opkts);
31111042SErik.Nordmark@Sun.COM BUMP_LOCAL(sctp->sctp_obchunks);
31211042SErik.Nordmark@Sun.COM
31311042SErik.Nordmark@Sun.COM (void) ip_output_simple(hmp, ixa);
31411042SErik.Nordmark@Sun.COM ixa_refrele(ixa);
31511042SErik.Nordmark@Sun.COM }
3161676Sjpk
31711042SErik.Nordmark@Sun.COM /*
31811042SErik.Nordmark@Sun.COM * OOTB version of the above.
31911042SErik.Nordmark@Sun.COM * If iserror == 0, sends an abort. If iserror != 0, sends an error.
32011042SErik.Nordmark@Sun.COM */
32111042SErik.Nordmark@Sun.COM void
sctp_ootb_send_abort(uint32_t vtag,uint16_t serror,char * details,size_t len,const mblk_t * inmp,int iserror,boolean_t tbit,ip_recv_attr_t * ira,ip_stack_t * ipst)32211042SErik.Nordmark@Sun.COM sctp_ootb_send_abort(uint32_t vtag, uint16_t serror, char *details,
32311042SErik.Nordmark@Sun.COM size_t len, const mblk_t *inmp, int iserror, boolean_t tbit,
32411042SErik.Nordmark@Sun.COM ip_recv_attr_t *ira, ip_stack_t *ipst)
32511042SErik.Nordmark@Sun.COM {
32611042SErik.Nordmark@Sun.COM uint32_t ip_hdr_len;
32711042SErik.Nordmark@Sun.COM size_t ahlen;
32811042SErik.Nordmark@Sun.COM ipha_t *ipha = NULL;
32911042SErik.Nordmark@Sun.COM ip6_t *ip6h = NULL;
33011042SErik.Nordmark@Sun.COM sctp_hdr_t *insctph;
33111042SErik.Nordmark@Sun.COM int i;
33211042SErik.Nordmark@Sun.COM uint16_t port;
33311042SErik.Nordmark@Sun.COM ssize_t alen;
33411042SErik.Nordmark@Sun.COM int isv4;
33511042SErik.Nordmark@Sun.COM mblk_t *mp;
33611042SErik.Nordmark@Sun.COM netstack_t *ns = ipst->ips_netstack;
33711042SErik.Nordmark@Sun.COM sctp_stack_t *sctps = ns->netstack_sctp;
33811042SErik.Nordmark@Sun.COM ip_xmit_attr_t ixas;
33911042SErik.Nordmark@Sun.COM
34011042SErik.Nordmark@Sun.COM bzero(&ixas, sizeof (ixas));
34111042SErik.Nordmark@Sun.COM
34211042SErik.Nordmark@Sun.COM isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
34311042SErik.Nordmark@Sun.COM ip_hdr_len = ira->ira_ip_hdr_length;
34411042SErik.Nordmark@Sun.COM ahlen = ip_hdr_len + sizeof (sctp_hdr_t);
34511042SErik.Nordmark@Sun.COM
3460Sstevel@tonic-gate /*
34711042SErik.Nordmark@Sun.COM * If this is a labeled system, then check to see if we're allowed to
34811042SErik.Nordmark@Sun.COM * send a response to this particular sender. If not, then just drop.
3490Sstevel@tonic-gate */
35011042SErik.Nordmark@Sun.COM if (is_system_labeled() && !tsol_can_reply_error(inmp, ira))
3510Sstevel@tonic-gate return;
35211042SErik.Nordmark@Sun.COM
35311042SErik.Nordmark@Sun.COM mp = allocb(ahlen + sctps->sctps_wroff_xtra, BPRI_MED);
35411042SErik.Nordmark@Sun.COM if (mp == NULL) {
3550Sstevel@tonic-gate return;
3560Sstevel@tonic-gate }
35711042SErik.Nordmark@Sun.COM mp->b_rptr += sctps->sctps_wroff_xtra;
35811042SErik.Nordmark@Sun.COM mp->b_wptr = mp->b_rptr + ahlen;
35911042SErik.Nordmark@Sun.COM bcopy(inmp->b_rptr, mp->b_rptr, ahlen);
36011042SErik.Nordmark@Sun.COM
3610Sstevel@tonic-gate /*
36211042SErik.Nordmark@Sun.COM * We follow the logic in tcp_xmit_early_reset() in that we skip
36311042SErik.Nordmark@Sun.COM * reversing source route (i.e. replace all IP options with EOL).
3640Sstevel@tonic-gate */
36511042SErik.Nordmark@Sun.COM if (isv4) {
36611042SErik.Nordmark@Sun.COM ipaddr_t v4addr;
36711042SErik.Nordmark@Sun.COM
36811042SErik.Nordmark@Sun.COM ipha = (ipha_t *)mp->b_rptr;
36911042SErik.Nordmark@Sun.COM for (i = IP_SIMPLE_HDR_LENGTH; i < (int)ip_hdr_len; i++)
37011042SErik.Nordmark@Sun.COM mp->b_rptr[i] = IPOPT_EOL;
37111042SErik.Nordmark@Sun.COM /* Swap addresses */
37211042SErik.Nordmark@Sun.COM ipha->ipha_length = htons(ahlen);
37311042SErik.Nordmark@Sun.COM v4addr = ipha->ipha_src;
37411042SErik.Nordmark@Sun.COM ipha->ipha_src = ipha->ipha_dst;
37511042SErik.Nordmark@Sun.COM ipha->ipha_dst = v4addr;
37611042SErik.Nordmark@Sun.COM ipha->ipha_ident = 0;
37711042SErik.Nordmark@Sun.COM ipha->ipha_ttl = (uchar_t)sctps->sctps_ipv4_ttl;
37811042SErik.Nordmark@Sun.COM
37911042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
38011042SErik.Nordmark@Sun.COM } else {
38111042SErik.Nordmark@Sun.COM in6_addr_t v6addr;
38211042SErik.Nordmark@Sun.COM
38311042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
38411042SErik.Nordmark@Sun.COM /* Remove any extension headers assuming partial overlay */
38511042SErik.Nordmark@Sun.COM if (ip_hdr_len > IPV6_HDR_LEN) {
38611042SErik.Nordmark@Sun.COM uint8_t *to;
38711042SErik.Nordmark@Sun.COM
38811042SErik.Nordmark@Sun.COM to = mp->b_rptr + ip_hdr_len - IPV6_HDR_LEN;
38911042SErik.Nordmark@Sun.COM ovbcopy(ip6h, to, IPV6_HDR_LEN);
39011042SErik.Nordmark@Sun.COM mp->b_rptr += ip_hdr_len - IPV6_HDR_LEN;
39111042SErik.Nordmark@Sun.COM ip_hdr_len = IPV6_HDR_LEN;
39211042SErik.Nordmark@Sun.COM ip6h = (ip6_t *)mp->b_rptr;
39311042SErik.Nordmark@Sun.COM ip6h->ip6_nxt = IPPROTO_SCTP;
39411042SErik.Nordmark@Sun.COM ahlen = ip_hdr_len + sizeof (sctp_hdr_t);
39511042SErik.Nordmark@Sun.COM }
39611042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons(ahlen - IPV6_HDR_LEN);
39711042SErik.Nordmark@Sun.COM v6addr = ip6h->ip6_src;
39811042SErik.Nordmark@Sun.COM ip6h->ip6_src = ip6h->ip6_dst;
39911042SErik.Nordmark@Sun.COM ip6h->ip6_dst = v6addr;
40011042SErik.Nordmark@Sun.COM ip6h->ip6_hops = (uchar_t)sctps->sctps_ipv6_hoplimit;
40111042SErik.Nordmark@Sun.COM
40211042SErik.Nordmark@Sun.COM ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
40311042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_dst)) {
40411042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_SCOPEID_SET;
40511042SErik.Nordmark@Sun.COM ixas.ixa_scopeid = ira->ira_ruifindex;
40611042SErik.Nordmark@Sun.COM }
4070Sstevel@tonic-gate }
40811042SErik.Nordmark@Sun.COM insctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_len);
40911042SErik.Nordmark@Sun.COM
41011042SErik.Nordmark@Sun.COM /* Swap ports. Verification tag is reused. */
41111042SErik.Nordmark@Sun.COM port = insctph->sh_sport;
41211042SErik.Nordmark@Sun.COM insctph->sh_sport = insctph->sh_dport;
41311042SErik.Nordmark@Sun.COM insctph->sh_dport = port;
41411042SErik.Nordmark@Sun.COM insctph->sh_verf = vtag;
41511042SErik.Nordmark@Sun.COM
41611042SErik.Nordmark@Sun.COM /* Link in the abort chunk */
41711042SErik.Nordmark@Sun.COM if ((alen = sctp_link_abort(mp, serror, details, len, iserror, tbit))
41811042SErik.Nordmark@Sun.COM < 0) {
41911042SErik.Nordmark@Sun.COM freemsg(mp);
42011042SErik.Nordmark@Sun.COM return;
42111042SErik.Nordmark@Sun.COM }
42211042SErik.Nordmark@Sun.COM
42311042SErik.Nordmark@Sun.COM ixas.ixa_pktlen = ahlen + alen;
42411042SErik.Nordmark@Sun.COM ixas.ixa_ip_hdr_length = ip_hdr_len;
42511042SErik.Nordmark@Sun.COM
42611042SErik.Nordmark@Sun.COM if (isv4) {
42711042SErik.Nordmark@Sun.COM ipha->ipha_length = htons(ixas.ixa_pktlen);
42811042SErik.Nordmark@Sun.COM } else {
42911042SErik.Nordmark@Sun.COM ip6h->ip6_plen = htons(ixas.ixa_pktlen - IPV6_HDR_LEN);
43011042SErik.Nordmark@Sun.COM }
43111042SErik.Nordmark@Sun.COM
43211042SErik.Nordmark@Sun.COM ixas.ixa_protocol = IPPROTO_SCTP;
43311042SErik.Nordmark@Sun.COM ixas.ixa_zoneid = ira->ira_zoneid;
43411042SErik.Nordmark@Sun.COM ixas.ixa_ipst = ipst;
43511042SErik.Nordmark@Sun.COM ixas.ixa_ifindex = 0;
43611042SErik.Nordmark@Sun.COM
43712869SKacheong.Poon@Sun.COM SCTPS_BUMP_MIB(sctps, sctpAborted);
43811042SErik.Nordmark@Sun.COM
43911042SErik.Nordmark@Sun.COM if (is_system_labeled()) {
44011042SErik.Nordmark@Sun.COM ASSERT(ira->ira_tsl != NULL);
44111042SErik.Nordmark@Sun.COM
44211042SErik.Nordmark@Sun.COM ixas.ixa_tsl = ira->ira_tsl; /* A multi-level responder */
44311042SErik.Nordmark@Sun.COM }
44411042SErik.Nordmark@Sun.COM
44511042SErik.Nordmark@Sun.COM if (ira->ira_flags & IRAF_IPSEC_SECURE) {
44611042SErik.Nordmark@Sun.COM /*
44711042SErik.Nordmark@Sun.COM * Apply IPsec based on how IPsec was applied to
44811042SErik.Nordmark@Sun.COM * the packet that was out of the blue.
44911042SErik.Nordmark@Sun.COM */
45011042SErik.Nordmark@Sun.COM if (!ipsec_in_to_out(ira, &ixas, mp, ipha, ip6h)) {
45111042SErik.Nordmark@Sun.COM BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
45211042SErik.Nordmark@Sun.COM /* Note: mp already consumed and ip_drop_packet done */
45311042SErik.Nordmark@Sun.COM return;
45411042SErik.Nordmark@Sun.COM }
45511042SErik.Nordmark@Sun.COM } else {
45611042SErik.Nordmark@Sun.COM /*
45711042SErik.Nordmark@Sun.COM * This is in clear. The abort message we are building
45811042SErik.Nordmark@Sun.COM * here should go out in clear, independent of our policy.
45911042SErik.Nordmark@Sun.COM */
46011042SErik.Nordmark@Sun.COM ixas.ixa_flags |= IXAF_NO_IPSEC;
46111042SErik.Nordmark@Sun.COM }
46211042SErik.Nordmark@Sun.COM
46311042SErik.Nordmark@Sun.COM (void) ip_output_simple(mp, &ixas);
46411042SErik.Nordmark@Sun.COM ixa_cleanup(&ixas);
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate /*ARGSUSED*/
4680Sstevel@tonic-gate mblk_t *
sctp_make_err(sctp_t * sctp,uint16_t serror,void * details,size_t len)4690Sstevel@tonic-gate sctp_make_err(sctp_t *sctp, uint16_t serror, void *details, size_t len)
4700Sstevel@tonic-gate {
4710Sstevel@tonic-gate
4720Sstevel@tonic-gate mblk_t *emp;
4730Sstevel@tonic-gate size_t elen;
4740Sstevel@tonic-gate sctp_chunk_hdr_t *ecp;
4750Sstevel@tonic-gate sctp_parm_hdr_t *eph;
4760Sstevel@tonic-gate int pad;
4770Sstevel@tonic-gate
4784964Skcpoon if ((pad = len % SCTP_ALIGN) != 0) {
4794964Skcpoon pad = SCTP_ALIGN - pad;
4800Sstevel@tonic-gate }
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate elen = sizeof (*ecp) + sizeof (*eph) + len;
4830Sstevel@tonic-gate emp = allocb(elen + pad, BPRI_MED);
4840Sstevel@tonic-gate if (emp == NULL) {
4850Sstevel@tonic-gate return (NULL);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate emp->b_wptr = emp->b_rptr + elen + pad;
4890Sstevel@tonic-gate
4900Sstevel@tonic-gate /* Chunk header */
4910Sstevel@tonic-gate ecp = (sctp_chunk_hdr_t *)emp->b_rptr;
4920Sstevel@tonic-gate ecp->sch_id = CHUNK_ERROR;
4930Sstevel@tonic-gate ecp->sch_flags = 0;
4940Sstevel@tonic-gate ecp->sch_len = htons(elen);
4950Sstevel@tonic-gate
4960Sstevel@tonic-gate eph = (sctp_parm_hdr_t *)(ecp + 1);
4970Sstevel@tonic-gate eph->sph_type = htons(serror);
4980Sstevel@tonic-gate eph->sph_len = htons(len + sizeof (*eph));
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate if (len > 0) {
5010Sstevel@tonic-gate bcopy(details, eph + 1, len);
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate if (pad != 0) {
5050Sstevel@tonic-gate bzero((uchar_t *)(eph + 1) + len, pad);
5060Sstevel@tonic-gate }
5070Sstevel@tonic-gate
5080Sstevel@tonic-gate return (emp);
5090Sstevel@tonic-gate }
5100Sstevel@tonic-gate
5114964Skcpoon /*
5124964Skcpoon * Called from sctp_input_data() to add one error chunk to the error
5134964Skcpoon * chunks list. The error chunks list will be processed at the end
5144964Skcpoon * of sctp_input_data() by calling sctp_process_err().
5154964Skcpoon */
5160Sstevel@tonic-gate void
sctp_add_err(sctp_t * sctp,uint16_t serror,void * details,size_t len,sctp_faddr_t * dest)5174964Skcpoon sctp_add_err(sctp_t *sctp, uint16_t serror, void *details, size_t len,
5184964Skcpoon sctp_faddr_t *dest)
5190Sstevel@tonic-gate {
5204964Skcpoon sctp_stack_t *sctps = sctp->sctp_sctps;
5214964Skcpoon mblk_t *emp;
5224964Skcpoon uint32_t emp_len;
5234964Skcpoon uint32_t mss;
5244964Skcpoon mblk_t *sendmp;
5254964Skcpoon sctp_faddr_t *fp;
5260Sstevel@tonic-gate
5274964Skcpoon emp = sctp_make_err(sctp, serror, details, len);
5284964Skcpoon if (emp == NULL)
5294964Skcpoon return;
5304964Skcpoon emp_len = MBLKL(emp);
5314964Skcpoon if (sctp->sctp_err_chunks != NULL) {
5324964Skcpoon fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks);
5330Sstevel@tonic-gate } else {
5344964Skcpoon fp = dest;
5354964Skcpoon SCTP_SET_CHUNK_DEST(emp, dest);
5364964Skcpoon }
537*13009SChandrasekar.Marimuthu@Sun.COM mss = fp->sf_pmss;
5384964Skcpoon
5394964Skcpoon /*
5404964Skcpoon * If the current output packet cannot include the new error chunk,
5414964Skcpoon * send out the current packet and then add the new error chunk
5424964Skcpoon * to the new output packet.
5434964Skcpoon */
5444964Skcpoon if (sctp->sctp_err_len + emp_len > mss) {
5454964Skcpoon if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) {
5463448Sdh155122 SCTP_KSTAT(sctps, sctp_send_err_failed);
5474964Skcpoon /* Just free the latest error chunk. */
5484964Skcpoon freeb(emp);
5490Sstevel@tonic-gate return;
5500Sstevel@tonic-gate }
5514964Skcpoon sendmp->b_cont = sctp->sctp_err_chunks;
552*13009SChandrasekar.Marimuthu@Sun.COM sctp_set_iplen(sctp, sendmp, fp->sf_ixa);
553*13009SChandrasekar.Marimuthu@Sun.COM (void) conn_ip_output(sendmp, fp->sf_ixa);
55411042SErik.Nordmark@Sun.COM BUMP_LOCAL(sctp->sctp_opkts);
5554964Skcpoon
5564964Skcpoon sctp->sctp_err_chunks = emp;
5574964Skcpoon sctp->sctp_err_len = emp_len;
5584964Skcpoon SCTP_SET_CHUNK_DEST(emp, dest);
5594964Skcpoon } else {
5604964Skcpoon if (sctp->sctp_err_chunks != NULL)
5614964Skcpoon linkb(sctp->sctp_err_chunks, emp);
5624964Skcpoon else
5634964Skcpoon sctp->sctp_err_chunks = emp;
5644964Skcpoon sctp->sctp_err_len += emp_len;
5650Sstevel@tonic-gate }
5664964Skcpoon /* Assume that we will send it out... */
5670Sstevel@tonic-gate BUMP_LOCAL(sctp->sctp_obchunks);
5684964Skcpoon }
5690Sstevel@tonic-gate
5704964Skcpoon /*
5714964Skcpoon * Called from sctp_input_data() to send out error chunks created during
5724964Skcpoon * the processing of all the chunks in an incoming packet.
5734964Skcpoon */
5744964Skcpoon void
sctp_process_err(sctp_t * sctp)5754964Skcpoon sctp_process_err(sctp_t *sctp)
5764964Skcpoon {
5774964Skcpoon sctp_stack_t *sctps = sctp->sctp_sctps;
5784964Skcpoon mblk_t *errmp;
5794964Skcpoon mblk_t *sendmp;
58011042SErik.Nordmark@Sun.COM sctp_faddr_t *fp;
5814964Skcpoon
5824964Skcpoon ASSERT(sctp->sctp_err_chunks != NULL);
5834964Skcpoon errmp = sctp->sctp_err_chunks;
58411042SErik.Nordmark@Sun.COM fp = SCTP_CHUNK_DEST(errmp);
58511042SErik.Nordmark@Sun.COM if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) {
5864964Skcpoon SCTP_KSTAT(sctps, sctp_send_err_failed);
5874964Skcpoon freemsg(errmp);
5884964Skcpoon goto done;
5894964Skcpoon }
5904964Skcpoon sendmp->b_cont = errmp;
591*13009SChandrasekar.Marimuthu@Sun.COM sctp_set_iplen(sctp, sendmp, fp->sf_ixa);
592*13009SChandrasekar.Marimuthu@Sun.COM (void) conn_ip_output(sendmp, fp->sf_ixa);
59311042SErik.Nordmark@Sun.COM BUMP_LOCAL(sctp->sctp_opkts);
5944964Skcpoon done:
5954964Skcpoon sctp->sctp_err_chunks = NULL;
5964964Skcpoon sctp->sctp_err_len = 0;
5970Sstevel@tonic-gate }
5980Sstevel@tonic-gate
5990Sstevel@tonic-gate /*
6000Sstevel@tonic-gate * Returns 0 on non-fatal error, otherwise a system error on fatal
6010Sstevel@tonic-gate * error.
6020Sstevel@tonic-gate */
6030Sstevel@tonic-gate int
sctp_handle_error(sctp_t * sctp,sctp_hdr_t * sctph,sctp_chunk_hdr_t * ch,mblk_t * mp,ip_recv_attr_t * ira)6040Sstevel@tonic-gate sctp_handle_error(sctp_t *sctp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ch,
60511042SErik.Nordmark@Sun.COM mblk_t *mp, ip_recv_attr_t *ira)
6060Sstevel@tonic-gate {
6070Sstevel@tonic-gate sctp_parm_hdr_t *errh;
6080Sstevel@tonic-gate sctp_chunk_hdr_t *uch;
6090Sstevel@tonic-gate
6100Sstevel@tonic-gate if (ch->sch_len == htons(sizeof (*ch))) {
6110Sstevel@tonic-gate /* no error cause given */
6120Sstevel@tonic-gate return (0);
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate errh = (sctp_parm_hdr_t *)(ch + 1);
61511953Sanil.udupa@sun.com sctp_error_event(sctp, ch, B_FALSE);
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate switch (errh->sph_type) {
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate * Both BAD_SID and NO_USR_DATA errors
6200Sstevel@tonic-gate * indicate a serious bug in our stack,
6210Sstevel@tonic-gate * so complain and abort the association.
6220Sstevel@tonic-gate */
6230Sstevel@tonic-gate case SCTP_ERR_BAD_SID:
6240Sstevel@tonic-gate cmn_err(CE_WARN, "BUG! send to invalid SID");
62511042SErik.Nordmark@Sun.COM sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
62611042SErik.Nordmark@Sun.COM ira);
6270Sstevel@tonic-gate return (ECONNABORTED);
6280Sstevel@tonic-gate case SCTP_ERR_NO_USR_DATA:
6290Sstevel@tonic-gate cmn_err(CE_WARN, "BUG! no usr data");
63011042SErik.Nordmark@Sun.COM sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
63111042SErik.Nordmark@Sun.COM ira);
6320Sstevel@tonic-gate return (ECONNABORTED);
6330Sstevel@tonic-gate case SCTP_ERR_UNREC_CHUNK:
6340Sstevel@tonic-gate /* Pull out the unrecognized chunk type */
6350Sstevel@tonic-gate if (ntohs(errh->sph_len) < (sizeof (*errh) + sizeof (*uch))) {
6360Sstevel@tonic-gate /* Not enough to process */
6370Sstevel@tonic-gate return (0);
6380Sstevel@tonic-gate }
6390Sstevel@tonic-gate uch = (sctp_chunk_hdr_t *)(errh + 1);
6400Sstevel@tonic-gate if (uch->sch_id == CHUNK_ASCONF) {
6410Sstevel@tonic-gate /* Turn on ASCONF sending */
6420Sstevel@tonic-gate sctp->sctp_understands_asconf = B_FALSE;
6430Sstevel@tonic-gate /*
6440Sstevel@tonic-gate * Hand off to asconf to clear out the unacked
6450Sstevel@tonic-gate * asconf chunk.
6460Sstevel@tonic-gate */
6470Sstevel@tonic-gate if (ntohs(uch->sch_len) !=
6480Sstevel@tonic-gate (ntohs(errh->sph_len) - sizeof (*errh))) {
6490Sstevel@tonic-gate /* malformed */
6500Sstevel@tonic-gate dprint(0, ("Malformed Unrec Chunk error\n"));
6510Sstevel@tonic-gate return (0);
6520Sstevel@tonic-gate }
653852Svi117747 sctp_asconf_free_cxmit(sctp, uch);
6540Sstevel@tonic-gate return (0);
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate /* Else drop it */
6570Sstevel@tonic-gate break;
6580Sstevel@tonic-gate default:
6590Sstevel@tonic-gate break;
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate return (0);
6630Sstevel@tonic-gate }
664