1 /* tcp_input.c 1.12 81/10/30 */ 2 3 #include "../h/param.h" 4 #include "../h/systm.h" 5 #include "../h/mbuf.h" 6 #include "../h/socket.h" 7 #include "../inet/inet.h" 8 #include "../inet/inet_systm.h" 9 #include "../inet/imp.h" 10 #include "../inet/inet_host.h" 11 #include "../inet/ip.h" 12 #include "../inet/tcp.h" 13 #include "../inet/tcp_fsm.h" 14 15 int tcpcksum = 1; 16 17 tcp_input(mp) 18 register struct mbuf *mp; 19 { 20 register struct th *n; /* known to be r10 */ 21 register int j; /* known to be r9 */ 22 register struct tcb *tp; 23 int nstate; 24 struct mbuf *m; 25 struct ucb *up; 26 int hlen, tlen; 27 u_short lport, fport; 28 #ifdef TCPDEBUG 29 struct tcp_debug tdb; 30 #endif 31 COUNT(TCP_INPUT); 32 33 /* 34 * Build extended tcp header 35 */ 36 n = (struct th *)((int)mp + mp->m_off); 37 tlen = ((struct ip *)n)->ip_len; 38 n->t_len = htons(tlen); 39 n->t_next = NULL; 40 n->t_prev = NULL; 41 n->t_x1 = 0; 42 lport = ntohs(n->t_dst); 43 fport = ntohs(n->t_src); 44 45 /* WONT BE POSSIBLE WHEN MBUFS ARE 256 BYTES */ 46 if ((hlen = n->t_off << 2) > mp->m_len) 47 { printf("tcp header overflow\n"); m_freem(mp); return; } 48 49 if (tcpcksum) { 50 /* 51 * Checksum extended header and data 52 */ 53 j = n->t_sum; n->t_sum = 0; 54 #ifdef vax 55 if (tlen == 20) { 56 asm("addl3 $8,r10,r0; movl (r0)+,r1; addl2 (r0)+,r1"); 57 asm("adwc (r0)+,r1; adwc (r0)+,r1; adwc (r0)+,r1"); 58 asm("adwc (r0)+,r1; adwc (r0)+,r1; adwc (r0)+,r1"); 59 asm("adwc $0,r1; ashl $-16,r1,r0; addw2 r0,r1"); 60 asm("adwc $0,r1"); /* ### */ 61 asm("mcoml r1,r1; movzwl r1,r1; subl2 r1,r9"); 62 } else 63 #endif 64 j -= cksum(mp, sizeof (struct ip) + tlen); 65 if (j != 0) { 66 netstat.t_badsum++; 67 m_freem(mp); 68 return; 69 } 70 } 71 72 /* 73 * Find tcb for message (SHOULDN'T USE LINEAR SEARCH!) 74 */ 75 for (tp = tcb_head; tp != 0; tp = tp->t_tcb_next) 76 if (tp->t_lport == lport && tp->t_fport == fport && 77 tp->t_ucb->uc_host->h_addr.s_addr == n->t_s.s_addr) 78 goto found; 79 for (tp = tcb_head; tp != 0; tp = tp->t_tcb_next) 80 if (tp->t_lport == lport && 81 (tp->t_fport==fport || tp->t_fport==0) && 82 (tp->t_ucb->uc_host->h_addr.s_addr == n->t_s.s_addr || 83 tp->t_ucb->uc_host->h_addr.s_addr == 0)) 84 goto found; 85 goto notwanted; 86 found: 87 88 /* 89 * Byte swap header 90 */ 91 n->t_len = tlen - hlen; 92 n->t_src = fport; 93 n->t_dst = lport; 94 n->t_seq = ntohl(n->t_seq); 95 n->t_ackno = ntohl(n->t_ackno); 96 n->t_win = ntohs(n->t_win); 97 n->t_urp = ntohs(n->t_urp); 98 99 /* 100 * Check segment seq # and do rst processing 101 */ 102 switch (tp->t_state) { 103 104 case LISTEN: 105 if ((n->th_flags&TH_ACK) || !syn_ok(tp, n)) { 106 tcp_sndrst(tp, n); 107 goto badseg; 108 } 109 if (n->th_flags&TH_RST) 110 goto badseg; 111 goto goodseg; 112 113 case SYN_SENT: 114 if (!ack_ok(tp, n) || !syn_ok(tp, n)) { 115 tcp_sndrst(tp, n); /* 71,72,75 */ 116 goto badseg; 117 } 118 if (n->th_flags&TH_RST) { 119 tcp_close(tp, URESET); /* 70 */ 120 tp->t_state = CLOSED; 121 goto badseg; 122 } 123 goto goodseg; 124 125 default: 126 if ((n->th_flags&TH_RST) == 0) 127 goto common; 128 if (n->t_seq < tp->rcv_nxt) /* bad rst */ 129 goto badseg; /* 69 */ 130 switch (tp->t_state) { 131 132 case L_SYN_RCVD: 133 if (ack_ok(tp, n) == 0) 134 goto badseg; /* 69 */ 135 tp->t_rexmt = 0; 136 tp->t_rexmttl = 0; 137 tp->t_persist = 0; 138 h_free(tp->t_ucb->uc_host); 139 tp->t_state = LISTEN; 140 goto badseg; 141 142 default: 143 tcp_close(tp, URESET); /* 66 */ 144 tp->t_state = CLOSED; 145 goto badseg; 146 } 147 /*NOTREACHED*/ 148 149 case SYN_RCVD: 150 common: 151 if (ack_ok(tp, n) == 0) { 152 tcp_sndrst(tp, n); /* 74 */ 153 goto badseg; 154 } 155 if (syn_ok(tp, n) && n->t_seq != tp->irs) { 156 tcp_sndnull(tp); /* 74 */ 157 goto badseg; 158 } 159 goto goodseg; 160 } 161 badseg: 162 m_freem(mp); 163 return; 164 165 goodseg: 166 #ifdef notdef 167 /* DO SOMETHING ABOUT UNACK!!! */ 168 /* 169 * Defer processing if no buffer space for this connection. 170 */ 171 up = tp->t_ucb; 172 if (up->uc_rcc > up->uc_rhiwat && 173 && n->t_len != 0 && mbstat.m_bufs < mbstat.m_lowat) { 174 mp->m_act = (struct mbuf *)0; 175 if ((m = tp->t_rcv_unack) != NULL) { 176 while (m->m_act != NULL) 177 m = m->m_act; 178 m->m_act = mp; 179 } else 180 tp->t_rcv_unack = mp; 181 return; 182 } 183 #endif 184 185 /* 186 * Discard ip header, and do tcp input processing. 187 */ 188 hlen += sizeof(struct ip); 189 mp->m_off += hlen; 190 mp->m_len -= hlen; 191 nstate = tp->t_state; 192 tp->tc_flags &= ~TC_NET_KEEP; 193 acounts[tp->t_state][INRECV]++; 194 #ifdef TCPDEBUG 195 if ((tp->t_ucb->uc_flags & UDEBUG) || tcpconsdebug) { 196 tdb_setup(tp, n, INRECV, &tdb); 197 } else 198 tdb.td_tod = 0; 199 #endif 200 switch (tp->t_state) { 201 202 case LISTEN: 203 if (!syn_ok(tp, n) || 204 ((tp->t_ucb->uc_host = h_make(&n->t_s)) == 0)) { 205 nstate = EFAILEC; 206 goto done; 207 } 208 tp->t_fport = n->t_src; 209 tp->t_ucb->uc_template = tcp_template(tp); 210 tcp_ctldat(tp, n, 1); 211 if (tp->tc_flags&TC_FIN_RCVD) { 212 tp->t_finack = T_2ML; /* 3 */ 213 tp->tc_flags &= ~TC_WAITED_2_ML; 214 nstate = CLOSE_WAIT; 215 } else { 216 tp->t_init = T_INIT / 2; /* 4 */ 217 nstate = L_SYN_RCVD; 218 } 219 goto done; 220 221 case SYN_SENT: 222 if (!syn_ok(tp, n)) { 223 nstate = EFAILEC; 224 goto done; 225 } 226 tcp_ctldat(tp, n, 1); 227 if (tp->tc_flags&TC_FIN_RCVD) { 228 if (n->th_flags&TH_ACK) { 229 if (n->t_ackno > tp->iss) 230 present_data(tp); /* 32 */ 231 } else { 232 tp->t_finack = T_2ML; /* 9 */ 233 tp->tc_flags &= ~TC_WAITED_2_ML; 234 } 235 nstate = CLOSE_WAIT; 236 goto done; 237 } 238 if (n->th_flags&TH_ACK) { 239 present_data(tp); /* 11 */ 240 nstate = ESTAB; 241 } else 242 nstate = SYN_RCVD; /* 8 */ 243 goto done; 244 245 case SYN_RCVD: 246 case L_SYN_RCVD: 247 if ((n->th_flags&TH_ACK) == 0 || 248 (n->th_flags&TH_ACK) && n->t_ackno <= tp->iss) { 249 nstate = EFAILEC; 250 goto done; 251 } 252 goto input; 253 254 case ESTAB: 255 case FIN_W1: 256 case FIN_W2: 257 case TIME_WAIT: 258 input: 259 tcp_ctldat(tp, n, 1); /* 39 */ 260 present_data(tp); 261 switch (tp->t_state) { 262 263 case ESTAB: 264 if (tp->tc_flags&TC_FIN_RCVD) 265 nstate = CLOSE_WAIT; 266 break; 267 268 case SYN_RCVD: 269 case L_SYN_RCVD: 270 nstate = (tp->tc_flags&TC_FIN_RCVD) ? 271 CLOSE_WAIT : ESTAB; /* 33:5 */ 272 break; 273 274 case FIN_W1: 275 j = ack_fin(tp, n); 276 if ((tp->tc_flags & TC_FIN_RCVD) == 0) { 277 if (j) 278 nstate = FIN_W2; /* 27 */ 279 break; 280 } 281 tp->t_finack = T_2ML; 282 tp->tc_flags &= ~TC_WAITED_2_ML; 283 nstate = j ? TIME_WAIT : CLOSING1; /* 28:26 */ 284 break; 285 286 case FIN_W2: 287 if (tp->tc_flags&TC_FIN_RCVD) { 288 tp->t_finack = T_2ML; /* 29 */ 289 tp->tc_flags &= ~TC_WAITED_2_ML; 290 nstate = TIME_WAIT; 291 break; 292 } 293 break; 294 } 295 goto done; 296 297 case CLOSE_WAIT: 298 if (n->th_flags&TH_FIN) { 299 if ((n->th_flags&TH_ACK) && 300 n->t_ackno <= tp->seq_fin) { 301 tcp_ctldat(tp, n, 0); /* 30 */ 302 tp->t_finack = T_2ML; 303 tp->tc_flags &= ~TC_WAITED_2_ML; 304 } else 305 tcp_sndctl(tp); /* 31 */ 306 goto done; 307 } 308 goto input; 309 310 case CLOSING1: 311 j = ack_fin(tp, n); 312 if (n->th_flags&TH_FIN) { 313 tcp_ctldat(tp, n, 0); 314 tp->t_finack = T_2ML; 315 tp->tc_flags &= ~TC_WAITED_2_ML; 316 if (j) 317 nstate = TIME_WAIT; /* 23 */ 318 goto done; 319 } 320 if (j) { 321 if (tp->tc_flags&TC_WAITED_2_ML) 322 if (rcv_empty(tp)) { 323 tcp_close(tp, UCLOSED); /* 15 */ 324 nstate = CLOSED; 325 } else 326 nstate = RCV_WAIT; /* 18 */ 327 else 328 nstate = TIME_WAIT; 329 goto done; 330 } 331 goto input; 332 333 case CLOSING2: 334 if (ack_fin(tp, n)) { 335 if (rcv_empty(tp)) { /* 16 */ 336 tcp_close(tp, UCLOSED); 337 nstate = CLOSED; 338 } else 339 nstate = RCV_WAIT; /* 19 */ 340 goto done; 341 } 342 if (n->th_flags&TH_FIN) { 343 tcp_sndctl(tp); /* 31 */ 344 goto done; 345 } 346 goto input; 347 348 case RCV_WAIT: 349 if ((n->th_flags&TH_FIN) && (n->th_flags&TH_ACK) && 350 n->t_ackno <= tp->seq_fin) { 351 tcp_ctldat(tp, n, 0); 352 tp->t_finack = T_2ML; 353 tp->tc_flags &= ~TC_WAITED_2_ML; /* 30 */ 354 } 355 goto done; 356 } 357 panic("tcp_input"); 358 done: 359 360 /* 361 * Done with state*input specific processing. 362 * Form trace records, free input if not needed, 363 * and enter new state. 364 */ 365 #ifdef TCPDEBUG 366 if (tdb.td_tod) 367 tdb_stuff(&tdb, nstate); 368 #endif 369 switch (nstate) { 370 371 case EFAILEC: 372 m_freem(mp); 373 return; 374 375 default: 376 tp->t_state = nstate; 377 /* fall into ... */ 378 379 case CLOSED: 380 /* IF CLOSED CANT LOOK AT tc_flags */ 381 if ((tp->tc_flags&TC_NET_KEEP) == 0) 382 m_freem(mp); 383 return; 384 } 385 /* NOTREACHED */ 386 387 /* 388 * Unwanted packed; free everything 389 * but the header and return an rst. 390 */ 391 notwanted: 392 m_freem(mp->m_next); 393 mp->m_next = NULL; 394 mp->m_len = sizeof(struct th); 395 #define xchg(a,b) j=a; a=b; b=j 396 xchg(n->t_d.s_addr, n->t_s.s_addr); xchg(n->t_dst, n->t_src); 397 #undef xchg 398 if (n->th_flags&TH_ACK) 399 n->t_seq = n->t_ackno; 400 else { 401 n->t_ackno = htonl(ntohl(n->t_seq) + tlen - hlen); 402 n->t_seq = 0; 403 } 404 n->th_flags = TH_RST; /* not TH_FIN, TH_SYN */ 405 n->th_flags ^= TH_ACK; 406 n->t_len = htons(TCPSIZE); 407 n->t_off = 5; 408 n->t_sum = cksum(mp, sizeof(struct th)); 409 ((struct ip *)n)->ip_len = sizeof(struct th); 410 ip_output(mp); 411 netstat.t_badsegs++; 412 } 413 414 tcp_ctldat(tp, n, dataok) 415 register struct tcb *tp; 416 register struct th *n; 417 { 418 register struct mbuf *m; 419 int sent; 420 COUNT(TCP_CTLDAT); 421 422 tp->tc_flags &= ~(TC_DROPPED_TXT|TC_ACK_DUE|TC_NEW_WINDOW); 423 /* syn */ 424 if ((tp->tc_flags&TC_SYN_RCVD) == 0 && (n->th_flags&TH_SYN)) { 425 tp->irs = n->t_seq; 426 tp->rcv_nxt = n->t_seq + 1; 427 tp->snd_wl = tp->rcv_urp = tp->irs; 428 tp->tc_flags |= (TC_SYN_RCVD|TC_ACK_DUE); 429 } 430 /* ack */ 431 if ((n->th_flags&TH_ACK) && (tp->tc_flags&TC_SYN_RCVD) && 432 n->t_ackno > tp->snd_una) { 433 register struct mbuf *mn; 434 register struct ucb *up; 435 register int len; 436 437 up = tp->t_ucb; 438 439 /* update snd_una and snd_nxt */ 440 tp->snd_una = n->t_ackno; 441 if (tp->snd_una > tp->snd_nxt) 442 tp->snd_nxt = tp->snd_una; 443 444 /* if timed msg acked, set retrans time value */ 445 if ((tp->tc_flags&TC_SYN_ACKED) && 446 tp->snd_una > tp->t_xmt_val) { 447 tp->t_xmtime = (tp->t_xmt != 0 ? tp->t_xmt : T_REXMT); 448 if (tp->t_xmtime > T_REMAX) 449 tp->t_xmtime = T_REMAX; 450 } 451 452 /* remove acked data from send buf */ 453 len = tp->snd_una - tp->snd_off; 454 m = up->uc_sbuf; 455 while (len > 0 && m != NULL) 456 if (m->m_len <= len) { 457 len -= m->m_len; 458 if (m->m_off > MMAXOFF) 459 up->uc_ssize -= NMBPG; 460 MFREE(m, mn); 461 m = mn; 462 up->uc_ssize--; 463 } else { 464 m->m_len -= len; 465 m->m_off += len; 466 break; 467 } 468 up->uc_sbuf = m; 469 tp->snd_off = tp->snd_una; 470 if ((tp->tc_flags&TC_SYN_ACKED) == 0 && 471 (tp->snd_una > tp->iss)) { 472 tp->tc_flags |= TC_SYN_ACKED; 473 tp->t_init = 0; 474 } 475 if (tp->seq_fin != tp->iss && tp->snd_una > tp->seq_fin) 476 tp->tc_flags &= ~TC_SND_FIN; 477 tp->t_rexmt = 0; 478 tp->t_rexmttl = 0; 479 tp->tc_flags |= TC_CANCELLED; 480 netwakeup(tp->t_ucb); /* wasteful */ 481 } 482 /* win */ 483 if ((tp->tc_flags & TC_SYN_RCVD) && n->t_seq >= tp->snd_wl) { 484 tp->snd_wl = n->t_seq; 485 tp->snd_wnd = n->t_win; 486 tp->tc_flags |= TC_NEW_WINDOW; 487 tp->t_persist = 0; 488 } 489 if (dataok == 0) 490 goto ctlonly; 491 /* text */ 492 if (n->t_len == 0) 493 goto notext; 494 { register int i; 495 register struct th *p, *q; 496 register struct mbuf *m; 497 int overage; 498 499 /* 500 * Discard duplicate data already passed to user. 501 */ 502 if (SEQ_LT(n->t_seq, tp->rcv_nxt)) { 503 i = tp->rcv_nxt - n->t_seq; 504 if (i >= n->t_len) 505 goto notext; 506 n->t_seq += i; 507 n->t_len -= i; 508 m_adj(dtom(n), i); 509 } 510 511 /* 512 * Find a segment which begins after this one does. 513 */ 514 for (q = tp->t_rcv_next; q != (struct th *)tp; q = q->t_next) 515 if (SEQ_GT(q->t_seq, n->t_seq)) 516 break; 517 518 /* 519 * If there is a preceding segment, it may provide some of 520 * our data already. If so, drop the data from the incoming 521 * segment. If it provides all of our data, drop us. 522 */ 523 if (q->t_prev != (struct th *)tp) { 524 /* conversion to int (in i) handles seq wraparound */ 525 i = q->t_prev->t_seq + q->t_prev->t_len - n->t_seq; 526 if (i > 0) { 527 if (i >= n->t_len) 528 goto notext; /* w/o setting TC_NET_KEEP */ 529 m_adj(dtom(tp), i); 530 n->t_len -= i; 531 n->t_seq += i; 532 } 533 } 534 535 /* 536 * While we overlap succeeding segments trim them or, 537 * if they are completely covered, dequeue them. 538 */ 539 while (q != (struct th *)tp && SEQ_GT(n->t_seq + n->t_len, q->t_seq)) { 540 i = (n->t_seq + n->t_len) - q->t_seq; 541 if (i < q->t_len) { 542 q->t_len -= i; 543 m_adj(dtom(q), i); 544 break; 545 } 546 q = q->t_next; 547 m_freem(dtom(q->t_prev)); 548 remque(q->t_prev); 549 } 550 551 /* 552 * Stick new segment in its place. 553 */ 554 insque(n, q->t_prev); 555 tp->seqcnt += n->t_len; 556 557 #ifdef notdef 558 /* 559 * Calculate available space and discard segments for 560 * which there is too much. 561 */ 562 q = tp->t_rcv_prev; 563 overage = 564 (tp->t_ucb->uc_rcc + tp->rcv_seqcnt) - tp->t_ucb->uc_rhiwat; 565 if (overage > 0) 566 for (;;) { 567 i = MIN(q->t_len, overage); 568 overage -= i; 569 q->t_len -= i; 570 m_adj(q, -i); 571 if (q == n) 572 tp->tc_flags |= TC_DROPPED_TXT; 573 if (q->t_len) 574 break; 575 if (q == n) 576 panic("tcp_text dropall"); 577 q = q->t_prev; 578 remque(q->t_next); 579 } 580 #endif 581 582 /* 583 * Advance rcv_next through 584 * newly completed sequence space 585 * and return forcing an ack. 586 */ 587 while (n->t_seq == tp->rcv_nxt) { 588 /* present data belongs here */ 589 tp->rcv_nxt += n->t_len; 590 n = n->t_next; 591 if (n == (struct th *)tp) 592 break; 593 } 594 tp->tc_flags |= (TC_ACK_DUE|TC_NET_KEEP); 595 } 596 notext: 597 /* urg */ 598 if (n->th_flags&TH_URG) { 599 unsigned urgent; 600 601 urgent = n->t_urp + n->t_seq; 602 if (tp->rcv_nxt < urgent) { 603 if (tp->rcv_urp <= tp->rcv_nxt) 604 to_user(tp->t_ucb, UURGENT); 605 tp->rcv_urp = urgent; 606 } 607 } 608 /* eol */ 609 if ((n->th_flags&TH_EOL) && 610 (tp->tc_flags&TC_DROPPED_TXT) == 0 && 611 tp->t_rcv_prev != (struct th *)tp) { 612 /* mark last mbuf */ 613 m = dtom(tp->t_rcv_prev); 614 if (m != NULL) { 615 while (m->m_next != NULL) 616 m = m->m_next; 617 m->m_act = 618 (struct mbuf *)(m->m_off + m->m_len - 1); 619 } 620 } 621 ctlonly: 622 /* fin */ 623 if ((n->th_flags&TH_FIN) && (tp->tc_flags&TC_DROPPED_TXT) == 0) { 624 seq_t last; 625 626 if ((tp->tc_flags&TC_FIN_RCVD) == 0) { 627 /* do we really have fin ? */ 628 last = firstempty(tp); 629 if (tp->t_rcv_prev == (struct th *)tp || 630 last == t_end(tp->t_rcv_prev)) { 631 tp->tc_flags |= TC_FIN_RCVD; 632 netwakeup(tp->t_ucb); /* poke */ 633 } 634 if ((tp->tc_flags&TC_FIN_RCVD) && 635 tp->rcv_nxt >= last) { 636 tp->rcv_nxt = last + 1; /* fin seq */ 637 tp->tc_flags |= TC_ACK_DUE; 638 } 639 } else 640 tp->tc_flags |= TC_ACK_DUE; 641 } 642 643 /* respond */ 644 sent = 0; 645 if (tp->tc_flags&TC_ACK_DUE) 646 sent = tcp_sndctl(tp); 647 else if (tp->tc_flags&TC_NEW_WINDOW) { 648 seq_t last = tp->snd_off; 649 for (m = tp->t_ucb->uc_sbuf; m != NULL; m = m->m_next) /*###*/ 650 last += m->m_len; /*###*/ 651 if (tp->snd_nxt <= last || (tp->tc_flags&TC_SND_FIN)) 652 sent = tcp_send(tp); 653 } 654 655 /* set for retrans */ 656 if (!sent && tp->snd_una < tp->snd_nxt && 657 (tp->tc_flags&TC_CANCELLED)) { 658 tp->t_rexmt = tp->t_xmtime; 659 tp->t_rexmttl = T_REXMTTL; 660 tp->t_rexmt_val = tp->t_rtl_val = tp->snd_lst; 661 tp->tc_flags &= ~TC_CANCELLED; 662 } 663 } 664 665 present_data(tp) 666 register struct tcb *tp; 667 { 668 register struct th *t; 669 register struct ucb *up; 670 register struct mbuf *m, **mp; 671 seq_t ready; 672 COUNT(PRESENT_DATA); 673 674 /* connection must be synced and data available for user */ 675 if ((tp->tc_flags&TC_SYN_ACKED) == 0) 676 return; 677 up = tp->t_ucb; 678 mp = &up->uc_rbuf; 679 while (*mp) 680 mp = &(*mp)->m_next; 681 t = tp->t_rcv_next; 682 /* SHOULD PACK DATA IN HERE */ 683 while (t != (struct th *)tp && t->t_seq < tp->rcv_nxt) { 684 remque(t); 685 m = dtom(t); 686 up->uc_rcc += t->t_len; 687 tp->seqcnt -= t->t_len; 688 if (tp->seqcnt < 0) panic("present_data"); 689 t = t->t_next; 690 while (m) { 691 if (m->m_len == 0) { 692 m = m_free(m); 693 continue; 694 } 695 *mp = m; 696 mp = &m->m_next; 697 m = *mp; 698 } 699 } 700 if (up->uc_rcc != 0) 701 netwakeup(up); 702 if ((tp->tc_flags&TC_FIN_RCVD) && /* ### */ 703 (tp->tc_flags&TC_USR_CLOSED) == 0 && /* ### */ 704 rcv_empty(tp)) /* ### */ 705 to_user(up, UCLOSED); /* ### */ 706 } 707