2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1991, 1993
24 * The Regents of the University of California. All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. All advertising materials mentioning features or use of this software
35 * must display the following acknowledgement:
36 * This product includes software developed by the University of
37 * California, Berkeley and its contributors.
38 * 4. Neither the name of the University nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * @(#)clnp_output.c 8.1 (Berkeley) 6/10/93
57 /***********************************************************
58 Copyright IBM Corporation 1987
62 Permission to use, copy, modify, and distribute this software and its
63 documentation for any purpose and without fee is hereby granted,
64 provided that the above copyright notice appear in all copies and that
65 both that copyright notice and this permission notice appear in
66 supporting documentation, and that the name of IBM not be
67 used in advertising or publicity pertaining to distribution of the
68 software without specific, written prior permission.
70 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
71 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
72 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
73 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
74 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
75 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
78 ******************************************************************/
81 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
84 #include <sys/param.h>
86 #include <sys/domain.h>
87 #include <sys/protosw.h>
88 #include <sys/socket.h>
89 #include <sys/socketvar.h>
90 #include <sys/errno.h>
94 #include <net/route.h>
96 #include <netiso/iso.h>
97 #include <netiso/iso_var.h>
98 #include <netiso/iso_pcb.h>
99 #include <netiso/clnp.h>
100 #include <netiso/clnp_stat.h>
101 #include <netiso/argo_debug.h>
103 static struct clnp_fixed dt_template
= {
104 ISO8473_CLNP
, /* network identifier */
106 ISO8473_V1
, /* version */
108 CLNP_DT
|CNF_SEG_OK
|CNF_ERR_OK
, /* type */
109 0, /* segment length */
113 static struct clnp_fixed raw_template
= {
114 ISO8473_CLNP
, /* network identifier */
116 ISO8473_V1
, /* version */
118 CLNP_RAW
|CNF_SEG_OK
|CNF_ERR_OK
, /* type */
119 0, /* segment length */
123 static struct clnp_fixed echo_template
= {
124 ISO8473_CLNP
, /* network identifier */
126 ISO8473_V1
, /* version */
128 CLNP_EC
|CNF_SEG_OK
|CNF_ERR_OK
, /* type */
129 0, /* segment length */
133 static struct clnp_fixed echor_template
= {
134 ISO8473_CLNP
, /* network identifier */
136 ISO8473_V1
, /* version */
138 CLNP_ECR
|CNF_SEG_OK
|CNF_ERR_OK
, /* type */
139 0, /* segment length */
144 u_char qos_option
[] = {CLNPOVAL_QOS
, 1,
145 CLNPOVAL_GLOBAL
|CLNPOVAL_SEQUENCING
|CLNPOVAL_LOWDELAY
};
148 int clnp_id
= 0; /* id for segmented dgrams */
151 * FUNCTION: clnp_output
153 * PURPOSE: output the data in the mbuf as a clnp datagram
155 * The data specified by m0 is sent as a clnp datagram.
156 * The mbuf chain m0 will be freed when this routine has
159 * If options is non-null, it points to an mbuf which contains
160 * options to be sent with the datagram. The options must
161 * be formatted in the mbuf according to clnp rules. Options
164 * Datalen specifies the length of the data in m0.
166 * Src and dst are the addresses for the packet.
168 * If route is non-null, it is used as the route for
171 * By default, a DT is sent. However, if flags & CNLP_SEND_ER
172 * then an ER will be sent. If flags & CLNP_SEND_RAW, then
173 * the packet will be send as raw clnp.
176 * appropriate error code
181 * Flags are interpretated as follows:
182 * CLNP_NO_SEG - do not allow this pkt to be segmented.
183 * CLNP_NO_ER - have pkt request ER suppression.
184 * CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT
185 * CLNP_NO_CKSUM - don't compute clnp checksum
186 * CLNP_ECHO - send as ECHO packet
188 * When checking for a cached packet, clnp checks
189 * that the route taken is still up. It does not
190 * check that the route is still to the same destination.
191 * This means that any entity that alters an existing
192 * route for an isopcb (such as when a redirect arrives)
193 * must invalidate the clnp cache. It might be perferable
194 * to have clnp check that the route has the same dest, but
195 * by avoiding this check, we save a call to iso_addrmatch1.
197 clnp_output(m0
, isop
, datalen
, flags
)
198 struct mbuf
*m0
; /* data for the packet */
199 struct isopcb
*isop
; /* iso pcb */
200 int datalen
; /* number of bytes of data in m0 */
201 int flags
; /* flags */
203 int error
= 0; /* return value of function */
204 register struct mbuf
*m
= m0
; /* mbuf for clnp header chain */
205 register struct clnp_fixed
*clnp
; /* ptr to fixed part of hdr */
206 register caddr_t hoff
; /* offset into header */
207 int total_len
; /* total length of packet */
208 struct iso_addr
*src
; /* ptr to source address */
209 struct iso_addr
*dst
; /* ptr to destination address */
210 struct clnp_cache clc
; /* storage for cache information */
211 struct clnp_cache
*clcp
= NULL
; /* ptr to clc */
214 dst
= &isop
->isop_faddr
->siso_addr
;
215 if (isop
->isop_laddr
== 0) {
216 struct iso_ifaddr
*ia
= 0;
217 clnp_route(dst
, &isop
->isop_route
, flags
, 0, &ia
);
218 if (ia
== 0 || ia
->ia_ifa
.ifa_addr
->sa_family
!= AF_ISO
)
219 return (ENETUNREACH
);
220 src
= &ia
->ia_addr
.siso_addr
;
222 src
= &isop
->isop_laddr
->siso_addr
;
225 printf("clnp_output: to %s", clnp_iso_addrp(dst
));
226 printf(" from %s of %d bytes\n", clnp_iso_addrp(src
), datalen
);
227 printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n",
228 isop
->isop_options
, flags
, isop
->isop_clnpcache
);
231 if (isop
->isop_clnpcache
!= NULL
) {
232 clcp
= mtod(isop
->isop_clnpcache
, struct clnp_cache
*);
236 * Check if cache is valid ...
239 printf("clnp_output: ck cache: clcp %x\n", clcp
);
241 printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp
->clc_dst
));
242 printf("\tisop_opts x%x, clc_opts x%x\n", isop
->isop_options
,
244 if (isop
->isop_route
.ro_rt
)
245 printf("\tro_rt x%x, rt_flags x%x\n",
246 isop
->isop_route
.ro_rt
, isop
->isop_route
.ro_rt
->rt_flags
);
247 printf("\tflags x%x, clc_flags x%x\n", flags
, clcp
->clc_flags
);
248 printf("\tclc_hdr x%x\n", clcp
->clc_hdr
);
251 if ((clcp
!= NULL
) && /* cache exists */
252 (isop
->isop_options
== clcp
->clc_options
) && /* same options */
253 (iso_addrmatch1(dst
, &clcp
->clc_dst
)) && /* dst still same */
254 (isop
->isop_route
.ro_rt
!= NULL
) && /* route exists */
255 (isop
->isop_route
.ro_rt
== clcp
->clc_rt
) && /* and is cached */
256 (isop
->isop_route
.ro_rt
->rt_flags
& RTF_UP
) && /* route still up */
257 (flags
== clcp
->clc_flags
) && /* same flags */
258 (clcp
->clc_hdr
!= NULL
)) { /* hdr mbuf exists */
264 printf("clnp_output: using cache\n");
267 m
= m_copy(clcp
->clc_hdr
, 0, (int)M_COPYALL
);
270 * No buffers left to copy cached packet header. Use
271 * the cached packet header this time, and
272 * mark the hdr as vacant
275 clcp
->clc_hdr
= NULL
;
277 m
->m_next
= m0
; /* ASSUMES pkt hdr is 1 mbuf long */
278 clnp
= mtod(m
, struct clnp_fixed
*);
280 struct clnp_optidx
*oidx
= NULL
; /* index to clnp options */
283 * The cache is not valid. Allocate an mbuf (if necessary)
284 * to hold cached info. If one is not available, then
285 * don't bother with the cache
287 INCSTAT(cns_cachemiss
);
288 if (flags
& CLNP_NOCACHE
) {
291 if (isop
->isop_clnpcache
== NULL
) {
293 * There is no clnpcache. Allocate an mbuf to hold one
295 if ((isop
->isop_clnpcache
= m_get(M_DONTWAIT
, MT_HEADER
))
298 * No mbufs available. Pretend that we don't want
302 printf("clnp_output: no mbufs to allocate to cache\n");
304 flags
|= CLNP_NOCACHE
;
307 clcp
= mtod(isop
->isop_clnpcache
, struct clnp_cache
*);
311 * A clnpcache mbuf exists. If the clc_hdr is not null,
312 * we must free it, as a new one is about to be created.
314 clcp
= mtod(isop
->isop_clnpcache
, struct clnp_cache
*);
315 if (clcp
->clc_hdr
!= NULL
) {
317 * The clc_hdr is not null but a clnpcache mbuf exists.
318 * This means that there was a cache, but the existing
319 * copy of the hdr is no longer valid. Free it now
320 * before we lose the pointer to it.
323 printf("clnp_output: freeing old clc_hdr 0x%x\n",
326 m_free(clcp
->clc_hdr
);
328 printf("clnp_output: freed old clc_hdr (done)\n");
334 printf("clnp_output: NEW clcp x%x\n",clcp
);
336 bzero((caddr_t
)clcp
, sizeof(struct clnp_cache
));
338 if (isop
->isop_optindex
)
339 oidx
= mtod(isop
->isop_optindex
, struct clnp_optidx
*);
342 * Don't allow packets with security, quality of service,
343 * priority, or error report options to be sent.
345 if ((isop
->isop_options
) && (oidx
)) {
346 if ((oidx
->cni_securep
) ||
347 (oidx
->cni_priorp
) ||
348 (oidx
->cni_qos_formatp
) ||
349 (oidx
->cni_er_reason
!= ER_INVALREAS
)) {
351 printf("clnp_output: pkt dropped - option unsupported\n");
359 * Don't allow any invalid flags to be set
361 if ((flags
& (CLNP_VFLAGS
)) != flags
) {
363 printf("clnp_output: packet dropped - flags unsupported\n");
365 INCSTAT(cns_odropped
);
371 * Don't allow funny lengths on dst; src may be zero in which
372 * case we insert the source address based upon the interface
374 if ((src
->isoa_len
> sizeof(struct iso_addr
)) ||
375 (dst
->isoa_len
== 0) ||
376 (dst
->isoa_len
> sizeof(struct iso_addr
))) {
378 INCSTAT(cns_odropped
);
379 return(ENAMETOOLONG
);
383 * Grab mbuf to contain header
385 MGETHDR(m
, M_DONTWAIT
, MT_HEADER
);
388 INCSTAT(cns_odropped
);
393 clnp
= mtod(m
, struct clnp_fixed
*);
394 clcp
->clc_segoff
= 0;
397 * Fill in all of fixed hdr except lengths and checksum
399 if (flags
& CLNP_SEND_RAW
) {
400 *clnp
= raw_template
;
401 } else if (flags
& CLNP_ECHO
) {
402 *clnp
= echo_template
;
403 } else if (flags
& CLNP_ECHOR
) {
404 *clnp
= echor_template
;
408 if (flags
& CLNP_NO_SEG
)
409 clnp
->cnf_type
&= ~CNF_SEG_OK
;
410 if (flags
& CLNP_NO_ER
)
411 clnp
->cnf_type
&= ~CNF_ERR_OK
;
414 * Route packet; special case for source rt
416 if ((isop
->isop_options
) && CLNPSRCRT_VALID(oidx
)) {
418 printf("clnp_output: calling clnp_srcroute\n");
420 error
= clnp_srcroute(isop
->isop_options
, oidx
, &isop
->isop_route
,
421 &clcp
->clc_firsthop
, &clcp
->clc_ifa
, dst
);
425 error
= clnp_route(dst
, &isop
->isop_route
, flags
,
426 &clcp
->clc_firsthop
, &clcp
->clc_ifa
);
428 if (error
|| (clcp
->clc_ifa
== 0)) {
430 printf("clnp_output: route failed, errno %d\n", error
);
432 dump_buf(clcp
, sizeof (struct clnp_cache
));
436 clcp
->clc_rt
= isop
->isop_route
.ro_rt
; /* XXX */
437 clcp
->clc_ifp
= clcp
->clc_ifa
->ia_ifp
; /* XXX */
440 printf("clnp_output: packet routed to %s\n",
442 &((struct sockaddr_iso
*)clcp
->clc_firsthop
)->siso_addr
));
446 * If src address is not yet specified, use address of
447 * interface. NOTE: this will now update the laddr field in
448 * the isopcb. Is this desirable? RAH?
450 if (src
->isoa_len
== 0) {
451 src
= &(clcp
->clc_ifa
->ia_addr
.siso_addr
);
453 printf("clnp_output: new src %s\n", clnp_iso_addrp(src
));
458 * Insert the source and destination address,
460 hoff
= (caddr_t
)clnp
+ sizeof(struct clnp_fixed
);
461 CLNP_INSERT_ADDR(hoff
, *dst
);
462 CLNP_INSERT_ADDR(hoff
, *src
);
465 * Leave room for the segment part, if segmenting is selected
467 if (clnp
->cnf_type
& CNF_SEG_OK
) {
468 clcp
->clc_segoff
= hoff
- (caddr_t
)clnp
;
469 hoff
+= sizeof(struct clnp_segment
);
472 clnp
->cnf_hdr_len
= m
->m_len
= (u_char
)(hoff
- (caddr_t
)clnp
);
473 hdrlen
= clnp
->cnf_hdr_len
;
477 * Add the globally unique QOS (with room for congestion experienced
478 * bit). I can safely assume that this option is not in the options
479 * mbuf below because I checked that the option was not specified
482 if ((m
->m_len
+ sizeof(qos_option
)) < MLEN
) {
483 bcopy((caddr_t
)qos_option
, hoff
, sizeof(qos_option
));
484 clnp
->cnf_hdr_len
+= sizeof(qos_option
);
485 hdrlen
+= sizeof(qos_option
);
486 m
->m_len
+= sizeof(qos_option
);
491 * If an options mbuf is present, concatenate a copy to the hdr mbuf.
493 if (isop
->isop_options
) {
494 struct mbuf
*opt_copy
= m_copy(isop
->isop_options
, 0, (int)M_COPYALL
);
495 if (opt_copy
== NULL
) {
500 opt_copy
->m_next
= m
->m_next
;
501 m
->m_next
= opt_copy
;
503 /* update size of header */
504 clnp
->cnf_hdr_len
+= opt_copy
->m_len
;
505 hdrlen
+= opt_copy
->m_len
;
508 if (hdrlen
> CLNP_HDR_MAX
) {
514 * Now set up the cache entry in the pcb
516 if ((flags
& CLNP_NOCACHE
) == 0) {
517 if (clcp
->clc_hdr
= m_copy(m
, 0, (int)clnp
->cnf_hdr_len
)) {
518 clcp
->clc_dst
= *dst
;
519 clcp
->clc_flags
= flags
;
520 clcp
->clc_options
= isop
->isop_options
;
525 * If small enough for interface, send directly
526 * Fill in segmentation part of hdr if using the full protocol
528 total_len
= clnp
->cnf_hdr_len
+ datalen
;
529 if (clnp
->cnf_type
& CNF_SEG_OK
) {
530 struct clnp_segment seg_part
; /* segment part of hdr */
531 seg_part
.cng_id
= htons(clnp_id
++);
532 seg_part
.cng_off
= htons(0);
533 seg_part
.cng_tot_len
= htons(total_len
);
534 (void) bcopy((caddr_t
)&seg_part
, (caddr_t
) clnp
+ clcp
->clc_segoff
,
537 if (total_len
<= SN_MTU(clcp
->clc_ifp
, clcp
->clc_rt
)) {
538 HTOC(clnp
->cnf_seglen_msb
, clnp
->cnf_seglen_lsb
, total_len
);
539 m
->m_pkthdr
.len
= total_len
;
541 * Compute clnp checksum (on header only)
543 if (flags
& CLNP_NO_CKSUM
) {
544 HTOC(clnp
->cnf_cksum_msb
, clnp
->cnf_cksum_lsb
, 0);
546 iso_gen_csum(m
, CLNP_CKSUM_OFF
, (int)clnp
->cnf_hdr_len
);
550 struct mbuf
*mdump
= m
;
551 printf("clnp_output: sending dg:\n");
552 while (mdump
!= NULL
) {
553 dump_buf(mtod(mdump
, caddr_t
), mdump
->m_len
);
554 mdump
= mdump
->m_next
;
558 error
= SN_OUTPUT(clcp
, m
);
562 * Too large for interface; fragment if possible.
564 error
= clnp_fragment(clcp
->clc_ifp
, m
, clcp
->clc_firsthop
,
565 total_len
, clcp
->clc_segoff
, flags
, clcp
->clc_rt
);
572 clnp_stat
.cns_sent
--;
573 clnp_stat
.cns_odropped
++;