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