xref: /original-bsd/sys/netiso/clnp_subr.c (revision c3e32dec)
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_subr.c	7.17 (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_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $ */
37 /* $Source: /var/src/sys/netiso/RCS/clnp_subr.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 #include <sys/time.h>
49 
50 #include <net/if.h>
51 #include <net/route.h>
52 #include <net/if_dl.h>
53 
54 #include <netiso/iso.h>
55 #include <netiso/iso_var.h>
56 #include <netiso/iso_pcb.h>
57 #include <netiso/iso_snpac.h>
58 #include <netiso/clnp.h>
59 #include <netiso/clnp_stat.h>
60 #include <netiso/argo_debug.h>
61 
62 /*
63  * FUNCTION:		clnp_data_ck
64  *
65  * PURPOSE:			Check that the amount of data in the mbuf chain is
66  *					at least as much as the clnp header would have us
67  *					expect. Trim mbufs if longer than expected, drop
68  *					packet if shorter than expected.
69  *
70  * RETURNS:			success - ptr to mbuf chain
71  *					failure - 0
72  *
73  * SIDE EFFECTS:
74  *
75  * NOTES:
76  */
77 struct mbuf *
78 clnp_data_ck(m, length)
79 register struct mbuf	*m;		/* ptr to mbuf chain containing hdr & data */
80 int						length;	/* length (in bytes) of packet */
81  {
82 	register int 			len;		/* length of data */
83 	register struct mbuf	*mhead;		/* ptr to head of chain */
84 
85 	len = -length;
86 	mhead = m;
87 	for (;;) {
88 		len += m->m_len;
89 		if (m->m_next == 0)
90 			break;
91 		m = m->m_next;
92 	}
93 	if (len != 0) {
94 		if (len < 0) {
95 			INCSTAT(cns_toosmall);
96 			clnp_discard(mhead, GEN_INCOMPLETE);
97 			return 0;
98 		}
99 		if (len <= m->m_len)
100 			m->m_len -= len;
101 		else
102 			m_adj(mhead, -len);
103 	}
104 	return mhead;
105 }
106 
107 #ifdef notdef
108 /*
109  * FUNCTION:		clnp_extract_addr
110  *
111  * PURPOSE:			Extract the source and destination address from the
112  *					supplied buffer. Place them in the supplied address buffers.
113  *					If insufficient data is supplied, then fail.
114  *
115  * RETURNS:			success - Address of first byte in the packet past
116  *						the address part.
117  *					failure - 0
118  *
119  * SIDE EFFECTS:
120  *
121  * NOTES:
122  */
123 caddr_t
124 clnp_extract_addr(bufp, buflen, srcp, destp)
125 caddr_t					bufp;		/* ptr to buffer containing addresses */
126 int						buflen;		/* length of buffer */
127 register struct iso_addr	*srcp;		/* ptr to source address buffer */
128 register struct iso_addr	*destp;		/* ptr to destination address buffer */
129  {
130 	int	len;		/* argument to bcopy */
131 
132 	/*
133 	 *	check that we have enough data. Plus1 is for length octet
134 	 */
135 	if ((u_char)*bufp + 1 > buflen) {
136 		return((caddr_t)0);
137 	}
138 	len = destp->isoa_len = (u_char)*bufp++;
139 	(void) bcopy(bufp, (caddr_t)destp, len);
140 	buflen -= len;
141 	bufp += len;
142 
143 	/*
144 	 *	check that we have enough data. Plus1 is for length octet
145 	 */
146 	if ((u_char)*bufp + 1 > buflen) {
147 		return((caddr_t)0);
148 	}
149 	len = srcp->isoa_len = (u_char)* bufp++;
150 	(void) bcopy(bufp, (caddr_t)srcp, len);
151 	bufp += len;
152 
153 	/*
154 	 *	Insure that the addresses make sense
155 	 */
156 	if (iso_ck_addr(srcp) && iso_ck_addr(destp))
157 		return bufp;
158 	else
159 		return (caddr_t) 0;
160 }
161 #endif	/* notdef */
162 
163 /*
164  * FUNCTION:		clnp_ours
165  *
166  * PURPOSE:			Decide whether the supplied packet is destined for
167  *					us, or that it should be forwarded on.
168  *
169  * RETURNS:			packet is for us - 1
170  *					packet is not for us - 0
171  *
172  * SIDE EFFECTS:
173  *
174  * NOTES:
175  */
176 clnp_ours(dst)
177 register struct iso_addr *dst;		/* ptr to destination address */
178 {
179 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
180 
181 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
182 		IFDEBUG(D_ROUTE)
183 			printf("clnp_ours: ia_sis x%x, dst x%x\n", &ia->ia_addr,
184 				dst);
185 		ENDDEBUG
186 		/*
187 		 * XXX Warning:
188 		 * We are overloading siso_tlen in the if's address, as an nsel length.
189 		 */
190 		if (dst->isoa_len == ia->ia_addr.siso_nlen &&
191 			bcmp((caddr_t)ia->ia_addr.siso_addr.isoa_genaddr,
192 				 (caddr_t)dst->isoa_genaddr,
193 				 ia->ia_addr.siso_nlen - ia->ia_addr.siso_tlen) == 0)
194 					return 1;
195 	}
196 	return 0;
197 }
198 
199 /* Dec bit set if ifp qlen is greater than congest_threshold */
200 int congest_threshold = 0;
201 
202 /*
203  * FUNCTION:		clnp_forward
204  *
205  * PURPOSE:			Forward the datagram passed
206  *					clnpintr guarantees that the header will be
207  *					contigious (a cluster mbuf will be used if necessary).
208  *
209  *					If oidx is NULL, no options are present.
210  *
211  * RETURNS:			nothing
212  *
213  * SIDE EFFECTS:
214  *
215  * NOTES:
216  */
217 clnp_forward(m, len, dst, oidx, seg_off, inbound_shp)
218 struct mbuf			*m;		/* pkt to forward */
219 int					len;	/* length of pkt */
220 struct iso_addr		*dst;	/* destination address */
221 struct clnp_optidx	*oidx;	/* option index */
222 int					seg_off;/* offset of segmentation part */
223 struct snpa_hdr		*inbound_shp;	/* subnetwork header of inbound packet */
224 {
225 	struct clnp_fixed		*clnp;	/* ptr to fixed part of header */
226 	int						error;	/* return value of route function */
227 	struct sockaddr			*next_hop;	/* next hop for dgram */
228 	struct ifnet			*ifp;	/* ptr to outgoing interface */
229 	struct iso_ifaddr		*ia = 0;/* ptr to iso name for ifp */
230 	struct route_iso		route;	/* filled in by clnp_route */
231 	extern int				iso_systype;
232 
233 	clnp = mtod(m, struct clnp_fixed *);
234 	bzero((caddr_t)&route, sizeof(route)); /* MUST be done before "bad:" */
235 
236 	/*
237 	 *	Don't forward multicast or broadcast packets
238 	 */
239 	if ((inbound_shp) && (IS_MULTICAST(inbound_shp->snh_dhost))) {
240 		IFDEBUG(D_FORWARD)
241 			printf("clnp_forward: dropping multicast packet\n");
242 		ENDDEBUG
243 		clnp->cnf_type &= ~CNF_ERR_OK; /* so we don't generate an ER */
244 		clnp_discard(m, 0);
245 		INCSTAT(cns_cantforward);
246 		goto done;
247 	}
248 
249 	IFDEBUG(D_FORWARD)
250 		printf("clnp_forward: %d bytes, to %s, options x%x\n", len,
251 			clnp_iso_addrp(dst), oidx);
252 	ENDDEBUG
253 
254 	/*
255 	 *	Decrement ttl, and if zero drop datagram
256 	 *	Can't compare ttl as less than zero 'cause its a unsigned
257 	 */
258 	if ((clnp->cnf_ttl == 0) || (--clnp->cnf_ttl == 0)) {
259 		IFDEBUG(D_FORWARD)
260 			printf("clnp_forward: discarding datagram because ttl is zero\n");
261 		ENDDEBUG
262 		INCSTAT(cns_ttlexpired);
263 		clnp_discard(m, TTL_EXPTRANSIT);
264 		goto done;
265 	}
266 	/*
267 	 *	Route packet; special case for source rt
268 	 */
269 	if CLNPSRCRT_VALID(oidx) {
270 		/*
271 		 *	Update src route first
272 		 */
273 		clnp_update_srcrt(m, oidx);
274 		error = clnp_srcroute(m, oidx, &route, &next_hop, &ia, dst);
275 	} else {
276 		error = clnp_route(dst, &route, 0, &next_hop, &ia);
277 	}
278 	if (error || ia == 0) {
279 		IFDEBUG(D_FORWARD)
280 			printf("clnp_forward: can't route packet (errno %d)\n", error);
281 		ENDDEBUG
282 		clnp_discard(m, ADDR_DESTUNREACH);
283 		INCSTAT(cns_cantforward);
284 		goto done;
285 	}
286 	ifp = ia->ia_ifp;
287 
288 	IFDEBUG(D_FORWARD)
289 		printf("clnp_forward: packet routed to %s\n",
290 			clnp_iso_addrp(&((struct sockaddr_iso *)next_hop)->siso_addr));
291 	ENDDEBUG
292 
293 	INCSTAT(cns_forward);
294 
295 	/*
296 	 *	If we are an intermediate system and
297 	 *	we are routing outbound on the same ifp that the packet
298 	 *	arrived upon, and we know the next hop snpa,
299 	 *	then generate a redirect request
300 	 */
301 	if ((iso_systype & SNPA_IS) && (inbound_shp) &&
302 		(ifp == inbound_shp->snh_ifp))
303 		    esis_rdoutput(inbound_shp, m, oidx, dst, route.ro_rt);
304 	/*
305 	 *	If options are present, update them
306 	 */
307 	if (oidx) {
308 		struct iso_addr	*mysrc = &ia->ia_addr.siso_addr;
309 		if (mysrc == NULL) {
310 			clnp_discard(m, ADDR_DESTUNREACH);
311 			INCSTAT(cns_cantforward);
312 			clnp_stat.cns_forward--;
313 			goto done;
314 		} else {
315 			(void) clnp_dooptions(m, oidx, ifp, mysrc);
316 		}
317 	}
318 
319 #ifdef	DECBIT
320 	if (ifp->if_snd.ifq_len > congest_threshold) {
321 		/*
322 		 *	Congestion! Set the Dec Bit and thank Dave Oran
323 		 */
324 		IFDEBUG(D_FORWARD)
325 			printf("clnp_forward: congestion experienced\n");
326 		ENDDEBUG
327 		if ((oidx) && (oidx->cni_qos_formatp)) {
328 			caddr_t	qosp = CLNP_OFFTOOPT(m, oidx->cni_qos_formatp);
329 			u_char	qos = *qosp;
330 			IFDEBUG(D_FORWARD)
331 				printf("clnp_forward: setting congestion bit (qos x%x)\n", qos);
332 			ENDDEBUG
333 			if ((qos & CLNPOVAL_GLOBAL) == CLNPOVAL_GLOBAL) {
334 				qos |= CLNPOVAL_CONGESTED;
335 				INCSTAT(cns_congest_set);
336 				*qosp = qos;
337 			}
338 		}
339 	}
340 #endif	/* DECBIT */
341 
342 	/*
343 	 *	Dispatch the datagram if it is small enough, otherwise fragment
344 	 */
345 	if (len <= SN_MTU(ifp, route.ro_rt)) {
346 		iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
347 		(void) (*ifp->if_output)(ifp, m, next_hop, route.ro_rt);
348 	} else {
349 		(void) clnp_fragment(ifp, m, next_hop, len, seg_off, /* flags */0, route.ro_rt);
350 	}
351 
352 done:
353 	/*
354 	 *	Free route
355 	 */
356 	if (route.ro_rt != NULL) {
357 		RTFREE(route.ro_rt);
358 	}
359 }
360 
361 #ifdef	notdef
362 /*
363  * FUNCTION:		clnp_insert_addr
364  *
365  * PURPOSE:			Insert the address part into a clnp datagram.
366  *
367  * RETURNS:			Address of first byte after address part in datagram.
368  *
369  * SIDE EFFECTS:
370  *
371  * NOTES:			Assume that there is enough space for the address part.
372  */
373 caddr_t
374 clnp_insert_addr(bufp, srcp, dstp)
375 caddr_t						bufp;	/* address of where addr part goes */
376 register struct iso_addr	*srcp;	/* ptr to src addr */
377 register struct iso_addr	*dstp;	/* ptr to dst addr */
378 {
379 	*bufp++ = dstp->isoa_len;
380 	(void) bcopy((caddr_t)dstp, bufp, dstp->isoa_len);
381 	bufp += dstp->isoa_len;
382 
383 	*bufp++ = srcp->isoa_len;
384 	(void) bcopy((caddr_t)srcp, bufp, srcp->isoa_len);
385 	bufp += srcp->isoa_len;
386 
387 	return bufp;
388 }
389 
390 #endif	/* notdef */
391 
392 /*
393  * FUNCTION:		clnp_route
394  *
395  * PURPOSE:			Route a clnp datagram to the first hop toward its
396  *					destination. In many cases, the first hop will be
397  *					the destination. The address of a route
398  *					is specified. If a routing entry is present in
399  *					that route, and it is still up to the same destination,
400  *					then no further action is necessary. Otherwise, a
401  *					new routing entry will be allocated.
402  *
403  * RETURNS:			route found - 0
404  *					unix error code
405  *
406  * SIDE EFFECTS:
407  *
408  * NOTES:			It is up to the caller to free the routing entry
409  *					allocated in route.
410  */
411 clnp_route(dst, ro, flags, first_hop, ifa)
412 	struct iso_addr	*dst;			/* ptr to datagram destination */
413 	register struct	route_iso *ro;	/* existing route structure */
414 	int flags;						/* flags for routing */
415 	struct sockaddr **first_hop;	/* result: fill in with ptr to firsthop */
416 	struct iso_ifaddr **ifa;		/* result: fill in with ptr to interface */
417 {
418 	if (flags & SO_DONTROUTE) {
419 		struct iso_ifaddr *ia;
420 
421 		if (ro->ro_rt) {
422 			RTFREE(ro->ro_rt);
423 			ro->ro_rt = 0;
424 		}
425 		bzero((caddr_t)&ro->ro_dst, sizeof(ro->ro_dst));
426 		bcopy((caddr_t)dst, (caddr_t)&ro->ro_dst.siso_addr,
427 			1 + (unsigned)dst->isoa_len);
428 		ro->ro_dst.siso_family = AF_ISO;
429 		ro->ro_dst.siso_len = sizeof(ro->ro_dst);
430 		ia = iso_localifa(&ro->ro_dst);
431 		if (ia == 0)
432 			return EADDRNOTAVAIL;
433 		if (ifa)
434 			*ifa = ia;
435 		if (first_hop)
436 			*first_hop = (struct sockaddr *)&ro->ro_dst;
437 		return 0;
438 	}
439 	/*
440 	 *	If there is a cached route, check that it is still up and to
441 	 *	the same destination. If not, free it and try again.
442 	 */
443 	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
444 		(Bcmp(ro->ro_dst.siso_data, dst->isoa_genaddr, dst->isoa_len)))) {
445 		IFDEBUG(D_ROUTE)
446 			printf("clnp_route: freeing old route: ro->ro_rt 0x%x\n",
447 				ro->ro_rt);
448 			printf("clnp_route: old route refcnt: 0x%x\n",
449 				ro->ro_rt->rt_refcnt);
450 		ENDDEBUG
451 
452 		/* free old route entry */
453 		RTFREE(ro->ro_rt);
454 		ro->ro_rt = (struct rtentry *)0;
455 	} else {
456 		IFDEBUG(D_ROUTE)
457 			printf("clnp_route: OK route exists\n");
458 		ENDDEBUG
459 	}
460 
461 	if (ro->ro_rt == 0) {
462 		/* set up new route structure */
463 		bzero((caddr_t)&ro->ro_dst, sizeof(ro->ro_dst));
464 		ro->ro_dst.siso_len = sizeof(ro->ro_dst);
465 		ro->ro_dst.siso_family = AF_ISO;
466 		Bcopy(dst, &ro->ro_dst.siso_addr, 1 + dst->isoa_len);
467 		/* allocate new route */
468 		IFDEBUG(D_ROUTE)
469 			printf("clnp_route: allocating new route to %s\n",
470 				clnp_iso_addrp(dst));
471 		ENDDEBUG
472 		rtalloc((struct route *)ro);
473 	}
474 	if (ro->ro_rt == 0)
475 		return(ENETUNREACH);	/* rtalloc failed */
476 	ro->ro_rt->rt_use++;
477 	if (ifa)
478 		if ((*ifa = (struct iso_ifaddr *)ro->ro_rt->rt_ifa) == 0)
479 			panic("clnp_route");
480 	if (first_hop) {
481 		if (ro->ro_rt->rt_flags & RTF_GATEWAY)
482 			*first_hop = ro->ro_rt->rt_gateway;
483 		else
484 			*first_hop = (struct sockaddr *)&ro->ro_dst;
485 	}
486 	return(0);
487 }
488 
489 /*
490  * FUNCTION:		clnp_srcroute
491  *
492  * PURPOSE:			Source route the datagram. If complete source
493  *					routing is specified but not possible, then
494  *					return an error. If src routing is terminated, then
495  *					try routing on destination.
496  *					Usage of first_hop,
497  *					ifp, and error return is identical to clnp_route.
498  *
499  * RETURNS:			0 or unix error code
500  *
501  * SIDE EFFECTS:
502  *
503  * NOTES:			Remember that option index pointers are really
504  *					offsets from the beginning of the mbuf.
505  */
506 clnp_srcroute(options, oidx, ro, first_hop, ifa, final_dst)
507 struct mbuf			*options;		/* ptr to options */
508 struct clnp_optidx	*oidx;			/* index to options */
509 struct route_iso	*ro;			/* route structure */
510 struct sockaddr		**first_hop;	/* RETURN: fill in with ptr to firsthop */
511 struct iso_ifaddr	**ifa;			/* RETURN: fill in with ptr to interface */
512 struct iso_addr		*final_dst;		/* final destination */
513 {
514 	struct iso_addr	dst;		/* first hop specified by src rt */
515 	int				error = 0;	/* return code */
516 
517 	/*
518 	 *	Check if we have run out of routes
519 	 *	If so, then try to route on destination.
520 	 */
521 	if CLNPSRCRT_TERM(oidx, options) {
522 		dst.isoa_len = final_dst->isoa_len;
523 		bcopy(final_dst->isoa_genaddr, dst.isoa_genaddr, dst.isoa_len);
524 	} else {
525 		/*
526 		 * setup dst based on src rt specified
527 		 */
528 		dst.isoa_len = CLNPSRCRT_CLEN(oidx, options);
529 		bcopy(CLNPSRCRT_CADDR(oidx, options), dst.isoa_genaddr, dst.isoa_len);
530 	}
531 
532 	/*
533 	 *	try to route it
534 	 */
535 	error = clnp_route(&dst, ro, 0, first_hop, ifa);
536 	if (error != 0)
537 		return error;
538 
539 	/*
540 	 *	If complete src rt, first hop must be equal to dst
541 	 */
542 	if ((CLNPSRCRT_TYPE(oidx, options) == CLNPOVAL_COMPRT) &&
543 	 (!iso_addrmatch1(&(*(struct sockaddr_iso **)first_hop)->siso_addr,&dst))){
544 		IFDEBUG(D_OPTIONS)
545 			printf("clnp_srcroute: complete src route failed\n");
546 		ENDDEBUG
547 		return EHOSTUNREACH; /* RAH? would like ESRCRTFAILED */
548 	}
549 
550 	return error;
551 }
552 
553 /*
554  * FUNCTION:		clnp_echoreply
555  *
556  * PURPOSE:			generate an echo reply packet and transmit
557  *
558  * RETURNS:			result of clnp_output
559  *
560  * SIDE EFFECTS:
561  */
562 clnp_echoreply(ec_m, ec_len, ec_src, ec_dst, ec_oidxp)
563 struct mbuf			*ec_m;		/* echo request */
564 int					ec_len;		/* length of ec */
565 struct sockaddr_iso	*ec_src;		/* src of ec */
566 struct sockaddr_iso	*ec_dst; 	/* destination of ec (i.e., us) */
567 struct clnp_optidx	*ec_oidxp;	/* options index to ec packet */
568 {
569 	struct isopcb	isopcb;
570 	int				flags = CLNP_NOCACHE|CLNP_ECHOR;
571 	int				ret;
572 
573 	/* fill in fake isopcb to pass to output function */
574 	bzero(&isopcb, sizeof(isopcb));
575 	isopcb.isop_laddr = ec_dst;
576 	isopcb.isop_faddr = ec_src;
577 
578 	/* forget copying the options for now. If implemented, need only
579 	 * copy record route option, but it must be reset to zero length */
580 
581 	ret = clnp_output(ec_m, &isopcb, ec_len, flags);
582 
583 	IFDEBUG(D_OUTPUT)
584 		printf("clnp_echoreply: output returns %d\n", ret);
585 	ENDDEBUG
586 	return ret;
587 }
588 
589 /*
590  * FUNCTION:		clnp_badmtu
591  *
592  * PURPOSE:			print notice of route with mtu not initialized.
593  *
594  * RETURNS:			mtu of ifp.
595  *
596  * SIDE EFFECTS:	prints notice, slows down system.
597  */
598 clnp_badmtu(ifp, rt, line, file)
599 struct ifnet *ifp;	/* outgoing interface */
600 struct rtentry *rt; /* dst route */
601 int line;			/* where the dirty deed occured */
602 char *file;			/* where the dirty deed occured */
603 {
604 	printf("sending on route 0x%x with no mtu, line %d of file %s\n",
605 		rt, line, file);
606 #ifdef ARGO_DEBUG
607 	printf("route dst is ");
608 	dump_isoaddr(rt_key(rt));
609 #endif
610 	return ifp->if_mtu;
611 }
612 
613 /*
614  * FUNCTION:		clnp_ypocb - backwards bcopy
615  *
616  * PURPOSE:			bcopy starting at end of src rather than beginning.
617  *
618  * RETURNS:			none
619  *
620  * SIDE EFFECTS:
621  *
622  * NOTES:			No attempt has been made to make this efficient
623  */
624 clnp_ypocb(from, to, len)
625 caddr_t from;		/* src buffer */
626 caddr_t to;			/* dst buffer */
627 u_int	len;		/* number of bytes */
628 {
629 	while (len--)
630 		*(to + len) = *(from + len);
631 }
632 #endif	/* ISO */
633