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