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