kill tsol ("Trusted Solaris") aka TX ("Trusted Extensions")
[unleashed.git] / kernel / net / sctp / sctp_error.c
blob0f7e64eb5f356b4baed6136f4ad0a012cae3bdb9
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/types.h>
27 #include <sys/systm.h>
28 #include <sys/stream.h>
29 #include <sys/cmn_err.h>
30 #include <sys/ddi.h>
31 #include <sys/strsubr.h>
33 #include <netinet/in.h>
34 #include <netinet/ip6.h>
36 #include <inet/ipsec_impl.h>
37 #include <inet/common.h>
38 #include <inet/ip.h>
39 #include <inet/ip6.h>
40 #include <inet/ipsec_impl.h>
41 #include <inet/mib2.h>
42 #include <inet/sctp_ip.h>
43 #include <inet/ipclassifier.h>
44 #include <inet/ip_ire.h>
45 #include <inet/sctp/sctp_impl.h>
46 #include <inet/sctp/sctp_asconf.h>
48 ssize_t
49 sctp_link_abort(mblk_t *mp, uint16_t serror, char *details, size_t len,
50 int iserror, boolean_t tbit)
52 size_t alen;
53 mblk_t *amp;
54 sctp_chunk_hdr_t *acp;
55 sctp_parm_hdr_t *eph;
57 ASSERT(mp != NULL && mp->b_cont == NULL);
59 alen = sizeof (*acp) + (serror != 0 ? (sizeof (*eph) + len) : 0);
61 amp = allocb(alen, BPRI_MED);
62 if (amp == NULL) {
63 return (-1);
66 amp->b_wptr = amp->b_rptr + alen;
68 /* Chunk header */
69 acp = (sctp_chunk_hdr_t *)amp->b_rptr;
70 acp->sch_id = iserror ? CHUNK_ERROR : CHUNK_ABORT;
71 acp->sch_flags = 0;
72 acp->sch_len = htons(alen);
73 if (tbit)
74 SCTP_SET_TBIT(acp);
76 linkb(mp, amp);
78 if (serror == 0) {
79 return (alen);
82 eph = (sctp_parm_hdr_t *)(acp + 1);
83 eph->sph_type = htons(serror);
84 eph->sph_len = htons(len + sizeof (*eph));
86 if (len > 0) {
87 bcopy(details, eph + 1, len);
90 /* XXX pad */
92 return (alen);
95 void
96 sctp_user_abort(sctp_t *sctp, mblk_t *data)
98 mblk_t *mp;
99 int len, hdrlen;
100 char *cause;
101 sctp_faddr_t *fp = sctp->sctp_current;
102 ip_xmit_attr_t *ixa = fp->sf_ixa;
103 sctp_stack_t *sctps = sctp->sctp_sctps;
106 * Don't need notification if connection is not yet setup,
107 * call sctp_clean_death() to reclaim resources.
108 * Any pending connect call(s) will error out.
110 if (sctp->sctp_state < SCTPS_COOKIE_WAIT) {
111 sctp_clean_death(sctp, ECONNABORTED);
112 return;
115 mp = sctp_make_mp(sctp, fp, 0);
116 if (mp == NULL) {
117 SCTP_KSTAT(sctps, sctp_send_user_abort_failed);
118 return;
122 * Create abort chunk.
124 if (data) {
125 if (fp->sf_isv4) {
126 hdrlen = sctp->sctp_hdr_len;
127 } else {
128 hdrlen = sctp->sctp_hdr6_len;
130 hdrlen += sizeof (sctp_chunk_hdr_t) + sizeof (sctp_parm_hdr_t);
131 cause = (char *)data->b_rptr;
132 len = data->b_wptr - data->b_rptr;
134 if (len + hdrlen > fp->sf_pmss) {
135 len = fp->sf_pmss - hdrlen;
137 } else {
138 cause = NULL;
139 len = 0;
142 * Since it is a user abort, we should have the sctp_t and hence
143 * the correct verification tag. So we should not set the T-bit
144 * in the ABORT.
146 if ((len = sctp_link_abort(mp, SCTP_ERR_USER_ABORT, cause, len, 0,
147 B_FALSE)) < 0) {
148 freemsg(mp);
149 return;
151 SCTPS_BUMP_MIB(sctps, sctpAborted);
152 BUMP_LOCAL(sctp->sctp_opkts);
153 BUMP_LOCAL(sctp->sctp_obchunks);
155 sctp_set_iplen(sctp, mp, ixa);
156 ASSERT(ixa->ixa_ire != NULL);
157 ASSERT(ixa->ixa_cred != NULL);
159 (void) conn_ip_output(mp, ixa);
161 sctp_assoc_event(sctp, SCTP_COMM_LOST, 0, NULL);
162 sctp_clean_death(sctp, ECONNABORTED);
166 * If iserror == 0, sends an abort. If iserror != 0, sends an error.
168 void
169 sctp_send_abort(sctp_t *sctp, uint32_t vtag, uint16_t serror, char *details,
170 size_t len, mblk_t *inmp, int iserror, boolean_t tbit, ip_recv_attr_t *ira)
173 mblk_t *hmp;
174 uint32_t ip_hdr_len;
175 ipha_t *iniph;
176 ipha_t *ahiph = NULL;
177 ip6_t *inip6h;
178 ip6_t *ahip6h = NULL;
179 sctp_hdr_t *sh;
180 sctp_hdr_t *insh;
181 size_t ahlen;
182 uchar_t *p;
183 ssize_t alen;
184 int isv4;
185 conn_t *connp = sctp->sctp_connp;
186 sctp_stack_t *sctps = sctp->sctp_sctps;
187 ip_xmit_attr_t *ixa;
189 isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
190 if (isv4) {
191 ahlen = sctp->sctp_hdr_len;
192 } else {
193 ahlen = sctp->sctp_hdr6_len;
196 hmp = allocb(sctps->sctps_wroff_xtra + ahlen, BPRI_MED);
197 if (hmp == NULL) {
198 /* XXX no resources */
199 return;
202 /* copy in the IP / SCTP header */
203 p = hmp->b_rptr + sctps->sctps_wroff_xtra;
204 hmp->b_rptr = p;
205 hmp->b_wptr = p + ahlen;
206 if (isv4) {
207 bcopy(sctp->sctp_iphc, p, sctp->sctp_hdr_len);
209 * Composite is likely incomplete at this point, so pull
210 * info from the incoming IP / SCTP headers.
212 ahiph = (ipha_t *)p;
213 iniph = (ipha_t *)inmp->b_rptr;
214 ip_hdr_len = IPH_HDR_LENGTH(inmp->b_rptr);
216 sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr_len);
217 ASSERT(OK_32PTR(sh));
219 insh = (sctp_hdr_t *)((uchar_t *)iniph + ip_hdr_len);
220 ASSERT(OK_32PTR(insh));
222 /* Copy in the peer's IP addr */
223 ahiph->ipha_dst = iniph->ipha_src;
224 ahiph->ipha_src = iniph->ipha_dst;
225 } else {
226 bcopy(sctp->sctp_iphc6, p, sctp->sctp_hdr6_len);
227 ahip6h = (ip6_t *)p;
228 inip6h = (ip6_t *)inmp->b_rptr;
229 ip_hdr_len = ip_hdr_length_v6(inmp, inip6h);
231 sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr6_len);
232 ASSERT(OK_32PTR(sh));
234 insh = (sctp_hdr_t *)((uchar_t *)inip6h + ip_hdr_len);
235 ASSERT(OK_32PTR(insh));
237 /* Copy in the peer's IP addr */
238 ahip6h->ip6_dst = inip6h->ip6_src;
239 ahip6h->ip6_src = inip6h->ip6_dst;
242 /* Fill in the holes in the SCTP common header */
243 sh->sh_sport = insh->sh_dport;
244 sh->sh_dport = insh->sh_sport;
245 sh->sh_verf = vtag;
247 /* Link in the abort chunk */
248 if ((alen = sctp_link_abort(hmp, serror, details, len, iserror, tbit))
249 < 0) {
250 freemsg(hmp);
251 return;
255 * Base the transmission on any routing-related socket options
256 * that have been set on the listener/connection.
258 ixa = conn_get_ixa_exclusive(connp);
259 if (ixa == NULL) {
260 freemsg(hmp);
261 return;
263 ixa->ixa_flags &= ~IXAF_VERIFY_PMTU;
265 ixa->ixa_pktlen = ahlen + alen;
266 if (isv4) {
267 ixa->ixa_flags |= IXAF_IS_IPV4;
268 ahiph->ipha_length = htons(ixa->ixa_pktlen);
269 ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr_len;
270 } else {
271 ixa->ixa_flags &= ~IXAF_IS_IPV4;
272 ahip6h->ip6_plen = htons(ixa->ixa_pktlen - IPV6_HDR_LEN);
273 ixa->ixa_ip_hdr_length = sctp->sctp_ip_hdr6_len;
276 SCTPS_BUMP_MIB(sctps, sctpAborted);
277 BUMP_LOCAL(sctp->sctp_obchunks);
279 if (ira->ira_flags & IRAF_IPSEC_SECURE) {
281 * Apply IPsec based on how IPsec was applied to
282 * the packet that caused the abort.
284 if (!ipsec_in_to_out(ira, ixa, hmp, ahiph, ahip6h)) {
285 ip_stack_t *ipst = sctps->sctps_netstack->netstack_ip;
287 BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
288 /* Note: mp already consumed and ip_drop_packet done */
289 ixa_refrele(ixa);
290 return;
292 } else {
293 ixa->ixa_flags |= IXAF_NO_IPSEC;
296 BUMP_LOCAL(sctp->sctp_opkts);
297 BUMP_LOCAL(sctp->sctp_obchunks);
299 (void) ip_output_simple(hmp, ixa);
300 ixa_refrele(ixa);
304 * OOTB version of the above.
305 * If iserror == 0, sends an abort. If iserror != 0, sends an error.
307 void
308 sctp_ootb_send_abort(uint32_t vtag, uint16_t serror, char *details,
309 size_t len, const mblk_t *inmp, int iserror, boolean_t tbit,
310 ip_recv_attr_t *ira, ip_stack_t *ipst)
312 uint32_t ip_hdr_len;
313 size_t ahlen;
314 ipha_t *ipha = NULL;
315 ip6_t *ip6h = NULL;
316 sctp_hdr_t *insctph;
317 int i;
318 uint16_t port;
319 ssize_t alen;
320 int isv4;
321 mblk_t *mp;
322 netstack_t *ns = ipst->ips_netstack;
323 sctp_stack_t *sctps = ns->netstack_sctp;
324 ip_xmit_attr_t ixas;
326 bzero(&ixas, sizeof (ixas));
328 isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
329 ip_hdr_len = ira->ira_ip_hdr_length;
330 ahlen = ip_hdr_len + sizeof (sctp_hdr_t);
332 mp = allocb(ahlen + sctps->sctps_wroff_xtra, BPRI_MED);
333 if (mp == NULL) {
334 return;
336 mp->b_rptr += sctps->sctps_wroff_xtra;
337 mp->b_wptr = mp->b_rptr + ahlen;
338 bcopy(inmp->b_rptr, mp->b_rptr, ahlen);
341 * We follow the logic in tcp_xmit_early_reset() in that we skip
342 * reversing source route (i.e. replace all IP options with EOL).
344 if (isv4) {
345 ipaddr_t v4addr;
347 ipha = (ipha_t *)mp->b_rptr;
348 for (i = IP_SIMPLE_HDR_LENGTH; i < (int)ip_hdr_len; i++)
349 mp->b_rptr[i] = IPOPT_EOL;
350 /* Swap addresses */
351 ipha->ipha_length = htons(ahlen);
352 v4addr = ipha->ipha_src;
353 ipha->ipha_src = ipha->ipha_dst;
354 ipha->ipha_dst = v4addr;
355 ipha->ipha_ident = 0;
356 ipha->ipha_ttl = (uchar_t)sctps->sctps_ipv4_ttl;
358 ixas.ixa_flags = IXAF_BASIC_SIMPLE_V4;
359 } else {
360 in6_addr_t v6addr;
362 ip6h = (ip6_t *)mp->b_rptr;
363 /* Remove any extension headers assuming partial overlay */
364 if (ip_hdr_len > IPV6_HDR_LEN) {
365 uint8_t *to;
367 to = mp->b_rptr + ip_hdr_len - IPV6_HDR_LEN;
368 ovbcopy(ip6h, to, IPV6_HDR_LEN);
369 mp->b_rptr += ip_hdr_len - IPV6_HDR_LEN;
370 ip_hdr_len = IPV6_HDR_LEN;
371 ip6h = (ip6_t *)mp->b_rptr;
372 ip6h->ip6_nxt = IPPROTO_SCTP;
373 ahlen = ip_hdr_len + sizeof (sctp_hdr_t);
375 ip6h->ip6_plen = htons(ahlen - IPV6_HDR_LEN);
376 v6addr = ip6h->ip6_src;
377 ip6h->ip6_src = ip6h->ip6_dst;
378 ip6h->ip6_dst = v6addr;
379 ip6h->ip6_hops = (uchar_t)sctps->sctps_ipv6_hoplimit;
381 ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
382 if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_dst)) {
383 ixas.ixa_flags |= IXAF_SCOPEID_SET;
384 ixas.ixa_scopeid = ira->ira_ruifindex;
387 insctph = (sctp_hdr_t *)(mp->b_rptr + ip_hdr_len);
389 /* Swap ports. Verification tag is reused. */
390 port = insctph->sh_sport;
391 insctph->sh_sport = insctph->sh_dport;
392 insctph->sh_dport = port;
393 insctph->sh_verf = vtag;
395 /* Link in the abort chunk */
396 if ((alen = sctp_link_abort(mp, serror, details, len, iserror, tbit))
397 < 0) {
398 freemsg(mp);
399 return;
402 ixas.ixa_pktlen = ahlen + alen;
403 ixas.ixa_ip_hdr_length = ip_hdr_len;
405 if (isv4) {
406 ipha->ipha_length = htons(ixas.ixa_pktlen);
407 } else {
408 ip6h->ip6_plen = htons(ixas.ixa_pktlen - IPV6_HDR_LEN);
411 ixas.ixa_protocol = IPPROTO_SCTP;
412 ixas.ixa_zoneid = ira->ira_zoneid;
413 ixas.ixa_ipst = ipst;
414 ixas.ixa_ifindex = 0;
416 SCTPS_BUMP_MIB(sctps, sctpAborted);
418 if (ira->ira_flags & IRAF_IPSEC_SECURE) {
420 * Apply IPsec based on how IPsec was applied to
421 * the packet that was out of the blue.
423 if (!ipsec_in_to_out(ira, &ixas, mp, ipha, ip6h)) {
424 BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
425 /* Note: mp already consumed and ip_drop_packet done */
426 return;
428 } else {
430 * This is in clear. The abort message we are building
431 * here should go out in clear, independent of our policy.
433 ixas.ixa_flags |= IXAF_NO_IPSEC;
436 (void) ip_output_simple(mp, &ixas);
437 ixa_cleanup(&ixas);
440 /*ARGSUSED*/
441 mblk_t *
442 sctp_make_err(sctp_t *sctp, uint16_t serror, void *details, size_t len)
445 mblk_t *emp;
446 size_t elen;
447 sctp_chunk_hdr_t *ecp;
448 sctp_parm_hdr_t *eph;
449 int pad;
451 if ((pad = len % SCTP_ALIGN) != 0) {
452 pad = SCTP_ALIGN - pad;
455 elen = sizeof (*ecp) + sizeof (*eph) + len;
456 emp = allocb(elen + pad, BPRI_MED);
457 if (emp == NULL) {
458 return (NULL);
461 emp->b_wptr = emp->b_rptr + elen + pad;
463 /* Chunk header */
464 ecp = (sctp_chunk_hdr_t *)emp->b_rptr;
465 ecp->sch_id = CHUNK_ERROR;
466 ecp->sch_flags = 0;
467 ecp->sch_len = htons(elen);
469 eph = (sctp_parm_hdr_t *)(ecp + 1);
470 eph->sph_type = htons(serror);
471 eph->sph_len = htons(len + sizeof (*eph));
473 if (len > 0) {
474 bcopy(details, eph + 1, len);
477 if (pad != 0) {
478 bzero((uchar_t *)(eph + 1) + len, pad);
481 return (emp);
485 * Called from sctp_input_data() to add one error chunk to the error
486 * chunks list. The error chunks list will be processed at the end
487 * of sctp_input_data() by calling sctp_process_err().
489 void
490 sctp_add_err(sctp_t *sctp, uint16_t serror, void *details, size_t len,
491 sctp_faddr_t *dest)
493 sctp_stack_t *sctps = sctp->sctp_sctps;
494 mblk_t *emp;
495 uint32_t emp_len;
496 uint32_t mss;
497 mblk_t *sendmp;
498 sctp_faddr_t *fp;
500 emp = sctp_make_err(sctp, serror, details, len);
501 if (emp == NULL)
502 return;
503 emp_len = MBLKL(emp);
504 if (sctp->sctp_err_chunks != NULL) {
505 fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks);
506 } else {
507 fp = dest;
508 SCTP_SET_CHUNK_DEST(emp, dest);
510 mss = fp->sf_pmss;
513 * If the current output packet cannot include the new error chunk,
514 * send out the current packet and then add the new error chunk
515 * to the new output packet.
517 if (sctp->sctp_err_len + emp_len > mss) {
518 if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) {
519 SCTP_KSTAT(sctps, sctp_send_err_failed);
520 /* Just free the latest error chunk. */
521 freeb(emp);
522 return;
524 sendmp->b_cont = sctp->sctp_err_chunks;
525 sctp_set_iplen(sctp, sendmp, fp->sf_ixa);
526 (void) conn_ip_output(sendmp, fp->sf_ixa);
527 BUMP_LOCAL(sctp->sctp_opkts);
529 sctp->sctp_err_chunks = emp;
530 sctp->sctp_err_len = emp_len;
531 SCTP_SET_CHUNK_DEST(emp, dest);
532 } else {
533 if (sctp->sctp_err_chunks != NULL)
534 linkb(sctp->sctp_err_chunks, emp);
535 else
536 sctp->sctp_err_chunks = emp;
537 sctp->sctp_err_len += emp_len;
539 /* Assume that we will send it out... */
540 BUMP_LOCAL(sctp->sctp_obchunks);
544 * Called from sctp_input_data() to send out error chunks created during
545 * the processing of all the chunks in an incoming packet.
547 void
548 sctp_process_err(sctp_t *sctp)
550 sctp_stack_t *sctps = sctp->sctp_sctps;
551 mblk_t *errmp;
552 mblk_t *sendmp;
553 sctp_faddr_t *fp;
555 ASSERT(sctp->sctp_err_chunks != NULL);
556 errmp = sctp->sctp_err_chunks;
557 fp = SCTP_CHUNK_DEST(errmp);
558 if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) {
559 SCTP_KSTAT(sctps, sctp_send_err_failed);
560 freemsg(errmp);
561 goto done;
563 sendmp->b_cont = errmp;
564 sctp_set_iplen(sctp, sendmp, fp->sf_ixa);
565 (void) conn_ip_output(sendmp, fp->sf_ixa);
566 BUMP_LOCAL(sctp->sctp_opkts);
567 done:
568 sctp->sctp_err_chunks = NULL;
569 sctp->sctp_err_len = 0;
573 * Returns 0 on non-fatal error, otherwise a system error on fatal
574 * error.
577 sctp_handle_error(sctp_t *sctp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ch,
578 mblk_t *mp, ip_recv_attr_t *ira)
580 sctp_parm_hdr_t *errh;
581 sctp_chunk_hdr_t *uch;
583 if (ch->sch_len == htons(sizeof (*ch))) {
584 /* no error cause given */
585 return (0);
587 errh = (sctp_parm_hdr_t *)(ch + 1);
588 sctp_error_event(sctp, ch, B_FALSE);
590 switch (errh->sph_type) {
592 * Both BAD_SID and NO_USR_DATA errors
593 * indicate a serious bug in our stack,
594 * so complain and abort the association.
596 case SCTP_ERR_BAD_SID:
597 cmn_err(CE_WARN, "BUG! send to invalid SID");
598 sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
599 ira);
600 return (ECONNABORTED);
601 case SCTP_ERR_NO_USR_DATA:
602 cmn_err(CE_WARN, "BUG! no usr data");
603 sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0,
604 ira);
605 return (ECONNABORTED);
606 case SCTP_ERR_UNREC_CHUNK:
607 /* Pull out the unrecognized chunk type */
608 if (ntohs(errh->sph_len) < (sizeof (*errh) + sizeof (*uch))) {
609 /* Not enough to process */
610 return (0);
612 uch = (sctp_chunk_hdr_t *)(errh + 1);
613 if (uch->sch_id == CHUNK_ASCONF) {
614 /* Turn on ASCONF sending */
615 sctp->sctp_understands_asconf = B_FALSE;
617 * Hand off to asconf to clear out the unacked
618 * asconf chunk.
620 if (ntohs(uch->sch_len) !=
621 (ntohs(errh->sph_len) - sizeof (*errh))) {
622 /* malformed */
623 dprint(0, ("Malformed Unrec Chunk error\n"));
624 return (0);
626 sctp_asconf_free_cxmit(sctp, uch);
627 return (0);
629 /* Else drop it */
630 break;
631 default:
632 break;
635 return (0);