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