xref: /csrg-svn/sys/netiso/clnp_output.c (revision 49267)
1*49267Sbostic /*-
2*49267Sbostic  * Copyright (c) 1991 The Regents of the University of California.
3*49267Sbostic  * All rights reserved.
4*49267Sbostic  *
5*49267Sbostic  * %sccs.include.redist.c%
6*49267Sbostic  *
7*49267Sbostic  *	@(#)clnp_output.c	7.10 (Berkeley) 05/06/91
8*49267Sbostic  */
9*49267Sbostic 
1036372Ssklower /***********************************************************
1136372Ssklower 		Copyright IBM Corporation 1987
1236372Ssklower 
1336372Ssklower                       All Rights Reserved
1436372Ssklower 
1536372Ssklower Permission to use, copy, modify, and distribute this software and its
1636372Ssklower documentation for any purpose and without fee is hereby granted,
1736372Ssklower provided that the above copyright notice appear in all copies and that
1836372Ssklower both that copyright notice and this permission notice appear in
1936372Ssklower supporting documentation, and that the name of IBM not be
2036372Ssklower used in advertising or publicity pertaining to distribution of the
2136372Ssklower software without specific, written prior permission.
2236372Ssklower 
2336372Ssklower IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2436372Ssklower ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2536372Ssklower IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2636372Ssklower ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2736372Ssklower WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2836372Ssklower ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2936372Ssklower SOFTWARE.
3036372Ssklower 
3136372Ssklower ******************************************************************/
3236372Ssklower 
3336372Ssklower /*
3436372Ssklower  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
3536372Ssklower  */
3636768Ssklower /* $Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $ */
3736768Ssklower /* $Source: /var/src/sys/netiso/RCS/clnp_output.c,v $ */
3836372Ssklower 
3937469Ssklower #include "param.h"
4037469Ssklower #include "mbuf.h"
4137469Ssklower #include "domain.h"
4237469Ssklower #include "protosw.h"
4337469Ssklower #include "socket.h"
4437469Ssklower #include "socketvar.h"
4537469Ssklower #include "errno.h"
4637469Ssklower #include "time.h"
4736372Ssklower 
4836372Ssklower #include "../net/if.h"
4936372Ssklower #include "../net/route.h"
5036372Ssklower 
5137469Ssklower #include "iso.h"
5237469Ssklower #include "iso_var.h"
5337469Ssklower #include "iso_pcb.h"
5437469Ssklower #include "clnp.h"
5537469Ssklower #include "clnp_stat.h"
5637469Ssklower #include "argo_debug.h"
5736372Ssklower 
5836372Ssklower static struct clnp_fixed dt_template = {
5936372Ssklower 	ISO8473_CLNP,	/* network identifier */
6036372Ssklower 	0,				/* length */
6136372Ssklower 	ISO8473_V1,		/* version */
6236372Ssklower 	CLNP_TTL,		/* ttl */
6337469Ssklower 	CLNP_DT|CNF_SEG_OK|CNF_ERR_OK,		/* type */
6436372Ssklower 	0,				/* segment length */
6536372Ssklower 	0				/* checksum */
6636372Ssklower };
6736372Ssklower 
6836372Ssklower static struct clnp_fixed raw_template = {
6936372Ssklower 	ISO8473_CLNP,	/* network identifier */
7036372Ssklower 	0,				/* length */
7136372Ssklower 	ISO8473_V1,		/* version */
7236372Ssklower 	CLNP_TTL,		/* ttl */
7337469Ssklower 	CLNP_RAW|CNF_SEG_OK|CNF_ERR_OK,		/* type */
7436372Ssklower 	0,				/* segment length */
7536372Ssklower 	0				/* checksum */
7636372Ssklower };
7736372Ssklower 
7836372Ssklower static struct clnp_fixed echo_template = {
7936372Ssklower 	ISO8473_CLNP,	/* network identifier */
8036372Ssklower 	0,				/* length */
8136372Ssklower 	ISO8473_V1,		/* version */
8236372Ssklower 	CLNP_TTL,		/* ttl */
8337469Ssklower 	CLNP_EC|CNF_SEG_OK|CNF_ERR_OK,		/* type */
8436372Ssklower 	0,				/* segment length */
8536372Ssklower 	0				/* checksum */
8636372Ssklower };
8736372Ssklower 
8836768Ssklower #ifdef	DECBIT
8936768Ssklower u_char qos_option[] = {CLNPOVAL_QOS, 1,
9036768Ssklower 	CLNPOVAL_GLOBAL|CLNPOVAL_SEQUENCING|CLNPOVAL_LOWDELAY};
9136768Ssklower #endif	DECBIT
9236768Ssklower 
9336372Ssklower int				clnp_id = 0;		/* id for segmented dgrams */
9436372Ssklower 
9536372Ssklower /*
9636372Ssklower  * FUNCTION:		clnp_output
9736372Ssklower  *
9836372Ssklower  * PURPOSE:			output the data in the mbuf as a clnp datagram
9936372Ssklower  *
10036372Ssklower  *					The data specified by m0 is sent as a clnp datagram.
10136372Ssklower  *					The mbuf chain m0 will be freed when this routine has
10236372Ssklower  *					returned.
10336372Ssklower  *
10436372Ssklower  *					If options is non-null, it points to an mbuf which contains
10536372Ssklower  *					options to be sent with the datagram. The options must
10636372Ssklower  *					be formatted in the mbuf according to clnp rules. Options
10736372Ssklower  *					will not be freed.
10836372Ssklower  *
10936372Ssklower  *					Datalen specifies the length of the data in m0.
11036372Ssklower  *
11136372Ssklower  *					Src and dst are the addresses for the packet.
11236372Ssklower  *
11336372Ssklower  *					If route is non-null, it is used as the route for
11436372Ssklower  *					the packet.
11536372Ssklower  *
11636372Ssklower  *					By default, a DT is sent. However, if flags & CNLP_SEND_ER
11736372Ssklower  *					then an ER will be sent. If flags & CLNP_SEND_RAW, then
11836372Ssklower  *					the packet will be send as raw clnp.
11936372Ssklower  *
12036372Ssklower  * RETURNS:			0	success
12136372Ssklower  *					appropriate error code
12236372Ssklower  *
12336372Ssklower  * SIDE EFFECTS:	none
12436372Ssklower  *
12536372Ssklower  * NOTES:
12636372Ssklower  *					Flags are interpretated as follows:
12736372Ssklower  *						CLNP_NO_SEG - do not allow this pkt to be segmented.
12836372Ssklower  *						CLNP_NO_ER  - have pkt request ER suppression.
12936372Ssklower  *						CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT
13036372Ssklower  *						CLNP_NO_CKSUM - don't compute clnp checksum
13136372Ssklower  *						CLNP_ECHO - send as ECHO packet
13236372Ssklower  *
13336372Ssklower  *					When checking for a cached packet, clnp checks
13436372Ssklower  *					that the route taken is still up. It does not
13536372Ssklower  *					check that the route is still to the same destination.
13636372Ssklower  *					This means that any entity that alters an existing
13736372Ssklower  *					route for an isopcb (such as when a redirect arrives)
13836372Ssklower  *					must invalidate the clnp cache. It might be perferable
13936372Ssklower  *					to have clnp check that the route has the same dest, but
14036372Ssklower  *					by avoiding this check, we save a call to iso_addrmatch1.
14136372Ssklower  */
14238841Ssklower clnp_output(m0, isop, datalen, flags)
14336372Ssklower struct mbuf			*m0;		/* data for the packet */
14436372Ssklower struct isopcb		*isop;		/* iso pcb */
14538841Ssklower int					datalen;	/* number of bytes of data in m0 */
14636372Ssklower int					flags;		/* flags */
14736372Ssklower {
14836372Ssklower 	int							error = 0;		/* return value of function */
14937469Ssklower 	register struct mbuf		*m = m0;		/* mbuf for clnp header chain */
15036372Ssklower 	register struct clnp_fixed	*clnp;			/* ptr to fixed part of hdr */
15136372Ssklower 	register caddr_t			hoff;			/* offset into header */
15236372Ssklower 	int							total_len;		/* total length of packet */
15336372Ssklower 	struct iso_addr				*src;		/* ptr to source address */
15436372Ssklower 	struct iso_addr				*dst;		/* ptr to destination address */
15536372Ssklower 	struct clnp_cache			clc;		/* storage for cache information */
15636372Ssklower 	struct clnp_cache			*clcp = NULL;	/* ptr to clc */
15736768Ssklower 	int							hdrlen = 0;
15836372Ssklower 
15937469Ssklower 	dst = &isop->isop_faddr->siso_addr;
16041338Ssklower 	if (isop->isop_laddr == 0) {
16141338Ssklower 		struct iso_ifaddr *ia = 0;
16241338Ssklower 		clnp_route(dst, &isop->isop_route, flags, 0, &ia);
16341338Ssklower 		if (ia == 0 || ia->ia_ifa.ifa_addr->sa_family != AF_ISO)
16441338Ssklower 			return (ENETUNREACH);
16541338Ssklower 		src = &ia->ia_addr.siso_addr;
16641338Ssklower 	} else
16741338Ssklower 		src = &isop->isop_laddr->siso_addr;
16836372Ssklower 
16936372Ssklower 	IFDEBUG(D_OUTPUT)
17036372Ssklower 		printf("clnp_output: to %s", clnp_iso_addrp(dst));
17136372Ssklower 		printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen);
17236372Ssklower 		printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
17336372Ssklower 			isop->isop_options, flags, isop->isop_clnpcache);
17436372Ssklower 	ENDDEBUG
17536372Ssklower 
17636372Ssklower 	if (isop->isop_clnpcache != NULL) {
17736372Ssklower 		clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
17836372Ssklower 	}
17936372Ssklower 
18036372Ssklower 	/*
18136372Ssklower 	 *	Check if cache is valid ...
18236372Ssklower 	 */
18336372Ssklower 	IFDEBUG(D_OUTPUT)
18436372Ssklower 		printf("clnp_output: ck cache: clcp %x\n", clcp);
18536372Ssklower 		if (clcp != NULL) {
18636372Ssklower 			printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst));
18736372Ssklower 			printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options,
18836372Ssklower 				clcp->clc_options);
18936372Ssklower 			if (isop->isop_route.ro_rt)
19036372Ssklower 				printf("\tro_rt x%x, rt_flags x%x\n",
19136372Ssklower 					isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags);
19236372Ssklower 			printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags);
19336372Ssklower 			printf("\tclc_hdr x%x\n", clcp->clc_hdr);
19436372Ssklower 		}
19536372Ssklower 	ENDDEBUG
19636372Ssklower 	if ((clcp != NULL) &&								/* cache exists */
19736372Ssklower 		(isop->isop_options == clcp->clc_options) && 	/* same options */
19836372Ssklower 		(iso_addrmatch1(dst, &clcp->clc_dst)) &&		/* dst still same */
19936372Ssklower 		(isop->isop_route.ro_rt != NULL) &&				/* route exists */
20040777Ssklower 		(isop->isop_route.ro_rt == clcp->clc_rt) &&		/* and is cached */
20136372Ssklower 		(isop->isop_route.ro_rt->rt_flags & RTF_UP) &&	/* route still up */
20236372Ssklower 		(flags == clcp->clc_flags) &&					/* same flags */
20336372Ssklower 		(clcp->clc_hdr != NULL)) {						/* hdr mbuf exists */
20436372Ssklower 		/*
20536372Ssklower 		 *	The cache is valid
20636372Ssklower 		 */
20736372Ssklower 
20836372Ssklower 		IFDEBUG(D_OUTPUT)
20936372Ssklower 			printf("clnp_output: using cache\n");
21036372Ssklower 		ENDDEBUG
21136372Ssklower 
21237469Ssklower 		m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
21336372Ssklower 		if (m == NULL) {
21436372Ssklower 			/*
21536372Ssklower 			 *	No buffers left to copy cached packet header. Use
21636372Ssklower 			 *	the cached packet header this time, and
21736372Ssklower 			 *	mark the hdr as vacant
21836372Ssklower 			 */
21936372Ssklower 			m = clcp->clc_hdr;
22036372Ssklower 			clcp->clc_hdr = NULL;
22136372Ssklower 		}
22236372Ssklower 		m->m_next = m0;	/* ASSUMES pkt hdr is 1 mbuf long */
22336372Ssklower 		clnp = mtod(m, struct clnp_fixed *);
22436372Ssklower 	} else {
22536372Ssklower 		struct clnp_optidx	*oidx = NULL;		/* index to clnp options */
22636372Ssklower 
22736372Ssklower 		/*
22836372Ssklower 		 *	The cache is not valid. Allocate an mbuf (if necessary)
22936372Ssklower 		 *	to hold cached info. If one is not available, then
23036372Ssklower 		 *	don't bother with the cache
23136372Ssklower 		 */
23236372Ssklower 		INCSTAT(cns_cachemiss);
23336372Ssklower 		if (flags & CLNP_NOCACHE) {
23436372Ssklower 			clcp = &clc;
23536372Ssklower 		} else {
23636372Ssklower 			if (isop->isop_clnpcache == NULL) {
23736372Ssklower 				/*
23836372Ssklower 				 *	There is no clnpcache. Allocate an mbuf to hold one
23936372Ssklower 				 */
24036372Ssklower 				if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
24136372Ssklower 					== NULL) {
24236372Ssklower 					/*
24336372Ssklower 					 *	No mbufs available. Pretend that we don't want
24436372Ssklower 					 *	caching this time.
24536372Ssklower 					 */
24636372Ssklower 					IFDEBUG(D_OUTPUT)
24736372Ssklower 						printf("clnp_output: no mbufs to allocate to cache\n");
24836372Ssklower 					ENDDEBUG
24936372Ssklower 					flags  |= CLNP_NOCACHE;
25036372Ssklower 					clcp = &clc;
25136372Ssklower 				} else {
25236372Ssklower 					clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
25336372Ssklower 				}
25436372Ssklower 			} else {
25536372Ssklower 				/*
25636372Ssklower 				 *	A clnpcache mbuf exists. If the clc_hdr is not null,
25736372Ssklower 				 *	we must free it, as a new one is about to be created.
25836372Ssklower 				 */
25936372Ssklower 				clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
26036372Ssklower 				if (clcp->clc_hdr != NULL) {
26136372Ssklower 					/*
26236372Ssklower 					 *	The clc_hdr is not null but a clnpcache mbuf exists.
26336372Ssklower 					 *	This means that there was a cache, but the existing
26436372Ssklower 					 *	copy of the hdr is no longer valid. Free it now
26536372Ssklower 					 *	before we lose the pointer to it.
26636372Ssklower 					 */
26736372Ssklower 					IFDEBUG(D_OUTPUT)
26836372Ssklower 						printf("clnp_output: freeing old clc_hdr 0x%x\n",
26936372Ssklower 						clcp->clc_hdr);
27036372Ssklower 					ENDDEBUG
27136372Ssklower 					m_free(clcp->clc_hdr);
27236372Ssklower 					IFDEBUG(D_OUTPUT)
27336372Ssklower 						printf("clnp_output: freed old clc_hdr (done)\n");
27436372Ssklower 					ENDDEBUG
27536372Ssklower 				}
27636372Ssklower 			}
27736372Ssklower 		}
27836372Ssklower 		IFDEBUG(D_OUTPUT)
27936372Ssklower 			printf("clnp_output: NEW clcp x%x\n",clcp);
28036372Ssklower 		ENDDEBUG
28136372Ssklower 		bzero((caddr_t)clcp, sizeof(struct clnp_cache));
28236372Ssklower 
28336372Ssklower 		if (isop->isop_optindex)
28436372Ssklower 			oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
28536372Ssklower 
28636372Ssklower 		/*
28736372Ssklower 		 *	Don't allow packets with security, quality of service,
28836372Ssklower 		 *	priority, or error report options to be sent.
28936372Ssklower 		 */
29036372Ssklower 		if ((isop->isop_options) && (oidx)) {
29136372Ssklower 			if ((oidx->cni_securep) ||
29236372Ssklower 				(oidx->cni_priorp) ||
29336372Ssklower 				(oidx->cni_qos_formatp) ||
29436372Ssklower 				(oidx->cni_er_reason != ER_INVALREAS)) {
29536372Ssklower 				IFDEBUG(D_OUTPUT)
29636372Ssklower 					printf("clnp_output: pkt dropped - option unsupported\n");
29736372Ssklower 				ENDDEBUG
29836372Ssklower 				m_freem(m0);
29936372Ssklower 				return(EINVAL);
30036372Ssklower 			}
30136372Ssklower 		}
30236372Ssklower 
30336372Ssklower 		/*
30436372Ssklower 		 *	Don't allow any invalid flags to be set
30536372Ssklower 		 */
30636372Ssklower 		if ((flags & (CLNP_VFLAGS)) != flags) {
30736372Ssklower 			IFDEBUG(D_OUTPUT)
30836372Ssklower 				printf("clnp_output: packet dropped - flags unsupported\n");
30936372Ssklower 			ENDDEBUG
31039195Ssklower 			INCSTAT(cns_odropped);
31136372Ssklower 			m_freem(m0);
31236372Ssklower 			return(EINVAL);
31336372Ssklower 		}
31436372Ssklower 
31536372Ssklower 		/*
31636372Ssklower 		 *	Don't allow funny lengths on dst; src may be zero in which
31736372Ssklower 		 *	case we insert the source address based upon the interface
31836372Ssklower 		 */
31936372Ssklower 		if ((src->isoa_len > sizeof(struct iso_addr)) ||
32036372Ssklower 			(dst->isoa_len == 0) ||
32136372Ssklower 			(dst->isoa_len > sizeof(struct iso_addr))) {
32236372Ssklower 			m_freem(m0);
32339195Ssklower 			INCSTAT(cns_odropped);
32436372Ssklower 			return(ENAMETOOLONG);
32536372Ssklower 		}
32636372Ssklower 
32736372Ssklower 		/*
32836372Ssklower 		 *	Grab mbuf to contain header
32936372Ssklower 		 */
33037469Ssklower 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
33136372Ssklower 		if (m == 0) {
33236372Ssklower 			m_freem(m0);
33339195Ssklower 			INCSTAT(cns_odropped);
33436372Ssklower 			return(ENOBUFS);
33536372Ssklower 		}
33639195Ssklower 		INCSTAT(cns_sent);
33736372Ssklower 		m->m_next = m0;
33836372Ssklower 		clnp = mtod(m, struct clnp_fixed *);
33936372Ssklower 		clcp->clc_segoff = 0;
34036372Ssklower 
34136372Ssklower 		/*
34236372Ssklower 		 *	Fill in all of fixed hdr except lengths and checksum
34336372Ssklower 		 */
34436372Ssklower 		if (flags & CLNP_SEND_RAW) {
34536372Ssklower 			*clnp = raw_template;
34636372Ssklower 		} else if (flags & CLNP_ECHO) {
34736372Ssklower 			*clnp = echo_template;
34836372Ssklower 		} else {
34936372Ssklower 			*clnp = dt_template;
35036372Ssklower 		}
35136372Ssklower 		if (flags & CLNP_NO_SEG)
35237469Ssklower 			clnp->cnf_type &= ~CNF_SEG_OK;
35336372Ssklower 		if (flags & CLNP_NO_ER)
35437469Ssklower 			clnp->cnf_type &= ~CNF_ERR_OK;
35536372Ssklower 
35636372Ssklower 		/*
35736372Ssklower 		 *	Route packet; special case for source rt
35836372Ssklower 		 */
35936372Ssklower 		if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
36036372Ssklower 			IFDEBUG(D_OUTPUT)
36136372Ssklower 				printf("clnp_output: calling clnp_srcroute\n");
36236372Ssklower 			ENDDEBUG
36336372Ssklower 			error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
36437469Ssklower 				&clcp->clc_firsthop, &clcp->clc_ifa, dst);
36536372Ssklower 		} else {
36636372Ssklower 			IFDEBUG(D_OUTPUT)
36736372Ssklower 			ENDDEBUG
36836372Ssklower 			error = clnp_route(dst, &isop->isop_route, flags,
36937469Ssklower 				&clcp->clc_firsthop, &clcp->clc_ifa);
37036372Ssklower 		}
37137469Ssklower 		if (error || (clcp->clc_ifa == 0)) {
37236372Ssklower 			IFDEBUG(D_OUTPUT)
37336372Ssklower 				printf("clnp_output: route failed, errno %d\n", error);
37436372Ssklower 				printf("@clcp:\n");
37536372Ssklower 				dump_buf(clcp, sizeof (struct clnp_cache));
37636372Ssklower 			ENDDEBUG
37736372Ssklower 			goto bad;
37836372Ssklower 		}
37940777Ssklower 		clcp->clc_rt = isop->isop_route.ro_rt;	/* XXX */
38048742Ssklower 		clcp->clc_ifp = clcp->clc_ifa->ia_ifp;  /* XXX */
38136372Ssklower 
38236372Ssklower 		IFDEBUG(D_OUTPUT)
38336372Ssklower 			printf("clnp_output: packet routed to %s\n",
38436372Ssklower 				clnp_iso_addrp(
38536372Ssklower 					&((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
38636372Ssklower 		ENDDEBUG
38736372Ssklower 
38836372Ssklower 		/*
38936372Ssklower 		 *	If src address is not yet specified, use address of
39036372Ssklower 		 *	interface. NOTE: this will now update the laddr field in
39136372Ssklower 		 *	the isopcb. Is this desirable? RAH?
39236372Ssklower 		 */
39336372Ssklower 		if (src->isoa_len == 0) {
39437469Ssklower 			src = &(clcp->clc_ifa->ia_addr.siso_addr);
39536372Ssklower 			IFDEBUG(D_OUTPUT)
39636372Ssklower 				printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
39736372Ssklower 			ENDDEBUG
39836372Ssklower 		}
39936372Ssklower 
40036372Ssklower 		/*
40136372Ssklower 		 *	Insert the source and destination address,
40236372Ssklower 		 */
40336372Ssklower 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
40437469Ssklower 		CLNP_INSERT_ADDR(hoff, *dst);
40537469Ssklower 		CLNP_INSERT_ADDR(hoff, *src);
40636372Ssklower 
40736372Ssklower 		/*
40836372Ssklower 		 *	Leave room for the segment part, if segmenting is selected
40936372Ssklower 		 */
41037469Ssklower 		if (clnp->cnf_type & CNF_SEG_OK) {
41136372Ssklower 			clcp->clc_segoff = hoff - (caddr_t)clnp;
41236372Ssklower 			hoff += sizeof(struct clnp_segment);
41336372Ssklower 		}
41436372Ssklower 
41536372Ssklower 		clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
41636768Ssklower 		hdrlen = clnp->cnf_hdr_len;
41736372Ssklower 
41836768Ssklower #ifdef	DECBIT
41936372Ssklower 		/*
42036768Ssklower 		 *	Add the globally unique QOS (with room for congestion experienced
42136768Ssklower 		 *	bit). I can safely assume that this option is not in the options
42236768Ssklower 		 *	mbuf below because I checked that the option was not specified
42336768Ssklower 		 *	previously
42436768Ssklower 		 */
42536768Ssklower 		if ((m->m_len + sizeof(qos_option)) < MLEN) {
42636768Ssklower 			bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
42736768Ssklower 			clnp->cnf_hdr_len += sizeof(qos_option);
42836768Ssklower 			hdrlen += sizeof(qos_option);
42936768Ssklower 			m->m_len += sizeof(qos_option);
43036768Ssklower 		}
43136768Ssklower #endif	DECBIT
43236768Ssklower 
43336768Ssklower 		/*
43436372Ssklower 		 *	If an options mbuf is present, concatenate a copy to the hdr mbuf.
43536372Ssklower 		 */
43636372Ssklower 		if (isop->isop_options) {
43737469Ssklower 			struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
43836372Ssklower 			if (opt_copy == NULL) {
43936372Ssklower 				error = ENOBUFS;
44036372Ssklower 				goto bad;
44136372Ssklower 			}
44236372Ssklower 			/* Link in place */
44336372Ssklower 			opt_copy->m_next = m->m_next;
44436372Ssklower 			m->m_next = opt_copy;
44536372Ssklower 
44636372Ssklower 			/* update size of header */
44736372Ssklower 			clnp->cnf_hdr_len += opt_copy->m_len;
44836768Ssklower 			hdrlen += opt_copy->m_len;
44936372Ssklower 		}
45036372Ssklower 
45136768Ssklower 		if (hdrlen > CLNP_HDR_MAX) {
45236768Ssklower 			error = EMSGSIZE;
45336768Ssklower 			goto bad;
45436768Ssklower 		}
45536768Ssklower 
45636372Ssklower 		/*
45736372Ssklower 		 *	Now set up the cache entry in the pcb
45836372Ssklower 		 */
45936372Ssklower 		if ((flags & CLNP_NOCACHE) == 0) {
46037469Ssklower 			if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
46137469Ssklower 				clcp->clc_dst  = *dst;
46236372Ssklower 				clcp->clc_flags = flags;
46336372Ssklower 				clcp->clc_options = isop->isop_options;
46436372Ssklower 			}
46536372Ssklower 		}
46636372Ssklower 	}
46736372Ssklower 	/*
46836372Ssklower 	 *	If small enough for interface, send directly
46936372Ssklower 	 *	Fill in segmentation part of hdr if using the full protocol
47036372Ssklower 	 */
47142865Ssklower 	total_len = clnp->cnf_hdr_len + datalen;
47242865Ssklower 	if (clnp->cnf_type & CNF_SEG_OK) {
47342865Ssklower 		struct clnp_segment	seg_part;		/* segment part of hdr */
47442865Ssklower 		seg_part.cng_id = htons(clnp_id++);
47542865Ssklower 		seg_part.cng_off = htons(0);
47642865Ssklower 		seg_part.cng_tot_len = htons(total_len);
47742865Ssklower 		(void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
47842865Ssklower 			sizeof(seg_part));
47942865Ssklower 	}
48048742Ssklower 	if (total_len <= SN_MTU(clcp->clc_ifp, clcp->clc_rt)) {
48136372Ssklower 		HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
48237469Ssklower 		m->m_pkthdr.len = total_len;
48336372Ssklower 		/*
48436372Ssklower 		 *	Compute clnp checksum (on header only)
48536372Ssklower 		 */
48636372Ssklower 		if (flags & CLNP_NO_CKSUM) {
48736372Ssklower 			HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
48836372Ssklower 		} else {
48936372Ssklower 			iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
49036372Ssklower 		}
49136372Ssklower 
49236372Ssklower 		IFDEBUG(D_DUMPOUT)
49336372Ssklower 			struct mbuf *mdump = m;
49436372Ssklower 			printf("clnp_output: sending dg:\n");
49536372Ssklower 			while (mdump != NULL) {
49636372Ssklower 				dump_buf(mtod(mdump, caddr_t), mdump->m_len);
49736372Ssklower 				mdump = mdump->m_next;
49836372Ssklower 			}
49936372Ssklower 		ENDDEBUG
50036372Ssklower 
50136372Ssklower 		error = SN_OUTPUT(clcp, m);
50236372Ssklower 		goto done;
50336372Ssklower 	} else {
50436372Ssklower 		/*
50536372Ssklower 		 * Too large for interface; fragment if possible.
50636372Ssklower 		 */
50748742Ssklower 		error = clnp_fragment(clcp->clc_ifp, m, clcp->clc_firsthop,
50840777Ssklower 							total_len, clcp->clc_segoff, flags, clcp->clc_rt);
50936372Ssklower 		goto done;
51036372Ssklower 	}
51136372Ssklower bad:
51236372Ssklower 	m_freem(m);
51336372Ssklower done:
51439195Ssklower 	if (error) {
51539195Ssklower 		clnp_stat.cns_sent--;
51639195Ssklower 		clnp_stat.cns_odropped++;
51739195Ssklower 	}
51839195Ssklower 	return (error);
51936372Ssklower }
52036372Ssklower 
52136372Ssklower int clnp_ctloutput()
52236372Ssklower {
52336372Ssklower }
524