xref: /original-bsd/sys/netiso/clnp_output.c (revision 9cf05c58)
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.9 (Berkeley) 04/26/91 */
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 	dst = &isop->isop_faddr->siso_addr;
156 	if (isop->isop_laddr == 0) {
157 		struct iso_ifaddr *ia = 0;
158 		clnp_route(dst, &isop->isop_route, flags, 0, &ia);
159 		if (ia == 0 || ia->ia_ifa.ifa_addr->sa_family != AF_ISO)
160 			return (ENETUNREACH);
161 		src = &ia->ia_addr.siso_addr;
162 	} else
163 		src = &isop->isop_laddr->siso_addr;
164 
165 	IFDEBUG(D_OUTPUT)
166 		printf("clnp_output: to %s", clnp_iso_addrp(dst));
167 		printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen);
168 		printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
169 			isop->isop_options, flags, isop->isop_clnpcache);
170 	ENDDEBUG
171 
172 	if (isop->isop_clnpcache != NULL) {
173 		clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
174 	}
175 
176 	/*
177 	 *	Check if cache is valid ...
178 	 */
179 	IFDEBUG(D_OUTPUT)
180 		printf("clnp_output: ck cache: clcp %x\n", clcp);
181 		if (clcp != NULL) {
182 			printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst));
183 			printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options,
184 				clcp->clc_options);
185 			if (isop->isop_route.ro_rt)
186 				printf("\tro_rt x%x, rt_flags x%x\n",
187 					isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags);
188 			printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags);
189 			printf("\tclc_hdr x%x\n", clcp->clc_hdr);
190 		}
191 	ENDDEBUG
192 	if ((clcp != NULL) &&								/* cache exists */
193 		(isop->isop_options == clcp->clc_options) && 	/* same options */
194 		(iso_addrmatch1(dst, &clcp->clc_dst)) &&		/* dst still same */
195 		(isop->isop_route.ro_rt != NULL) &&				/* route exists */
196 		(isop->isop_route.ro_rt == clcp->clc_rt) &&		/* and is cached */
197 		(isop->isop_route.ro_rt->rt_flags & RTF_UP) &&	/* route still up */
198 		(flags == clcp->clc_flags) &&					/* same flags */
199 		(clcp->clc_hdr != NULL)) {						/* hdr mbuf exists */
200 		/*
201 		 *	The cache is valid
202 		 */
203 
204 		IFDEBUG(D_OUTPUT)
205 			printf("clnp_output: using cache\n");
206 		ENDDEBUG
207 
208 		m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
209 		if (m == NULL) {
210 			/*
211 			 *	No buffers left to copy cached packet header. Use
212 			 *	the cached packet header this time, and
213 			 *	mark the hdr as vacant
214 			 */
215 			m = clcp->clc_hdr;
216 			clcp->clc_hdr = NULL;
217 		}
218 		m->m_next = m0;	/* ASSUMES pkt hdr is 1 mbuf long */
219 		clnp = mtod(m, struct clnp_fixed *);
220 	} else {
221 		struct clnp_optidx	*oidx = NULL;		/* index to clnp options */
222 
223 		/*
224 		 *	The cache is not valid. Allocate an mbuf (if necessary)
225 		 *	to hold cached info. If one is not available, then
226 		 *	don't bother with the cache
227 		 */
228 		INCSTAT(cns_cachemiss);
229 		if (flags & CLNP_NOCACHE) {
230 			clcp = &clc;
231 		} else {
232 			if (isop->isop_clnpcache == NULL) {
233 				/*
234 				 *	There is no clnpcache. Allocate an mbuf to hold one
235 				 */
236 				if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER))
237 					== NULL) {
238 					/*
239 					 *	No mbufs available. Pretend that we don't want
240 					 *	caching this time.
241 					 */
242 					IFDEBUG(D_OUTPUT)
243 						printf("clnp_output: no mbufs to allocate to cache\n");
244 					ENDDEBUG
245 					flags  |= CLNP_NOCACHE;
246 					clcp = &clc;
247 				} else {
248 					clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
249 				}
250 			} else {
251 				/*
252 				 *	A clnpcache mbuf exists. If the clc_hdr is not null,
253 				 *	we must free it, as a new one is about to be created.
254 				 */
255 				clcp = mtod(isop->isop_clnpcache, struct clnp_cache *);
256 				if (clcp->clc_hdr != NULL) {
257 					/*
258 					 *	The clc_hdr is not null but a clnpcache mbuf exists.
259 					 *	This means that there was a cache, but the existing
260 					 *	copy of the hdr is no longer valid. Free it now
261 					 *	before we lose the pointer to it.
262 					 */
263 					IFDEBUG(D_OUTPUT)
264 						printf("clnp_output: freeing old clc_hdr 0x%x\n",
265 						clcp->clc_hdr);
266 					ENDDEBUG
267 					m_free(clcp->clc_hdr);
268 					IFDEBUG(D_OUTPUT)
269 						printf("clnp_output: freed old clc_hdr (done)\n");
270 					ENDDEBUG
271 				}
272 			}
273 		}
274 		IFDEBUG(D_OUTPUT)
275 			printf("clnp_output: NEW clcp x%x\n",clcp);
276 		ENDDEBUG
277 		bzero((caddr_t)clcp, sizeof(struct clnp_cache));
278 
279 		if (isop->isop_optindex)
280 			oidx = mtod(isop->isop_optindex, struct clnp_optidx *);
281 
282 		/*
283 		 *	Don't allow packets with security, quality of service,
284 		 *	priority, or error report options to be sent.
285 		 */
286 		if ((isop->isop_options) && (oidx)) {
287 			if ((oidx->cni_securep) ||
288 				(oidx->cni_priorp) ||
289 				(oidx->cni_qos_formatp) ||
290 				(oidx->cni_er_reason != ER_INVALREAS)) {
291 				IFDEBUG(D_OUTPUT)
292 					printf("clnp_output: pkt dropped - option unsupported\n");
293 				ENDDEBUG
294 				m_freem(m0);
295 				return(EINVAL);
296 			}
297 		}
298 
299 		/*
300 		 *	Don't allow any invalid flags to be set
301 		 */
302 		if ((flags & (CLNP_VFLAGS)) != flags) {
303 			IFDEBUG(D_OUTPUT)
304 				printf("clnp_output: packet dropped - flags unsupported\n");
305 			ENDDEBUG
306 			INCSTAT(cns_odropped);
307 			m_freem(m0);
308 			return(EINVAL);
309 		}
310 
311 		/*
312 		 *	Don't allow funny lengths on dst; src may be zero in which
313 		 *	case we insert the source address based upon the interface
314 		 */
315 		if ((src->isoa_len > sizeof(struct iso_addr)) ||
316 			(dst->isoa_len == 0) ||
317 			(dst->isoa_len > sizeof(struct iso_addr))) {
318 			m_freem(m0);
319 			INCSTAT(cns_odropped);
320 			return(ENAMETOOLONG);
321 		}
322 
323 		/*
324 		 *	Grab mbuf to contain header
325 		 */
326 		MGETHDR(m, M_DONTWAIT, MT_HEADER);
327 		if (m == 0) {
328 			m_freem(m0);
329 			INCSTAT(cns_odropped);
330 			return(ENOBUFS);
331 		}
332 		INCSTAT(cns_sent);
333 		m->m_next = m0;
334 		clnp = mtod(m, struct clnp_fixed *);
335 		clcp->clc_segoff = 0;
336 
337 		/*
338 		 *	Fill in all of fixed hdr except lengths and checksum
339 		 */
340 		if (flags & CLNP_SEND_RAW) {
341 			*clnp = raw_template;
342 		} else if (flags & CLNP_ECHO) {
343 			*clnp = echo_template;
344 		} else {
345 			*clnp = dt_template;
346 		}
347 		if (flags & CLNP_NO_SEG)
348 			clnp->cnf_type &= ~CNF_SEG_OK;
349 		if (flags & CLNP_NO_ER)
350 			clnp->cnf_type &= ~CNF_ERR_OK;
351 
352 		/*
353 		 *	Route packet; special case for source rt
354 		 */
355 		if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) {
356 			IFDEBUG(D_OUTPUT)
357 				printf("clnp_output: calling clnp_srcroute\n");
358 			ENDDEBUG
359 			error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route,
360 				&clcp->clc_firsthop, &clcp->clc_ifa, dst);
361 		} else {
362 			IFDEBUG(D_OUTPUT)
363 			ENDDEBUG
364 			error = clnp_route(dst, &isop->isop_route, flags,
365 				&clcp->clc_firsthop, &clcp->clc_ifa);
366 		}
367 		if (error || (clcp->clc_ifa == 0)) {
368 			IFDEBUG(D_OUTPUT)
369 				printf("clnp_output: route failed, errno %d\n", error);
370 				printf("@clcp:\n");
371 				dump_buf(clcp, sizeof (struct clnp_cache));
372 			ENDDEBUG
373 			goto bad;
374 		}
375 		clcp->clc_rt = isop->isop_route.ro_rt;	/* XXX */
376 		clcp->clc_ifp = clcp->clc_ifa->ia_ifp;  /* XXX */
377 
378 		IFDEBUG(D_OUTPUT)
379 			printf("clnp_output: packet routed to %s\n",
380 				clnp_iso_addrp(
381 					&((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
382 		ENDDEBUG
383 
384 		/*
385 		 *	If src address is not yet specified, use address of
386 		 *	interface. NOTE: this will now update the laddr field in
387 		 *	the isopcb. Is this desirable? RAH?
388 		 */
389 		if (src->isoa_len == 0) {
390 			src = &(clcp->clc_ifa->ia_addr.siso_addr);
391 			IFDEBUG(D_OUTPUT)
392 				printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
393 			ENDDEBUG
394 		}
395 
396 		/*
397 		 *	Insert the source and destination address,
398 		 */
399 		hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
400 		CLNP_INSERT_ADDR(hoff, *dst);
401 		CLNP_INSERT_ADDR(hoff, *src);
402 
403 		/*
404 		 *	Leave room for the segment part, if segmenting is selected
405 		 */
406 		if (clnp->cnf_type & CNF_SEG_OK) {
407 			clcp->clc_segoff = hoff - (caddr_t)clnp;
408 			hoff += sizeof(struct clnp_segment);
409 		}
410 
411 		clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
412 		hdrlen = clnp->cnf_hdr_len;
413 
414 #ifdef	DECBIT
415 		/*
416 		 *	Add the globally unique QOS (with room for congestion experienced
417 		 *	bit). I can safely assume that this option is not in the options
418 		 *	mbuf below because I checked that the option was not specified
419 		 *	previously
420 		 */
421 		if ((m->m_len + sizeof(qos_option)) < MLEN) {
422 			bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
423 			clnp->cnf_hdr_len += sizeof(qos_option);
424 			hdrlen += sizeof(qos_option);
425 			m->m_len += sizeof(qos_option);
426 		}
427 #endif	DECBIT
428 
429 		/*
430 		 *	If an options mbuf is present, concatenate a copy to the hdr mbuf.
431 		 */
432 		if (isop->isop_options) {
433 			struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
434 			if (opt_copy == NULL) {
435 				error = ENOBUFS;
436 				goto bad;
437 			}
438 			/* Link in place */
439 			opt_copy->m_next = m->m_next;
440 			m->m_next = opt_copy;
441 
442 			/* update size of header */
443 			clnp->cnf_hdr_len += opt_copy->m_len;
444 			hdrlen += opt_copy->m_len;
445 		}
446 
447 		if (hdrlen > CLNP_HDR_MAX) {
448 			error = EMSGSIZE;
449 			goto bad;
450 		}
451 
452 		/*
453 		 *	Now set up the cache entry in the pcb
454 		 */
455 		if ((flags & CLNP_NOCACHE) == 0) {
456 			if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
457 				clcp->clc_dst  = *dst;
458 				clcp->clc_flags = flags;
459 				clcp->clc_options = isop->isop_options;
460 			}
461 		}
462 	}
463 	/*
464 	 *	If small enough for interface, send directly
465 	 *	Fill in segmentation part of hdr if using the full protocol
466 	 */
467 	total_len = clnp->cnf_hdr_len + datalen;
468 	if (clnp->cnf_type & CNF_SEG_OK) {
469 		struct clnp_segment	seg_part;		/* segment part of hdr */
470 		seg_part.cng_id = htons(clnp_id++);
471 		seg_part.cng_off = htons(0);
472 		seg_part.cng_tot_len = htons(total_len);
473 		(void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
474 			sizeof(seg_part));
475 	}
476 	if (total_len <= SN_MTU(clcp->clc_ifp, clcp->clc_rt)) {
477 		HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
478 		m->m_pkthdr.len = total_len;
479 		/*
480 		 *	Compute clnp checksum (on header only)
481 		 */
482 		if (flags & CLNP_NO_CKSUM) {
483 			HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
484 		} else {
485 			iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
486 		}
487 
488 		IFDEBUG(D_DUMPOUT)
489 			struct mbuf *mdump = m;
490 			printf("clnp_output: sending dg:\n");
491 			while (mdump != NULL) {
492 				dump_buf(mtod(mdump, caddr_t), mdump->m_len);
493 				mdump = mdump->m_next;
494 			}
495 		ENDDEBUG
496 
497 		error = SN_OUTPUT(clcp, m);
498 		goto done;
499 	} else {
500 		/*
501 		 * Too large for interface; fragment if possible.
502 		 */
503 		error = clnp_fragment(clcp->clc_ifp, m, clcp->clc_firsthop,
504 							total_len, clcp->clc_segoff, flags, clcp->clc_rt);
505 		goto done;
506 	}
507 bad:
508 	m_freem(m);
509 done:
510 	if (error) {
511 		clnp_stat.cns_sent--;
512 		clnp_stat.cns_odropped++;
513 	}
514 	return (error);
515 }
516 
517 int clnp_ctloutput()
518 {
519 }
520