xref: /csrg-svn/sys/netiso/clnp_subr.c (revision 36770)
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_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $ */
28 /* $Source: /var/src/sys/netiso/RCS/clnp_subr.c,v $ */
29 
30 #ifndef lint
31 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_subr.c,v 5.1 89/02/09 16:20:46 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 /* Dec bit set if ifp qlen is greater than congest_threshold */
188 int congest_threshold = 0;
189 
190 /*
191  * FUNCTION:		clnp_forward
192  *
193  * PURPOSE:			Forward the datagram passed
194  *					clnpintr guarantees that the header will be
195  *					contigious (a cluster mbuf will be used if necessary).
196  *
197  *					If oidx is NULL, no options are present.
198  *
199  * RETURNS:			nothing
200  *
201  * SIDE EFFECTS:
202  *
203  * NOTES:
204  */
205 clnp_forward(m, len, dst, oidx, seg_off, inbound_shp)
206 struct mbuf			*m;		/* pkt to forward */
207 int					len;	/* length of pkt */
208 struct iso_addr		*dst;	/* destination address */
209 struct clnp_optidx	*oidx;	/* option index */
210 int					seg_off;/* offset of segmentation part */
211 struct snpa_hdr		*inbound_shp;	/* subnetwork header of inbound packet */
212 {
213 	struct clnp_fixed		*clnp;	/* ptr to fixed part of header */
214 	int						error;	/* return value of route function */
215 	struct sockaddr			*next_hop;	/* next hop for dgram */
216 	struct ifnet			*ifp;	/* ptr to outgoing interface */
217 	struct route_iso		route;	/* filled in by clnp_route */
218 	extern int				iso_systype;
219 
220 	clnp = mtod(m, struct clnp_fixed *);
221 	bzero((caddr_t)&route, sizeof(route)); /* MUST be done before "bad:" */
222 
223 	/*
224 	 *	Don't forward multicast or broadcast packets
225 	 */
226 	if ((inbound_shp) && (IS_MULTICAST(inbound_shp->snh_dhost))) {
227 		IFDEBUG(D_FORWARD)
228 			printf("clnp_forward: dropping multicast packet\n");
229 		ENDDEBUG
230 		clnp->cnf_err_ok = 0;	/* so we don't generate an ER */
231 		clnp_discard(m, 0);
232 		goto done;
233 	}
234 
235 	IFDEBUG(D_FORWARD)
236 		printf("clnp_forward: %d bytes, to %s, options x%x\n", len,
237 			clnp_iso_addrp(dst), oidx);
238 	ENDDEBUG
239 
240 	/*
241 	 *	Decrement ttl, and if zero drop datagram
242 	 *	Can't compare ttl as less than zero 'cause its a unsigned
243 	 */
244 	if ((clnp->cnf_ttl == 0) || (--clnp->cnf_ttl == 0)) {
245 		IFDEBUG(D_FORWARD)
246 			printf("clnp_forward: discarding datagram because ttl is zero\n");
247 		ENDDEBUG
248 		INCSTAT(cns_ttlexpired);
249 		clnp_discard(m, TTL_EXPTRANSIT);
250 		goto done;
251 	}
252 
253 	/*
254 	 *	Route packet; special case for source rt
255 	 */
256 	if CLNPSRCRT_VALID(oidx) {
257 		/*
258 		 *	Update src route first
259 		 */
260 		clnp_update_srcrt(m, oidx);
261 		error = clnp_srcroute(m, oidx, &route, &next_hop, &ifp, dst);
262 	} else {
263 		error = clnp_route(dst, &route, 0, &next_hop, &ifp);
264 	}
265 	if (error) {
266 		IFDEBUG(D_FORWARD)
267 			printf("clnp_forward: can't route packet (errno %d)\n", error);
268 		ENDDEBUG
269 		clnp_discard(m, ADDR_DESTUNREACH);
270 		goto done;
271 	}
272 
273 	IFDEBUG(D_FORWARD)
274 		printf("clnp_forward: packet routed to %s\n",
275 			clnp_iso_addrp(&((struct sockaddr_iso *)next_hop)->siso_addr));
276 	ENDDEBUG
277 
278 	INCSTAT(cns_forward);
279 
280 	/*
281 	 *	If we are an intermediate system and
282 	 *	we are routing outbound on the same ifp that the packet
283 	 *	arrived upon, and we know the next hop snpa,
284 	 *	then generate a redirect request
285 	 */
286 	if ((iso_systype & SNPA_IS) && (inbound_shp) &&
287 		(ifp == inbound_shp->snh_ifp)) {
288 		struct snpa_cache 			*sc;
289 
290 		sc = snpac_look(&((struct sockaddr_iso *)next_hop)->siso_addr);
291 		if (sc != NULL) {
292 			esis_rdoutput(inbound_shp, m, oidx, dst, sc);
293 		}
294 	}
295 
296 	/*
297 	 *	If options are present, update them
298 	 */
299 	if (oidx) {
300 		struct iso_addr	*mysrc =
301 			clnp_srcaddr(ifp, &((struct sockaddr_iso *)next_hop)->siso_addr);
302 		if (mysrc == NULL) {
303 			clnp_discard(m, ADDR_DESTUNREACH);
304 			goto done;
305 		} else {
306 			(void) clnp_dooptions(m, oidx, ifp, mysrc);
307 		}
308 	}
309 
310 #ifdef	DECBIT
311 	if (ifp->if_snd.ifq_len > congest_threshold) {
312 		/*
313 		 *	Congestion! Set the Dec Bit and thank Dave Oran
314 		 */
315 		IFDEBUG(D_FORWARD)
316 			printf("clnp_forward: congestion experienced\n");
317 		ENDDEBUG
318 		if ((oidx) && (oidx->cni_qos_formatp)) {
319 			caddr_t	qosp = CLNP_OFFTOOPT(m, oidx->cni_qos_formatp);
320 			u_char	qos = *qosp;
321 			IFDEBUG(D_FORWARD)
322 				printf("clnp_forward: setting congestion bit (qos x%x)\n", qos);
323 			ENDDEBUG
324 			if ((qos & CLNPOVAL_GLOBAL) == CLNPOVAL_GLOBAL) {
325 				qos |= CLNPOVAL_CONGESTED;
326 				INCSTAT(cns_congest_set);
327 				*qosp = qos;
328 			}
329 		}
330 	}
331 #endif	DECBIT
332 
333 	/*
334 	 *	Dispatch the datagram if it is small enough, otherwise fragment
335 	 */
336 	if (len <= SN_MTU(ifp)) {
337 		iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
338 		(void) (*ifp->if_output)(ifp, m, next_hop);
339 	} else {
340 		(void) clnp_fragment(ifp, m, next_hop, len, seg_off, /* flags */0);
341 	}
342 
343 done:
344 	/*
345 	 *	Free route
346 	 */
347 	if (route.ro_rt != NULL) {
348 		RTFREE(route.ro_rt);
349 	}
350 }
351 
352 #ifdef	ndef
353 /*
354  * FUNCTION:		clnp_insert_addr
355  *
356  * PURPOSE:			Insert the address part into a clnp datagram.
357  *
358  * RETURNS:			Address of first byte after address part in datagram.
359  *
360  * SIDE EFFECTS:
361  *
362  * NOTES:			Assume that there is enough space for the address part.
363  */
364 caddr_t
365 clnp_insert_addr(bufp, srcp, dstp)
366 caddr_t						bufp;	/* address of where addr part goes */
367 register struct iso_addr	*srcp;	/* ptr to src addr */
368 register struct iso_addr	*dstp;	/* ptr to dst addr */
369 {
370 	*bufp++ = dstp->isoa_len;
371 	(void) bcopy((caddr_t)dstp, bufp, dstp->isoa_len);
372 	bufp += dstp->isoa_len;
373 
374 	*bufp++ = srcp->isoa_len;
375 	(void) bcopy((caddr_t)srcp, bufp, srcp->isoa_len);
376 	bufp += srcp->isoa_len;
377 
378 	return bufp;
379 }
380 
381 #endif	ndef
382 
383 /*
384  * FUNCTION:		clnp_route
385  *
386  * PURPOSE:			Route a clnp datagram to the first hop toward its
387  *					destination. In many cases, the first hop will be
388  *					the destination. The address of a route
389  *					is specified. If a routing entry is present in
390  *					that route, and it is still up to the same destination,
391  *					then no further action is necessary. Otherwise, a
392  *					new routing entry will be allocated.
393  *
394  * RETURNS:			route found - 0
395  *					unix error code
396  *
397  * SIDE EFFECTS:
398  *
399  * NOTES:			It is up to the caller to free the routing entry
400  *					allocated in route.
401  */
402 clnp_route(dst, ro, flags, first_hop, ifp)
403 struct iso_addr		*dst;			/* ptr to datagram destination */
404 struct route_iso	*ro;			/* existing route structure */
405 int					flags;			/* flags for routing */
406 struct sockaddr		**first_hop;	/* result: fill in with ptr to firsthop */
407 struct ifnet		**ifp;			/* result: fill in with ptr to interface */
408 {
409 	register struct sockaddr_iso	*ro_dst;	/* ptr to route's destination */
410 
411 	ro_dst = (struct sockaddr_iso *)&ro->ro_dst;
412 
413 	/*
414 	 *	If there is a cached route, check that it is still up and to
415 	 *	the same destination. If not, free it and try again.
416 	 */
417 	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
418 		(!iso_addrmatch1(&ro_dst->siso_addr, dst)))) {
419 		IFDEBUG(D_ROUTE)
420 			printf("clnp_route: freeing old route: ro->ro_rt 0x%x\n",
421 				ro->ro_rt);
422 			printf("clnp_route: old route refcnt: 0x%x\n",
423 				ro->ro_rt->rt_refcnt);
424 		ENDDEBUG
425 
426 		/* free old route entry */
427 		RTFREE(ro->ro_rt);
428 		ro->ro_rt = (struct rtentry *)0;
429 	} else {
430 		IFDEBUG(D_ROUTE)
431 			printf("clnp_route: OK route exists\n");
432 		ENDDEBUG
433 	}
434 
435 	if (ro->ro_rt == 0) {
436 		/* set up new route structure */
437 		ro_dst->siso_family = AF_ISO;
438 		ro_dst->siso_addr = *dst;
439 
440 		/* allocate new route */
441 		IFDEBUG(D_ROUTE)
442 			printf("clnp_route: allocating new route to %s\n",
443 				clnp_iso_addrp(dst));
444 		ENDDEBUG
445 		rtalloc(ro);
446 	}
447 
448 	if ((ro->ro_rt == 0) || ((*ifp = ro->ro_rt->rt_ifp) == 0)) {
449 		return(ENETUNREACH);	/* rtalloc failed */
450 	}
451 
452 	ro->ro_rt->rt_use++;
453 	if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST))
454 		*first_hop = &ro->ro_rt->rt_gateway;
455 	else
456 		*first_hop = (struct sockaddr *)ro_dst;
457 
458 	return(0);
459 }
460 
461 /*
462  * FUNCTION:		clnp_srcroute
463  *
464  * PURPOSE:			Source route the datagram. If complete source
465  *					routing is specified but not possible, then
466  *					return an error. If src routing is terminated, then
467  *					try routing on destination.
468  *					Usage of first_hop,
469  *					ifp, and error return is identical to clnp_route.
470  *
471  * RETURNS:			0 or unix error code
472  *
473  * SIDE EFFECTS:
474  *
475  * NOTES:			Remember that option index pointers are really
476  *					offsets from the beginning of the mbuf.
477  */
478 clnp_srcroute(options, oidx, route, first_hop, ifp, final_dst)
479 struct mbuf			*options;		/* ptr to options */
480 struct clnp_optidx	*oidx;			/* index to options */
481 struct route		*route;			/* route structure */
482 struct sockaddr		**first_hop;	/* RETURN: fill in with ptr to firsthop */
483 struct ifnet		**ifp;			/* RETURN: fill in with ptr to interface */
484 struct iso_addr		*final_dst;		/* final destination */
485 {
486 	struct iso_addr	dst;		/* first hop specified by src rt */
487 	int				error = 0;	/* return code */
488 
489 	/*
490 	 *	Check if we have run out of routes
491 	 *	If so, then try to route on destination.
492 	 */
493 	if CLNPSRCRT_TERM(oidx, options) {
494 		dst.isoa_len = final_dst->isoa_len;
495 		bcopy((caddr_t)final_dst, (caddr_t)&dst, dst.isoa_len);
496 	} else {
497 		/*
498 		 * setup dst based on src rt specified
499 		 */
500 		dst.isoa_len = CLNPSRCRT_CLEN(oidx, options);
501 		bcopy(CLNPSRCRT_CADDR(oidx, options), (caddr_t)&dst, dst.isoa_len);
502 	}
503 
504 	/*
505 	 *	try to route it
506 	 */
507 	error = clnp_route(&dst, route, 0, first_hop, ifp);
508 	if (error != 0)
509 		return error;
510 
511 	/*
512 	 *	If complete src rt, first hop must be equal to dst
513 	 */
514 	if ((CLNPSRCRT_TYPE(oidx, options) == CLNPOVAL_COMPRT) &&
515 	 (!iso_addrmatch1(&(*(struct sockaddr_iso **)first_hop)->siso_addr,&dst))){
516 		IFDEBUG(D_OPTIONS)
517 			printf("clnp_srcroute: complete src route failed\n");
518 		ENDDEBUG
519 		return EHOSTUNREACH; /* RAH? would like ESRCRTFAILED */
520 	}
521 
522 	return error;
523 }
524 
525 /*
526  * FUNCTION:		clnp_srcaddr
527  *
528  * PURPOSE:			Build the correct source address for a datagram based on the
529  *					outgoing interface and firsthop. The firsthop information
530  *					is needed inorder to decide which addr to use if
531  *					>1 ISO addr is present for an ifp.
532  *
533  * RETURNS:			a ptr to a static copy of the source address.
534  *					or NULL
535  *
536  * SIDE EFFECTS:
537  *
538  * NOTES:			The ifp must be valid, or we will return NULL
539  */
540 static struct iso_addr mysrc;
541 struct iso_addr *
542 clnp_srcaddr(ifp, firsthop)
543 struct ifnet	*ifp;		/* ptr to interface to send packet on */
544 struct iso_addr	*firsthop;	/* ptr to first hop for packet */
545 {
546 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
547 	struct iso_addr				*maybe = NULL;
548 
549 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
550 		if (ia->ia_ifp == ifp) {
551 			struct iso_addr	*isoa = &IA_SIS(ia)->siso_addr;
552 
553 			IFDEBUG(D_ROUTE)
554 				printf("clnp_srcaddr: isoa is %s\n", clnp_iso_addrp(isoa));
555 			ENDDEBUG
556 
557 			if (iso_eqtype(isoa, firsthop)) {
558 				mysrc.isoa_len = isoa->isoa_len;
559 				bcopy((caddr_t)isoa, (caddr_t)&mysrc, mysrc.isoa_len);
560 				return(&mysrc);
561 			} else
562 				maybe = isoa;
563 		}
564 	}
565 
566 	if (maybe != NULL) {
567 		mysrc.isoa_len = maybe->isoa_len;
568 		bcopy((caddr_t)maybe, (caddr_t)&mysrc, mysrc.isoa_len);
569 		return(&mysrc);
570 	} else {
571 		/*
572 		 *	This will only happen if there are routes involving
573 		 *	an interface that has just had all iso addresses deleted
574 		 *	from it. This will happen if esisd has added a default
575 		 *	route to an interface, and then the interface was
576 		 *	marked down. As soon as esisd tries to send a pdu on that
577 		 *	interface, it will discover it is down, and remove the
578 		 *	route.  Nonetheless, there is a window for this discrepancy,
579 		 *	so we will return null here rather than panicing.
580 		 */
581 		return(NULL);
582 	}
583 }
584 
585 /*
586  * FUNCTION:		clnp_ypocb - backwards bcopy
587  *
588  * PURPOSE:			bcopy starting at end of src rather than beginning.
589  *
590  * RETURNS:			none
591  *
592  * SIDE EFFECTS:
593  *
594  * NOTES:			No attempt has been made to make this efficient
595  */
596 clnp_ypocb(from, to, len)
597 caddr_t from;		/* src buffer */
598 caddr_t to;			/* dst buffer */
599 u_int	len;		/* number of bytes */
600 {
601 	while (len--)
602 		*(to + len) = *(from + len);
603 }
604 
605 /*
606  * FUNCTION:		clnp_hdrsize
607  *
608  * PURPOSE:			Return the size of a typical clnp hdr.
609  *
610  * RETURNS:			Size of hdr in bytes.
611  *
612  * SIDE EFFECTS:
613  *
614  * NOTES:			Assumes segmenting subset. If addrlen is
615  *					zero, default to largest nsap address size.
616  */
617 clnp_hdrsize(addrlen)
618 u_char	addrlen;		/* length of nsap address */
619 {
620 	if (addrlen == 0)
621 		addrlen = 20;
622 
623 	addrlen++;			/* length of address byte */
624 	addrlen *= 2;		/* src and dst addresses */
625 	addrlen += sizeof(struct clnp_fixed) + sizeof(struct clnp_segment);
626 
627 	return(addrlen);
628 }
629 #endif	ISO
630