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 /* 28 * ARGO TP 29 * $Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $ 30 * $Source: /var/src/sys/netiso/RCS/tp_iso.c,v $ 31 * @(#)tp_iso.c 7.5 (Berkeley) 01/16/90 32 * 33 * Here is where you find the iso-dependent code. We've tried 34 * keep all net-level and (primarily) address-family-dependent stuff 35 * out of the tp source, and everthing here is reached indirectly 36 * through a switch table (struct nl_protosw *) tpcb->tp_nlproto 37 * (see tp_pcb.c). 38 * The routines here are: 39 * iso_getsufx: gets transport suffix out of an isopcb structure. 40 * iso_putsufx: put transport suffix into an isopcb structure. 41 * iso_putnetaddr: put a whole net addr into an isopcb. 42 * iso_getnetaddr: get a whole net addr from an isopcb. 43 * iso_recycle_suffix: clear suffix for reuse in isopcb 44 * tpclnp_ctlinput: handle ER CNLPdu : icmp-like stuff 45 * tpclnp_mtu: figure out what size tpdu to use 46 * tpclnp_input: take a pkt from clnp, strip off its clnp header, 47 * give to tp 48 * tpclnp_output_dg: package a pkt for clnp given 2 addresses & some data 49 * tpclnp_output: package a pkt for clnp given an isopcb & some data 50 */ 51 52 #ifndef lint 53 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $"; 54 #endif lint 55 56 #ifdef ISO 57 58 #include "param.h" 59 #include "socket.h" 60 #include "socketvar.h" 61 #include "domain.h" 62 #include "malloc.h" 63 #include "mbuf.h" 64 #include "errno.h" 65 #include "time.h" 66 #include "protosw.h" 67 68 #include "../net/if.h" 69 #include "../net/route.h" 70 71 #include "argo_debug.h" 72 #include "tp_param.h" 73 #include "tp_stat.h" 74 #include "tp_pcb.h" 75 #include "tp_trace.h" 76 #include "tp_stat.h" 77 #include "tp_tpdu.h" 78 #include "tp_clnp.h" 79 #include "cltp_var.h" 80 81 /* 82 * CALLED FROM: 83 * pr_usrreq() on PRU_BIND, PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR 84 * FUNCTION, ARGUMENTS: 85 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 86 */ 87 88 iso_getsufx(isop, lenp, data_out, which) 89 struct isopcb *isop; 90 u_short *lenp; 91 caddr_t data_out; 92 int which; 93 { 94 register struct sockaddr_iso *addr = 0; 95 96 switch (which) { 97 case TP_LOCAL: 98 addr = isop->isop_laddr; 99 break; 100 101 case TP_FOREIGN: 102 addr = isop->isop_faddr; 103 } 104 if (addr) 105 bcopy(TSEL(addr), data_out, (*lenp = addr->siso_tlen)); 106 } 107 108 /* CALLED FROM: 109 * tp_newsocket(); i.e., when a connection is being established by an 110 * incoming CR_TPDU. 111 * 112 * FUNCTION, ARGUMENTS: 113 * Put a transport suffix (found in name) into an isopcb structure (isop). 114 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 115 */ 116 void 117 iso_putsufx(isop, sufxloc, sufxlen, which) 118 struct isopcb *isop; 119 caddr_t sufxloc; 120 int sufxlen, which; 121 { 122 struct sockaddr_iso **dst, *backup; 123 register struct sockaddr_iso *addr; 124 struct mbuf *m; 125 int len; 126 127 switch (which) { 128 default: 129 return; 130 131 case TP_LOCAL: 132 dst = &isop->isop_laddr; 133 backup = &isop->isop_sladdr; 134 break; 135 136 case TP_FOREIGN: 137 dst = &isop->isop_faddr; 138 backup = &isop->isop_sfaddr; 139 } 140 if ((addr = *dst) == 0) { 141 addr = *dst = backup; 142 addr->siso_nlen = 0; 143 addr->siso_slen = 0; 144 addr->siso_plen = 0; 145 printf("iso_putsufx on un-initialized isopcb\n"); 146 } 147 len = sufxlen + addr->siso_nlen + 148 (sizeof(*addr) - sizeof(addr->siso_data)); 149 if (addr == backup) { 150 if (len > sizeof(*addr)) { 151 m = m_getclr(M_DONTWAIT, MT_SONAME); 152 if (m == 0) 153 return; 154 addr = *dst = mtod(m, struct sockaddr_iso *); 155 *addr = *backup; 156 m->m_len = len; 157 } 158 } 159 bcopy(sufxloc, TSEL(addr), sufxlen); 160 addr->siso_tlen = sufxlen; 161 addr->siso_len = len; 162 } 163 164 /* 165 * CALLED FROM: 166 * tp.trans whenever we go into REFWAIT state. 167 * FUNCTION and ARGUMENT: 168 * Called when a ref is frozen, to allow the suffix to be reused. 169 * (isop) is the net level pcb. This really shouldn't have to be 170 * done in a NET level pcb but... for the internet world that just 171 * the way it is done in BSD... 172 * The alternative is to have the port unusable until the reference 173 * timer goes off. 174 */ 175 void 176 iso_recycle_tsuffix(isop) 177 struct isopcb *isop; 178 { 179 isop->isop_laddr->siso_tlen = isop->isop_faddr->siso_tlen = 0; 180 } 181 182 /* 183 * CALLED FROM: 184 * tp_newsocket(); i.e., when a connection is being established by an 185 * incoming CR_TPDU. 186 * 187 * FUNCTION and ARGUMENTS: 188 * Copy a whole net addr from a struct sockaddr (name). 189 * into an isopcb (isop). 190 * The argument (which) takes values TP_LOCAL or TP_FOREIGN 191 */ 192 void 193 iso_putnetaddr(isop, name, which) 194 register struct isopcb *isop; 195 struct sockaddr_iso *name; 196 int which; 197 { 198 struct sockaddr_iso **sisop, *backup; 199 register struct sockaddr_iso *siso; 200 201 switch (which) { 202 case TP_LOCAL: 203 sisop = &isop->isop_laddr; 204 backup = &isop->isop_sladdr; 205 break; 206 case TP_FOREIGN: 207 sisop = &isop->isop_faddr; 208 backup = &isop->isop_sfaddr; 209 } 210 siso = ((*sisop == 0) ? (*sisop = backup) : *sisop); 211 IFDEBUG(D_TPISO) 212 printf("ISO_PUTNETADDR\n"); 213 dump_isoaddr(isop->isop_faddr); 214 ENDDEBUG 215 siso->siso_addr = name->siso_addr; 216 } 217 218 /* 219 * CALLED FROM: 220 * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR 221 * FUNCTION and ARGUMENTS: 222 * Copy a whole net addr from an isopcb (isop) into 223 * a struct sockaddr (name). 224 * The argument (which) takes values TP_LOCAL or TP_FOREIGN. 225 */ 226 227 void 228 iso_getnetaddr( isop, name, which) 229 struct isopcb *isop; 230 struct mbuf *name; 231 int which; 232 { 233 struct sockaddr_iso *siso = 234 (which == TP_LOCAL ? isop->isop_laddr : isop->isop_faddr); 235 if (siso) 236 bcopy((caddr_t)siso, mtod(name, caddr_t), 237 (unsigned)(name->m_len = siso->siso_len)); 238 else 239 name->m_len = 0; 240 } 241 242 /* 243 * CALLED FROM: 244 * tp_input() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT 245 * FUNCTION, ARGUMENTS, SIDE EFFECTS and RETURN VALUE: 246 * Determine the proper maximum transmission unit, i.e., MTU, to use, given 247 * a) the header size for the network protocol and the max transmission 248 * unit on the subnet interface, determined from the information in (isop), 249 * b) the max size negotiated so far (negot) 250 * c) the window size used by the tp connection (found in so), 251 * 252 * The result is put in the integer *size in its integer form and in 253 * *negot in its logarithmic form. 254 * 255 * The rules are: 256 * a) can only negotiate down from the value found in *negot. 257 * b) the MTU must be < the windowsize, 258 * c) If src and dest are on the same net, 259 * we will negotiate the closest size larger than MTU but really USE 260 * the actual device mtu - ll hdr sizes. 261 * otherwise we negotiate the closest size smaller than MTU - ll hdr sizes. 262 */ 263 264 void 265 tpclnp_mtu(so, isop, size, negot ) 266 struct socket *so; 267 struct isopcb *isop; 268 int *size; 269 u_char *negot; 270 { 271 struct ifnet *ifp = 0; 272 struct iso_ifaddr *ia = 0; 273 register int i; 274 int windowsize = so->so_rcv.sb_hiwat; 275 int clnp_size; 276 int sizeismtu = 0; 277 register struct rtentry *rt = isop->isop_route.ro_rt; 278 279 IFDEBUG(D_CONN) 280 printf("tpclnp_mtu(0x%x,0x%x,0x%x,0x%x)\n", so, isop, size, negot); 281 ENDDEBUG 282 IFTRACE(D_CONN) 283 tptrace(TPPTmisc, "ENTER GET MTU: size negot \n",*size, *negot, 0, 0); 284 ENDTRACE 285 286 *size = 1 << *negot; 287 288 if( *size > windowsize ) { 289 *size = windowsize; 290 } 291 292 if (rt == 0 || (rt->rt_flags & RTF_UP == 0) || 293 (ia = (struct iso_ifaddr *)rt->rt_ifa) == 0 || 294 (ifp = ia->ia_ifp) == 0) { 295 IFDEBUG(D_CONN) 296 printf("tpclnp_mtu routing abort rt=0x%x ia=0x%x ifp=0x%x\n", 297 rt, ia, ifp) 298 ENDDEBUG 299 return; 300 } 301 302 /* TODO - make this indirect off the socket structure to the 303 * network layer to get headersize 304 */ 305 if (isop->isop_laddr) 306 clnp_size = clnp_hdrsize(isop->isop_laddr->siso_addr.isoa_len); 307 else 308 clnp_size = 20; 309 310 if(*size > ifp->if_mtu - clnp_size) { 311 *size = ifp->if_mtu - clnp_size; 312 sizeismtu = 1; 313 } 314 /* have to transform size to the log2 of size */ 315 for(i=TP_MIN_TPDUSIZE; (i<TP_MAX_TPDUSIZE && ((1<<i) <= *size)) ; i++) 316 ; 317 i--; 318 319 IFTRACE(D_CONN) 320 tptrace(TPPTmisc, "GET MTU MID: tpcb size negot i \n", 321 *size, *negot, i, 0); 322 ENDTRACE 323 324 *size = 1<<i; 325 *negot = i; 326 327 IFDEBUG(D_CONN) 328 printf("GET MTU RETURNS: ifp %s size 0x%x negot 0x%x\n", 329 ifp->if_name, *size, *negot); 330 ENDDEBUG 331 IFTRACE(D_CONN) 332 tptrace(TPPTmisc, "EXIT GET MTU: tpcb size negot \n", 333 *size, *negot, 0, 0); 334 ENDTRACE 335 } 336 337 338 /* 339 * CALLED FROM: 340 * tp_emit() 341 * FUNCTION and ARGUMENTS: 342 * Take a packet(m0) from tp and package it so that clnp will accept it. 343 * This means prepending space for the clnp header and filling in a few 344 * of the fields. 345 * inp is the isopcb structure; datalen is the length of the data in the 346 * mbuf string m0. 347 * RETURN VALUE: 348 * whatever (E*) is returned form the net layer output routine. 349 */ 350 351 int 352 tpclnp_output(isop, m0, datalen, nochksum) 353 struct isopcb *isop; 354 struct mbuf *m0; 355 int datalen; 356 int nochksum; 357 { 358 register struct mbuf *m = m0; 359 IncStat(ts_tpdu_sent); 360 361 IFDEBUG(D_TPISO) 362 struct tpdu *hdr = mtod(m0, struct tpdu *); 363 364 printf( 365 "abt to call clnp_output: datalen 0x%x, hdr.li 0x%x, hdr.dutype 0x%x nocsum x%x dst addr:\n", 366 datalen, 367 (int)hdr->tpdu_li, (int)hdr->tpdu_type, nochksum); 368 dump_isoaddr(isop->isop_faddr); 369 printf("\nsrc addr:\n"); 370 dump_isoaddr(isop->isop_laddr); 371 dump_mbuf(m0, "at tpclnp_output"); 372 ENDDEBUG 373 374 return 375 clnp_output(m0, isop, datalen, /* flags */nochksum ? CLNP_NO_CKSUM : 0); 376 } 377 378 /* 379 * CALLED FROM: 380 * tp_error_emit() 381 * FUNCTION and ARGUMENTS: 382 * This is a copy of tpclnp_output that takes the addresses 383 * instead of a pcb. It's used by the tp_error_emit, when we 384 * don't have an iso_pcb with which to call the normal output rtn. 385 * RETURN VALUE: 386 * ENOBUFS or 387 * whatever (E*) is returned form the net layer output routine. 388 */ 389 390 int 391 tpclnp_output_dg(laddr, faddr, m0, datalen, ro, nochksum) 392 struct iso_addr *laddr, *faddr; 393 struct mbuf *m0; 394 int datalen; 395 struct route *ro; 396 int nochksum; 397 { 398 struct isopcb tmppcb; 399 int err; 400 int flags; 401 register struct mbuf *m = m0; 402 403 IFDEBUG(D_TPISO) 404 printf("tpclnp_output_dg datalen 0x%x m0 0x%x\n", datalen, m0); 405 ENDDEBUG 406 407 /* 408 * Fill in minimal portion of isopcb so that clnp can send the 409 * packet. 410 */ 411 bzero((caddr_t)&tmppcb, sizeof(tmppcb)); 412 tmppcb.isop_laddr = &tmppcb.isop_sladdr; 413 tmppcb.isop_laddr->siso_addr = *laddr; 414 tmppcb.isop_faddr = &tmppcb.isop_sfaddr; 415 tmppcb.isop_faddr->siso_addr = *faddr; 416 417 IFDEBUG(D_TPISO) 418 printf("tpclnp_output_dg faddr: \n"); 419 dump_isoaddr(&tmppcb.isop_sfaddr); 420 printf("\ntpclnp_output_dg laddr: \n"); 421 dump_isoaddr(&tmppcb.isop_sladdr); 422 printf("\n"); 423 ENDDEBUG 424 425 /* 426 * Do not use packet cache since this is a one shot error packet 427 */ 428 flags = (CLNP_NOCACHE|(nochksum?CLNP_NO_CKSUM:0)); 429 430 IncStat(ts_tpdu_sent); 431 432 err = clnp_output(m0, &tmppcb, datalen, flags); 433 434 /* 435 * Free route allocated by clnp (if the route was indeed allocated) 436 */ 437 if (tmppcb.isop_route.ro_rt) 438 RTFREE(tmppcb.isop_route.ro_rt); 439 440 return(err); 441 } 442 /* 443 * CALLED FROM: 444 * clnp's input routine, indirectly through the protosw. 445 * FUNCTION and ARGUMENTS: 446 * Take a packet (m) from clnp, strip off the clnp header and give it to tp 447 * No return value. 448 */ 449 ProtoHook 450 tpclnp_input(m, src, dst, clnp_len) 451 register struct mbuf *m; 452 struct sockaddr_iso *src, *dst; 453 int clnp_len; 454 { 455 int s = splnet(); 456 struct mbuf *tp_inputprep(); 457 int tp_input(), cltp_input(), (*input)() = tp_input; 458 459 IncStat(ts_pkt_rcvd); 460 461 IFDEBUG(D_TPINPUT) 462 printf("tpclnp_input: m 0x%x clnp_len 0x%x\n", m, clnp_len); 463 dump_mbuf(m, "at tpclnp_input"); 464 ENDDEBUG 465 /* 466 * CLNP gives us an mbuf chain WITH the clnp header pulled up, 467 * and the length of the clnp header. 468 * First, strip off the Clnp header. leave the mbuf there for the 469 * pullup that follows. 470 */ 471 472 m->m_len -= clnp_len; 473 m->m_data += clnp_len; 474 475 m = tp_inputprep(m); 476 if (m == 0) 477 return 0; 478 if (mtod(m, u_char *)[1] == UD_TPDU_type) 479 input = cltp_input; 480 481 IFDEBUG(D_TPINPUT) 482 dump_mbuf(m, "after tpclnp_input both pullups"); 483 ENDDEBUG 484 485 IFDEBUG(D_TPISO) 486 printf("calling %sinput : src 0x%x, dst 0x%x, src addr:\n", 487 (input == tp_input ? "tp_" : "clts_"), src, dst); 488 dump_isoaddr(src); 489 printf(" dst addr:\n"); 490 dump_isoaddr(dst); 491 ENDDEBUG 492 493 (void) (*input)(m, (struct sockaddr *)src, (struct sockaddr *)dst, 494 0, tpclnp_output_dg); 495 496 IFDEBUG(D_QUENCH) 497 { 498 if(time.tv_usec & 0x4 && time.tv_usec & 0x40) { 499 printf("tpclnp_input: FAKING %s\n", 500 tp_stat.ts_pkt_rcvd & 0x1?"QUENCH":"QUENCH2"); 501 if(tp_stat.ts_pkt_rcvd & 0x1) { 502 tpclnp_ctlinput(PRC_QUENCH, &src); 503 } else { 504 tpclnp_ctlinput(PRC_QUENCH2, &src); 505 } 506 } 507 } 508 ENDDEBUG 509 510 splx(s); 511 return 0; 512 } 513 514 ProtoHook 515 iso_rtchange() 516 { 517 return 0; 518 } 519 520 /* 521 * CALLED FROM: 522 * tpclnp_ctlinput() 523 * FUNCTION and ARGUMENTS: 524 * find the tpcb pointer and pass it to tp_quench 525 */ 526 void 527 tpiso_decbit(isop) 528 struct isopcb *isop; 529 { 530 tp_quench((struct tp_pcb *)isop->isop_socket->so_tpcb, PRC_QUENCH2); 531 } 532 /* 533 * CALLED FROM: 534 * tpclnp_ctlinput() 535 * FUNCTION and ARGUMENTS: 536 * find the tpcb pointer and pass it to tp_quench 537 */ 538 void 539 tpiso_quench(isop) 540 struct isopcb *isop; 541 { 542 tp_quench((struct tp_pcb *)isop->isop_socket->so_tpcb, PRC_QUENCH); 543 } 544 545 /* 546 * CALLED FROM: 547 * The network layer through the protosw table. 548 * FUNCTION and ARGUMENTS: 549 * When clnp an ICMP-like msg this gets called. 550 * It either returns an error status to the user or 551 * it causes all connections on this address to be aborted 552 * by calling the appropriate xx_notify() routine. 553 * (cmd) is the type of ICMP error. 554 * (siso) is the address of the guy who sent the ER CLNPDU 555 */ 556 ProtoHook 557 tpclnp_ctlinput(cmd, siso) 558 int cmd; 559 struct sockaddr_iso *siso; 560 { 561 extern u_char inetctlerrmap[]; 562 extern ProtoHook tpiso_abort(); 563 extern ProtoHook iso_rtchange(); 564 extern ProtoHook tpiso_reset(); 565 void iso_pcbnotify(); 566 567 IFDEBUG(D_TPINPUT) 568 printf("tpclnp_ctlinput1: cmd 0x%x addr: \n", cmd); 569 dump_isoaddr(siso); 570 ENDDEBUG 571 572 if (cmd < 0 || cmd > PRC_NCMDS) 573 return 0; 574 if (siso->siso_family != AF_ISO) 575 return 0; 576 switch (cmd) { 577 578 case PRC_QUENCH2: 579 iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_decbit); 580 break; 581 582 case PRC_QUENCH: 583 iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_quench); 584 break; 585 586 case PRC_TIMXCEED_REASS: 587 case PRC_ROUTEDEAD: 588 iso_pcbnotify(&tp_isopcb, siso, 0, tpiso_reset); 589 break; 590 591 case PRC_HOSTUNREACH: 592 case PRC_UNREACH_NET: 593 case PRC_IFDOWN: 594 case PRC_HOSTDEAD: 595 iso_pcbnotify(&tp_isopcb, siso, 596 (int)inetctlerrmap[cmd], iso_rtchange); 597 break; 598 599 default: 600 /* 601 case PRC_MSGSIZE: 602 case PRC_UNREACH_HOST: 603 case PRC_UNREACH_PROTOCOL: 604 case PRC_UNREACH_PORT: 605 case PRC_UNREACH_NEEDFRAG: 606 case PRC_UNREACH_SRCFAIL: 607 case PRC_REDIRECT_NET: 608 case PRC_REDIRECT_HOST: 609 case PRC_REDIRECT_TOSNET: 610 case PRC_REDIRECT_TOSHOST: 611 case PRC_TIMXCEED_INTRANS: 612 case PRC_PARAMPROB: 613 */ 614 iso_pcbnotify(&tp_isopcb, siso, (int)inetctlerrmap[cmd], tpiso_abort); 615 break; 616 } 617 return 0; 618 } 619 /* 620 * XXX - Variant which is called by clnp_er.c with an isoaddr rather 621 * than a sockaddr_iso. 622 */ 623 624 static struct sockaddr_iso siso = {sizeof(siso), AF_ISO}; 625 tpclnp_ctlinput1(cmd, isoa) 626 int cmd; 627 struct iso_addr *isoa; 628 { 629 bzero((caddr_t)&siso.siso_addr, sizeof(siso.siso_addr)); 630 bcopy((caddr_t)isoa, (caddr_t)&siso.siso_addr, isoa->isoa_len); 631 tpclnp_ctlinput(cmd, &siso); 632 } 633 634 /* 635 * These next 2 routines are 636 * CALLED FROM: 637 * xxx_notify() from tp_ctlinput() when 638 * net level gets some ICMP-equiv. type event. 639 * FUNCTION and ARGUMENTS: 640 * Cause the connection to be aborted with some sort of error 641 * reason indicating that the network layer caused the abort. 642 * Fakes an ER TPDU so we can go through the driver. 643 * abort always aborts the TP connection. 644 * reset may or may not, depending on the TP class that's in use. 645 */ 646 ProtoHook 647 tpiso_abort(isop) 648 struct isopcb *isop; 649 { 650 struct tp_event e; 651 652 IFDEBUG(D_CONN) 653 printf("tpiso_abort 0x%x\n", isop); 654 ENDDEBUG 655 e.ev_number = ER_TPDU; 656 e.ATTR(ER_TPDU).e_reason = ECONNABORTED; 657 return tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e); 658 } 659 660 ProtoHook 661 tpiso_reset(isop) 662 struct isopcb *isop; 663 { 664 struct tp_event e; 665 666 e.ev_number = T_NETRESET; 667 return tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e); 668 669 } 670 671 #include "cltp_usrreq.c" 672 #endif ISO 673