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