sctp_error.c revision 3448:aaf16568054b
1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/types.h>
30#include <sys/systm.h>
31#include <sys/stream.h>
32#include <sys/cmn_err.h>
33#include <sys/ddi.h>
34#include <sys/strsubr.h>
35#include <sys/tsol/tnet.h>
36
37#include <netinet/in.h>
38#include <netinet/ip6.h>
39
40#include <inet/common.h>
41#include <inet/ip.h>
42#include <inet/ip6.h>
43#include <inet/mib2.h>
44#include <inet/sctp_ip.h>
45#include <inet/ipclassifier.h>
46#include <inet/ip_ire.h>
47#include "sctp_impl.h"
48#include "sctp_asconf.h"
49
50ssize_t
51sctp_link_abort(mblk_t *mp, uint16_t serror, char *details, size_t len,
52    int iserror, boolean_t tbit)
53{
54	size_t alen;
55	mblk_t *amp;
56	sctp_chunk_hdr_t *acp;
57	sctp_parm_hdr_t *eph;
58
59	ASSERT(mp != NULL && mp->b_cont == NULL);
60
61	alen = sizeof (*acp) + (serror != 0 ? (sizeof (*eph) + len) : 0);
62
63	amp = allocb(alen, BPRI_MED);
64	if (amp == NULL) {
65		return (-1);
66	}
67
68	amp->b_wptr = amp->b_rptr + alen;
69
70	/* Chunk header */
71	acp = (sctp_chunk_hdr_t *)amp->b_rptr;
72	acp->sch_id = iserror ? CHUNK_ERROR : CHUNK_ABORT;
73	acp->sch_flags = 0;
74	acp->sch_len = htons(alen);
75	if (tbit)
76		SCTP_SET_TBIT(acp);
77
78	linkb(mp, amp);
79
80	if (serror == 0) {
81		return (alen);
82	}
83
84	eph = (sctp_parm_hdr_t *)(acp + 1);
85	eph->sph_type = htons(serror);
86	eph->sph_len = htons(len + sizeof (*eph));
87
88	if (len > 0) {
89		bcopy(details, eph + 1, len);
90	}
91
92	/* XXX pad */
93
94	return (alen);
95}
96
97void
98sctp_user_abort(sctp_t *sctp, mblk_t *data, boolean_t tbit)
99{
100	mblk_t *mp;
101	int len, hdrlen;
102	char *cause;
103	sctp_faddr_t *fp = sctp->sctp_current;
104	sctp_stack_t	*sctps = sctp->sctp_sctps;
105
106	mp = sctp_make_mp(sctp, fp, 0);
107	if (mp == NULL) {
108		SCTP_KSTAT(sctps, sctp_send_user_abort_failed);
109		return;
110	}
111
112	/*
113	 * Create abort chunk.
114	 */
115	if (data) {
116		if (fp->isv4) {
117			hdrlen = sctp->sctp_hdr_len;
118		} else {
119			hdrlen = sctp->sctp_hdr6_len;
120		}
121		hdrlen += sizeof (sctp_chunk_hdr_t) + sizeof (sctp_parm_hdr_t);
122		cause = (char *)data->b_rptr;
123		len = data->b_wptr - data->b_rptr;
124
125		if (len + hdrlen > fp->sfa_pmss) {
126			len = fp->sfa_pmss - hdrlen;
127		}
128	} else {
129		cause = NULL;
130		len = 0;
131	}
132	if ((len = sctp_link_abort(mp, SCTP_ERR_USER_ABORT, cause, len, 0,
133		tbit)) < 0) {
134		freemsg(mp);
135		return;
136	}
137	sctp_set_iplen(sctp, mp);
138	BUMP_MIB(&sctps->sctps_mib, sctpAborted);
139	BUMP_LOCAL(sctp->sctp_opkts);
140	BUMP_LOCAL(sctp->sctp_obchunks);
141
142	CONN_INC_REF(sctp->sctp_connp);
143	mp->b_flag |= MSGHASREF;
144	IP_PUT(mp, sctp->sctp_connp, fp->isv4);
145}
146
147/*
148 * If iserror == 0, sends an abort. If iserror != 0, sends an error.
149 */
150void
151sctp_send_abort(sctp_t *sctp, uint32_t vtag, uint16_t serror, char *details,
152    size_t len, mblk_t *inmp, int iserror, boolean_t tbit)
153{
154
155	mblk_t		*hmp;
156	uint32_t	ip_hdr_len;
157	ipha_t		*iniph;
158	ipha_t		*ahiph;
159	ip6_t		*inip6h;
160	ip6_t		*ahip6h;
161	sctp_hdr_t	*sh;
162	sctp_hdr_t	*insh;
163	size_t		ahlen;
164	uchar_t		*p;
165	ssize_t		alen;
166	int		isv4;
167	ire_t		*ire;
168	irb_t		*irb;
169	ts_label_t	*tsl;
170	conn_t		*connp;
171	cred_t		*cr = NULL;
172	sctp_stack_t	*sctps = sctp->sctp_sctps;
173	ip_stack_t	*ipst;
174
175	isv4 = (IPH_HDR_VERSION(inmp->b_rptr) == IPV4_VERSION);
176	if (isv4) {
177		ahlen = sctp->sctp_hdr_len;
178	} else {
179		ahlen = sctp->sctp_hdr6_len;
180	}
181
182	/*
183	 * If this is a labeled system, then check to see if we're allowed to
184	 * send a response to this particular sender.  If not, then just drop.
185	 */
186	if (is_system_labeled() && !tsol_can_reply_error(inmp))
187		return;
188
189	hmp = allocb_cred(sctps->sctps_wroff_xtra + ahlen,
190	    CONN_CRED(sctp->sctp_connp));
191	if (hmp == NULL) {
192		/* XXX no resources */
193		return;
194	}
195
196	/* copy in the IP / SCTP header */
197	p = hmp->b_rptr + sctps->sctps_wroff_xtra;
198	hmp->b_rptr = p;
199	hmp->b_wptr = p + ahlen;
200	if (isv4) {
201		bcopy(sctp->sctp_iphc, p, sctp->sctp_hdr_len);
202		/*
203		 * Composite is likely incomplete at this point, so pull
204		 * info from the incoming IP / SCTP headers.
205		 */
206		ahiph = (ipha_t *)p;
207		iniph = (ipha_t *)inmp->b_rptr;
208		ip_hdr_len = IPH_HDR_LENGTH(inmp->b_rptr);
209
210		sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr_len);
211		ASSERT(OK_32PTR(sh));
212
213		insh = (sctp_hdr_t *)((uchar_t *)iniph + ip_hdr_len);
214		ASSERT(OK_32PTR(insh));
215
216		/* Copy in the peer's IP addr */
217		ahiph->ipha_dst = iniph->ipha_src;
218		ahiph->ipha_src = iniph->ipha_dst;
219	} else {
220		bcopy(sctp->sctp_iphc6, p, sctp->sctp_hdr6_len);
221		ahip6h = (ip6_t *)p;
222		inip6h = (ip6_t *)inmp->b_rptr;
223		ip_hdr_len = sizeof (ip6_t);
224
225		sh = (sctp_hdr_t *)(p + sctp->sctp_ip_hdr6_len);
226		ASSERT(OK_32PTR(sh));
227
228		insh = (sctp_hdr_t *)((uchar_t *)inip6h + ip_hdr_len);
229		ASSERT(OK_32PTR(insh));
230
231		/* Copy in the peer's IP addr */
232		ahip6h->ip6_dst = inip6h->ip6_src;
233		ahip6h->ip6_src = inip6h->ip6_dst;
234	}
235
236	/* Fill in the holes in the SCTP common header */
237	sh->sh_sport = insh->sh_dport;
238	sh->sh_dport = insh->sh_sport;
239	sh->sh_verf = vtag;
240
241	/* Link in the abort chunk */
242	if ((alen = sctp_link_abort(hmp, serror, details, len, iserror, tbit))
243	    < 0) {
244		freemsg(hmp);
245		return;
246	}
247
248	if (isv4) {
249		ahiph->ipha_length = htons(ahlen + alen);
250	} else {
251		ahip6h->ip6_plen = htons(alen + sizeof (*sh));
252	}
253
254	BUMP_MIB(&sctps->sctps_mib, sctpAborted);
255	BUMP_LOCAL(sctp->sctp_obchunks);
256
257	connp = sctp->sctp_connp;
258	if (is_system_labeled() && (cr = DB_CRED(inmp)) != NULL &&
259	    crgetlabel(cr) != NULL) {
260		int err, adjust;
261
262		if (isv4)
263			err = tsol_check_label(cr, &hmp, &adjust,
264			    connp->conn_mac_exempt,
265			    sctps->sctps_netstack->netstack_ip);
266		else
267			err = tsol_check_label_v6(cr, &hmp, &adjust,
268			    connp->conn_mac_exempt,
269			    sctps->sctps_netstack->netstack_ip);
270		if (err != 0) {
271			freemsg(hmp);
272			return;
273		}
274		if (isv4) {
275			ahiph = (ipha_t *)hmp->b_rptr;
276			adjust += ntohs(ahiph->ipha_length);
277			ahiph->ipha_length = htons(adjust);
278		}
279	}
280
281	/* Stash the conn ptr info. for IP */
282	SCTP_STASH_IPINFO(hmp, NULL);
283
284	CONN_INC_REF(connp);
285	hmp->b_flag |= MSGHASREF;
286	IP_PUT(hmp, connp, sctp->sctp_current == NULL ? B_TRUE :
287	    sctp->sctp_current->isv4);
288	/*
289	 * Let's just mark the IRE for this destination as temporary
290	 * to prevent any DoS attack.
291	 */
292	ipst = sctps->sctps_netstack->netstack_ip;
293	tsl = cr == NULL ? NULL : crgetlabel(cr);
294	if (isv4) {
295		ire = ire_cache_lookup(iniph->ipha_src, sctp->sctp_zoneid, tsl,
296		    ipst);
297	} else {
298		ire = ire_cache_lookup_v6(&inip6h->ip6_src, sctp->sctp_zoneid,
299		    tsl, ipst);
300	}
301	/*
302	 * In the normal case the ire would be non-null, however it could be
303	 * null, say, if IP needs to resolve the gateway for this address. We
304	 * only care about IRE_CACHE.
305	 */
306	if (ire == NULL)
307		return;
308	if (ire->ire_type != IRE_CACHE) {
309		ire_refrele(ire);
310		return;
311	}
312	irb = ire->ire_bucket;
313	/* ire_lock is not needed, as ire_marks is protected by irb_lock */
314	rw_enter(&irb->irb_lock, RW_WRITER);
315	/*
316	 * Only increment the temporary IRE count if the original
317	 * IRE is not already marked temporary.
318	 */
319	if (!(ire->ire_marks & IRE_MARK_TEMPORARY)) {
320		irb->irb_tmp_ire_cnt++;
321		ire->ire_marks |= IRE_MARK_TEMPORARY;
322	}
323	rw_exit(&irb->irb_lock);
324	ire_refrele(ire);
325}
326
327/*ARGSUSED*/
328mblk_t *
329sctp_make_err(sctp_t *sctp, uint16_t serror, void *details, size_t len)
330{
331
332	mblk_t *emp;
333	size_t elen;
334	sctp_chunk_hdr_t *ecp;
335	sctp_parm_hdr_t *eph;
336	int pad;
337
338	if ((pad = len % 4) != 0) {
339		pad = 4 - pad;
340	}
341
342	elen = sizeof (*ecp) + sizeof (*eph) + len;
343	emp = allocb(elen + pad, BPRI_MED);
344	if (emp == NULL) {
345		return (NULL);
346	}
347
348	emp->b_wptr = emp->b_rptr + elen + pad;
349
350	/* Chunk header */
351	ecp = (sctp_chunk_hdr_t *)emp->b_rptr;
352	ecp->sch_id = CHUNK_ERROR;
353	ecp->sch_flags = 0;
354	ecp->sch_len = htons(elen);
355
356	eph = (sctp_parm_hdr_t *)(ecp + 1);
357	eph->sph_type = htons(serror);
358	eph->sph_len = htons(len + sizeof (*eph));
359
360	if (len > 0) {
361		bcopy(details, eph + 1, len);
362	}
363
364	if (pad != 0) {
365		bzero((uchar_t *)(eph + 1) + len, pad);
366	}
367
368	return (emp);
369}
370
371void
372sctp_send_err(sctp_t *sctp, mblk_t *emp, sctp_faddr_t *dest)
373{
374	mblk_t	*sendmp;
375	sctp_stack_t	*sctps = sctp->sctp_sctps;
376
377	sendmp = sctp_make_sack(sctp, dest, NULL);
378	if (sendmp != NULL) {
379		linkb(sendmp, emp);
380	} else {
381		sendmp = sctp_make_mp(sctp, dest, 0);
382		if (sendmp == NULL) {
383			SCTP_KSTAT(sctps, sctp_send_err_failed);
384			freemsg(emp);
385			return;
386		}
387		sendmp->b_cont = emp;
388	}
389	BUMP_LOCAL(sctp->sctp_obchunks);
390
391	sctp_set_iplen(sctp, sendmp);
392	sctp_add_sendq(sctp, sendmp);
393}
394
395/*
396 * Returns 0 on non-fatal error, otherwise a system error on fatal
397 * error.
398 */
399int
400sctp_handle_error(sctp_t *sctp, sctp_hdr_t *sctph, sctp_chunk_hdr_t *ch,
401    mblk_t *mp)
402{
403	sctp_parm_hdr_t *errh;
404	sctp_chunk_hdr_t *uch;
405
406	if (ch->sch_len == htons(sizeof (*ch))) {
407		/* no error cause given */
408		return (0);
409	}
410	errh = (sctp_parm_hdr_t *)(ch + 1);
411	sctp_error_event(sctp, ch);
412
413	switch (errh->sph_type) {
414	/*
415	 * Both BAD_SID and NO_USR_DATA errors
416	 * indicate a serious bug in our stack,
417	 * so complain and abort the association.
418	 */
419	case SCTP_ERR_BAD_SID:
420		cmn_err(CE_WARN, "BUG! send to invalid SID");
421		sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0);
422		return (ECONNABORTED);
423	case SCTP_ERR_NO_USR_DATA:
424		cmn_err(CE_WARN, "BUG! no usr data");
425		sctp_send_abort(sctp, sctph->sh_verf, 0, NULL, 0, mp, 0, 0);
426		return (ECONNABORTED);
427	case SCTP_ERR_UNREC_CHUNK:
428		/* Pull out the unrecognized chunk type */
429		if (ntohs(errh->sph_len) < (sizeof (*errh) + sizeof (*uch))) {
430			/* Not enough to process */
431			return (0);
432		}
433		uch = (sctp_chunk_hdr_t *)(errh + 1);
434		if (uch->sch_id == CHUNK_ASCONF) {
435			/* Turn on ASCONF sending */
436			sctp->sctp_understands_asconf = B_FALSE;
437			/*
438			 * Hand off to asconf to clear out the unacked
439			 * asconf chunk.
440			 */
441			if (ntohs(uch->sch_len) !=
442			    (ntohs(errh->sph_len) - sizeof (*errh))) {
443				/* malformed */
444				dprint(0, ("Malformed Unrec Chunk error\n"));
445				return (0);
446			}
447			sctp_asconf_free_cxmit(sctp, uch);
448			return (0);
449		}
450		/* Else drop it */
451		break;
452	default:
453		break;
454	}
455
456	return (0);
457}
458