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