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 * 30 * $Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $ 31 * $Source: /usr/argo/sys/netiso/RCS/tp_subr2.c,v $ 32 * 33 * Some auxiliary routines: 34 * tp_protocol_error: required by xebec- called when a combo of state, 35 * event, predicate isn't covered for by the transition file. 36 * tp_indicate: gives indications(signals) to the user process 37 * tp_getoptions: initializes variables that are affected by the options 38 * chosen. 39 */ 40 41 #ifndef lint 42 static char *rcsid = "$Header: tp_subr2.c,v 5.5 88/11/18 17:28:55 nhall Exp $"; 43 #endif lint 44 45 #include "argoxtwentyfive.h" 46 47 /* this def'n is to cause the expansion of this macro in the 48 * routine tp_local_credit : 49 */ 50 #define LOCAL_CREDIT_EXPAND 51 52 #include "param.h" 53 #include "mbuf.h" 54 #include "socket.h" 55 #include "socketvar.h" 56 #include "domain.h" 57 #include "protosw.h" 58 #include "errno.h" 59 #include "types.h" 60 #include "time.h" 61 #include "kernel.h" 62 #undef MNULL 63 #include "../netiso/tp_ip.h" 64 #include "../netiso/tp_param.h" 65 #include "../netiso/tp_timer.h" 66 #include "../netiso/tp_stat.h" 67 #include "../netiso/argo_debug.h" 68 #include "../netiso/tp_tpdu.h" 69 #include "../netiso/iso.h" 70 #include "../netiso/iso_errno.h" 71 #include "../netiso/tp_pcb.h" 72 #include "../netiso/tp_seq.h" 73 #include "../netiso/tp_trace.h" 74 #include "../netiso/iso_pcb.h" 75 #include "../netiso/tp_user.h" 76 #include "../netiso/cons.h" 77 78 /* 79 * NAME: tp_local_credit() 80 * 81 * CALLED FROM: 82 * tp_emit(), tp_usrreq() 83 * 84 * FUNCTION and ARGUMENTS: 85 * Computes the local credit and stashes it in tpcb->tp_lcredit. 86 * It's a macro in the production system rather than a procdure. 87 * 88 * RETURNS: 89 * 90 * SIDE EFFECTS: 91 * 92 * NOTES: 93 * This doesn't actually get called in a production system - 94 * the macro gets expanded instead in place of calls to this proc. 95 * But for debugging, we call this and that allows us to add 96 * debugging messages easily here. 97 */ 98 void 99 tp_local_credit(tpcb) 100 struct tp_pcb *tpcb; 101 { 102 LOCAL_CREDIT(tpcb); 103 IFDEBUG(D_CREDIT) 104 printf("ref 0x%x lcdt 0x%x l_tpdusize 0x%x decbit 0x%x\n", 105 tpcb->tp_refp - tp_ref, 106 tpcb->tp_lcredit, 107 tpcb->tp_l_tpdusize, 108 tpcb->tp_decbit, 109 tpcb->tp_cong_win 110 ); 111 ENDDEBUG 112 IFTRACE(D_CREDIT) 113 tptraceTPCB(TPPTmisc, 114 "lcdt tpdusz \n", 115 tpcb->tp_lcredit, tpcb->tp_l_tpdusize, 0, 0); 116 ENDTRACE 117 } 118 119 /* 120 * NAME: tp_protocol_error() 121 * 122 * CALLED FROM: 123 * tp_driver(), when it doesn't know what to do with 124 * a combo of event, state, predicate 125 * 126 * FUNCTION and ARGUMENTS: 127 * print error mesg 128 * 129 * RETURN VALUE: 130 * EIO - always 131 * 132 * SIDE EFFECTS: 133 * 134 * NOTES: 135 */ 136 int 137 tp_protocol_error(e,tpcb) 138 struct tp_event *e; 139 struct tp_pcb *tpcb; 140 { 141 printf("TP PROTOCOL ERROR! tpcb 0x%x event 0x%x, state 0x%x\n", 142 tpcb, e->ev_number, tpcb->tp_state); 143 IFTRACE(D_DRIVER) 144 tptraceTPCB(TPPTmisc, "PROTOCOL ERROR tpcb event state", 145 tpcb, e->ev_number, tpcb->tp_state, 0 ); 146 ENDTRACE 147 return EIO; /* for lack of anything better */ 148 } 149 150 151 /* Not used at the moment */ 152 ProtoHook 153 tp_drain() 154 { 155 return 0; 156 } 157 158 159 /* 160 * NAME: tp_indicate() 161 * 162 * CALLED FROM: 163 * tp.trans when XPD arrive, when a connection is being disconnected by 164 * the arrival of a DR or ER, and when a connection times out. 165 * 166 * FUNCTION and ARGUMENTS: 167 * (ind) is the type of indication : T_DISCONNECT, T_XPD 168 * (error) is an E* value that will be put in the socket structure 169 * to be passed along to the user later. 170 * Gives a SIGURG to the user process or group indicated by the socket 171 * attached to the tpcb. 172 * 173 * RETURNS: Rien 174 * 175 * SIDE EFFECTS: 176 * 177 * NOTES: 178 */ 179 void 180 tp_indicate(ind, tpcb, error) 181 int ind; 182 u_short error; 183 register struct tp_pcb *tpcb; 184 { 185 register struct socket *so = tpcb->tp_sock; 186 IFTRACE(D_INDICATION) 187 tptraceTPCB(TPPTindicate, ind, *(int *)(tpcb->tp_lsuffix), 188 *(int *)(tpcb->tp_fsuffix), error,so->so_pgrp); 189 ENDTRACE 190 IFDEBUG(D_INDICATION) 191 u_char *ls, *fs; 192 ls = tpcb->tp_lsuffix, 193 fs = tpcb->tp_fsuffix, 194 195 printf( 196 "indicate 0x%x lsuf 0x%02x%02x fsuf 0x%02x%02x err 0x%x prgp 0x%x noind 0x%x ref 0x%x\n", 197 ind, 198 *ls, *(ls+1), *fs, *(fs+1), 199 error,so->so_pgrp, 200 tpcb->tp_no_disc_indications, 201 tpcb->tp_lref); 202 ENDDEBUG 203 204 so->so_error = error; 205 206 if (ind == T_DISCONNECT) { 207 if ( tpcb->tp_no_disc_indications ) 208 return; 209 } 210 IFTRACE(D_INDICATION) 211 tptraceTPCB(TPPTmisc, "doing sohasoutofband(so)", so,0,0,0); 212 ENDTRACE 213 sohasoutofband(so); 214 } 215 216 /* 217 * NAME : tp_getoptions() 218 * 219 * CALLED FROM: 220 * tp.trans whenever we go into OPEN state 221 * 222 * FUNCTION and ARGUMENTS: 223 * sets the proper flags and values in the tpcb, to control 224 * the appropriate actions for the given class, options, 225 * sequence space, etc, etc. 226 * 227 * RETURNS: Nada 228 * 229 * SIDE EFFECTS: 230 * 231 * NOTES: 232 */ 233 void 234 tp_getoptions(tpcb) 235 struct tp_pcb *tpcb; 236 { 237 tpcb->tp_seqmask = 238 tpcb->tp_xtd_format ? TP_XTD_FMT_MASK : TP_NML_FMT_MASK ; 239 tpcb->tp_seqbit = 240 tpcb->tp_xtd_format ? TP_XTD_FMT_BIT : TP_NML_FMT_BIT ; 241 tpcb->tp_seqhalf = tpcb->tp_seqbit >> 1; 242 tpcb->tp_dt_ticks = 243 MAX(tpcb->tp_dt_ticks, (tpcb->tp_peer_acktime + 2)); 244 245 } 246 247 /* 248 * NAME: tp_recycle_tsuffix() 249 * 250 * CALLED FROM: 251 * Called when a ref is frozen. 252 * 253 * FUNCTION and ARGUMENTS: 254 * allows the suffix to be reused. 255 * 256 * RETURNS: zilch 257 * 258 * SIDE EFFECTS: 259 * 260 * NOTES: 261 */ 262 void 263 tp_recycle_tsuffix(tpcb) 264 struct tp_pcb *tpcb; 265 { 266 bzero( tpcb->tp_lsuffix, sizeof( tpcb->tp_lsuffix)); 267 bzero( tpcb->tp_fsuffix, sizeof( tpcb->tp_fsuffix)); 268 tpcb->tp_fsuffixlen = tpcb->tp_lsuffixlen = 0; 269 270 (tpcb->tp_nlproto->nlp_recycle_suffix)(tpcb->tp_npcb); 271 } 272 273 /* 274 * NAME: tp_quench() 275 * 276 * CALLED FROM: 277 * tp{af}_quench() when ICMP source quench or similar thing arrives. 278 * 279 * FUNCTION and ARGUMENTS: 280 * Drop the congestion window back to 1. 281 * Congestion window scheme: 282 * Initial value is 1. ("slow start" as Nagle, et. al. call it) 283 * For each good ack that arrives, the congestion window is increased 284 * by 1 (up to max size of logical infinity, which is to say, 285 * it doesn't wrap around). 286 * Source quench causes it to drop back to 1. 287 * tp_send() uses the smaller of (regular window, congestion window). 288 * One retransmission strategy option is to have any retransmission 289 * cause reset the congestion window back to 1. 290 * 291 * (cmd) is either PRC_QUENCH: source quench, or 292 * PRC_QUENCH2: dest. quench (dec bit) 293 * 294 * RETURNS: 295 * 296 * SIDE EFFECTS: 297 * 298 * NOTES: 299 */ 300 void 301 tp_quench( tpcb, cmd ) 302 struct tp_pcb *tpcb; 303 int cmd; 304 { 305 IFDEBUG(D_QUENCH) 306 printf("tp_quench tpcb 0x%x ref 0x%x sufx 0x%x\n", 307 tpcb, tpcb->tp_lref, *(int *)(tpcb->tp_lsuffix)); 308 printf("cong_win 0x%x decbit 0x%x \n", 309 tpcb->tp_cong_win, tpcb->tp_decbit); 310 ENDDEBUG 311 switch(cmd) { 312 case PRC_QUENCH: 313 tpcb->tp_cong_win = 1; 314 IncStat(ts_quench); 315 break; 316 case PRC_QUENCH2: 317 tpcb->tp_cong_win = 1; /* might as well quench source also */ 318 tpcb->tp_decbit = TP_DECBIT_CLEAR_COUNT; 319 IncStat(ts_rcvdecbit); 320 break; 321 } 322 } 323 324 325 /* 326 * NAME: tp_netcmd() 327 * 328 * CALLED FROM: 329 * 330 * FUNCTION and ARGUMENTS: 331 * 332 * RETURNS: 333 * 334 * SIDE EFFECTS: 335 * 336 * NOTES: 337 */ 338 tp_netcmd( tpcb, cmd ) 339 struct tp_pcb *tpcb; 340 int cmd; 341 { 342 #if NARGOXTWENTYFIVE > 0 343 switch (cmd) { 344 345 case CONN_CLOSE: 346 case CONN_REFUSE: 347 cons_netcmd( cmd, tpcb->tp_npcb, 0, tpcb->tp_class == TP_CLASS_4); 348 /* TODO: can this last param be replaced by 349 * tpcb->tp_netserv != ISO_CONS?) 350 */ 351 break; 352 353 default: 354 printf("tp_netcmd(0x%x, 0x%x) NOT IMPLEMENTED\n", tpcb, cmd); 355 break; 356 } 357 #else NARGOXTWENTYFIVE 358 printf("tp_netcmd(): X25 NOT CONFIGURED!!\n"); 359 #endif NARGOXTWENTYFIVE > 0 360 } 361 /* 362 * CALLED FROM: 363 * tp_ctloutput() and tp_emit() 364 * FUNCTION and ARGUMENTS: 365 * Convert a class mask to the highest numeric value it represents. 366 */ 367 368 int 369 tp_mask_to_num(x) 370 u_char x; 371 { 372 register int j; 373 374 for(j = 4; j>=0 ;j--) { 375 if(x & (1<<j)) 376 break; 377 } 378 ASSERT( (j == 4) || (j == 0) ); /* for now */ 379 if( (j != 4) && (j != 0) ) { 380 printf("ASSERTION ERROR: tp_mask_to_num: x 0x%x j %d\n", 381 x, j); 382 } 383 IFTRACE(D_TPINPUT) 384 tptrace(TPPTmisc, "tp_mask_to_num(x) returns j", x, j, 0, 0); 385 ENDTRACE 386 IFDEBUG(D_TPINPUT) 387 printf("tp_mask_to_num(0x%x) returns 0x%x\n", x, j); 388 ENDDEBUG 389 return j; 390 } 391 392 static 393 copyQOSparms(src, dst) 394 struct tp_conn_param *src, *dst; 395 { 396 /* copy all but the bits stuff at the end */ 397 #define COPYSIZE (12 * sizeof(short)) 398 399 bcopy( src, dst, COPYSIZE); 400 dst->p_tpdusize = src->p_tpdusize; 401 dst->p_ack_strat = src->p_ack_strat; 402 dst->p_rx_strat = src->p_rx_strat; 403 #undef COPYSIZE 404 } 405 406 /* 407 * CALLED FROM: 408 * tp_usrreq on PRU_CONNECT and tp_input on receipt of CR 409 * 410 * FUNCTION and ARGUMENTS: 411 * route directly to x.25 if the address is type 37 - GROT. 412 * furthermore, let TP0 handle only type-37 addresses 413 * 414 * Since this assumes that its address argument is in a mbuf, the 415 * parameter was changed to reflect this assumtion. This also 416 * implies that an mbuf must be allocated when this is 417 * called from tp_input 418 * 419 * RETURNS: 420 * errno value : 421 * EAFNOSUPPORT if can't find an nl_protosw for x.25 (really could panic) 422 * ECONNREFUSED if trying to run TP0 with non-type 37 address 423 * possibly other E* returned from cons_netcmd() 424 * NOTE: 425 * Would like to eliminate as much of this as possible -- 426 * only one set of defaults (let the user set the parms according 427 * to parameters provided in the directory service). 428 * Left here for now 'cause we don't yet have a clean way to handle 429 * it on the passive end. 430 */ 431 int 432 tp_route_to( m, tpcb, channel) 433 struct mbuf *m; 434 register struct tp_pcb *tpcb; 435 u_int channel; 436 { 437 register struct sockaddr_iso *siso; /* NOTE: this may be a sockaddr_in */ 438 extern struct tp_conn_param tp_conn_param[]; 439 int error = 0; 440 int vc_to_kill = 0; /* kludge */ 441 442 siso = mtod(m, struct sockaddr_iso *); 443 IFTRACE(D_CONN) 444 tptraceTPCB(TPPTmisc, 445 "route_to: so afi netservice class", 446 tpcb->tp_sock, siso->siso_addr.isoa_afi, tpcb->tp_netservice, 447 tpcb->tp_class); 448 ENDTRACE 449 IFDEBUG(D_CONN) 450 printf("tp_route_to( m x%x, channel 0x%x, tpcb 0x%x netserv 0x%x)\n", 451 m, channel, tpcb, tpcb->tp_netservice); 452 printf("m->mlen x%x, m->m_data:\n", m->m_len); 453 dump_buf(mtod(m, caddr_t), m->m_len); 454 ENDDEBUG 455 if( siso->siso_family != tpcb->tp_domain ) { 456 error = EAFNOSUPPORT; 457 goto done; 458 } 459 { 460 register int save_netservice = tpcb->tp_netservice; 461 462 switch(tpcb->tp_netservice) { 463 case ISO_COSNS: 464 case ISO_CLNS: 465 /* This is a kludge but seems necessary so the passive end 466 * can get long enough timers. sigh. 467 */ 468 if( siso->siso_addr.osinet_idi[1] == (u_char)IDI_OSINET ) { 469 if( tpcb->tp_dont_change_params == 0) { 470 copyQOSparms( &tp_conn_param[ISO_COSNS], 471 &tpcb->_tp_param); 472 } 473 tpcb->tp_flags |= TPF_NLQOS_PDN; 474 } 475 /* drop through to IN_CLNS*/ 476 case IN_CLNS: 477 if( (tpcb->tp_class & TP_CLASS_4)==0 ) { 478 error = EPROTOTYPE; 479 break; 480 } 481 tpcb->tp_class = TP_CLASS_4; /* IGNORE dont_change_parms */ 482 break; 483 484 case ISO_CONS: 485 #if NARGOXTWENTYFIVE > 0 486 tpcb->tp_flags |= TPF_NLQOS_PDN; 487 if( tpcb->tp_dont_change_params == 0 ) { 488 copyQOSparms( &tp_conn_param[ISO_CONS], 489 &tpcb->_tp_param); 490 } 491 /* 492 * for use over x.25 really need a small receive window, 493 * need to start slowly, need small max negotiable tpdu size, 494 * and need to use the congestion window to the max 495 * IGNORES tp_dont_change_params for these! 496 */ 497 if( tpcb->tp_sock->so_snd.sb_hiwat > 512 ) { 498 (void) soreserve(tpcb->tp_sock, 512, 512 );/* GAG */ 499 } 500 tpcb->tp_rx_strat = TPRX_USE_CW; 501 502 if( (tpcb->tp_nlproto != &nl_protosw[ISO_CONS]) ) { 503 /* if the listener was restricting us to clns, 504 * ( we never get here if the listener isn't af_iso ) 505 * refuse the connection : 506 * but we don't have a way to restrict thus - it's 507 * utterly permissive. 508 if(channel) { 509 (void) cons_netcmd(CONN_REFUSE, tpcb->tp_npcb, 510 channel, tpcb->tp_class == TP_CLASS_4); 511 error = EPFNOSUPPORT; 512 goto done; 513 } 514 */ 515 IFDEBUG(D_CONN) 516 printf( 517 "tp_route_to( CHANGING nlproto old 0x%x new 0x%x)\n", 518 tpcb->tp_nlproto , &nl_protosw[ISO_CONS]); 519 ENDDEBUG 520 tpcb->tp_nlproto = &nl_protosw[ISO_CONS]; 521 } 522 /* Now we've got the right nl_protosw. 523 * If we already have a channel (we were called from tp_input()) 524 * tell cons that we'll hang onto this channel. 525 * If we don't already have one (we were called from usrreq()) 526 * -and if it's TP0 open a net connection and wait for it to finish. 527 */ 528 if( channel ) { 529 error = cons_netcmd( CONN_CONFIRM, tpcb->tp_npcb, 530 channel, tpcb->tp_class == TP_CLASS_4); 531 vc_to_kill ++; 532 } else if( tpcb->tp_class != TP_CLASS_4 /* class 4 only */) { 533 /* better open vc if any possibility of ending up 534 * in non-multiplexing class 535 */ 536 error = cons_openvc(tpcb->tp_npcb, siso, tpcb->tp_sock); 537 vc_to_kill ++; 538 } 539 /* class 4 doesn't need to open a vc now - may use one already 540 * opened or may open one only when it sends a pkt. 541 */ 542 #else NARGOXTWENTYFIVE > 0 543 error = ECONNREFUSED; 544 #endif NARGOXTWENTYFIVE > 0 545 break; 546 default: 547 error = EPROTOTYPE; 548 } 549 550 ASSERT( save_netservice == tpcb->tp_netservice); 551 } 552 if( error ) 553 goto done; 554 IFDEBUG(D_CONN) 555 printf("tp_route_to calling nlp_pcbconn, netserv %d\n", 556 tpcb->tp_netservice); 557 ENDDEBUG 558 error = (tpcb->tp_nlproto->nlp_pcbconn)(tpcb->tp_sock->so_pcb, m); 559 560 if( error && vc_to_kill ) { 561 tp_netcmd( tpcb, CONN_CLOSE); 562 goto done; 563 } 564 565 /* PHASE 2: replace iso_netmatch with iso_on_localnet(foreign addr) */ 566 if( iso_netmatch( 567 &(((struct isopcb *)(tpcb->tp_sock->so_pcb))->isop_laddr), 568 &(((struct isopcb *)(tpcb->tp_sock->so_pcb))->isop_faddr) 569 )) { 570 tpcb->tp_flags |= TPF_PEER_ON_SAMENET; 571 } 572 573 { /* start with the global rtt, rtv stats */ 574 register int i = 575 (int) tpcb->tp_flags & (TPF_PEER_ON_SAMENET | TPF_NLQOS_PDN); 576 577 tpcb->tp_rtt = tp_stat.ts_rtt[i]; 578 tpcb->tp_rtv = tp_stat.ts_rtv[i]; 579 } 580 done: 581 IFDEBUG(D_CONN) 582 printf("tp_route_to returns 0x%x\n", error); 583 ENDDEBUG 584 IFTRACE(D_CONN) 585 tptraceTPCB(TPPTmisc, "route_to: returns: error netserv class", error, 586 tpcb->tp_netservice, tpcb->tp_class, 0); 587 ENDTRACE 588 return error; 589 } 590 591 #ifdef TP_PERF_MEAS 592 /* 593 * CALLED FROM: 594 * tp_ctloutput() when the user sets TPOPT_PERF_MEAS on 595 * and tp_newsocket() when a new connection is made from 596 * a listening socket with tp_perf_on == true. 597 * FUNCTION and ARGUMENTS: 598 * (tpcb) is the usual; this procedure gets a clear cluster mbuf for 599 * a tp_pmeas structure, and makes tpcb->tp_p_meas point to it. 600 * RETURN VALUE: 601 * ENOBUFS if it cannot get a cluster mbuf. 602 */ 603 604 int 605 tp_setup_perf(tpcb) 606 register struct tp_pcb *tpcb; 607 { 608 register struct mbuf *q; 609 610 if( tpcb->tp_p_meas == (struct tp_pmeas *)0 ) { 611 612 /* allocate a cluster for all the stats */ 613 MGET(q, M_DONTWAIT, TPMT_PERF); /* something we don't otherwise use */ 614 if (q == 0) 615 return ENOBUFS; 616 q->m_act = MNULL; 617 MCLGET(q); /* for the tp_pmeas struct */ 618 if(q->m_len == 0) { 619 m_free(q); 620 return ENOBUFS; 621 } else { 622 /* point into the cluster */ 623 tpcb->tp_p_meas = mtod(q, struct tp_pmeas *); 624 /* get rid of the original little mbuf */ 625 q->m_off = 0; q->m_len = 0; 626 m_free(q); 627 bzero( (caddr_t)tpcb->tp_p_meas, sizeof (struct tp_pmeas) ); 628 IFDEBUG(D_PERF_MEAS) 629 printf( 630 "tpcb 0x%x so 0x%x ref 0x%x tp_p_meas 0x%x tp_perf_on 0x%x\n", 631 tpcb, tpcb->tp_sock, tpcb->tp_lref, 632 tpcb->tp_p_meas, tpcb->tp_perf_on); 633 ENDDEBUG 634 tpcb->tp_perf_on = 1; 635 } 636 } 637 return 0; 638 } 639 #endif TP_PERF_MEAS 640 641 #ifdef ARGO_DEBUG 642 dump_addr (addr) 643 register struct sockaddr *addr; 644 { 645 switch( addr->sa_family ) { 646 case AF_INET: 647 dump_inaddr(addr); 648 break; 649 case AF_ISO: 650 dump_isoaddr(addr); 651 break; 652 default: 653 printf("BAD AF: 0x%x\n", addr->sa_family); 654 break; 655 } 656 } 657 658 #endif ARGO_DEBUG 659 660