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