xref: /csrg-svn/sys/netiso/clnp_output.c (revision 41338)
136372Ssklower /***********************************************************
236372Ssklower 		Copyright IBM Corporation 1987
336372Ssklower 
436372Ssklower                       All Rights Reserved
536372Ssklower 
636372Ssklower Permission to use, copy, modify, and distribute this software and its
736372Ssklower documentation for any purpose and without fee is hereby granted,
836372Ssklower provided that the above copyright notice appear in all copies and that
936372Ssklower both that copyright notice and this permission notice appear in
1036372Ssklower supporting documentation, and that the name of IBM not be
1136372Ssklower used in advertising or publicity pertaining to distribution of the
1236372Ssklower software without specific, written prior permission.
1336372Ssklower 
1436372Ssklower IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1536372Ssklower ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1636372Ssklower IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1736372Ssklower ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1836372Ssklower WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1936372Ssklower ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2036372Ssklower SOFTWARE.
2136372Ssklower 
2236372Ssklower ******************************************************************/
2336372Ssklower 
2436372Ssklower /*
2536372Ssklower  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
2636372Ssklower  */
2736768Ssklower /* $Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $ */
2836768Ssklower /* $Source: /var/src/sys/netiso/RCS/clnp_output.c,v $ */
29*41338Ssklower /*	@(#)clnp_output.c	7.7 (Berkeley) 05/03/90 */
3036372Ssklower 
3136372Ssklower #ifndef lint
3236768Ssklower static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $";
3336372Ssklower #endif lint
3436372Ssklower 
3537469Ssklower #include "param.h"
3637469Ssklower #include "mbuf.h"
3737469Ssklower #include "domain.h"
3837469Ssklower #include "protosw.h"
3937469Ssklower #include "socket.h"
4037469Ssklower #include "socketvar.h"
4137469Ssklower #include "errno.h"
4237469Ssklower #include "time.h"
4336372Ssklower 
4436372Ssklower #include "../net/if.h"
4536372Ssklower #include "../net/route.h"
4636372Ssklower 
4737469Ssklower #include "iso.h"
4837469Ssklower #include "iso_var.h"
4937469Ssklower #include "iso_pcb.h"
5037469Ssklower #include "clnp.h"
5137469Ssklower #include "clnp_stat.h"
5237469Ssklower #include "argo_debug.h"
5336372Ssklower 
5436372Ssklower static struct clnp_fixed dt_template = {
5536372Ssklower 	ISO8473_CLNP,	/* network identifier */
5636372Ssklower 	0,				/* length */
5736372Ssklower 	ISO8473_V1,		/* version */
5836372Ssklower 	CLNP_TTL,		/* ttl */
5937469Ssklower 	CLNP_DT|CNF_SEG_OK|CNF_ERR_OK,		/* type */
6036372Ssklower 	0,				/* segment length */
6136372Ssklower 	0				/* checksum */
6236372Ssklower };
6336372Ssklower 
6436372Ssklower static struct clnp_fixed raw_template = {
6536372Ssklower 	ISO8473_CLNP,	/* network identifier */
6636372Ssklower 	0,				/* length */
6736372Ssklower 	ISO8473_V1,		/* version */
6836372Ssklower 	CLNP_TTL,		/* ttl */
6937469Ssklower 	CLNP_RAW|CNF_SEG_OK|CNF_ERR_OK,		/* type */
7036372Ssklower 	0,				/* segment length */
7136372Ssklower 	0				/* checksum */
7236372Ssklower };
7336372Ssklower 
7436372Ssklower static struct clnp_fixed echo_template = {
7536372Ssklower 	ISO8473_CLNP,	/* network identifier */
7636372Ssklower 	0,				/* length */
7736372Ssklower 	ISO8473_V1,		/* version */
7836372Ssklower 	CLNP_TTL,		/* ttl */
7937469Ssklower 	CLNP_EC|CNF_SEG_OK|CNF_ERR_OK,		/* type */
8036372Ssklower 	0,				/* segment length */
8136372Ssklower 	0				/* checksum */
8236372Ssklower };
8336372Ssklower 
8436768Ssklower #ifdef	DECBIT
8536768Ssklower u_char qos_option[] = {CLNPOVAL_QOS, 1,
8636768Ssklower 	CLNPOVAL_GLOBAL|CLNPOVAL_SEQUENCING|CLNPOVAL_LOWDELAY};
8736768Ssklower #endif	DECBIT
8836768Ssklower 
8936372Ssklower int				clnp_id = 0;		/* id for segmented dgrams */
9036372Ssklower 
9136372Ssklower /*
9236372Ssklower  * FUNCTION:		clnp_output
9336372Ssklower  *
9436372Ssklower  * PURPOSE:			output the data in the mbuf as a clnp datagram
9536372Ssklower  *
9636372Ssklower  *					The data specified by m0 is sent as a clnp datagram.
9736372Ssklower  *					The mbuf chain m0 will be freed when this routine has
9836372Ssklower  *					returned.
9936372Ssklower  *
10036372Ssklower  *					If options is non-null, it points to an mbuf which contains
10136372Ssklower  *					options to be sent with the datagram. The options must
10236372Ssklower  *					be formatted in the mbuf according to clnp rules. Options
10336372Ssklower  *					will not be freed.
10436372Ssklower  *
10536372Ssklower  *					Datalen specifies the length of the data in m0.
10636372Ssklower  *
10736372Ssklower  *					Src and dst are the addresses for the packet.
10836372Ssklower  *
10936372Ssklower  *					If route is non-null, it is used as the route for
11036372Ssklower  *					the packet.
11136372Ssklower  *
11236372Ssklower  *					By default, a DT is sent. However, if flags & CNLP_SEND_ER
11336372Ssklower  *					then an ER will be sent. If flags & CLNP_SEND_RAW, then
11436372Ssklower  *					the packet will be send as raw clnp.
11536372Ssklower  *
11636372Ssklower  * RETURNS:			0	success
11736372Ssklower  *					appropriate error code
11836372Ssklower  *
11936372Ssklower  * SIDE EFFECTS:	none
12036372Ssklower  *
12136372Ssklower  * NOTES:
12236372Ssklower  *					Flags are interpretated as follows:
12336372Ssklower  *						CLNP_NO_SEG - do not allow this pkt to be segmented.
12436372Ssklower  *						CLNP_NO_ER  - have pkt request ER suppression.
12536372Ssklower  *						CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT
12636372Ssklower  *						CLNP_NO_CKSUM - don't compute clnp checksum
12736372Ssklower  *						CLNP_ECHO - send as ECHO packet
12836372Ssklower  *
12936372Ssklower  *					When checking for a cached packet, clnp checks
13036372Ssklower  *					that the route taken is still up. It does not
13136372Ssklower  *					check that the route is still to the same destination.
13236372Ssklower  *					This means that any entity that alters an existing
13336372Ssklower  *					route for an isopcb (such as when a redirect arrives)
13436372Ssklower  *					must invalidate the clnp cache. It might be perferable
13536372Ssklower  *					to have clnp check that the route has the same dest, but
13636372Ssklower  *					by avoiding this check, we save a call to iso_addrmatch1.
13736372Ssklower  */
13838841Ssklower clnp_output(m0, isop, datalen, flags)
13936372Ssklower struct mbuf			*m0;		/* data for the packet */
14036372Ssklower struct isopcb		*isop;		/* iso pcb */
14138841Ssklower int					datalen;	/* number of bytes of data in m0 */
14236372Ssklower int					flags;		/* flags */
14336372Ssklower {
14436372Ssklower 	int							error = 0;		/* return value of function */
14537469Ssklower 	register struct mbuf		*m = m0;		/* mbuf for clnp header chain */
14636372Ssklower 	register struct clnp_fixed	*clnp;			/* ptr to fixed part of hdr */
14736372Ssklower 	register caddr_t			hoff;			/* offset into header */
14836372Ssklower 	int							total_len;		/* total length of packet */
14936372Ssklower 	struct iso_addr				*src;		/* ptr to source address */
15036372Ssklower 	struct iso_addr				*dst;		/* ptr to destination address */
15136372Ssklower 	struct clnp_cache			clc;		/* storage for cache information */
15236372Ssklower 	struct clnp_cache			*clcp = NULL;	/* ptr to clc */
15336768Ssklower 	int							hdrlen = 0;
15436372Ssklower 
15537469Ssklower 	dst = &isop->isop_faddr->siso_addr;
156*41338Ssklower 	if (isop->isop_laddr == 0) {
157*41338Ssklower 		struct iso_ifaddr *ia = 0;
158*41338Ssklower 		clnp_route(dst, &isop->isop_route, flags, 0, &ia);
159*41338Ssklower 		if (ia == 0 || ia->ia_ifa.ifa_addr->sa_family != AF_ISO)
160*41338Ssklower 			return (ENETUNREACH);
161*41338Ssklower 		src = &ia->ia_addr.siso_addr;
162*41338Ssklower 	} else
163*41338Ssklower 		src = &isop->isop_laddr->siso_addr;
16436372Ssklower 
16536372Ssklower 	IFDEBUG(D_OUTPUT)
16636372Ssklower 		printf("clnp_output: to %s", clnp_iso_addrp(dst));
16736372Ssklower 		printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen);
16836372Ssklower 		printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
16936372Ssklower 			isop->isop_options, flags, isop->isop_clnpcache);
17036372Ssklower 	ENDDEBUG
17136372Ssklower 
17236372Ssklower 	if (isop->isop_clnpcache != NULL) {
17336372Ssklower 		clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
17436372Ssklower 	}
17536372Ssklower 
17636372Ssklower 	/*
17736372Ssklower 	 *	Check if cache is valid ...
17836372Ssklower 	 */
17936372Ssklower 	IFDEBUG(D_OUTPUT)
18036372Ssklower 		printf("clnp_output: ck cache: clcp %x\n", clcp);
18136372Ssklower 		if (clcp != NULL) {
18236372Ssklower 			printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst));
18336372Ssklower 			printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options,
18436372Ssklower 				clcp->clc_options);
18536372Ssklower 			if (isop->isop_route.ro_rt)
18636372Ssklower 				printf("\tro_rt x%x, rt_flags x%x\n",
18736372Ssklower 					isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags);
18836372Ssklower 			printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags);
18936372Ssklower 			printf("\tclc_hdr x%x\n", clcp->clc_hdr);
19036372Ssklower 		}
19136372Ssklower 	ENDDEBUG
19236372Ssklower 	if ((clcp != NULL) &&								/* cache exists */
19336372Ssklower 		(isop->isop_options == clcp->clc_options) && 	/* same options */
19436372Ssklower 		(iso_addrmatch1(dst, &clcp->clc_dst)) &&		/* dst still same */
19536372Ssklower 		(isop->isop_route.ro_rt != NULL) &&				/* route exists */
19640777Ssklower 		(isop->isop_route.ro_rt == clcp->clc_rt) &&		/* and is cached */
19736372Ssklower 		(isop->isop_route.ro_rt->rt_flags & RTF_UP) &&	/* route still up */
19836372Ssklower 		(flags == clcp->clc_flags) &&					/* same flags */
19936372Ssklower 		(clcp->clc_hdr != NULL)) {						/* hdr mbuf exists */
20036372Ssklower 		/*
20136372Ssklower 		 *	The cache is valid
20236372Ssklower 		 */
20336372Ssklower 
20436372Ssklower 		IFDEBUG(D_OUTPUT)
20536372Ssklower 			printf("clnp_output: using cache\n");
20636372Ssklower 		ENDDEBUG
20736372Ssklower 
20837469Ssklower 		m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
20936372Ssklower 		if (m == NULL) {
21036372Ssklower 			/*
21136372Ssklower 			 *	No buffers left to copy cached packet header. Use
21236372Ssklower 			 *	the cached packet header this time, and
21336372Ssklower 			 *	mark the hdr as vacant
21436372Ssklower 			 */
21536372Ssklower 			m = clcp->clc_hdr;
21636372Ssklower 			clcp->clc_hdr = NULL;
21736372Ssklower 		}
21836372Ssklower 		m->m_next = m0;	/* ASSUMES pkt hdr is 1 mbuf long */
21936372Ssklower 		clnp = mtod(m, struct clnp_fixed *);
22036372Ssklower 	} else {
22136372Ssklower 		struct clnp_optidx	*oidx = NULL;		/* index to clnp options */
22236372Ssklower 
22336372Ssklower 		/*
22436372Ssklower 		 *	The cache is not valid. Allocate an mbuf (if necessary)
22536372Ssklower 		 *	to hold cached info. If one is not available, then
22636372Ssklower 		 *	don't bother with the cache
22736372Ssklower 		 */
22836372Ssklower 		INCSTAT(cns_cachemiss);
22936372Ssklower 		if (flags & CLNP_NOCACHE) {
23036372Ssklower 			clcp = &clc;
23136372Ssklower 		} else {
23236372Ssklower 			if (isop->isop_clnpcache == NULL) {
23336372Ssklower 				/*
23436372Ssklower 				 *	There is no clnpcache. Allocate an mbuf to hold one
23536372Ssklower 				 */
23636372Ssklower 				if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
23736372Ssklower 					== NULL) {
23836372Ssklower 					/*
23936372Ssklower 					 *	No mbufs available. Pretend that we don't want
24036372Ssklower 					 *	caching this time.
24136372Ssklower 					 */
24236372Ssklower 					IFDEBUG(D_OUTPUT)
24336372Ssklower 						printf("clnp_output: no mbufs to allocate to cache\n");
24436372Ssklower 					ENDDEBUG
24536372Ssklower 					flags  |= CLNP_NOCACHE;
24636372Ssklower 					clcp = &clc;
24736372Ssklower 				} else {
24836372Ssklower 					clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
24936372Ssklower 				}
25036372Ssklower 			} else {
25136372Ssklower 				/*
25236372Ssklower 				 *	A clnpcache mbuf exists. If the clc_hdr is not null,
25336372Ssklower 				 *	we must free it, as a new one is about to be created.
25436372Ssklower 				 */
25536372Ssklower 				clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
25636372Ssklower 				if (clcp->clc_hdr != NULL) {
25736372Ssklower 					/*
25836372Ssklower 					 *	The clc_hdr is not null but a clnpcache mbuf exists.
25936372Ssklower 					 *	This means that there was a cache, but the existing
26036372Ssklower 					 *	copy of the hdr is no longer valid. Free it now
26136372Ssklower 					 *	before we lose the pointer to it.
26236372Ssklower 					 */
26336372Ssklower 					IFDEBUG(D_OUTPUT)
26436372Ssklower 						printf("clnp_output: freeing old clc_hdr 0x%x\n",
26536372Ssklower 						clcp->clc_hdr);
26636372Ssklower 					ENDDEBUG
26736372Ssklower 					m_free(clcp->clc_hdr);
26836372Ssklower 					IFDEBUG(D_OUTPUT)
26936372Ssklower 						printf("clnp_output: freed old clc_hdr (done)\n");
27036372Ssklower 					ENDDEBUG
27136372Ssklower 				}
27236372Ssklower 			}
27336372Ssklower 		}
27436372Ssklower 		IFDEBUG(D_OUTPUT)
27536372Ssklower 			printf("clnp_output: NEW clcp x%x\n",clcp);
27636372Ssklower 		ENDDEBUG
27736372Ssklower 		bzero((caddr_t)clcp, sizeof(struct clnp_cache));
27836372Ssklower 
27936372Ssklower 		if (isop->isop_optindex)
28036372Ssklower 			oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
28136372Ssklower 
28236372Ssklower 		/*
28336372Ssklower 		 *	Don't allow packets with security, quality of service,
28436372Ssklower 		 *	priority, or error report options to be sent.
28536372Ssklower 		 */
28636372Ssklower 		if ((isop->isop_options) && (oidx)) {
28736372Ssklower 			if ((oidx->cni_securep) ||
28836372Ssklower 				(oidx->cni_priorp) ||
28936372Ssklower 				(oidx->cni_qos_formatp) ||
29036372Ssklower 				(oidx->cni_er_reason != ER_INVALREAS)) {
29136372Ssklower 				IFDEBUG(D_OUTPUT)
29236372Ssklower 					printf("clnp_output: pkt dropped - option unsupported\n");
29336372Ssklower 				ENDDEBUG
29436372Ssklower 				m_freem(m0);
29536372Ssklower 				return(EINVAL);
29636372Ssklower 			}
29736372Ssklower 		}
29836372Ssklower 
29936372Ssklower 		/*
30036372Ssklower 		 *	Don't allow any invalid flags to be set
30136372Ssklower 		 */
30236372Ssklower 		if ((flags & (CLNP_VFLAGS)) != flags) {
30336372Ssklower 			IFDEBUG(D_OUTPUT)
30436372Ssklower 				printf("clnp_output: packet dropped - flags unsupported\n");
30536372Ssklower 			ENDDEBUG
30639195Ssklower 			INCSTAT(cns_odropped);
30736372Ssklower 			m_freem(m0);
30836372Ssklower 			return(EINVAL);
30936372Ssklower 		}
31036372Ssklower 
31136372Ssklower 		/*
31236372Ssklower 		 *	Don't allow funny lengths on dst; src may be zero in which
31336372Ssklower 		 *	case we insert the source address based upon the interface
31436372Ssklower 		 */
31536372Ssklower 		if ((src->isoa_len > sizeof(struct iso_addr)) ||
31636372Ssklower 			(dst->isoa_len == 0) ||
31736372Ssklower 			(dst->isoa_len > sizeof(struct iso_addr))) {
31836372Ssklower 			m_freem(m0);
31939195Ssklower 			INCSTAT(cns_odropped);
32036372Ssklower 			return(ENAMETOOLONG);
32136372Ssklower 		}
32236372Ssklower 
32336372Ssklower 		/*
32436372Ssklower 		 *	Grab mbuf to contain header
32536372Ssklower 		 */
32637469Ssklower 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
32736372Ssklower 		if (m == 0) {
32836372Ssklower 			m_freem(m0);
32939195Ssklower 			INCSTAT(cns_odropped);
33036372Ssklower 			return(ENOBUFS);
33136372Ssklower 		}
33239195Ssklower 		INCSTAT(cns_sent);
33336372Ssklower 		m->m_next = m0;
33436372Ssklower 		clnp = mtod(m, struct clnp_fixed *);
33536372Ssklower 		clcp->clc_segoff = 0;
33636372Ssklower 
33736372Ssklower 		/*
33836372Ssklower 		 *	Fill in all of fixed hdr except lengths and checksum
33936372Ssklower 		 */
34036372Ssklower 		if (flags & CLNP_SEND_RAW) {
34136372Ssklower 			*clnp = raw_template;
34236372Ssklower 		} else if (flags & CLNP_ECHO) {
34336372Ssklower 			*clnp = echo_template;
34436372Ssklower 		} else {
34536372Ssklower 			*clnp = dt_template;
34636372Ssklower 		}
34736372Ssklower 		if (flags & CLNP_NO_SEG)
34837469Ssklower 			clnp->cnf_type &= ~CNF_SEG_OK;
34936372Ssklower 		if (flags & CLNP_NO_ER)
35037469Ssklower 			clnp->cnf_type &= ~CNF_ERR_OK;
35136372Ssklower 
35236372Ssklower 		/*
35336372Ssklower 		 *	Route packet; special case for source rt
35436372Ssklower 		 */
35536372Ssklower 		if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
35636372Ssklower 			IFDEBUG(D_OUTPUT)
35736372Ssklower 				printf("clnp_output: calling clnp_srcroute\n");
35836372Ssklower 			ENDDEBUG
35936372Ssklower 			error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
36037469Ssklower 				&clcp->clc_firsthop, &clcp->clc_ifa, dst);
36136372Ssklower 		} else {
36236372Ssklower 			IFDEBUG(D_OUTPUT)
36336372Ssklower 			ENDDEBUG
36436372Ssklower 			error = clnp_route(dst, &isop->isop_route, flags,
36537469Ssklower 				&clcp->clc_firsthop, &clcp->clc_ifa);
36636372Ssklower 		}
36737469Ssklower 		if (error || (clcp->clc_ifa == 0)) {
36836372Ssklower 			IFDEBUG(D_OUTPUT)
36936372Ssklower 				printf("clnp_output: route failed, errno %d\n", error);
37036372Ssklower 				printf("@clcp:\n");
37136372Ssklower 				dump_buf(clcp, sizeof (struct clnp_cache));
37236372Ssklower 			ENDDEBUG
37336372Ssklower 			goto bad;
37436372Ssklower 		}
37540777Ssklower 		clcp->clc_rt = isop->isop_route.ro_rt;	/* XXX */
37636372Ssklower 
37736372Ssklower 		IFDEBUG(D_OUTPUT)
37836372Ssklower 			printf("clnp_output: packet routed to %s\n",
37936372Ssklower 				clnp_iso_addrp(
38036372Ssklower 					&((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
38136372Ssklower 		ENDDEBUG
38236372Ssklower 
38336372Ssklower 		/*
38436372Ssklower 		 *	If src address is not yet specified, use address of
38536372Ssklower 		 *	interface. NOTE: this will now update the laddr field in
38636372Ssklower 		 *	the isopcb. Is this desirable? RAH?
38736372Ssklower 		 */
38836372Ssklower 		if (src->isoa_len == 0) {
38937469Ssklower 			src = &(clcp->clc_ifa->ia_addr.siso_addr);
39036372Ssklower 			IFDEBUG(D_OUTPUT)
39136372Ssklower 				printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
39236372Ssklower 			ENDDEBUG
39336372Ssklower 		}
39436372Ssklower 
39536372Ssklower 		/*
39636372Ssklower 		 *	Insert the source and destination address,
39736372Ssklower 		 */
39836372Ssklower 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
39937469Ssklower 		CLNP_INSERT_ADDR(hoff, *dst);
40037469Ssklower 		CLNP_INSERT_ADDR(hoff, *src);
40136372Ssklower 
40236372Ssklower 		/*
40336372Ssklower 		 *	Leave room for the segment part, if segmenting is selected
40436372Ssklower 		 */
40537469Ssklower 		if (clnp->cnf_type & CNF_SEG_OK) {
40636372Ssklower 			clcp->clc_segoff = hoff - (caddr_t)clnp;
40736372Ssklower 			hoff += sizeof(struct clnp_segment);
40836372Ssklower 		}
40936372Ssklower 
41036372Ssklower 		clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
41136768Ssklower 		hdrlen = clnp->cnf_hdr_len;
41236372Ssklower 
41336768Ssklower #ifdef	DECBIT
41436372Ssklower 		/*
41536768Ssklower 		 *	Add the globally unique QOS (with room for congestion experienced
41636768Ssklower 		 *	bit). I can safely assume that this option is not in the options
41736768Ssklower 		 *	mbuf below because I checked that the option was not specified
41836768Ssklower 		 *	previously
41936768Ssklower 		 */
42036768Ssklower 		if ((m->m_len + sizeof(qos_option)) < MLEN) {
42136768Ssklower 			bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
42236768Ssklower 			clnp->cnf_hdr_len += sizeof(qos_option);
42336768Ssklower 			hdrlen += sizeof(qos_option);
42436768Ssklower 			m->m_len += sizeof(qos_option);
42536768Ssklower 		}
42636768Ssklower #endif	DECBIT
42736768Ssklower 
42836768Ssklower 		/*
42936372Ssklower 		 *	If an options mbuf is present, concatenate a copy to the hdr mbuf.
43036372Ssklower 		 */
43136372Ssklower 		if (isop->isop_options) {
43237469Ssklower 			struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
43336372Ssklower 			if (opt_copy == NULL) {
43436372Ssklower 				error = ENOBUFS;
43536372Ssklower 				goto bad;
43636372Ssklower 			}
43736372Ssklower 			/* Link in place */
43836372Ssklower 			opt_copy->m_next = m->m_next;
43936372Ssklower 			m->m_next = opt_copy;
44036372Ssklower 
44136372Ssklower 			/* update size of header */
44236372Ssklower 			clnp->cnf_hdr_len += opt_copy->m_len;
44336768Ssklower 			hdrlen += opt_copy->m_len;
44436372Ssklower 		}
44536372Ssklower 
44636768Ssklower 		if (hdrlen > CLNP_HDR_MAX) {
44736768Ssklower 			error = EMSGSIZE;
44836768Ssklower 			goto bad;
44936768Ssklower 		}
45036768Ssklower 
45136372Ssklower 		/*
45236372Ssklower 		 *	Now set up the cache entry in the pcb
45336372Ssklower 		 */
45436372Ssklower 		if ((flags & CLNP_NOCACHE) == 0) {
45537469Ssklower 			if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
45637469Ssklower 				clcp->clc_dst  = *dst;
45736372Ssklower 				clcp->clc_flags = flags;
45836372Ssklower 				clcp->clc_options = isop->isop_options;
45936372Ssklower 			}
46036372Ssklower 		}
46136372Ssklower 	}
46236372Ssklower 	/*
46336372Ssklower 	 *	If small enough for interface, send directly
46436372Ssklower 	 *	Fill in segmentation part of hdr if using the full protocol
46536372Ssklower 	 */
46637469Ssklower 	if ((total_len = clnp->cnf_hdr_len + datalen)
46737469Ssklower 			<= SN_MTU(clcp->clc_ifa->ia_ifp)) {
46837469Ssklower 		if (clnp->cnf_type & CNF_SEG_OK) {
46936372Ssklower 			struct clnp_segment	seg_part;		/* segment part of hdr */
47036372Ssklower 			seg_part.cng_id = htons(clnp_id++);
47136372Ssklower 			seg_part.cng_off = htons(0);
47236372Ssklower 			seg_part.cng_tot_len = htons(total_len);
47336372Ssklower 			(void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
47436372Ssklower 				sizeof(seg_part));
47536372Ssklower 		}
47636372Ssklower 		HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
47737469Ssklower 		m->m_pkthdr.len = total_len;
47836372Ssklower 		/*
47936372Ssklower 		 *	Compute clnp checksum (on header only)
48036372Ssklower 		 */
48136372Ssklower 		if (flags & CLNP_NO_CKSUM) {
48236372Ssklower 			HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
48336372Ssklower 		} else {
48436372Ssklower 			iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
48536372Ssklower 		}
48636372Ssklower 
48736372Ssklower 		IFDEBUG(D_DUMPOUT)
48836372Ssklower 			struct mbuf *mdump = m;
48936372Ssklower 			printf("clnp_output: sending dg:\n");
49036372Ssklower 			while (mdump != NULL) {
49136372Ssklower 				dump_buf(mtod(mdump, caddr_t), mdump->m_len);
49236372Ssklower 				mdump = mdump->m_next;
49336372Ssklower 			}
49436372Ssklower 		ENDDEBUG
49536372Ssklower 
49636372Ssklower 		error = SN_OUTPUT(clcp, m);
49736372Ssklower 		goto done;
49836372Ssklower 	} else {
49936372Ssklower 		/*
50036372Ssklower 		 * Too large for interface; fragment if possible.
50136372Ssklower 		 */
50239195Ssklower 		error = clnp_fragment(clcp->clc_ifa->ia_ifp, m, clcp->clc_firsthop,
50340777Ssklower 							total_len, clcp->clc_segoff, flags, clcp->clc_rt);
50436372Ssklower 		goto done;
50536372Ssklower 	}
50636372Ssklower bad:
50736372Ssklower 	m_freem(m);
50836372Ssklower done:
50939195Ssklower 	if (error) {
51039195Ssklower 		clnp_stat.cns_sent--;
51139195Ssklower 		clnp_stat.cns_odropped++;
51239195Ssklower 	}
51339195Ssklower 	return (error);
51436372Ssklower }
51536372Ssklower 
51636372Ssklower int clnp_ctloutput()
51736372Ssklower {
51836372Ssklower }
519