xref: /original-bsd/sys/netiso/clnp_options.c (revision 05bf53f3)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)clnp_options.c	7.10 (Berkeley) 06/04/93
8  */
9 
10 /***********************************************************
11 		Copyright IBM Corporation 1987
12 
13                       All Rights Reserved
14 
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of IBM not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22 
23 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 SOFTWARE.
30 
31 ******************************************************************/
32 
33 /*
34  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
35  */
36 /* $Header: /var/src/sys/netiso/RCS/clnp_options.c,v 5.1 89/02/09 16:20:37 hagens Exp $ */
37 /* $Source: /var/src/sys/netiso/RCS/clnp_options.c,v $ */
38 
39 #ifdef ISO
40 
41 #include <sys/param.h>
42 #include <sys/mbuf.h>
43 #include <sys/domain.h>
44 #include <sys/protosw.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/errno.h>
48 
49 #include <net/if.h>
50 #include <net/route.h>
51 
52 #include <netiso/iso.h>
53 #include <netiso/clnp.h>
54 #include <netiso/clnp_stat.h>
55 #include <netiso/argo_debug.h>
56 
57 /*
58  * FUNCTION:		clnp_update_srcrt
59  *
60  * PURPOSE:			Process src rt option accompanying a clnp datagram.
61  *						- bump src route ptr if src routing and
62  *							we appear current in src route list.
63  *
64  * RETURNS:			none
65  *
66  * SIDE EFFECTS:
67  *
68  * NOTES:			If source routing has been terminated, do nothing.
69  */
70 clnp_update_srcrt(options, oidx)
71 struct mbuf			*options;	/* ptr to options mbuf */
72 struct clnp_optidx	*oidx;		/* ptr to option index */
73 {
74 	u_char			len;	/* length of current address */
75 	struct iso_addr	isoa;	/* copy current address into here */
76 
77 	if (CLNPSRCRT_TERM(oidx, options)) {
78 		IFDEBUG(D_OPTIONS)
79 			printf("clnp_update_srcrt: src rt terminated\n");
80 		ENDDEBUG
81 		return;
82 	}
83 
84 	len = CLNPSRCRT_CLEN(oidx, options);
85 	bcopy(CLNPSRCRT_CADDR(oidx, options), (caddr_t)&isoa, len);
86 	isoa.isoa_len = len;
87 
88 	IFDEBUG(D_OPTIONS)
89 		printf("clnp_update_srcrt: current src rt: %s\n",
90 			clnp_iso_addrp(&isoa));
91 	ENDDEBUG
92 
93 	if (clnp_ours(&isoa)) {
94 		IFDEBUG(D_OPTIONS)
95 			printf("clnp_update_srcrt: updating src rt\n");
96 		ENDDEBUG
97 
98 		/* update pointer to next src route */
99 		len++;	/* count length byte too! */
100 		CLNPSRCRT_OFF(oidx, options) += len;
101 	}
102 }
103 
104 /*
105  * FUNCTION:		clnp_dooptions
106  *
107  * PURPOSE:			Process options accompanying a clnp datagram.
108  *					Processing includes
109  *						- log our address if recording route
110  *
111  * RETURNS:			none
112  *
113  * SIDE EFFECTS:
114  *
115  * NOTES:
116  */
117 clnp_dooptions(options, oidx, ifp, isoa)
118 struct mbuf			*options;	/* ptr to options mbuf */
119 struct clnp_optidx	*oidx;		/* ptr to option index */
120 struct ifnet		*ifp;		/* ptr to interface pkt is leaving on */
121 struct iso_addr		*isoa;		/* ptr to our address for this ifp */
122 {
123 	/*
124 	 *	If record route is specified, move all
125 	 *	existing records over, and insert the address of
126 	 *	interface passed
127 	 */
128 	if (oidx->cni_recrtp) {
129 		char 	*opt;			/* ptr to beginning of recrt option */
130 		u_char	off;			/* offset from opt of first free byte */
131 		char	*rec_start;		/* beginning of new rt recorded */
132 
133 		opt = CLNP_OFFTOOPT(options, oidx->cni_recrtp);
134 		off = *(opt + 1);
135 		rec_start = opt + off - 1;
136 
137 		IFDEBUG(D_OPTIONS)
138 			printf("clnp_dooptions: record route: option x%x for %d bytes\n",
139 				opt, oidx->cni_recrt_len);
140 			printf("\tfree slot offset x%x\n", off);
141 			printf("clnp_dooptions: recording %s\n", clnp_iso_addrp(isoa));
142 			printf("clnp_dooptions: option dump:\n");
143 			dump_buf(opt, oidx->cni_recrt_len);
144 		ENDDEBUG
145 
146 		/* proceed only if recording has not been terminated */
147 		if (off != 0xff) {
148 			int new_addrlen = isoa->isoa_len + 1;
149 			/*
150 			 *	if there is insufficient room to store the next address,
151 			 *	then terminate recording. Plus 1 on isoa_len is for the
152 			 *	length byte itself
153 			 */
154 			if (oidx->cni_recrt_len - (off - 1) < new_addrlen) {
155 				*(opt + 1) = 0xff;	/* terminate recording */
156 			} else {
157 				IFDEBUG(D_OPTIONS)
158 					printf("clnp_dooptions: new addr at x%x for %d\n",
159 						rec_start, new_addrlen);
160 				ENDDEBUG
161 
162 				bcopy((caddr_t)isoa, rec_start, new_addrlen);
163 
164 				/* update offset field */
165 				*(opt + 1) += new_addrlen;
166 
167 				IFDEBUG(D_OPTIONS)
168 					printf("clnp_dooptions: new option dump:\n");
169 					dump_buf(opt, oidx->cni_recrt_len);
170 				ENDDEBUG
171 			}
172 		}
173 	}
174 }
175 
176 /*
177  * FUNCTION:		clnp_set_opts
178  *
179  * PURPOSE:			Check the data mbuf passed for option sanity. If it is
180  *					ok, then set the options ptr to address the data mbuf.
181  *					If an options mbuf exists, free it. This implies that
182  *					any old options will be lost. If data is NULL, simply
183  *					free any old options.
184  *
185  * RETURNS:			unix error code
186  *
187  * SIDE EFFECTS:
188  *
189  * NOTES:
190  */
191 clnp_set_opts(options, data)
192 struct mbuf	**options;	/* target for option information */
193 struct mbuf	**data;		/* source of option information */
194 {
195 	int					error = 0;	/* error return value */
196 	struct clnp_optidx	dummy;		/* dummy index - not used */
197 
198 	/*
199 	 *	remove any existing options
200 	 */
201 	if (*options != NULL) {
202 		m_freem(*options);
203 		*options = NULL;
204 	}
205 
206 	if (*data != NULL) {
207 		/*
208 		 *	Insure that the options are reasonable.
209 		 *
210 		 *	Also, we do not support security, priority,
211 		 *	nor do we allow one to send an ER option
212 		 *
213 		 *	The QOS parameter is checked for the DECBIT.
214 		 */
215 		if ((clnp_opt_sanity(*data, mtod(*data, caddr_t), (*data)->m_len,
216 			&dummy) != 0) ||
217 				(dummy.cni_securep) ||
218 				(dummy.cni_priorp) ||
219 				(dummy.cni_er_reason != ER_INVALREAS)) {
220 			error = EINVAL;
221 		} else {
222 			*options = *data;
223 			*data = NULL;	/* so caller won't free mbuf @ *data */
224 		}
225 	}
226 	return error;
227 }
228 
229 /*
230  * FUNCTION:		clnp_opt_sanity
231  *
232  * PURPOSE:			Check the options (beginning at opts for len bytes) for
233  *					sanity. In addition, fill in the option index structure
234  *					in with information about each option discovered.
235  *
236  * RETURNS:			success (options check out) - 0
237  *					failure - an ER pdu error code describing failure
238  *
239  * SIDE EFFECTS:
240  *
241  * NOTES:			Each pointer field of the option index is filled in with
242  *					the offset from the beginning of the mbuf data, not the
243  *					actual address.
244  */
245 clnp_opt_sanity(m, opts, len, oidx)
246 struct mbuf 		*m;		/* mbuf options reside in */
247 caddr_t				opts;	/* ptr to buffer containing options */
248 int					len;	/* length of buffer */
249 struct clnp_optidx	*oidx;	/* RETURN: filled in with option idx info */
250 {
251 	u_char	opcode;			/* code of particular option */
252 	u_char	oplen;			/* length of a particular option */
253 	caddr_t	opts_end;		/* ptr to end of options */
254 	u_char	pad = 0, secure = 0, srcrt = 0, recrt = 0, qos = 0, prior = 0;
255 							/* flags for catching duplicate options */
256 
257 	IFDEBUG(D_OPTIONS)
258 		printf("clnp_opt_sanity: checking %d bytes of data:\n", len);
259 		dump_buf(opts, len);
260 	ENDDEBUG
261 
262 	/* clear option index field if passed */
263 	bzero((caddr_t)oidx, sizeof(struct clnp_optidx));
264 
265 	/*
266 	 *	We need to indicate whether the ER option is present. This is done
267 	 *	by overloading the er_reason field to also indicate presense of
268 	 *	the option along with the option value. I would like ER_INVALREAS
269 	 *	to have value 0, but alas, 0 is a valid er reason...
270 	 */
271 	oidx->cni_er_reason = ER_INVALREAS;
272 
273 	opts_end = opts + len;
274 	while (opts < opts_end) {
275 		/* must have at least 2 bytes per option (opcode and len) */
276 		if (opts + 2 > opts_end)
277 			return(GEN_INCOMPLETE);
278 
279 		opcode = *opts++;
280 		oplen = *opts++;
281 		IFDEBUG(D_OPTIONS)
282 			printf("clnp_opt_sanity: opcode is %x and oplen %d\n",
283 				opcode, oplen);
284 			printf("clnp_opt_sanity: clnpoval_SRCRT is %x\n", CLNPOVAL_SRCRT);
285 
286 				switch (opcode) {
287 					case CLNPOVAL_PAD: {
288 						printf("CLNPOVAL_PAD\n");
289 					} break;
290 					case CLNPOVAL_SECURE: {
291 						printf("CLNPOVAL_SECURE\n");
292 					} break;
293 					case CLNPOVAL_SRCRT: {
294 							printf("CLNPOVAL_SRCRT\n");
295 					} break;
296 					case CLNPOVAL_RECRT: {
297 						printf("CLNPOVAL_RECRT\n");
298 					} break;
299 					case CLNPOVAL_QOS: {
300 						printf("CLNPOVAL_QOS\n");
301 					} break;
302 					case CLNPOVAL_PRIOR: {
303 						printf("CLNPOVAL_PRIOR\n");
304 					} break;
305 					case CLNPOVAL_ERREAS: {
306 						printf("CLNPOVAL_ERREAS\n");
307 					} break;
308 					default:
309 						printf("UKNOWN option %x\n", opcode);
310 				}
311 		ENDDEBUG
312 
313 		/* don't allow crazy length values */
314 		if (opts + oplen > opts_end)
315 			return(GEN_INCOMPLETE);
316 
317 		switch (opcode) {
318 			case CLNPOVAL_PAD:
319 				/*
320 				 *	Padding: increment pointer by length of padding
321 				 */
322 				if (pad++)						/* duplicate ? */
323 					return(GEN_DUPOPT);
324 				opts += oplen;
325 				break;
326 
327 			case CLNPOVAL_SECURE: {
328 				u_char	format = *opts;
329 
330 				if (secure++)					/* duplicate ? */
331 					return(GEN_DUPOPT);
332 				/*
333 				 *	Security: high 2 bits of first octet indicate format
334 				 *	(00 in high bits is reserved).
335 				 *	Remaining bits must be 0. Remaining octets indicate
336 				 *	actual security
337 				 */
338 				if (((format & 0x3f) > 0) ||	/* low 6 bits set ? */
339 					((format & 0xc0) == 0))		/* high 2 bits zero ? */
340 					return(GEN_HDRSYNTAX);
341 
342 				oidx->cni_securep = CLNP_OPTTOOFF(m, opts);
343 				oidx->cni_secure_len = oplen;
344 				opts += oplen;
345 			} break;
346 
347 			case CLNPOVAL_SRCRT: {
348 				u_char	type, offset;	/* type of rt, offset of start */
349 				caddr_t	route_end;		/* address of end of route option */
350 
351 				IFDEBUG(D_OPTIONS)
352 					printf("clnp_opt_sanity: SRC RT\n");
353 				ENDDEBUG
354 
355 				if (srcrt++)					/* duplicate ? */
356 					return(GEN_DUPOPT);
357 				/*
358 				 *	source route: There must be 2 bytes following the length
359 				 *	field: type and offset. The type must be either
360 				 *	partial route or complete route. The offset field must
361 				 *	be within the option. A single exception is made, however.
362 				 *	The offset may be 1 greater than the length. This case
363 				 *	occurs when the last source route record is consumed.
364 				 *	In this case, we ignore the source route option.
365 				 *	RAH? You should be able to set offset to 'ff' like in record
366 				 *	route!
367 				 *	Following this is a series of address fields.
368 				 *	Each address field is composed of a (length, address) pair.
369 				 *	Insure that the offset and each address length is reasonable
370 				 */
371 				route_end = opts + oplen;
372 
373 				if (opts + 2 > route_end)
374 					return(SRCRT_SYNTAX);
375 
376 				type = *opts;
377 				offset = *(opts+1);
378 
379 
380 				/* type must be partial or complete */
381 				if (!((type == CLNPOVAL_PARTRT) || (type == CLNPOVAL_COMPRT)))
382 					return(SRCRT_SYNTAX);
383 
384 				oidx->cni_srcrt_s = CLNP_OPTTOOFF(m, opts);
385 				oidx->cni_srcrt_len = oplen;
386 
387 				opts += offset-1;	/*set opts to first addr in rt */
388 
389 				/*
390 				 *	Offset must be reasonable:
391 				 *	less than end of options, or equal to end of options
392 				 */
393 				if (opts >= route_end) {
394 					if (opts == route_end) {
395 						IFDEBUG(D_OPTIONS)
396 							printf("clnp_opt_sanity: end of src route info\n");
397 						ENDDEBUG
398 						break;
399 					} else
400 						return(SRCRT_SYNTAX);
401 				}
402 
403 				while (opts < route_end) {
404 					u_char	addrlen = *opts++;
405 					if (opts + addrlen > route_end)
406 						return(SRCRT_SYNTAX);
407 					opts += addrlen;
408 				}
409 			} break;
410 			case CLNPOVAL_RECRT: {
411 				u_char	type, offset;	/* type of rt, offset of start */
412 				caddr_t	record_end;		/* address of end of record option */
413 
414 				if (recrt++)					/* duplicate ? */
415 					return(GEN_DUPOPT);
416 				/*
417 				 *	record route: after the length field, expect a
418 				 *	type and offset. Type must be partial or complete.
419 				 *	Offset indicates where to start recording. Insure it
420 				 *	is within the option. All ones for offset means
421 				 *	recording is terminated.
422 				 */
423 				record_end = opts + oplen;
424 
425 				oidx->cni_recrtp = CLNP_OPTTOOFF(m, opts);
426 				oidx->cni_recrt_len = oplen;
427 
428 				if (opts + 2 > record_end)
429 					return(GEN_INCOMPLETE);
430 
431 				type = *opts;
432 				offset = *(opts+1);
433 
434 				/* type must be partial or complete */
435 				if (!((type == CLNPOVAL_PARTRT) || (type == CLNPOVAL_COMPRT)))
436 					return(GEN_HDRSYNTAX);
437 
438 				/* offset must be reasonable */
439 				if ((offset < 0xff) && (opts + offset > record_end))
440 					return(GEN_HDRSYNTAX);
441 				opts += oplen;
442 			} break;
443 			case CLNPOVAL_QOS: {
444 				u_char	format = *opts;
445 
446 				if (qos++)					/* duplicate ? */
447 					return(GEN_DUPOPT);
448 				/*
449 				 *	qos: high 2 bits of first octet indicate format
450 				 *	(00 in high bits is reserved).
451 				 *	Remaining bits must be 0 (unless format indicates
452 				 *	globally unique qos, in which case remaining bits indicate
453 				 *	qos (except bit 6 which is reserved)).  Otherwise,
454 				 *	remaining octets indicate actual qos.
455 				 */
456 				if (((format & 0xc0) == 0) ||	/* high 2 bits zero ? */
457 					(((format & 0xc0) != CLNPOVAL_GLOBAL) &&
458 						((format & 0x3f) > 0))) /* not global,low bits used ? */
459 					return(GEN_HDRSYNTAX);
460 
461 				oidx->cni_qos_formatp = CLNP_OPTTOOFF(m, opts);
462 				oidx->cni_qos_len = oplen;
463 
464 				opts += oplen;
465 			} break;
466 
467 			case CLNPOVAL_PRIOR: {
468 				if (prior++)				/* duplicate ? */
469 					return(GEN_DUPOPT);
470 				/*
471 				 *	priority: value must be one byte long
472 				 */
473 				if (oplen != 1)
474 					return(GEN_HDRSYNTAX);
475 
476 				oidx->cni_priorp = CLNP_OPTTOOFF(m, opts);
477 
478 				opts += oplen;
479 			} break;
480 
481 			case CLNPOVAL_ERREAS: {
482 				/*
483 				 *	er reason: value must be two bytes long
484 				 */
485 				if (oplen != 2)
486 					return(GEN_HDRSYNTAX);
487 
488 				oidx->cni_er_reason = *opts;
489 
490 				opts += oplen;
491 			} break;
492 
493 			default: {
494 				IFDEBUG(D_OPTIONS)
495 					printf("clnp_opt_sanity: UNKNOWN OPTION 0x%x\n", opcode);
496 				ENDDEBUG
497 				return(DISC_UNSUPPOPT);
498 			}
499 		}
500 	}
501 		IFDEBUG(D_OPTIONS)
502 			printf("clnp_opt_sanity: return(0)\n", opcode);
503 		ENDDEBUG
504 	return(0);
505 }
506 #endif	/* ISO */
507