xref: /csrg-svn/sys/netiso/clnp_output.c (revision 38841)
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_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $ */
28 /* $Source: /var/src/sys/netiso/RCS/clnp_output.c,v $ */
29 /*	@(#)clnp_output.c	7.4 (Berkeley) 08/29/89 */
30 
31 #ifndef lint
32 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $";
33 #endif lint
34 
35 #ifdef ISO
36 #include "types.h"
37 #include "param.h"
38 #include "mbuf.h"
39 #include "domain.h"
40 #include "protosw.h"
41 #include "socket.h"
42 #include "socketvar.h"
43 #include "errno.h"
44 #include "time.h"
45 
46 #include "../net/if.h"
47 #include "../net/route.h"
48 
49 #include "iso.h"
50 #include "iso_var.h"
51 #include "iso_pcb.h"
52 #include "clnp.h"
53 #include "clnp_stat.h"
54 #include "argo_debug.h"
55 
56 static struct clnp_fixed dt_template = {
57 	ISO8473_CLNP,	/* network identifier */
58 	0,				/* length */
59 	ISO8473_V1,		/* version */
60 	CLNP_TTL,		/* ttl */
61 	CLNP_DT|CNF_SEG_OK|CNF_ERR_OK,		/* type */
62 	0,				/* segment length */
63 	0				/* checksum */
64 };
65 
66 static struct clnp_fixed raw_template = {
67 	ISO8473_CLNP,	/* network identifier */
68 	0,				/* length */
69 	ISO8473_V1,		/* version */
70 	CLNP_TTL,		/* ttl */
71 	CLNP_RAW|CNF_SEG_OK|CNF_ERR_OK,		/* type */
72 	0,				/* segment length */
73 	0				/* checksum */
74 };
75 
76 static struct clnp_fixed echo_template = {
77 	ISO8473_CLNP,	/* network identifier */
78 	0,				/* length */
79 	ISO8473_V1,		/* version */
80 	CLNP_TTL,		/* ttl */
81 	CLNP_EC|CNF_SEG_OK|CNF_ERR_OK,		/* type */
82 	0,				/* segment length */
83 	0				/* checksum */
84 };
85 
86 #ifdef	DECBIT
87 u_char qos_option[] = {CLNPOVAL_QOS, 1,
88 	CLNPOVAL_GLOBAL|CLNPOVAL_SEQUENCING|CLNPOVAL_LOWDELAY};
89 #endif	DECBIT
90 
91 int				clnp_id = 0;		/* id for segmented dgrams */
92 
93 /*
94  * FUNCTION:		clnp_output
95  *
96  * PURPOSE:			output the data in the mbuf as a clnp datagram
97  *
98  *					The data specified by m0 is sent as a clnp datagram.
99  *					The mbuf chain m0 will be freed when this routine has
100  *					returned.
101  *
102  *					If options is non-null, it points to an mbuf which contains
103  *					options to be sent with the datagram. The options must
104  *					be formatted in the mbuf according to clnp rules. Options
105  *					will not be freed.
106  *
107  *					Datalen specifies the length of the data in m0.
108  *
109  *					Src and dst are the addresses for the packet.
110  *
111  *					If route is non-null, it is used as the route for
112  *					the packet.
113  *
114  *					By default, a DT is sent. However, if flags & CNLP_SEND_ER
115  *					then an ER will be sent. If flags & CLNP_SEND_RAW, then
116  *					the packet will be send as raw clnp.
117  *
118  * RETURNS:			0	success
119  *					appropriate error code
120  *
121  * SIDE EFFECTS:	none
122  *
123  * NOTES:
124  *					Flags are interpretated as follows:
125  *						CLNP_NO_SEG - do not allow this pkt to be segmented.
126  *						CLNP_NO_ER  - have pkt request ER suppression.
127  *						CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT
128  *						CLNP_NO_CKSUM - don't compute clnp checksum
129  *						CLNP_ECHO - send as ECHO packet
130  *
131  *					When checking for a cached packet, clnp checks
132  *					that the route taken is still up. It does not
133  *					check that the route is still to the same destination.
134  *					This means that any entity that alters an existing
135  *					route for an isopcb (such as when a redirect arrives)
136  *					must invalidate the clnp cache. It might be perferable
137  *					to have clnp check that the route has the same dest, but
138  *					by avoiding this check, we save a call to iso_addrmatch1.
139  */
140 clnp_output(m0, isop, datalen, flags)
141 struct mbuf			*m0;		/* data for the packet */
142 struct isopcb		*isop;		/* iso pcb */
143 int					datalen;	/* number of bytes of data in m0 */
144 int					flags;		/* flags */
145 {
146 	int							error = 0;		/* return value of function */
147 	register struct mbuf		*m = m0;		/* mbuf for clnp header chain */
148 	register struct clnp_fixed	*clnp;			/* ptr to fixed part of hdr */
149 	register caddr_t			hoff;			/* offset into header */
150 	int							total_len;		/* total length of packet */
151 	struct iso_addr				*src;		/* ptr to source address */
152 	struct iso_addr				*dst;		/* ptr to destination address */
153 	struct clnp_cache			clc;		/* storage for cache information */
154 	struct clnp_cache			*clcp = NULL;	/* ptr to clc */
155 	int							hdrlen = 0;
156 
157 	src = &isop->isop_laddr->siso_addr;
158 	dst = &isop->isop_faddr->siso_addr;
159 
160 	IFDEBUG(D_OUTPUT)
161 		printf("clnp_output: to %s", clnp_iso_addrp(dst));
162 		printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen);
163 		printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
164 			isop->isop_options, flags, isop->isop_clnpcache);
165 	ENDDEBUG
166 
167 	if (isop->isop_clnpcache != NULL) {
168 		clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
169 	}
170 
171 	/*
172 	 *	Check if cache is valid ...
173 	 */
174 	IFDEBUG(D_OUTPUT)
175 		printf("clnp_output: ck cache: clcp %x\n", clcp);
176 		if (clcp != NULL) {
177 			printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst));
178 			printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options,
179 				clcp->clc_options);
180 			if (isop->isop_route.ro_rt)
181 				printf("\tro_rt x%x, rt_flags x%x\n",
182 					isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags);
183 			printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags);
184 			printf("\tclc_hdr x%x\n", clcp->clc_hdr);
185 		}
186 	ENDDEBUG
187 	if ((clcp != NULL) &&								/* cache exists */
188 		(isop->isop_options == clcp->clc_options) && 	/* same options */
189 		(iso_addrmatch1(dst, &clcp->clc_dst)) &&		/* dst still same */
190 		(isop->isop_route.ro_rt != NULL) &&				/* route exists */
191 		(isop->isop_route.ro_rt->rt_flags & RTF_UP) &&	/* route still up */
192 		(flags == clcp->clc_flags) &&					/* same flags */
193 		(clcp->clc_hdr != NULL)) {						/* hdr mbuf exists */
194 		/*
195 		 *	The cache is valid
196 		 */
197 
198 		IFDEBUG(D_OUTPUT)
199 			printf("clnp_output: using cache\n");
200 		ENDDEBUG
201 
202 		m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
203 		if (m == NULL) {
204 			/*
205 			 *	No buffers left to copy cached packet header. Use
206 			 *	the cached packet header this time, and
207 			 *	mark the hdr as vacant
208 			 */
209 			m = clcp->clc_hdr;
210 			clcp->clc_hdr = NULL;
211 		}
212 		m->m_next = m0;	/* ASSUMES pkt hdr is 1 mbuf long */
213 		clnp = mtod(m, struct clnp_fixed *);
214 	} else {
215 		struct clnp_optidx	*oidx = NULL;		/* index to clnp options */
216 
217 		/*
218 		 *	The cache is not valid. Allocate an mbuf (if necessary)
219 		 *	to hold cached info. If one is not available, then
220 		 *	don't bother with the cache
221 		 */
222 		INCSTAT(cns_cachemiss);
223 		if (flags & CLNP_NOCACHE) {
224 			clcp = &clc;
225 		} else {
226 			if (isop->isop_clnpcache == NULL) {
227 				/*
228 				 *	There is no clnpcache. Allocate an mbuf to hold one
229 				 */
230 				if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
231 					== NULL) {
232 					/*
233 					 *	No mbufs available. Pretend that we don't want
234 					 *	caching this time.
235 					 */
236 					IFDEBUG(D_OUTPUT)
237 						printf("clnp_output: no mbufs to allocate to cache\n");
238 					ENDDEBUG
239 					flags  |= CLNP_NOCACHE;
240 					clcp = &clc;
241 				} else {
242 					clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
243 				}
244 			} else {
245 				/*
246 				 *	A clnpcache mbuf exists. If the clc_hdr is not null,
247 				 *	we must free it, as a new one is about to be created.
248 				 */
249 				clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
250 				if (clcp->clc_hdr != NULL) {
251 					/*
252 					 *	The clc_hdr is not null but a clnpcache mbuf exists.
253 					 *	This means that there was a cache, but the existing
254 					 *	copy of the hdr is no longer valid. Free it now
255 					 *	before we lose the pointer to it.
256 					 */
257 					IFDEBUG(D_OUTPUT)
258 						printf("clnp_output: freeing old clc_hdr 0x%x\n",
259 						clcp->clc_hdr);
260 					ENDDEBUG
261 					m_free(clcp->clc_hdr);
262 					IFDEBUG(D_OUTPUT)
263 						printf("clnp_output: freed old clc_hdr (done)\n");
264 					ENDDEBUG
265 				}
266 			}
267 		}
268 		IFDEBUG(D_OUTPUT)
269 			printf("clnp_output: NEW clcp x%x\n",clcp);
270 		ENDDEBUG
271 		bzero((caddr_t)clcp, sizeof(struct clnp_cache));
272 
273 		if (isop->isop_optindex)
274 			oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
275 
276 		/*
277 		 *	Don't allow packets with security, quality of service,
278 		 *	priority, or error report options to be sent.
279 		 */
280 		if ((isop->isop_options) && (oidx)) {
281 			if ((oidx->cni_securep) ||
282 				(oidx->cni_priorp) ||
283 				(oidx->cni_qos_formatp) ||
284 				(oidx->cni_er_reason != ER_INVALREAS)) {
285 				IFDEBUG(D_OUTPUT)
286 					printf("clnp_output: pkt dropped - option unsupported\n");
287 				ENDDEBUG
288 				m_freem(m0);
289 				return(EINVAL);
290 			}
291 		}
292 
293 		/*
294 		 *	Don't allow any invalid flags to be set
295 		 */
296 		if ((flags & (CLNP_VFLAGS)) != flags) {
297 			IFDEBUG(D_OUTPUT)
298 				printf("clnp_output: packet dropped - flags unsupported\n");
299 			ENDDEBUG
300 			m_freem(m0);
301 			return(EINVAL);
302 		}
303 
304 		/*
305 		 *	Don't allow funny lengths on dst; src may be zero in which
306 		 *	case we insert the source address based upon the interface
307 		 */
308 		if ((src->isoa_len > sizeof(struct iso_addr)) ||
309 			(dst->isoa_len == 0) ||
310 			(dst->isoa_len > sizeof(struct iso_addr))) {
311 			m_freem(m0);
312 			return(ENAMETOOLONG);
313 		}
314 
315 		/*
316 		 *	Grab mbuf to contain header
317 		 */
318 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
319 		if (m == 0) {
320 			m_freem(m0);
321 			return(ENOBUFS);
322 		}
323 
324 		m->m_next = m0;
325 		clnp = mtod(m, struct clnp_fixed *);
326 		clcp->clc_segoff = 0;
327 
328 		/*
329 		 *	Fill in all of fixed hdr except lengths and checksum
330 		 */
331 		if (flags & CLNP_SEND_RAW) {
332 			*clnp = raw_template;
333 		} else if (flags & CLNP_ECHO) {
334 			*clnp = echo_template;
335 		} else {
336 			*clnp = dt_template;
337 		}
338 		if (flags & CLNP_NO_SEG)
339 			clnp->cnf_type &= ~CNF_SEG_OK;
340 		if (flags & CLNP_NO_ER)
341 			clnp->cnf_type &= ~CNF_ERR_OK;
342 
343 		/*
344 		 *	Route packet; special case for source rt
345 		 */
346 		if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
347 			IFDEBUG(D_OUTPUT)
348 				printf("clnp_output: calling clnp_srcroute\n");
349 			ENDDEBUG
350 			error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
351 				&clcp->clc_firsthop, &clcp->clc_ifa, dst);
352 		} else {
353 			IFDEBUG(D_OUTPUT)
354 			ENDDEBUG
355 			error = clnp_route(dst, &isop->isop_route, flags,
356 				&clcp->clc_firsthop, &clcp->clc_ifa);
357 		}
358 		if (error || (clcp->clc_ifa == 0)) {
359 			IFDEBUG(D_OUTPUT)
360 				printf("clnp_output: route failed, errno %d\n", error);
361 				printf("@clcp:\n");
362 				dump_buf(clcp, sizeof (struct clnp_cache));
363 			ENDDEBUG
364 			goto bad;
365 		}
366 
367 		IFDEBUG(D_OUTPUT)
368 			printf("clnp_output: packet routed to %s\n",
369 				clnp_iso_addrp(
370 					&((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
371 		ENDDEBUG
372 
373 		/*
374 		 *	If src address is not yet specified, use address of
375 		 *	interface. NOTE: this will now update the laddr field in
376 		 *	the isopcb. Is this desirable? RAH?
377 		 */
378 		if (src->isoa_len == 0) {
379 			src = &(clcp->clc_ifa->ia_addr.siso_addr);
380 			IFDEBUG(D_OUTPUT)
381 				printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
382 			ENDDEBUG
383 		}
384 
385 		/*
386 		 *	Insert the source and destination address,
387 		 */
388 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
389 		CLNP_INSERT_ADDR(hoff, *dst);
390 		CLNP_INSERT_ADDR(hoff, *src);
391 
392 		/*
393 		 *	Leave room for the segment part, if segmenting is selected
394 		 */
395 		if (clnp->cnf_type & CNF_SEG_OK) {
396 			clcp->clc_segoff = hoff - (caddr_t)clnp;
397 			hoff += sizeof(struct clnp_segment);
398 		}
399 
400 		clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
401 		hdrlen = clnp->cnf_hdr_len;
402 
403 #ifdef	DECBIT
404 		/*
405 		 *	Add the globally unique QOS (with room for congestion experienced
406 		 *	bit). I can safely assume that this option is not in the options
407 		 *	mbuf below because I checked that the option was not specified
408 		 *	previously
409 		 */
410 		if ((m->m_len + sizeof(qos_option)) < MLEN) {
411 			bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
412 			clnp->cnf_hdr_len += sizeof(qos_option);
413 			hdrlen += sizeof(qos_option);
414 			m->m_len += sizeof(qos_option);
415 		}
416 #endif	DECBIT
417 
418 		/*
419 		 *	If an options mbuf is present, concatenate a copy to the hdr mbuf.
420 		 */
421 		if (isop->isop_options) {
422 			struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
423 			if (opt_copy == NULL) {
424 				error = ENOBUFS;
425 				goto bad;
426 			}
427 			/* Link in place */
428 			opt_copy->m_next = m->m_next;
429 			m->m_next = opt_copy;
430 
431 			/* update size of header */
432 			clnp->cnf_hdr_len += opt_copy->m_len;
433 			hdrlen += opt_copy->m_len;
434 		}
435 
436 		if (hdrlen > CLNP_HDR_MAX) {
437 			error = EMSGSIZE;
438 			goto bad;
439 		}
440 
441 		/*
442 		 *	Now set up the cache entry in the pcb
443 		 */
444 		if ((flags & CLNP_NOCACHE) == 0) {
445 			if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
446 				clcp->clc_dst  = *dst;
447 				clcp->clc_flags = flags;
448 				clcp->clc_options = isop->isop_options;
449 			}
450 		}
451 	}
452 	INCSTAT(cns_sent);
453 	/*
454 	 *	If small enough for interface, send directly
455 	 *	Fill in segmentation part of hdr if using the full protocol
456 	 */
457 	if ((total_len = clnp->cnf_hdr_len + datalen)
458 			<= SN_MTU(clcp->clc_ifa->ia_ifp)) {
459 		if (clnp->cnf_type & CNF_SEG_OK) {
460 			struct clnp_segment	seg_part;		/* segment part of hdr */
461 			seg_part.cng_id = htons(clnp_id++);
462 			seg_part.cng_off = htons(0);
463 			seg_part.cng_tot_len = htons(total_len);
464 			(void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
465 				sizeof(seg_part));
466 		}
467 		HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
468 		m->m_pkthdr.len = total_len;
469 		/*
470 		 *	Compute clnp checksum (on header only)
471 		 */
472 		if (flags & CLNP_NO_CKSUM) {
473 			HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
474 		} else {
475 			iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
476 		}
477 
478 		IFDEBUG(D_DUMPOUT)
479 			struct mbuf *mdump = m;
480 			printf("clnp_output: sending dg:\n");
481 			while (mdump != NULL) {
482 				dump_buf(mtod(mdump, caddr_t), mdump->m_len);
483 				mdump = mdump->m_next;
484 			}
485 		ENDDEBUG
486 
487 		error = SN_OUTPUT(clcp, m);
488 		goto done;
489 	} else {
490 		/*
491 		 * Too large for interface; fragment if possible.
492 		 */
493 		error = clnp_fragment(clcp->clc_ifa->ia_ifp, m, clcp->clc_firsthop, total_len,
494 			clcp->clc_segoff, flags);
495 		goto done;
496 	}
497 bad:
498 	m_freem(m);
499 
500 done:
501 	return(error);
502 }
503 
504 int clnp_ctloutput()
505 {
506 }
507 
508 #endif ISO
509