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