xref: /csrg-svn/sys/netiso/clnp_output.c (revision 39195)
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.5 (Berkeley) 09/22/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 #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->rt_flags & RTF_UP) &&	/* route still up */
190 		(flags == clcp->clc_flags) &&					/* same flags */
191 		(clcp->clc_hdr != NULL)) {						/* hdr mbuf exists */
192 		/*
193 		 *	The cache is valid
194 		 */
195 
196 		IFDEBUG(D_OUTPUT)
197 			printf("clnp_output: using cache\n");
198 		ENDDEBUG
199 
200 		m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
201 		if (m == NULL) {
202 			/*
203 			 *	No buffers left to copy cached packet header. Use
204 			 *	the cached packet header this time, and
205 			 *	mark the hdr as vacant
206 			 */
207 			m = clcp->clc_hdr;
208 			clcp->clc_hdr = NULL;
209 		}
210 		m->m_next = m0;	/* ASSUMES pkt hdr is 1 mbuf long */
211 		clnp = mtod(m, struct clnp_fixed *);
212 	} else {
213 		struct clnp_optidx	*oidx = NULL;		/* index to clnp options */
214 
215 		/*
216 		 *	The cache is not valid. Allocate an mbuf (if necessary)
217 		 *	to hold cached info. If one is not available, then
218 		 *	don't bother with the cache
219 		 */
220 		INCSTAT(cns_cachemiss);
221 		if (flags & CLNP_NOCACHE) {
222 			clcp = &clc;
223 		} else {
224 			if (isop->isop_clnpcache == NULL) {
225 				/*
226 				 *	There is no clnpcache. Allocate an mbuf to hold one
227 				 */
228 				if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
229 					== NULL) {
230 					/*
231 					 *	No mbufs available. Pretend that we don't want
232 					 *	caching this time.
233 					 */
234 					IFDEBUG(D_OUTPUT)
235 						printf("clnp_output: no mbufs to allocate to cache\n");
236 					ENDDEBUG
237 					flags  |= CLNP_NOCACHE;
238 					clcp = &clc;
239 				} else {
240 					clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
241 				}
242 			} else {
243 				/*
244 				 *	A clnpcache mbuf exists. If the clc_hdr is not null,
245 				 *	we must free it, as a new one is about to be created.
246 				 */
247 				clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
248 				if (clcp->clc_hdr != NULL) {
249 					/*
250 					 *	The clc_hdr is not null but a clnpcache mbuf exists.
251 					 *	This means that there was a cache, but the existing
252 					 *	copy of the hdr is no longer valid. Free it now
253 					 *	before we lose the pointer to it.
254 					 */
255 					IFDEBUG(D_OUTPUT)
256 						printf("clnp_output: freeing old clc_hdr 0x%x\n",
257 						clcp->clc_hdr);
258 					ENDDEBUG
259 					m_free(clcp->clc_hdr);
260 					IFDEBUG(D_OUTPUT)
261 						printf("clnp_output: freed old clc_hdr (done)\n");
262 					ENDDEBUG
263 				}
264 			}
265 		}
266 		IFDEBUG(D_OUTPUT)
267 			printf("clnp_output: NEW clcp x%x\n",clcp);
268 		ENDDEBUG
269 		bzero((caddr_t)clcp, sizeof(struct clnp_cache));
270 
271 		if (isop->isop_optindex)
272 			oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
273 
274 		/*
275 		 *	Don't allow packets with security, quality of service,
276 		 *	priority, or error report options to be sent.
277 		 */
278 		if ((isop->isop_options) && (oidx)) {
279 			if ((oidx->cni_securep) ||
280 				(oidx->cni_priorp) ||
281 				(oidx->cni_qos_formatp) ||
282 				(oidx->cni_er_reason != ER_INVALREAS)) {
283 				IFDEBUG(D_OUTPUT)
284 					printf("clnp_output: pkt dropped - option unsupported\n");
285 				ENDDEBUG
286 				m_freem(m0);
287 				return(EINVAL);
288 			}
289 		}
290 
291 		/*
292 		 *	Don't allow any invalid flags to be set
293 		 */
294 		if ((flags & (CLNP_VFLAGS)) != flags) {
295 			IFDEBUG(D_OUTPUT)
296 				printf("clnp_output: packet dropped - flags unsupported\n");
297 			ENDDEBUG
298 			INCSTAT(cns_odropped);
299 			m_freem(m0);
300 			return(EINVAL);
301 		}
302 
303 		/*
304 		 *	Don't allow funny lengths on dst; src may be zero in which
305 		 *	case we insert the source address based upon the interface
306 		 */
307 		if ((src->isoa_len > sizeof(struct iso_addr)) ||
308 			(dst->isoa_len == 0) ||
309 			(dst->isoa_len > sizeof(struct iso_addr))) {
310 			m_freem(m0);
311 			INCSTAT(cns_odropped);
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 			INCSTAT(cns_odropped);
322 			return(ENOBUFS);
323 		}
324 		INCSTAT(cns_sent);
325 		m->m_next = m0;
326 		clnp = mtod(m, struct clnp_fixed *);
327 		clcp->clc_segoff = 0;
328 
329 		/*
330 		 *	Fill in all of fixed hdr except lengths and checksum
331 		 */
332 		if (flags & CLNP_SEND_RAW) {
333 			*clnp = raw_template;
334 		} else if (flags & CLNP_ECHO) {
335 			*clnp = echo_template;
336 		} else {
337 			*clnp = dt_template;
338 		}
339 		if (flags & CLNP_NO_SEG)
340 			clnp->cnf_type &= ~CNF_SEG_OK;
341 		if (flags & CLNP_NO_ER)
342 			clnp->cnf_type &= ~CNF_ERR_OK;
343 
344 		/*
345 		 *	Route packet; special case for source rt
346 		 */
347 		if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
348 			IFDEBUG(D_OUTPUT)
349 				printf("clnp_output: calling clnp_srcroute\n");
350 			ENDDEBUG
351 			error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
352 				&clcp->clc_firsthop, &clcp->clc_ifa, dst);
353 		} else {
354 			IFDEBUG(D_OUTPUT)
355 			ENDDEBUG
356 			error = clnp_route(dst, &isop->isop_route, flags,
357 				&clcp->clc_firsthop, &clcp->clc_ifa);
358 		}
359 		if (error || (clcp->clc_ifa == 0)) {
360 			IFDEBUG(D_OUTPUT)
361 				printf("clnp_output: route failed, errno %d\n", error);
362 				printf("@clcp:\n");
363 				dump_buf(clcp, sizeof (struct clnp_cache));
364 			ENDDEBUG
365 			goto bad;
366 		}
367 
368 		IFDEBUG(D_OUTPUT)
369 			printf("clnp_output: packet routed to %s\n",
370 				clnp_iso_addrp(
371 					&((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
372 		ENDDEBUG
373 
374 		/*
375 		 *	If src address is not yet specified, use address of
376 		 *	interface. NOTE: this will now update the laddr field in
377 		 *	the isopcb. Is this desirable? RAH?
378 		 */
379 		if (src->isoa_len == 0) {
380 			src = &(clcp->clc_ifa->ia_addr.siso_addr);
381 			IFDEBUG(D_OUTPUT)
382 				printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
383 			ENDDEBUG
384 		}
385 
386 		/*
387 		 *	Insert the source and destination address,
388 		 */
389 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
390 		CLNP_INSERT_ADDR(hoff, *dst);
391 		CLNP_INSERT_ADDR(hoff, *src);
392 
393 		/*
394 		 *	Leave room for the segment part, if segmenting is selected
395 		 */
396 		if (clnp->cnf_type & CNF_SEG_OK) {
397 			clcp->clc_segoff = hoff - (caddr_t)clnp;
398 			hoff += sizeof(struct clnp_segment);
399 		}
400 
401 		clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
402 		hdrlen = clnp->cnf_hdr_len;
403 
404 #ifdef	DECBIT
405 		/*
406 		 *	Add the globally unique QOS (with room for congestion experienced
407 		 *	bit). I can safely assume that this option is not in the options
408 		 *	mbuf below because I checked that the option was not specified
409 		 *	previously
410 		 */
411 		if ((m->m_len + sizeof(qos_option)) < MLEN) {
412 			bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
413 			clnp->cnf_hdr_len += sizeof(qos_option);
414 			hdrlen += sizeof(qos_option);
415 			m->m_len += sizeof(qos_option);
416 		}
417 #endif	DECBIT
418 
419 		/*
420 		 *	If an options mbuf is present, concatenate a copy to the hdr mbuf.
421 		 */
422 		if (isop->isop_options) {
423 			struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
424 			if (opt_copy == NULL) {
425 				error = ENOBUFS;
426 				goto bad;
427 			}
428 			/* Link in place */
429 			opt_copy->m_next = m->m_next;
430 			m->m_next = opt_copy;
431 
432 			/* update size of header */
433 			clnp->cnf_hdr_len += opt_copy->m_len;
434 			hdrlen += opt_copy->m_len;
435 		}
436 
437 		if (hdrlen > CLNP_HDR_MAX) {
438 			error = EMSGSIZE;
439 			goto bad;
440 		}
441 
442 		/*
443 		 *	Now set up the cache entry in the pcb
444 		 */
445 		if ((flags & CLNP_NOCACHE) == 0) {
446 			if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
447 				clcp->clc_dst  = *dst;
448 				clcp->clc_flags = flags;
449 				clcp->clc_options = isop->isop_options;
450 			}
451 		}
452 	}
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,
494 									total_len, clcp->clc_segoff, flags);
495 		goto done;
496 	}
497 bad:
498 	m_freem(m);
499 done:
500 	if (error) {
501 		clnp_stat.cns_sent--;
502 		clnp_stat.cns_odropped++;
503 	}
504 	return (error);
505 }
506 
507 int clnp_ctloutput()
508 {
509 }
510