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