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: clnp_output.c,v 4.5 88/09/15 18:57:57 hagens Exp $ */ 28 /* $Source: /usr/argo/sys/netiso/RCS/clnp_output.c,v $ */ 29 30 #ifndef lint 31 static char *rcsid = "$Header: clnp_output.c,v 4.5 88/09/15 18:57:57 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 int clnp_id = 0; /* id for segmented dgrams */ 118 119 /* 120 * FUNCTION: clnp_output 121 * 122 * PURPOSE: output the data in the mbuf as a clnp datagram 123 * 124 * The data specified by m0 is sent as a clnp datagram. 125 * The mbuf chain m0 will be freed when this routine has 126 * returned. 127 * 128 * If options is non-null, it points to an mbuf which contains 129 * options to be sent with the datagram. The options must 130 * be formatted in the mbuf according to clnp rules. Options 131 * will not be freed. 132 * 133 * Datalen specifies the length of the data in m0. 134 * 135 * Src and dst are the addresses for the packet. 136 * 137 * If route is non-null, it is used as the route for 138 * the packet. 139 * 140 * By default, a DT is sent. However, if flags & CNLP_SEND_ER 141 * then an ER will be sent. If flags & CLNP_SEND_RAW, then 142 * the packet will be send as raw clnp. 143 * 144 * RETURNS: 0 success 145 * appropriate error code 146 * 147 * SIDE EFFECTS: none 148 * 149 * NOTES: 150 * Flags are interpretated as follows: 151 * CLNP_NO_SEG - do not allow this pkt to be segmented. 152 * CLNP_NO_ER - have pkt request ER suppression. 153 * CLNP_SEND_RAW - send pkt as RAW DT rather than TP DT 154 * CLNP_NO_CKSUM - don't compute clnp checksum 155 * CLNP_ECHO - send as ECHO packet 156 * 157 * When checking for a cached packet, clnp checks 158 * that the route taken is still up. It does not 159 * check that the route is still to the same destination. 160 * This means that any entity that alters an existing 161 * route for an isopcb (such as when a redirect arrives) 162 * must invalidate the clnp cache. It might be perferable 163 * to have clnp check that the route has the same dest, but 164 * by avoiding this check, we save a call to iso_addrmatch1. 165 */ 166 clnp_output(m0, isop, datalen, flags) 167 struct mbuf *m0; /* data for the packet */ 168 struct isopcb *isop; /* iso pcb */ 169 int datalen; /* number of bytes of data in m */ 170 int flags; /* flags */ 171 { 172 int error = 0; /* return value of function */ 173 register struct mbuf *m; /* mbuf for clnp header chain */ 174 register struct clnp_fixed *clnp; /* ptr to fixed part of hdr */ 175 register caddr_t hoff; /* offset into header */ 176 int total_len; /* total length of packet */ 177 struct iso_addr *src; /* ptr to source address */ 178 struct iso_addr *dst; /* ptr to destination address */ 179 struct clnp_cache clc; /* storage for cache information */ 180 struct clnp_cache *clcp = NULL; /* ptr to clc */ 181 182 src = &isop->isop_laddr.siso_addr; 183 dst = &isop->isop_faddr.siso_addr; 184 185 IFDEBUG(D_OUTPUT) 186 printf("clnp_output: to %s", clnp_iso_addrp(dst)); 187 printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen); 188 printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n", 189 isop->isop_options, flags, isop->isop_clnpcache); 190 ENDDEBUG 191 192 if (isop->isop_clnpcache != NULL) { 193 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 194 } 195 196 /* 197 * Check if cache is valid ... 198 */ 199 IFDEBUG(D_OUTPUT) 200 printf("clnp_output: ck cache: clcp %x\n", clcp); 201 if (clcp != NULL) { 202 printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst)); 203 printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options, 204 clcp->clc_options); 205 if (isop->isop_route.ro_rt) 206 printf("\tro_rt x%x, rt_flags x%x\n", 207 isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags); 208 printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags); 209 printf("\tclc_hdr x%x\n", clcp->clc_hdr); 210 } 211 ENDDEBUG 212 if ((clcp != NULL) && /* cache exists */ 213 (isop->isop_options == clcp->clc_options) && /* same options */ 214 (iso_addrmatch1(dst, &clcp->clc_dst)) && /* dst still same */ 215 (isop->isop_route.ro_rt != NULL) && /* route exists */ 216 (isop->isop_route.ro_rt->rt_flags & RTF_UP) && /* route still up */ 217 (flags == clcp->clc_flags) && /* same flags */ 218 (clcp->clc_hdr != NULL)) { /* hdr mbuf exists */ 219 /* 220 * The cache is valid 221 */ 222 223 IFDEBUG(D_OUTPUT) 224 printf("clnp_output: using cache\n"); 225 ENDDEBUG 226 227 m = m_copy(clcp->clc_hdr, 0, M_COPYALL); 228 if (m == NULL) { 229 /* 230 * No buffers left to copy cached packet header. Use 231 * the cached packet header this time, and 232 * mark the hdr as vacant 233 */ 234 m = clcp->clc_hdr; 235 clcp->clc_hdr = NULL; 236 } 237 m->m_next = m0; /* ASSUMES pkt hdr is 1 mbuf long */ 238 clnp = mtod(m, struct clnp_fixed *); 239 } else { 240 struct clnp_optidx *oidx = NULL; /* index to clnp options */ 241 242 /* 243 * The cache is not valid. Allocate an mbuf (if necessary) 244 * to hold cached info. If one is not available, then 245 * don't bother with the cache 246 */ 247 INCSTAT(cns_cachemiss); 248 if (flags & CLNP_NOCACHE) { 249 clcp = &clc; 250 } else { 251 if (isop->isop_clnpcache == NULL) { 252 /* 253 * There is no clnpcache. Allocate an mbuf to hold one 254 */ 255 if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER)) 256 == NULL) { 257 /* 258 * No mbufs available. Pretend that we don't want 259 * caching this time. 260 */ 261 IFDEBUG(D_OUTPUT) 262 printf("clnp_output: no mbufs to allocate to cache\n"); 263 ENDDEBUG 264 flags |= CLNP_NOCACHE; 265 clcp = &clc; 266 } else { 267 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 268 } 269 } else { 270 /* 271 * A clnpcache mbuf exists. If the clc_hdr is not null, 272 * we must free it, as a new one is about to be created. 273 */ 274 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 275 if (clcp->clc_hdr != NULL) { 276 /* 277 * The clc_hdr is not null but a clnpcache mbuf exists. 278 * This means that there was a cache, but the existing 279 * copy of the hdr is no longer valid. Free it now 280 * before we lose the pointer to it. 281 */ 282 IFDEBUG(D_OUTPUT) 283 printf("clnp_output: freeing old clc_hdr 0x%x\n", 284 clcp->clc_hdr); 285 ENDDEBUG 286 m_free(clcp->clc_hdr); 287 IFDEBUG(D_OUTPUT) 288 printf("clnp_output: freed old clc_hdr (done)\n"); 289 ENDDEBUG 290 } 291 } 292 } 293 IFDEBUG(D_OUTPUT) 294 printf("clnp_output: NEW clcp x%x\n",clcp); 295 ENDDEBUG 296 bzero((caddr_t)clcp, sizeof(struct clnp_cache)); 297 298 if (isop->isop_optindex) 299 oidx = mtod(isop->isop_optindex, struct clnp_optidx *); 300 301 /* 302 * Don't allow packets with security, quality of service, 303 * priority, or error report options to be sent. 304 */ 305 if ((isop->isop_options) && (oidx)) { 306 if ((oidx->cni_securep) || 307 (oidx->cni_priorp) || 308 (oidx->cni_qos_formatp) || 309 (oidx->cni_er_reason != ER_INVALREAS)) { 310 IFDEBUG(D_OUTPUT) 311 printf("clnp_output: pkt dropped - option unsupported\n"); 312 ENDDEBUG 313 m_freem(m0); 314 return(EINVAL); 315 } 316 } 317 318 /* 319 * Don't allow any invalid flags to be set 320 */ 321 if ((flags & (CLNP_VFLAGS)) != flags) { 322 IFDEBUG(D_OUTPUT) 323 printf("clnp_output: packet dropped - flags unsupported\n"); 324 ENDDEBUG 325 m_freem(m0); 326 return(EINVAL); 327 } 328 329 /* 330 * Don't allow funny lengths on dst; src may be zero in which 331 * case we insert the source address based upon the interface 332 */ 333 if ((src->isoa_len > sizeof(struct iso_addr)) || 334 (dst->isoa_len == 0) || 335 (dst->isoa_len > sizeof(struct iso_addr))) { 336 m_freem(m0); 337 return(ENAMETOOLONG); 338 } 339 340 /* 341 * Grab mbuf to contain header 342 */ 343 MGET(m, M_DONTWAIT, MT_HEADER); 344 if (m == 0) { 345 m_freem(m0); 346 return(ENOBUFS); 347 } 348 349 m->m_next = m0; 350 clnp = mtod(m, struct clnp_fixed *); 351 clcp->clc_segoff = 0; 352 353 /* 354 * Fill in all of fixed hdr except lengths and checksum 355 */ 356 if (flags & CLNP_SEND_RAW) { 357 *clnp = raw_template; 358 } else if (flags & CLNP_ECHO) { 359 *clnp = echo_template; 360 } else { 361 *clnp = dt_template; 362 } 363 if (flags & CLNP_NO_SEG) 364 clnp->cnf_seg_ok = 0; 365 if (flags & CLNP_NO_ER) 366 clnp->cnf_err_ok = 0; 367 368 /* 369 * Route packet; special case for source rt 370 */ 371 if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) { 372 IFDEBUG(D_OUTPUT) 373 printf("clnp_output: calling clnp_srcroute\n"); 374 ENDDEBUG 375 error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route, 376 &clcp->clc_firsthop, &clcp->clc_ifp, dst); 377 } else { 378 IFDEBUG(D_OUTPUT) 379 ENDDEBUG 380 error = clnp_route(dst, &isop->isop_route, flags, 381 &clcp->clc_firsthop, &clcp->clc_ifp); 382 } 383 if (error != 0) { 384 IFDEBUG(D_OUTPUT) 385 printf("clnp_output: route failed, errno %d\n", error); 386 printf("@clcp:\n"); 387 dump_buf(clcp, sizeof (struct clnp_cache)); 388 ENDDEBUG 389 goto bad; 390 } 391 392 IFDEBUG(D_OUTPUT) 393 printf("clnp_output: packet routed to %s\n", 394 clnp_iso_addrp( 395 &((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr)); 396 ENDDEBUG 397 398 /* 399 * If src address is not yet specified, use address of 400 * interface. NOTE: this will now update the laddr field in 401 * the isopcb. Is this desirable? RAH? 402 */ 403 if (src->isoa_len == 0) { 404 src = clnp_srcaddr(clcp->clc_ifp, 405 &((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr); 406 if (src == NULL) { 407 error = ENETDOWN; 408 goto bad; 409 } 410 IFDEBUG(D_OUTPUT) 411 printf("clnp_output: new src %s\n", clnp_iso_addrp(src)); 412 ENDDEBUG 413 } 414 415 /* 416 * Insert the source and destination address, 417 */ 418 hoff = (caddr_t)clnp + sizeof(struct clnp_fixed); 419 CLNP_INSERT_ADDR(hoff, dst); 420 CLNP_INSERT_ADDR(hoff, src); 421 422 /* 423 * Leave room for the segment part, if segmenting is selected 424 */ 425 if (clnp->cnf_seg_ok) { 426 clcp->clc_segoff = hoff - (caddr_t)clnp; 427 hoff += sizeof(struct clnp_segment); 428 } 429 430 clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp); 431 432 /* 433 * If an options mbuf is present, concatenate a copy to the hdr mbuf. 434 */ 435 if (isop->isop_options) { 436 struct mbuf *opt_copy = m_copy(isop->isop_options, 0, M_COPYALL); 437 if (opt_copy == NULL) { 438 error = ENOBUFS; 439 goto bad; 440 } 441 /* Link in place */ 442 opt_copy->m_next = m->m_next; 443 m->m_next = opt_copy; 444 445 /* update size of header */ 446 clnp->cnf_hdr_len += opt_copy->m_len; 447 } 448 449 /* 450 * Now set up the cache entry in the pcb 451 */ 452 if ((flags & CLNP_NOCACHE) == 0) { 453 if ((clcp->clc_hdr = m_copy(m, 0, clnp->cnf_hdr_len)) != NULL) { 454 bcopy((caddr_t)dst, (caddr_t)&clcp->clc_dst, 455 sizeof(struct iso_addr)); 456 clcp->clc_flags = flags; 457 clcp->clc_options = isop->isop_options; 458 } 459 } 460 } 461 INCSTAT(cns_sent); 462 /* 463 * If small enough for interface, send directly 464 * Fill in segmentation part of hdr if using the full protocol 465 */ 466 if ((total_len = clnp->cnf_hdr_len + datalen) <= SN_MTU(clcp->clc_ifp)) { 467 if (clnp->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 HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len); 476 477 /* 478 * Compute clnp checksum (on header only) 479 */ 480 if (flags & CLNP_NO_CKSUM) { 481 HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0); 482 } else { 483 iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len); 484 } 485 486 IFDEBUG(D_DUMPOUT) 487 struct mbuf *mdump = m; 488 printf("clnp_output: sending dg:\n"); 489 while (mdump != NULL) { 490 dump_buf(mtod(mdump, caddr_t), mdump->m_len); 491 mdump = mdump->m_next; 492 } 493 ENDDEBUG 494 495 error = SN_OUTPUT(clcp, m); 496 goto done; 497 } else { 498 /* 499 * Too large for interface; fragment if possible. 500 */ 501 error = clnp_fragment(clcp->clc_ifp, m, clcp->clc_firsthop, total_len, 502 clcp->clc_segoff, flags); 503 goto done; 504 } 505 bad: 506 m_freem(m); 507 508 done: 509 return(error); 510 } 511 512 int clnp_ctloutput() 513 { 514 } 515 516 #endif ISO 517