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