BSD 4_3_Reno release
[unix-history] / usr / src / sys / netiso / clnp_output.c
CommitLineData
e8370d1d
KS
1/***********************************************************
2 Copyright IBM Corporation 1987
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of IBM not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22******************************************************************/
23
24/*
25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26 */
e5c263cf
KS
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 $ */
1c15e888 29/* @(#)clnp_output.c 7.8 (Berkeley) 6/4/90 */
e8370d1d
KS
30
31#ifndef lint
e5c263cf 32static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_output.c,v 5.0 89/02/08 12:00:15 hagens Exp $";
e8370d1d
KS
33#endif lint
34
a50e2bc0
KS
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"
e8370d1d
KS
43
44#include "../net/if.h"
45#include "../net/route.h"
46
a50e2bc0
KS
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"
e8370d1d
KS
53
54static struct clnp_fixed dt_template = {
55 ISO8473_CLNP, /* network identifier */
56 0, /* length */
57 ISO8473_V1, /* version */
58 CLNP_TTL, /* ttl */
a50e2bc0 59 CLNP_DT|CNF_SEG_OK|CNF_ERR_OK, /* type */
e8370d1d
KS
60 0, /* segment length */
61 0 /* checksum */
62};
63
64static struct clnp_fixed raw_template = {
65 ISO8473_CLNP, /* network identifier */
66 0, /* length */
67 ISO8473_V1, /* version */
68 CLNP_TTL, /* ttl */
a50e2bc0 69 CLNP_RAW|CNF_SEG_OK|CNF_ERR_OK, /* type */
e8370d1d
KS
70 0, /* segment length */
71 0 /* checksum */
72};
73
74static struct clnp_fixed echo_template = {
75 ISO8473_CLNP, /* network identifier */
76 0, /* length */
77 ISO8473_V1, /* version */
78 CLNP_TTL, /* ttl */
a50e2bc0 79 CLNP_EC|CNF_SEG_OK|CNF_ERR_OK, /* type */
e8370d1d
KS
80 0, /* segment length */
81 0 /* checksum */
82};
83
e5c263cf
KS
84#ifdef DECBIT
85u_char qos_option[] = {CLNPOVAL_QOS, 1,
86 CLNPOVAL_GLOBAL|CLNPOVAL_SEQUENCING|CLNPOVAL_LOWDELAY};
87#endif DECBIT
88
e8370d1d
KS
89int 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 */
44f52ea5 138clnp_output(m0, isop, datalen, flags)
e8370d1d
KS
139struct mbuf *m0; /* data for the packet */
140struct isopcb *isop; /* iso pcb */
44f52ea5 141int datalen; /* number of bytes of data in m0 */
e8370d1d
KS
142int flags; /* flags */
143{
144 int error = 0; /* return value of function */
a50e2bc0 145 register struct mbuf *m = m0; /* mbuf for clnp header chain */
e8370d1d
KS
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 */
e5c263cf 153 int hdrlen = 0;
e8370d1d 154
a50e2bc0 155 dst = &isop->isop_faddr->siso_addr;
bec1b7dc
KS
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;
e8370d1d
KS
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 */
ddefa94c 196 (isop->isop_route.ro_rt == clcp->clc_rt) && /* and is cached */
e8370d1d
KS
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
a50e2bc0 208 m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL);
e8370d1d
KS
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
4f565be6 306 INCSTAT(cns_odropped);
e8370d1d
KS
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);
4f565be6 319 INCSTAT(cns_odropped);
e8370d1d
KS
320 return(ENAMETOOLONG);
321 }
322
323 /*
324 * Grab mbuf to contain header
325 */
a50e2bc0 326 MGETHDR(m, M_DONTWAIT, MT_HEADER);
e8370d1d
KS
327 if (m == 0) {
328 m_freem(m0);
4f565be6 329 INCSTAT(cns_odropped);
e8370d1d
KS
330 return(ENOBUFS);
331 }
4f565be6 332 INCSTAT(cns_sent);
e8370d1d
KS
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)
a50e2bc0 348 clnp->cnf_type &= ~CNF_SEG_OK;
e8370d1d 349 if (flags & CLNP_NO_ER)
a50e2bc0 350 clnp->cnf_type &= ~CNF_ERR_OK;
e8370d1d
KS
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,
a50e2bc0 360 &clcp->clc_firsthop, &clcp->clc_ifa, dst);
e8370d1d
KS
361 } else {
362 IFDEBUG(D_OUTPUT)
363 ENDDEBUG
364 error = clnp_route(dst, &isop->isop_route, flags,
a50e2bc0 365 &clcp->clc_firsthop, &clcp->clc_ifa);
e8370d1d 366 }
a50e2bc0 367 if (error || (clcp->clc_ifa == 0)) {
e8370d1d
KS
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 }
ddefa94c 375 clcp->clc_rt = isop->isop_route.ro_rt; /* XXX */
e8370d1d
KS
376
377 IFDEBUG(D_OUTPUT)
378 printf("clnp_output: packet routed to %s\n",
379 clnp_iso_addrp(
380 &((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr));
381 ENDDEBUG
382
383 /*
384 * If src address is not yet specified, use address of
385 * interface. NOTE: this will now update the laddr field in
386 * the isopcb. Is this desirable? RAH?
387 */
388 if (src->isoa_len == 0) {
a50e2bc0 389 src = &(clcp->clc_ifa->ia_addr.siso_addr);
e8370d1d
KS
390 IFDEBUG(D_OUTPUT)
391 printf("clnp_output: new src %s\n", clnp_iso_addrp(src));
392 ENDDEBUG
393 }
394
395 /*
396 * Insert the source and destination address,
397 */
398 hoff = (caddr_t)clnp + sizeof(struct clnp_fixed);
a50e2bc0
KS
399 CLNP_INSERT_ADDR(hoff, *dst);
400 CLNP_INSERT_ADDR(hoff, *src);
e8370d1d
KS
401
402 /*
403 * Leave room for the segment part, if segmenting is selected
404 */
a50e2bc0 405 if (clnp->cnf_type & CNF_SEG_OK) {
e8370d1d
KS
406 clcp->clc_segoff = hoff - (caddr_t)clnp;
407 hoff += sizeof(struct clnp_segment);
408 }
409
410 clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp);
e5c263cf
KS
411 hdrlen = clnp->cnf_hdr_len;
412
413#ifdef DECBIT
414 /*
415 * Add the globally unique QOS (with room for congestion experienced
416 * bit). I can safely assume that this option is not in the options
417 * mbuf below because I checked that the option was not specified
418 * previously
419 */
420 if ((m->m_len + sizeof(qos_option)) < MLEN) {
421 bcopy((caddr_t)qos_option, hoff, sizeof(qos_option));
422 clnp->cnf_hdr_len += sizeof(qos_option);
423 hdrlen += sizeof(qos_option);
424 m->m_len += sizeof(qos_option);
425 }
426#endif DECBIT
e8370d1d
KS
427
428 /*
429 * If an options mbuf is present, concatenate a copy to the hdr mbuf.
430 */
431 if (isop->isop_options) {
a50e2bc0 432 struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL);
e8370d1d
KS
433 if (opt_copy == NULL) {
434 error = ENOBUFS;
435 goto bad;
436 }
437 /* Link in place */
438 opt_copy->m_next = m->m_next;
439 m->m_next = opt_copy;
440
441 /* update size of header */
442 clnp->cnf_hdr_len += opt_copy->m_len;
e5c263cf
KS
443 hdrlen += opt_copy->m_len;
444 }
445
446 if (hdrlen > CLNP_HDR_MAX) {
447 error = EMSGSIZE;
448 goto bad;
e8370d1d
KS
449 }
450
451 /*
452 * Now set up the cache entry in the pcb
453 */
454 if ((flags & CLNP_NOCACHE) == 0) {
a50e2bc0
KS
455 if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) {
456 clcp->clc_dst = *dst;
e8370d1d
KS
457 clcp->clc_flags = flags;
458 clcp->clc_options = isop->isop_options;
459 }
460 }
461 }
e8370d1d
KS
462 /*
463 * If small enough for interface, send directly
464 * Fill in segmentation part of hdr if using the full protocol
465 */
282ab75d
KS
466 total_len = clnp->cnf_hdr_len + datalen;
467 if (clnp->cnf_type & CNF_SEG_OK) {
468 struct clnp_segment seg_part; /* segment part of hdr */
469 seg_part.cng_id = htons(clnp_id++);
470 seg_part.cng_off = htons(0);
471 seg_part.cng_tot_len = htons(total_len);
472 (void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff,
473 sizeof(seg_part));
474 }
475 if (total_len <= SN_MTU(clcp->clc_ifa->ia_ifp)) {
e8370d1d 476 HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len);
a50e2bc0 477 m->m_pkthdr.len = total_len;
e8370d1d
KS
478 /*
479 * Compute clnp checksum (on header only)
480 */
481 if (flags & CLNP_NO_CKSUM) {
482 HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0);
483 } else {
484 iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len);
485 }
486
487 IFDEBUG(D_DUMPOUT)
488 struct mbuf *mdump = m;
489 printf("clnp_output: sending dg:\n");
490 while (mdump != NULL) {
491 dump_buf(mtod(mdump, caddr_t), mdump->m_len);
492 mdump = mdump->m_next;
493 }
494 ENDDEBUG
495
496 error = SN_OUTPUT(clcp, m);
497 goto done;
498 } else {
499 /*
500 * Too large for interface; fragment if possible.
501 */
4f565be6 502 error = clnp_fragment(clcp->clc_ifa->ia_ifp, m, clcp->clc_firsthop,
ddefa94c 503 total_len, clcp->clc_segoff, flags, clcp->clc_rt);
e8370d1d
KS
504 goto done;
505 }
506bad:
507 m_freem(m);
e8370d1d 508done:
4f565be6
KS
509 if (error) {
510 clnp_stat.cns_sent--;
511 clnp_stat.cns_odropped++;
512 }
513 return (error);
e8370d1d
KS
514}
515
516int clnp_ctloutput()
517{
518}