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