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