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