1/*	$NetBSD: clnp_subr.c,v 1.32 2009/03/18 15:14:32 cegger Exp $	*/
2
3/*-
4 * Copyright (c) 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)clnp_subr.c	8.1 (Berkeley) 6/10/93
32 */
33
34/***********************************************************
35		Copyright IBM Corporation 1987
36
37                      All Rights Reserved
38
39Permission to use, copy, modify, and distribute this software and its
40documentation for any purpose and without fee is hereby granted,
41provided that the above copyright notice appear in all copies and that
42both that copyright notice and this permission notice appear in
43supporting documentation, and that the name of IBM not be
44used in advertising or publicity pertaining to distribution of the
45software without specific, written prior permission.
46
47IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53SOFTWARE.
54
55******************************************************************/
56
57/*
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
59 */
60
61#include <sys/cdefs.h>
62__KERNEL_RCSID(0, "$NetBSD: clnp_subr.c,v 1.32 2009/03/18 15:14:32 cegger Exp $");
63
64#include "opt_iso.h"
65
66#ifdef ISO
67
68#include <sys/param.h>
69#include <sys/mbuf.h>
70#include <sys/domain.h>
71#include <sys/protosw.h>
72#include <sys/socket.h>
73#include <sys/socketvar.h>
74#include <sys/errno.h>
75#include <sys/time.h>
76#include <sys/systm.h>
77
78#include <net/if.h>
79#include <net/route.h>
80#include <net/if_dl.h>
81
82#include <netiso/iso.h>
83#include <netiso/iso_var.h>
84#include <netiso/iso_pcb.h>
85#include <netiso/iso_snpac.h>
86#include <netiso/clnp.h>
87#include <netiso/clnp_stat.h>
88#include <netiso/argo_debug.h>
89#include <netiso/esis.h>
90
91/*
92 * FUNCTION:		clnp_data_ck
93 *
94 * PURPOSE:		Check that the amount of data in the mbuf chain is
95 *			at least as much as the clnp header would have us
96 *			expect. Trim mbufs if longer than expected, drop
97 *			packet if shorter than expected.
98 *
99 * RETURNS:		success - ptr to mbuf chain
100 *			failure - 0
101 *
102 * SIDE EFFECTS:
103 *
104 * NOTES:
105 */
106struct mbuf    *
107clnp_data_ck(
108	struct mbuf *m,		/* ptr to mbuf chain containing hdr & data */
109	int	length)		/* length (in bytes) of packet */
110{
111	int    len;		/* length of data */
112	struct mbuf *mhead;	/* ptr to head of chain */
113
114	len = -length;
115	mhead = m;
116	for (;;) {
117		len += m->m_len;
118		if (m->m_next == 0)
119			break;
120		m = m->m_next;
121	}
122	if (len != 0) {
123		if (len < 0) {
124			INCSTAT(cns_toosmall);
125			clnp_discard(mhead, GEN_INCOMPLETE);
126			return 0;
127		}
128		if (len <= m->m_len)
129			m->m_len -= len;
130		else
131			m_adj(mhead, -len);
132	}
133	return mhead;
134}
135
136#ifdef notdef
137/*
138 * FUNCTION:		clnp_extract_addr
139 *
140 * PURPOSE:		Extract the source and destination address from the
141 *			supplied buffer. Place them in the supplied address buffers.
142 *			If insufficient data is supplied, then fail.
143 *
144 * RETURNS:		success - Address of first byte in the packet past
145 *			the address part.
146 *			failure - 0
147 *
148 * SIDE EFFECTS:
149 *
150 * NOTES:
151 */
152void *
153clnp_extract_addr(
154	void *        bufp,	/* ptr to buffer containing addresses */
155	int             buflen,	/* length of buffer */
156	struct iso_addr *srcp,	/* ptr to source address buffer */
157	struct iso_addr *destp)	/* ptr to destination address
158						 * buffer */
159{
160	size_t             len;	/* argument to memcpy */
161
162	/*
163	 * check that we have enough data. Plus1 is for length octet
164	 */
165	len = (u_char)*bufp++;
166	if (len > buflen)
167		return NULL;
168	destp->isoa_len = len;
169	(void)memcpy(destp, bufp, len);
170	buflen -= len;
171	bufp += len;
172
173	/*
174	 * check that we have enough data. Plus1 is for length octet
175	 */
176	len = (u_char)*bufp++;
177	if (len > buflen)
178		return NULL;
179	srcp->isoa_len = len;
180	(void)memcpy(srcp, bufp, len);
181	bufp += len;
182
183	/*
184	 *	Insure that the addresses make sense
185	 */
186	if (iso_ck_addr(srcp) && iso_ck_addr(destp))
187		return bufp;
188	else
189		return NULL;
190}
191#endif				/* notdef */
192
193/*
194 * FUNCTION:		clnp_ours
195 *
196 * PURPOSE:		Decide whether the supplied packet is destined for
197 *			us, or that it should be forwarded on.
198 *
199 * RETURNS:		packet is for us - 1
200 *			packet is not for us - 0
201 *
202 * SIDE EFFECTS:
203 *
204 * NOTES:
205 */
206int
207clnp_ours(
208	struct iso_addr *dst)	/* ptr to destination address */
209{
210	struct iso_ifaddr *ia;	/* scan through interface addresses */
211
212	for (ia = iso_ifaddr.tqh_first; ia != 0; ia = ia->ia_list.tqe_next) {
213#ifdef ARGO_DEBUG
214		if (argo_debug[D_ROUTE]) {
215			printf("clnp_ours: ia_sis %p, dst %p\n",
216			    &ia->ia_addr, dst);
217		}
218#endif
219		/*
220		 * XXX Warning:
221		 * We are overloading siso_tlen in the if's address, as an nsel length.
222		 */
223		if (dst->isoa_len == ia->ia_addr.siso_nlen &&
224		    memcmp((void *) ia->ia_addr.siso_addr.isoa_genaddr,
225			 (void *) dst->isoa_genaddr,
226			 ia->ia_addr.siso_nlen - ia->ia_addr.siso_tlen) == 0)
227			return 1;
228	}
229	return 0;
230}
231
232/* Dec bit set if ifp qlen is greater than congest_threshold */
233int             congest_threshold = 0;
234
235/*
236 * FUNCTION:		clnp_forward
237 *
238 * PURPOSE:		Forward the datagram passed
239 *			clnpintr guarantees that the header will be
240 *			contigious (a cluster mbuf will be used if necessary).
241 *
242 *			If oidx is NULL, no options are present.
243 *
244 * RETURNS:		nothing
245 *
246 * SIDE EFFECTS:
247 *
248 * NOTES:
249 */
250void
251clnp_forward(
252	struct mbuf    *m,		/* pkt to forward */
253	int             len,		/* length of pkt */
254	struct iso_addr *dst,		/* destination address */
255	struct clnp_optidx *oidx,	/* option index */
256	int             seg_off,	/* offset of segmentation part */
257	struct snpa_hdr *inbound_shp)	/* subnetwork header of inbound
258					 * packet */
259{
260	struct clnp_fixed *clnp;	/* ptr to fixed part of header */
261	int             error;		/* return value of route function */
262	const struct sockaddr *next_hop;	/* next hop for dgram */
263	struct ifnet   *ifp;		/* ptr to outgoing interface */
264	struct iso_ifaddr *ia = 0;	/* ptr to iso name for ifp */
265	struct route route;		/* filled in by clnp_route */
266	struct rtentry *rt;
267	extern int      iso_systype;
268
269	clnp = mtod(m, struct clnp_fixed *);
270	memset((void *) & route, 0, sizeof(route));	/* MUST be done before
271							 * "bad:" */
272
273	/*
274	 *	Don't forward multicast or broadcast packets
275	 */
276	if ((inbound_shp) && (IS_MULTICAST(inbound_shp->snh_dhost))) {
277#ifdef ARGO_DEBUG
278		if (argo_debug[D_FORWARD]) {
279			printf("clnp_forward: dropping multicast packet\n");
280		}
281#endif
282		clnp->cnf_type &= ~CNF_ERR_OK;	/* so we don't generate an ER */
283		clnp_discard(m, 0);
284		INCSTAT(cns_cantforward);
285		goto done;
286	}
287#ifdef ARGO_DEBUG
288	if (argo_debug[D_FORWARD]) {
289		printf("clnp_forward: %d bytes, to %s, options %p\n", len,
290		       clnp_iso_addrp(dst), oidx);
291	}
292#endif
293
294	/*
295	 *	Decrement ttl, and if zero drop datagram
296	 *	Can't compare ttl as less than zero 'cause its a unsigned
297	 */
298	if ((clnp->cnf_ttl == 0) || (--clnp->cnf_ttl == 0)) {
299#ifdef ARGO_DEBUG
300		if (argo_debug[D_FORWARD]) {
301			printf("clnp_forward: discarding datagram because ttl is zero\n");
302		}
303#endif
304		INCSTAT(cns_ttlexpired);
305		clnp_discard(m, TTL_EXPTRANSIT);
306		goto done;
307	}
308	/*
309	 *	Route packet; special case for source rt
310	 */
311	if CLNPSRCRT_VALID
312		(oidx) {
313		/*
314		 *	Update src route first
315		 */
316		clnp_update_srcrt(m, oidx);
317		error = clnp_srcroute(m, oidx, &route, &next_hop, &ia, dst);
318	} else {
319		error = clnp_route(dst, &route, 0, &next_hop, &ia);
320	}
321	if (error || ia == 0) {
322#ifdef ARGO_DEBUG
323		if (argo_debug[D_FORWARD]) {
324			printf("clnp_forward: can't route packet (errno %d)\n", error);
325		}
326#endif
327		clnp_discard(m, ADDR_DESTUNREACH);
328		INCSTAT(cns_cantforward);
329		goto done;
330	}
331	ifp = ia->ia_ifp;
332
333#ifdef ARGO_DEBUG
334	if (argo_debug[D_FORWARD]) {
335		printf("clnp_forward: packet routed to %s\n",
336		       clnp_iso_addrp(&satocsiso(next_hop)->siso_addr));
337	}
338#endif
339
340	INCSTAT(cns_forward);
341
342	/*
343	 *	If we are an intermediate system and
344	 *	we are routing outbound on the same ifp that the packet
345	 *	arrived upon, and we know the next hop snpa,
346	 *	then generate a redirect request
347	 */
348	if ((iso_systype & SNPA_IS) && (inbound_shp) &&
349	    (ifp == inbound_shp->snh_ifp))
350		esis_rdoutput(inbound_shp, m, oidx, dst, rtcache_validate(&route));
351	/*
352	 *	If options are present, update them
353	 */
354	if (oidx) {
355		struct iso_addr *mysrc = &ia->ia_addr.siso_addr;
356		if (mysrc == NULL) {
357			clnp_discard(m, ADDR_DESTUNREACH);
358			INCSTAT(cns_cantforward);
359			clnp_stat.cns_forward--;
360			goto done;
361		} else {
362			(void) clnp_dooptions(m, oidx, ifp, mysrc);
363		}
364	}
365#ifdef	DECBIT
366	if (ifp->if_snd.ifq_len > congest_threshold) {
367		/*
368		 *	Congestion! Set the Dec Bit and thank Dave Oran
369		 */
370#ifdef ARGO_DEBUG
371		if (argo_debug[D_FORWARD]) {
372			printf("clnp_forward: congestion experienced\n");
373		}
374#endif
375		if ((oidx) && (oidx->cni_qos_formatp)) {
376			char *         qosp = CLNP_OFFTOOPT(m, oidx->cni_qos_formatp);
377			u_char          qos = *qosp;
378#ifdef ARGO_DEBUG
379			if (argo_debug[D_FORWARD]) {
380				printf("clnp_forward: setting congestion bit (qos x%x)\n", qos);
381			}
382#endif
383			if ((qos & CLNPOVAL_GLOBAL) == CLNPOVAL_GLOBAL) {
384				qos |= CLNPOVAL_CONGESTED;
385				INCSTAT(cns_congest_set);
386				*qosp = qos;
387			}
388		}
389	}
390#endif				/* DECBIT */
391
392	/*
393	 *	Dispatch the datagram if it is small enough, otherwise fragment
394	 */
395	if ((rt = rtcache_validate(&route)) == NULL)
396		;
397	else if (len <= SN_MTU(ifp, rt)) {
398		iso_gen_csum(m, CLNP_CKSUM_OFF, (int) clnp->cnf_hdr_len);
399		(void) (*ifp->if_output) (ifp, m, next_hop, rt);
400	} else {
401		(void) clnp_fragment(ifp, m, next_hop, len, seg_off, /* flags */ 0, rt);
402	}
403
404done:
405	/*
406	 *	Free route
407	 */
408	rtcache_free(&route);
409}
410
411#ifdef	notdef
412/*
413 * FUNCTION:		clnp_insert_addr
414 *
415 * PURPOSE:			Insert the address part into a clnp datagram.
416 *
417 * RETURNS:			Address of first byte after address part in datagram.
418 *
419 * SIDE EFFECTS:
420 *
421 * NOTES:			Assume that there is enough space for the address part.
422 */
423void *
424clnp_insert_addr(
425	void *        bufp,	/* address of where addr part goes */
426	struct iso_addr *srcp,	/* ptr to src addr */
427	struct iso_addr *dstp)	/* ptr to dst addr */
428{
429	*bufp++ = dstp->isoa_len;
430	(void)memcpy(bufp, dstp, dstp->isoa_len);
431	bufp += dstp->isoa_len;
432
433	*bufp++ = srcp->isoa_len;
434	(void)memcpy(bufp, srcp, srcp->isoa_len);
435	bufp += srcp->isoa_len;
436
437	return bufp;
438}
439
440#endif				/* notdef */
441
442/*
443 * FUNCTION:		clnp_route
444 *
445 * PURPOSE:		Route a clnp datagram to the first hop toward its
446 *			destination. In many cases, the first hop will be
447 *			the destination. The address of a route
448 *			is specified. If a routing entry is present in
449 *			that route, and it is still up to the same destination,
450 *			then no further action is necessary. Otherwise, a
451 *			new routing entry will be allocated.
452 *
453 * RETURNS:		route found - 0
454 *			unix error code
455 *
456 * SIDE EFFECTS:
457 *
458 * NOTES:		It is up to the caller to free the routing entry
459 *			allocated in route.
460 */
461int
462clnp_route(
463	struct iso_addr *dst,		/* ptr to datagram destination */
464	struct route *ro,		/* existing route structure */
465	int             flags,		/* flags for routing */
466	const struct sockaddr **first_hop,	/* result: fill in with ptr to
467					 	 * firsthop */
468	struct iso_ifaddr **ifa)	/* result: fill in with ptr to ifa */
469{
470	struct rtentry *rt;
471	int rc;
472	union {
473		struct sockaddr		dst;
474		struct sockaddr_iso	dsti;
475	} u;
476
477	if (flags & SO_DONTROUTE) {
478		struct iso_ifaddr *ia;
479
480		if ((rc = sockaddr_iso_init(&u.dsti, dst)) != 0)
481			return rc;
482		rtcache_setdst(ro, &u.dst);
483
484		if (rtcache_getdst(ro) == NULL)
485			return EADDRNOTAVAIL;
486		ia = iso_localifa(satocsiso(rtcache_getdst(ro)));
487		if (ia == NULL)
488			return EADDRNOTAVAIL;
489		if (ifa != NULL)
490			*ifa = ia;
491		if (first_hop != NULL)
492			*first_hop = rtcache_getdst(ro);
493		return 0;
494	}
495
496	/* set up new route structure */
497	if ((rc = sockaddr_iso_init(&u.dsti, dst)) != 0)
498		return rc;
499	if ((rt = rtcache_lookup(ro, &u.dst)) == NULL) {
500		rtcache_free(ro);
501		return ENETUNREACH;
502	}
503	rt->rt_use++;
504	if (ifa != NULL)
505		if ((*ifa = (struct iso_ifaddr *)rt->rt_ifa) == NULL)
506			panic("clnp_route");
507	if (first_hop != NULL) {
508		if (rt->rt_flags & RTF_GATEWAY)
509			*first_hop = rt->rt_gateway;
510		else
511			*first_hop = rtcache_getdst(ro);
512	}
513	return 0;
514}
515
516/*
517 * FUNCTION:		clnp_srcroute
518 *
519 * PURPOSE:		Source route the datagram. If complete source
520 *			routing is specified but not possible, then
521 *			return an error. If src routing is terminated, then
522 *			try routing on destination.
523 *			Usage of first_hop,
524 *			ifp, and error return is identical to clnp_route.
525 *
526 * RETURNS:		0 or unix error code
527 *
528 * SIDE EFFECTS:
529 *
530 * NOTES:		Remember that option index pointers are really
531 *			offsets from the beginning of the mbuf.
532 */
533int
534clnp_srcroute(
535	struct mbuf *options,		/* ptr to options */
536	struct clnp_optidx *oidx,	/* index to options */
537	struct route *ro,		/* route structure */
538	const struct sockaddr **first_hop,	/* RETURN: fill in with ptr to
539						 * firsthop */
540	struct iso_ifaddr **ifa,	/* RETURN: fill in with ptr to ifa */
541	struct iso_addr *final_dst)	/* final destination */
542{
543	struct iso_addr dst;	/* first hop specified by src rt */
544	int             error = 0;	/* return code */
545
546	/*
547	 *	Check if we have run out of routes
548	 *	If so, then try to route on destination.
549	 */
550	if CLNPSRCRT_TERM
551		(oidx, options) {
552		dst.isoa_len = final_dst->isoa_len;
553		if (sizeof(dst.isoa_genaddr) < (size_t)dst.isoa_len)
554			return EINVAL;
555		(void)memcpy(dst.isoa_genaddr, final_dst->isoa_genaddr,
556		    (size_t)dst.isoa_len);
557	} else {
558		/*
559		 * setup dst based on src rt specified
560		 */
561		dst.isoa_len = CLNPSRCRT_CLEN(oidx, options);
562		if (sizeof(dst.isoa_genaddr) < (unsigned)dst.isoa_len)
563			return EINVAL;
564		(void)memcpy(dst.isoa_genaddr, CLNPSRCRT_CADDR(oidx, options),
565		    (size_t)dst.isoa_len);
566	}
567
568	/*
569	 *	try to route it
570	 */
571	error = clnp_route(&dst, ro, 0, first_hop, ifa);
572	if (error != 0)
573		return error;
574
575	/*
576	 *	If complete src rt, first hop must be equal to dst
577	 */
578	if ((CLNPSRCRT_TYPE(oidx, options) == CLNPOVAL_COMPRT) &&
579	    (!iso_addrmatch1(&satocsiso(*first_hop)->siso_addr, &dst))) {
580#ifdef ARGO_DEBUG
581		if (argo_debug[D_OPTIONS]) {
582			printf("clnp_srcroute: complete src route failed\n");
583		}
584#endif
585		return EHOSTUNREACH;	/* RAH? would like ESRCRTFAILED */
586	}
587	return error;
588}
589
590/*
591 * FUNCTION:		clnp_echoreply
592 *
593 * PURPOSE:			generate an echo reply packet and transmit
594 *
595 * RETURNS:			result of clnp_output
596 *
597 * SIDE EFFECTS:
598 */
599int
600clnp_echoreply(
601    struct mbuf    *ec_m,		/* echo request */
602    int             ec_len,		/* length of ec */
603    struct sockaddr_iso *ec_src,	/* src of ec */
604    struct sockaddr_iso *ec_dst,	/* destination of ec (i.e., us) */
605    struct clnp_optidx *ec_oidxp) /* options index to ec packet */
606{
607	struct isopcb   isopcb;
608	int             flags = CLNP_NOCACHE | CLNP_ECHOR;
609	int             ret;
610
611	/* fill in fake isopcb to pass to output function */
612	memset(&isopcb, 0, sizeof(isopcb));
613	isopcb.isop_laddr = ec_dst;
614	isopcb.isop_faddr = ec_src;
615
616	/*
617	 * forget copying the options for now. If implemented, need only copy
618	 * record route option, but it must be reset to zero length
619	 */
620
621	ret = clnp_output(ec_m, &isopcb, ec_len, flags);
622
623#ifdef ARGO_DEBUG
624	if (argo_debug[D_OUTPUT]) {
625		printf("clnp_echoreply: output returns %d\n", ret);
626	}
627#endif
628	return ret;
629}
630
631/*
632 * FUNCTION:		clnp_badmtu
633 *
634 * PURPOSE:		print notice of route with mtu not initialized.
635 *
636 * RETURNS:		mtu of ifp.
637 *
638 * SIDE EFFECTS:	prints notice, slows down system.
639 */
640int
641clnp_badmtu(
642	struct ifnet   *ifp,	/* outgoing interface */
643	struct rtentry *rt,	/* dst route */
644	int             line,	/* where the dirty deed occurred */
645	const char     *file)	/* where the dirty deed occurred */
646{
647	printf("sending on route %p with no mtu, line %d of file %s\n",
648	    rt, line, file);
649#ifdef ARGO_DEBUG
650	printf("route dst is ");
651	dump_isoaddr(satocsiso(rt_getkey(rt)));
652#endif
653	return ifp->if_mtu;
654}
655
656/*
657 * FUNCTION:		clnp_ypocb - backwards bcopy
658 *
659 * PURPOSE:		bcopy starting at end of src rather than beginning.
660 *
661 * RETURNS:		none
662 *
663 * SIDE EFFECTS:
664 *
665 * NOTES:		No attempt has been made to make this efficient
666 */
667void
668clnp_ypocb(
669	void *        from,	/* src buffer */
670	void *        to,	/* dst buffer */
671	u_int           len)	/* number of bytes */
672{
673	while (len--)
674		*((char *)to + len) = *((char *)from + len);
675}
676#endif				/* ISO */
677