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