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