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