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