xref: /illumos-gate/usr/src/uts/common/inet/sctp/sctp_error.c (revision c81d47afd05baeb768e2f032636019b717899efd)
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 
50 ssize_t
51 sctp_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 
97 void
98 sctp_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  */
150 void
151 sctp_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*/
328 mblk_t *
329 sctp_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 
371 void
372 sctp_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  */
399 int
400 sctp_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