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