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