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.6 (Berkeley) 04/05/90 */ 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 src = &isop->isop_laddr->siso_addr; 156 dst = &isop->isop_faddr->siso_addr; 157 158 IFDEBUG(D_OUTPUT) 159 printf("clnp_output: to %s", clnp_iso_addrp(dst)); 160 printf(" from %s of %d bytes\n", clnp_iso_addrp(src), datalen); 161 printf("\toptions x%x, flags x%x, isop_clnpcache x%x\n", 162 isop->isop_options, flags, isop->isop_clnpcache); 163 ENDDEBUG 164 165 if (isop->isop_clnpcache != NULL) { 166 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 167 } 168 169 /* 170 * Check if cache is valid ... 171 */ 172 IFDEBUG(D_OUTPUT) 173 printf("clnp_output: ck cache: clcp %x\n", clcp); 174 if (clcp != NULL) { 175 printf("\tclc_dst %s\n", clnp_iso_addrp(&clcp->clc_dst)); 176 printf("\tisop_opts x%x, clc_opts x%x\n", isop->isop_options, 177 clcp->clc_options); 178 if (isop->isop_route.ro_rt) 179 printf("\tro_rt x%x, rt_flags x%x\n", 180 isop->isop_route.ro_rt, isop->isop_route.ro_rt->rt_flags); 181 printf("\tflags x%x, clc_flags x%x\n", flags, clcp->clc_flags); 182 printf("\tclc_hdr x%x\n", clcp->clc_hdr); 183 } 184 ENDDEBUG 185 if ((clcp != NULL) && /* cache exists */ 186 (isop->isop_options == clcp->clc_options) && /* same options */ 187 (iso_addrmatch1(dst, &clcp->clc_dst)) && /* dst still same */ 188 (isop->isop_route.ro_rt != NULL) && /* route exists */ 189 (isop->isop_route.ro_rt == clcp->clc_rt) && /* and is cached */ 190 (isop->isop_route.ro_rt->rt_flags & RTF_UP) && /* route still up */ 191 (flags == clcp->clc_flags) && /* same flags */ 192 (clcp->clc_hdr != NULL)) { /* hdr mbuf exists */ 193 /* 194 * The cache is valid 195 */ 196 197 IFDEBUG(D_OUTPUT) 198 printf("clnp_output: using cache\n"); 199 ENDDEBUG 200 201 m = m_copy(clcp->clc_hdr, 0, (int)M_COPYALL); 202 if (m == NULL) { 203 /* 204 * No buffers left to copy cached packet header. Use 205 * the cached packet header this time, and 206 * mark the hdr as vacant 207 */ 208 m = clcp->clc_hdr; 209 clcp->clc_hdr = NULL; 210 } 211 m->m_next = m0; /* ASSUMES pkt hdr is 1 mbuf long */ 212 clnp = mtod(m, struct clnp_fixed *); 213 } else { 214 struct clnp_optidx *oidx = NULL; /* index to clnp options */ 215 216 /* 217 * The cache is not valid. Allocate an mbuf (if necessary) 218 * to hold cached info. If one is not available, then 219 * don't bother with the cache 220 */ 221 INCSTAT(cns_cachemiss); 222 if (flags & CLNP_NOCACHE) { 223 clcp = &clc; 224 } else { 225 if (isop->isop_clnpcache == NULL) { 226 /* 227 * There is no clnpcache. Allocate an mbuf to hold one 228 */ 229 if ((isop->isop_clnpcache = m_get(M_DONTWAIT, MT_HEADER)) 230 == NULL) { 231 /* 232 * No mbufs available. Pretend that we don't want 233 * caching this time. 234 */ 235 IFDEBUG(D_OUTPUT) 236 printf("clnp_output: no mbufs to allocate to cache\n"); 237 ENDDEBUG 238 flags |= CLNP_NOCACHE; 239 clcp = &clc; 240 } else { 241 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 242 } 243 } else { 244 /* 245 * A clnpcache mbuf exists. If the clc_hdr is not null, 246 * we must free it, as a new one is about to be created. 247 */ 248 clcp = mtod(isop->isop_clnpcache, struct clnp_cache *); 249 if (clcp->clc_hdr != NULL) { 250 /* 251 * The clc_hdr is not null but a clnpcache mbuf exists. 252 * This means that there was a cache, but the existing 253 * copy of the hdr is no longer valid. Free it now 254 * before we lose the pointer to it. 255 */ 256 IFDEBUG(D_OUTPUT) 257 printf("clnp_output: freeing old clc_hdr 0x%x\n", 258 clcp->clc_hdr); 259 ENDDEBUG 260 m_free(clcp->clc_hdr); 261 IFDEBUG(D_OUTPUT) 262 printf("clnp_output: freed old clc_hdr (done)\n"); 263 ENDDEBUG 264 } 265 } 266 } 267 IFDEBUG(D_OUTPUT) 268 printf("clnp_output: NEW clcp x%x\n",clcp); 269 ENDDEBUG 270 bzero((caddr_t)clcp, sizeof(struct clnp_cache)); 271 272 if (isop->isop_optindex) 273 oidx = mtod(isop->isop_optindex, struct clnp_optidx *); 274 275 /* 276 * Don't allow packets with security, quality of service, 277 * priority, or error report options to be sent. 278 */ 279 if ((isop->isop_options) && (oidx)) { 280 if ((oidx->cni_securep) || 281 (oidx->cni_priorp) || 282 (oidx->cni_qos_formatp) || 283 (oidx->cni_er_reason != ER_INVALREAS)) { 284 IFDEBUG(D_OUTPUT) 285 printf("clnp_output: pkt dropped - option unsupported\n"); 286 ENDDEBUG 287 m_freem(m0); 288 return(EINVAL); 289 } 290 } 291 292 /* 293 * Don't allow any invalid flags to be set 294 */ 295 if ((flags & (CLNP_VFLAGS)) != flags) { 296 IFDEBUG(D_OUTPUT) 297 printf("clnp_output: packet dropped - flags unsupported\n"); 298 ENDDEBUG 299 INCSTAT(cns_odropped); 300 m_freem(m0); 301 return(EINVAL); 302 } 303 304 /* 305 * Don't allow funny lengths on dst; src may be zero in which 306 * case we insert the source address based upon the interface 307 */ 308 if ((src->isoa_len > sizeof(struct iso_addr)) || 309 (dst->isoa_len == 0) || 310 (dst->isoa_len > sizeof(struct iso_addr))) { 311 m_freem(m0); 312 INCSTAT(cns_odropped); 313 return(ENAMETOOLONG); 314 } 315 316 /* 317 * Grab mbuf to contain header 318 */ 319 MGETHDR(m, M_DONTWAIT, MT_HEADER); 320 if (m == 0) { 321 m_freem(m0); 322 INCSTAT(cns_odropped); 323 return(ENOBUFS); 324 } 325 INCSTAT(cns_sent); 326 m->m_next = m0; 327 clnp = mtod(m, struct clnp_fixed *); 328 clcp->clc_segoff = 0; 329 330 /* 331 * Fill in all of fixed hdr except lengths and checksum 332 */ 333 if (flags & CLNP_SEND_RAW) { 334 *clnp = raw_template; 335 } else if (flags & CLNP_ECHO) { 336 *clnp = echo_template; 337 } else { 338 *clnp = dt_template; 339 } 340 if (flags & CLNP_NO_SEG) 341 clnp->cnf_type &= ~CNF_SEG_OK; 342 if (flags & CLNP_NO_ER) 343 clnp->cnf_type &= ~CNF_ERR_OK; 344 345 /* 346 * Route packet; special case for source rt 347 */ 348 if ((isop->isop_options) && CLNPSRCRT_VALID(oidx)) { 349 IFDEBUG(D_OUTPUT) 350 printf("clnp_output: calling clnp_srcroute\n"); 351 ENDDEBUG 352 error = clnp_srcroute(isop->isop_options, oidx, &isop->isop_route, 353 &clcp->clc_firsthop, &clcp->clc_ifa, dst); 354 } else { 355 IFDEBUG(D_OUTPUT) 356 ENDDEBUG 357 error = clnp_route(dst, &isop->isop_route, flags, 358 &clcp->clc_firsthop, &clcp->clc_ifa); 359 } 360 if (error || (clcp->clc_ifa == 0)) { 361 IFDEBUG(D_OUTPUT) 362 printf("clnp_output: route failed, errno %d\n", error); 363 printf("@clcp:\n"); 364 dump_buf(clcp, sizeof (struct clnp_cache)); 365 ENDDEBUG 366 goto bad; 367 } 368 clcp->clc_rt = isop->isop_route.ro_rt; /* XXX */ 369 370 IFDEBUG(D_OUTPUT) 371 printf("clnp_output: packet routed to %s\n", 372 clnp_iso_addrp( 373 &((struct sockaddr_iso *)clcp->clc_firsthop)->siso_addr)); 374 ENDDEBUG 375 376 /* 377 * If src address is not yet specified, use address of 378 * interface. NOTE: this will now update the laddr field in 379 * the isopcb. Is this desirable? RAH? 380 */ 381 if (src->isoa_len == 0) { 382 src = &(clcp->clc_ifa->ia_addr.siso_addr); 383 IFDEBUG(D_OUTPUT) 384 printf("clnp_output: new src %s\n", clnp_iso_addrp(src)); 385 ENDDEBUG 386 } 387 388 /* 389 * Insert the source and destination address, 390 */ 391 hoff = (caddr_t)clnp + sizeof(struct clnp_fixed); 392 CLNP_INSERT_ADDR(hoff, *dst); 393 CLNP_INSERT_ADDR(hoff, *src); 394 395 /* 396 * Leave room for the segment part, if segmenting is selected 397 */ 398 if (clnp->cnf_type & CNF_SEG_OK) { 399 clcp->clc_segoff = hoff - (caddr_t)clnp; 400 hoff += sizeof(struct clnp_segment); 401 } 402 403 clnp->cnf_hdr_len = m->m_len = (u_char)(hoff - (caddr_t)clnp); 404 hdrlen = clnp->cnf_hdr_len; 405 406 #ifdef DECBIT 407 /* 408 * Add the globally unique QOS (with room for congestion experienced 409 * bit). I can safely assume that this option is not in the options 410 * mbuf below because I checked that the option was not specified 411 * previously 412 */ 413 if ((m->m_len + sizeof(qos_option)) < MLEN) { 414 bcopy((caddr_t)qos_option, hoff, sizeof(qos_option)); 415 clnp->cnf_hdr_len += sizeof(qos_option); 416 hdrlen += sizeof(qos_option); 417 m->m_len += sizeof(qos_option); 418 } 419 #endif DECBIT 420 421 /* 422 * If an options mbuf is present, concatenate a copy to the hdr mbuf. 423 */ 424 if (isop->isop_options) { 425 struct mbuf *opt_copy = m_copy(isop->isop_options, 0, (int)M_COPYALL); 426 if (opt_copy == NULL) { 427 error = ENOBUFS; 428 goto bad; 429 } 430 /* Link in place */ 431 opt_copy->m_next = m->m_next; 432 m->m_next = opt_copy; 433 434 /* update size of header */ 435 clnp->cnf_hdr_len += opt_copy->m_len; 436 hdrlen += opt_copy->m_len; 437 } 438 439 if (hdrlen > CLNP_HDR_MAX) { 440 error = EMSGSIZE; 441 goto bad; 442 } 443 444 /* 445 * Now set up the cache entry in the pcb 446 */ 447 if ((flags & CLNP_NOCACHE) == 0) { 448 if (clcp->clc_hdr = m_copy(m, 0, (int)clnp->cnf_hdr_len)) { 449 clcp->clc_dst = *dst; 450 clcp->clc_flags = flags; 451 clcp->clc_options = isop->isop_options; 452 } 453 } 454 } 455 /* 456 * If small enough for interface, send directly 457 * Fill in segmentation part of hdr if using the full protocol 458 */ 459 if ((total_len = clnp->cnf_hdr_len + datalen) 460 <= SN_MTU(clcp->clc_ifa->ia_ifp)) { 461 if (clnp->cnf_type & CNF_SEG_OK) { 462 struct clnp_segment seg_part; /* segment part of hdr */ 463 seg_part.cng_id = htons(clnp_id++); 464 seg_part.cng_off = htons(0); 465 seg_part.cng_tot_len = htons(total_len); 466 (void) bcopy((caddr_t)&seg_part, (caddr_t) clnp + clcp->clc_segoff, 467 sizeof(seg_part)); 468 } 469 HTOC(clnp->cnf_seglen_msb, clnp->cnf_seglen_lsb, total_len); 470 m->m_pkthdr.len = total_len; 471 /* 472 * Compute clnp checksum (on header only) 473 */ 474 if (flags & CLNP_NO_CKSUM) { 475 HTOC(clnp->cnf_cksum_msb, clnp->cnf_cksum_lsb, 0); 476 } else { 477 iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len); 478 } 479 480 IFDEBUG(D_DUMPOUT) 481 struct mbuf *mdump = m; 482 printf("clnp_output: sending dg:\n"); 483 while (mdump != NULL) { 484 dump_buf(mtod(mdump, caddr_t), mdump->m_len); 485 mdump = mdump->m_next; 486 } 487 ENDDEBUG 488 489 error = SN_OUTPUT(clcp, m); 490 goto done; 491 } else { 492 /* 493 * Too large for interface; fragment if possible. 494 */ 495 error = clnp_fragment(clcp->clc_ifa->ia_ifp, m, clcp->clc_firsthop, 496 total_len, clcp->clc_segoff, flags, clcp->clc_rt); 497 goto done; 498 } 499 bad: 500 m_freem(m); 501 done: 502 if (error) { 503 clnp_stat.cns_sent--; 504 clnp_stat.cns_odropped++; 505 } 506 return (error); 507 } 508 509 int clnp_ctloutput() 510 { 511 } 512