fixes for range locking
[unix-history] / usr / src / sys / deprecated / bbnnet / tcp_usrreq.c
CommitLineData
17efd7fe
MK
1#ifdef RCSIDENT
2static char rcsident[] = "$Header: tcp_usrreq.c,v 1.30 85/07/31 09:43:43 walsh Exp $";
3#endif RCSIDENT
4
5#include "../h/param.h"
6#include "../h/systm.h"
7#include "../h/mbuf.h"
8#include "../h/socket.h"
9#include "../h/socketvar.h"
10#include "../h/protosw.h"
11#include "../h/errno.h"
12#include "../h/ioctl.h"
13#include "../h/time.h"
14#include "../h/kernel.h"
15
16#include "../net/if.h"
17#include "../net/route.h"
18
19#include "../bbnnet/in.h"
20#include "../bbnnet/in_var.h"
21#include "../bbnnet/in_pcb.h"
22#include "../bbnnet/net.h"
23#include "../bbnnet/fsm.h"
24#include "../bbnnet/tcp.h"
25#include "../bbnnet/ip.h"
26#include "../bbnnet/icmp.h"
27#include "../bbnnet/macros.h"
28#include "../bbnnet/sws.h"
29
30/*
31 * TCP protocol interface to socket abstraction.
32 */
33
34#ifdef GPROF
35int tcp_acounts[TCP_NSTATES][PRU_NREQ];
36#endif
37
38extern tcp_pcbdisconnect();
39extern tcp_binding_used();
40
41struct inpcb tcp;
42struct tcp_stat tcpstat;
43sequence tcp_iss; /* tcp initial send seq # */
44
45struct dfilter tcp_dfilter;
46
47struct pr_advice tcp_advice =
48{
49 TCP_RESERVED, /* application reserved */
50 TCP_USERRESERVED, /* user reserved */
51 TCP_MAXPORT, /* max port */
52 TCP_USERRESERVED+1, /* random last used */
53 sizeof(u_short), /* port size */
54 tcp_binding_used, /* confirmation routine */
55} ;
56
57dowedebug(inp, so, filter)
58register struct inpcb *inp;
59struct socket *so;
60register struct dfilter *filter;
61{
62 register int count;
63
64 count = 0;
65 if (inp->inp_faddr.s_addr == filter->foreign_host.s_addr)
66 count ++;
67 if (inp->inp_fport == filter->foreign_port)
68 count ++;
69 if (inp->inp_laddr.s_addr == filter->local_host.s_addr)
70 count ++;
71 if (inp->inp_lport == filter->local_port)
72 count ++;
73
74 if (count >= filter->matches)
75 so->so_options |= SO_DEBUG;
76}
77
78int tcp_noact = 0; /* patchable */
79
80/*
81 * Allocate and initialize a new TCB
82 * tcp_usrreq calls tcp_attach calls us. tcp_usrreq splnet()'s
83 */
84struct tcpcb *tcp_newtcpcb(inp)
85register struct inpcb *inp;
86{
87 register struct tcpcb *tp;
88 register struct mbuf *m;
89
90 m = m_getclr(M_WAIT, MT_PCB);
91 if (m == NULL)
92 return(NULL);
93 tp = mtod(m, struct tcpcb *);
94
95 /* initialize non-zero tcb fields */
96
97 tp->t_rcv_next = (struct th *)tp;
98 tp->t_rcv_prev = (struct th *)tp;
99 /*
100 * Don't start off assuming minimum srtt/rxmitime. If we do, and
101 * TCP_tvRXMIN is small and we decide to communicate over a
102 * reliable, but slow, network then we may not find true values for
103 * these. We may assume an ACK was for a retransmission that
104 * we're measuring the srtt of, not the original packet.
105 *
106 * Instead, start high and approach from above in a deterministic
107 * fashion. We should get close to the right values fairly rapidly.
108 *
109 * 7/85: start from above by special casing first round trip time
110 * measurement. If srtt == 0, do not reset rtt, and do not use
111 * weighted averaging. srtt starts as time to ack(xmit [+ rxmit...])
112 * and then gets smoothed with new round trip times. This compromise
113 * for getting to long-term srtt more quickly on LANs should work
114 * on the Internet as well. It will only hurt Internet connections
115 * if packet loss is high, and even then would only slow getting
116 * to long term srtt.
117 * This method can be turned off by initializing srtt with a non-zero
118 * value.
119 */
120 /* tp->t_srtt = TCP_tvMAXSRTT; */
121 tp->t_rxmitime = TCP_tvMAXSRTT + 1;
122 tp->t_rttltimeo = TCP_tvRTTL;
123 tp->t_xmt_val = tp->snd_end = tp->seq_fin = tp->snd_nxt =
124 tp->snd_hi = tp->snd_una = tp->iss = tcp_iss;
125 tcp_iss += ISSINCR;
126
127 /*
128 * Imitate Berkeley code by setting push as a default. This should
129 * increase compatibility at the user code level.
130 */
131 tp->t_push = TRUE;
132
133 /*
134 * Berkeley 4.2 code sends a data byte beyond the window's edge to see
135 * if the other end is up. If other end does not respond, connection
136 * times out and aborts. This is dangerous since the byte may make its
137 * way into the input stream if the recipient is coded keeping in mind
138 * how expensive packets are.
139 *
140 * We'll provide for an optional method to send a well formed ack that
141 * will catch remote failure and generate a tcp reset. Note that we
142 * don't care if the other end ignores the ack; we only hope for a well
143 * coded tcp to respond with a reset in the right circumstances. This
144 * sort of handshaking/probing should really be done at the application
145 * level, but not all specs (eg., SMTP) provide for such a noop.
146 *
147 * Optional, since some networks charge for packets and since some might
148 * see this as unecessary traffic.
149 *
150 * also see tcp_ioctl()
151 */
152 if (tp->t_noact = tcp_noact)
153 tp->t_noactprobe = TRUE;
154
155 /* attach the tcpcb to the in_pcb */
156
157 inp->inp_ppcb = (caddr_t)tp;
158 tp->t_in_pcb = inp;
159
160 return(tp);
161}
162
163/*
164 * Is a tcp port/address pair already in use by some socket on this machine?
165 * Passed to in_pcbbind() to help it find a port/address binding
166 * that is unique for tcp.
167 */
168int tcp_binding_used(inp, lport, lsaddr, reuselocal)
169struct inpcb *inp;
170u_short lport;
171u_long lsaddr;
172{
173 register struct inpcb *i;
174
175 for (i = tcp.inp_next; i != &tcp; i = i->inp_next)
176 {
177 /*
178 * Since our inpcb is in this linked list, don't want to know
179 * if we, ourselves, are already using this binding.
180 */
181 if (i != inp)
182 if (i->inp_lport == lport)
183 /*
184 * Our/His address is unbound (INADDR_ANY) iff
185 * not yet connected to foreign host.
186 */
187 if ((i->inp_laddr.s_addr == lsaddr) ||
188 (i->inp_laddr.s_addr == INADDR_ANY) ||
189 (lsaddr == INADDR_ANY))
190 {
191 if (!reuselocal)
192 break;
193 if (i->inp_faddr.s_addr == INADDR_ANY)
194 /*
195 * We're both waiting for foreign
196 * connection. Could only re-use if
197 * he was already connected.
198 */
199 break;
200 }
201 }
202 return (i != &tcp);
203}
204
205/*
206 * returns a (struct tcpcb *) cast to a (char *). This is
207 * so in_pcbconnect() can correctly handle return value. All
208 * other uses promptly cast back.
209 */
210
211char *tcp_conn_used(inp, lport, lsaddr, fport, fsaddr)
212struct inpcb *inp;
213u_short lport;
214u_long lsaddr;
215u_short fport;
216u_long fsaddr;
217{
218 register struct inpcb *i;
219
220 for (i = tcp.inp_next; i != &tcp; i = i->inp_next)
221 {
222 /*
223 * Since our inpcb is in this linked list, don't want to know
224 * if we, ourselves, are already using this connetion.
225 */
226 if (i != inp)
227 if ((i->inp_lport == lport) &&
228 (i->inp_fport == fport) &&
229 (i->inp_laddr.s_addr == lsaddr) &&
230 (i->inp_faddr.s_addr == fsaddr))
231 return((char *)i->inp_ppcb);
232 }
233 return ((char *) NULL);
234}
235
236tcp_ioctl (tp, command, data)
237struct tcpcb *tp;
238int command;
239caddr_t data;
240{
241 switch (command)
242 {
243 /* push */
244 case SIOCSPUSH:
245 tp->t_push = TRUE;
246 break;
247
248 case SIOCCPUSH:
249 tp->t_push = FALSE;
250 break;
251
252 /* no activity timer */
253 case SIOCSNOACT:
254 {
255 u_long value;
256
257 value = *((u_long *) data);
258 /*
259 * A shutdown socket should still be able to request some sort of
260 * check on the status of the remote end. Also see tcp_newtcpcb().
261 */
262 tp->t_noactprobe = (value & TCP_NOACTPROBE) ? TRUE : FALSE;
263 tp->t_noactsig = (value & TCP_NOACTSIG) ? TRUE : FALSE;
264
265 if ((tp->t_state <= ESTAB) || (tp->t_state == CLOSE_WAIT))
266 {
267 /* don't interfere with system use of timer */
268 value &= ~(TCP_NOACTPROBE|TCP_NOACTSIG);
269 tp->t_noact = MIN (MAX_TCPTIMERVAL, value);
270 tp->t_timers[TNOACT] = tp->t_noact;
271 }
272 }
273 break;
274
275 case SIOCGNOACT:
276 {
277 u_long value;
278
279 value = tp->t_noact;
280 if (tp->t_noactprobe)
281 value |= TCP_NOACTPROBE;
282 if (tp->t_noactsig)
283 value |= TCP_NOACTSIG;
284
285 *((u_long *) data) = value;
286 }
287 break;
288
289 /* init timer */
290 case SIOCSINIT:
291 tp->t_itimeo = MIN (MAX_TCPTIMERVAL, *((unsigned *) data));
292 break;
293
294 case SIOCGINIT:
295 *((int *) data) = tp->t_itimeo;
296 break;
297
298 /* retransmit took too long timer */
299 case SIOCSRTTL:
300 tp->t_rttltimeo = MIN (MAX_TCPTIMERVAL, *((unsigned *) data));
301 break;
302
303 case SIOCGRTTL:
304 *((int *) data) = tp->t_rttltimeo;
305 break;
306
307 case SIOCABORT:
308 {
309 struct socket *so;
310
311 /* there really should be a generic way for
312 * a user to get to soabort()
313 */
314
315 tp->usr_abort = TRUE;
316 /*
317 * Just in case asked to abort a LISTENing socket,
318 * Don't leave unattached, unaccepted connections.
319 */
320 so = tp->t_in_pcb->inp_socket;
321 while (so->so_q0 && (so->so_q0 != so))
322 (void) soabort(so->so_q0);
323 while (so->so_q && (so->so_q != so))
324 (void) soabort(so->so_q);
325
326 w_alloc(IUABORT, 0, tp, tp->t_in_pcb);
327 }
328 break;
329
330 default:
331 /* not our ioctl, let lower level try ioctl */
332 return ip_ioctl (tp->t_in_pcb, command, data);
333 }
334
335 return (0);
336}
337
338
339/*
340 * Process a TCP user request for TCP tb. If this is a send request
341 * then m is the mbuf chain of send data. If this is a timer expiration
342 * (called from the software clock routine), then timertype tells which timer.
343 */
344/*ARGSUSED*/
345tcp_usrreq(so, req, m, nam, rights)
346struct socket *so;
347int req;
348struct mbuf *m, *nam, *rights;
349{
350 register struct inpcb *inp;
351 register struct tcpcb *tp;
352 register int s;
353 register int act, newstate;
354 int error = 0;
355
356 s = splnet();
357 inp = sotoinpcb(so);
358
359 /* keep in mind call from ifioctl() */
360 if (rights && req != PRU_CONTROL)
361 {
362 if (rights->m_len)
363 {
364 splx(s);
365 return (EINVAL);
366 }
367 }
368 /*
369 * When a TCP is attached to a socket, then there will be
370 * a (struct inpcb) pointed at by the socket, and this
371 * structure will point at a subsidary (struct tcpcb).
372 */
373 if (inp == NULL && req != PRU_ATTACH)
374 {
375 splx(s);
376 return (EINVAL); /* XXX */
377 }
378 if (inp)
379 {
380 tp = inptotcpcb(inp);
381 /* WHAT IF TP IS 0? */
382#ifdef GPROF
383 tcp_acounts[tp->t_state][req]++;
384#endif
385 }
386
387 /*
388 * This switch becomes a 'caseb', so put common ones at top.
389 */
390 switch (req)
391 {
392
393 case PRU_RCVD:
394 /*
395 * After a receive, possibly send window update to peer.
396 */
397 W_ALLOC(IURECV, 0, tp, NULL, so, act, newstate);
398 break;
399
400 case PRU_SEND:
401 /*
402 * Do a send by initiating the proper entry to the FSM.
403 * Don't let urgent continue.
404 */
405 tp->t_urg = FALSE;
406 W_ALLOC(IUSEND, 0, tp, m, so, act, newstate);
407 break;
408
409 /*
410 * TCP attaches to socket via PRU_ATTACH, reserving space,
411 * and an internet control block.
412 */
413 case PRU_ATTACH:
414 if (inp)
415 {
416 error = EISCONN;
417 break;
418 }
419 error = tcp_attach(so);
420 if (error)
421 break;
422 if ((so->so_options & SO_LINGER) && so->so_linger == 0)
423 so->so_linger = T_LINGERTIME;
424 tp = sototcpcb(so);
425 break;
426
427 /*
428 * PRU_DETACH detaches the TCP protocol from the socket.
429 * This is only done after SO_ISCONNECTED has been cleared.
430 */
431 case PRU_DETACH:
432 tcp_disconnect(tp);
433 break;
434
435 /*
436 * Give the socket an address.
437 */
438 case PRU_BIND:
439 error = in_pcbbind(inp, nam, &tcp_advice);
440 break;
441
442 /*
443 * Prepare to accept connections.
444 */
445 case PRU_LISTEN:
446 if (inp->inp_lport == 0)
447 error = in_pcbbind(inp, (struct mbuf *)0, &tcp_advice);
448 if (error == 0)
449 w_alloc(IUOPENA, 0, tp, NULL);
450 break;
451
452 /*
453 * Initiate connection to peer.
454 * Bind the local end if not already.
455 * Set the routing.
456 * Crank up the TCP state machine.
457 */
458 case PRU_CONNECT:
459 {
460 struct in_addr laddr;
461
462 laddr = inp->inp_laddr;
463 if (inp->inp_lport == 0)
464 {
465 error = in_pcbbind(inp, (struct mbuf *)0, &tcp_advice);
466 if (error)
467 break;
468 }
469 error = in_pcbconnect(inp, nam, tcp_conn_used);
470 if (error)
471 break;
472
473 if (in_broadcast(inp->inp_faddr))
474 {
475 in_pcbdisconnect (inp, tcp_pcbdisconnect);
476 inp->inp_laddr = laddr;
477 error = EADDRNOTAVAIL;
478 break;
479 }
480
481 if (! (tp->t_template = tcp_template(tp)))
482 {
483 in_pcbdisconnect (inp, tcp_pcbdisconnect);
484 inp->inp_laddr = laddr;
485 error = ENOBUFS;
486 break;
487 }
488
489 tp->sws_qff = SWS_QFF_DEF;
490
491 /*
492 * So can debug connection problems without having to change
493 * every program or apply debugging flag to each program every
494 * time run it.
495 */
496 dowedebug(inp, so, &tcp_dfilter);
497
498 soisconnecting(so);
499 w_alloc(IUOPENR, 0, tp, NULL);
500 }
501 break;
502
503 /*
504 * Create a TCP connection between two sockets.
505 */
506 case PRU_CONNECT2:
507 error = EOPNOTSUPP;
508 break;
509
510 /*
511 * Initiate disconnect from peer.
512 * If connection never passed embryonic stage, just drop;
513 * else if don't need to let data drain, then can just drop anyways,
514 * else have to begin TCP shutdown process: mark socket disconnecting,
515 * drain unread data, state switch to reflect user close, and
516 * send segment (e.g. FIN) to peer. Socket will be really disconnected
517 * when peer sends FIN and acks ours.
518 */
519 case PRU_DISCONNECT:
520 tcp_disconnect(tp);
521 break;
522
523 /*
524 * Accept a connection. Essentially all the work is
525 * done at higher levels; just return the address
526 * of the peer, storing through addr.
527 *
528 * BBN-NOTE: upper levels do all the waiting; this stays the same.
529 */
530 case PRU_ACCEPT:
531 {
532 struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
533
534 nam->m_len = sizeof (struct sockaddr_in);
535 sin->sin_family = AF_INET;
536 sin->sin_port = inp->inp_fport;
537 sin->sin_addr = inp->inp_faddr;
538 break;
539 }
540
541 /*
542 * Mark the connection as being incapable of further output.
543 */
544 case PRU_SHUTDOWN:
545 socantsendmore(so);
546 if (! tp->usr_closed)
547 w_alloc(IUCLOSE, 0, tp, inp);
548 break;
549
550 /*
551 * Abort the TCP.
552 */
553 case PRU_ABORT:
554 w_alloc(IUABORT, 0, tp, inp);
555 break;
556
557 case PRU_CONTROL:
558 error = tcp_ioctl(tp, (int) m, (caddr_t) nam);
559 break;
560
561
562/* SOME AS YET UNIMPLEMENTED HOOKS */
563 case PRU_SENSE:
564 error = EOPNOTSUPP;
565 break;
566/* END UNIMPLEMENTED HOOKS */
567
568 case PRU_RCVOOB:
569
570 {
571 int desired;
572
573 if (so->so_oobmark == 0 && (so->so_state & SS_RCVATMARK) == 0)
574 {
575 error = EINVAL;
576 break;
577 }
578 if (tp->oob_data == NULL)
579 {
580 error = EWOULDBLOCK;
581 break;
582 }
583 desired = *(mtod(m, int *));
584
585 while ((desired > 0) && (tp->oob_data))
586 {
587 char *p;
588 unsigned count;
589
590 p = mtod(m, caddr_t);
591 count = MIN(desired, tp->oob_data->m_len);
592 count = MIN(count, MLEN);
593 bcopy(mtod(tp->oob_data, caddr_t), p, count);
594 m->m_len = count;
595 desired -= count;
596
597 tp->oob_data->m_len -= count;
598 tp->oob_data->m_off += count;
599 if (tp->oob_data->m_len <= 0)
600 tp->oob_data = m_free(tp->oob_data);
601
602 if ((desired > 0) && (tp->oob_data))
603 {
604 m->m_next = m_get(M_WAIT, MT_DATA);
605 m = m->m_next;
606 }
607 }
608
609 }
610 break;
611
612 case PRU_SENDOOB:
613 /*
614 * allows up to MAX_TCPOOB bytes of out of band data
615 * even if user has used up all his allocated space.
616 */
617 if (sbspace(&so->so_snd) < (- MAX_TCPOOB))
618 {
619 m_freem(m);
620 error = ENOBUFS;
621 break;
622
623 }
624 tp->t_urg = TRUE;
625 w_alloc(IUSEND, 0, tp, m);
626 break;
627
628 /*
629 * Return the address of this socket (local-side binding)
630 */
631 case PRU_SOCKADDR:
632 in_setsockaddr(inp, nam);
633 break;
634
635 case PRU_PEERADDR:
636 in_setpeeraddr(inp, nam);
637 break;
638
639 /*
640 * TCP slow timer went off; run down all those timers.
641 */
642 case PRU_SLOWTIMO:
643 tcp_timeo();
644 break;
645
646 default:
647 panic("tcp_usrreq");
648 }
649 splx(s);
650 return (error);
651}
652
653/*
654 * getsockopt() / setsockopt()
655 */
656tcp_ctloutput (req,so,level,optname,optval)
657int req;
658struct socket *so;
659int level, optname;
660struct mbuf **optval;
661{
662 int s = splnet(); /* like PRU/packet/timer entry into net code */
663 int error;
664 struct inpcb *inp;
665
666 /*
667 * possibly for us?
668 * Follow Berkeley methods: level is protocol number if meant for the
669 * protocol layer. (Why not say if=0, arp=1, ip=2, udp/tcp/rdp=3....?)
670 *
671 * Problem: tcp needs to know about IP options in order to use right
672 * maxseg. This doesn't quite work with the layering.
673 *
674 * Why not combine ioctl/setsockopt/getsockopt paths, since ioctl can be
675 * seen as fixed size sockopt- tried at BBN; removed for 4.3
676 */
677
678 /* should be "mature" socket so pointers all valid... */
679 inp = sotoinpcb(so);
680
681 switch(req)
682 {
683 case PRCO_GETOPT:
684 error = tcp_getopt (inp, optname, optval);
685 break;
686
687 case PRCO_SETOPT:
688 error = tcp_setopt (inp, optname, optval);
689 break;
690
691 default:
692 panic("tcp_ctloutput");
693 }
694
695 splx(s);
696 return (error);
697}
698
699tcp_getopt (inp, command, data)
700struct inpcb *inp;
701struct mbuf **data;
702{
703 /*
704 * no TCP specific options accessed by getsockopt() as yet.
705 * let lower level at cmd
706 */
707 return ip_getopt (inp, command, data);
708}
709
710tcp_setopt (inp, command, data)
711struct inpcb *inp;
712struct mbuf **data;
713{
714 int error;
715 struct tcpcb *tp;
716
717 /* no TCP specific options accessed by setsockopt() as yet */
718 tp = inptotcpcb(inp);
719
720 if (command == SO_IPROUTE)
721 tp->t_maxseg += inp->inp_optlen;
722
723 error = ip_setopt(inp, command, data);
724
725 if (command == SO_IPROUTE)
726 tp->t_maxseg -= inp->inp_optlen;
727
728 return (error);
729}
730
731/*
732 * These numbers come from measurements described in the paper
733 * "Converting the BBN TCP/IP to 4.2BSD" (S.L.C. USENIX)
734 * If your network handles packets larger than an ethernet frame, you
735 * could change tcp_init back to determine the largest net's packet size,
736 * multiply that by some number, and round up to a multiple of a CLSIZE.
737 */
738int tcp_recvspace = 4096;
739int tcp_sendspace = 4096;
740
741/*
742 * Attach TCP protocol to socket, allocating
743 * internet protocol control block, tcp control block, buffer space.
744 */
745tcp_attach(so)
746struct socket *so;
747{
748 register struct tcpcb *tp;
749 struct inpcb *inp;
750 int error;
751
752 if (! (error = soreserve(so, tcp_sendspace, tcp_recvspace)))
753 {
754 if (! (error = in_pcballoc(so, &tcp)))
755 {
756 inp = sotoinpcb(so);
757 if (tp = tcp_newtcpcb(inp))
758 {
759 /*
760 * Should change state tables to have an UNOPENED state like
761 * the butterfly's which is different from SAME.
762 */
763 tp->t_state = 0;
764 return (0);
765 }
766 error = ENOBUFS;
767 in_pcbdetach(inp, (int (*)())0);
768 }
769 }
770 return (error);
771}
772
773/*
774 * Initiate (or continue) disconnect.
775 * If embryonic state, just send reset (once).
776 * If not in ``let data drain'' option, just drop.
777 * Otherwise (hard), mark socket disconnecting and drop
778 * current input data; switch states based on user close, and
779 * send segment to peer (with FIN).
780 */
781
782tcp_disconnect(tp)
783register struct tcpcb *tp;
784{
785 struct socket *so = tp->t_in_pcb->inp_socket;
786
787 soisdisconnecting(so);
788 sbflush(&so->so_rcv);
789 tp->usr_abort = TRUE;
790 if (!tp->usr_closed)
791 w_alloc(IUCLOSE, 0, tp, tp->t_in_pcb);
792}
793
794tcp_init()
795{
796 /*
797 * Leave these checks in! It's a pain in the ass to find out
798 * problems caused by too small mbufs if someone changes the
799 * size of an mbuf.
800 */
801 if (sizeof(struct inpcb) > MLEN)
802 panic("inpcb too big");
803
804 if (sizeof(struct socket) > MLEN)
805 panic("socket too big");
806
807 if (sizeof(struct th) > MLEN)
808 panic("th too big");
809
810 if (sizeof(struct tcpcb) > MLEN)
811 panic("tcpcb too big");
812
813 if (sizeof(struct t_debug) > MLEN)
814 panic("t_debug too big");
815
816 /* init queue */
817 tcp.inp_next = tcp.inp_prev = &tcp;
818
819 /* are only 4 things to match. turn off for now */
820 tcp_dfilter.matches = 5;
821
822 tcp_iss = time.tv_sec;
823
824 ipsw[IPPROTO_TCP].ipsw_hlen = sizeof(struct th);
825}
826
827tcp_ctlinput (prc_code, arg)
828caddr_t arg;
829{
830 int error;
831
832 error = inetctlerrmap[prc_code];
833
834 switch (prc_code)
835 {
836 case PRC_UNREACH_PROTOCOL: /* icmp message */
837 case PRC_UNREACH_PORT:
838 case PRC_MSGSIZE:
839 {
840 register struct th *tp;
841 struct tcpcb *t;
842
843 tp = (struct th *) (&((struct icmp *) arg)->ic_iphdr);
844 t = (struct tcpcb *)tcp_conn_used ((struct inpcb *) 0,
845 tp->t_src, tp->t_s.s_addr,
846 tp->t_dst, tp->t_d.s_addr);
847 if (t)
848 t_close(t, error);
849 }
850 break;
851
852 case PRC_UNREACH_NET:
853 case PRC_UNREACH_HOST:
854 {
855 register struct th *tp;
856 struct tcpcb *t;
857
858 tp = (struct th *) (&((struct icmp *) arg)->ic_iphdr);
859 t = (struct tcpcb *)tcp_conn_used ((struct inpcb *) 0,
860 tp->t_src, tp->t_s.s_addr,
861 tp->t_dst, tp->t_d.s_addr);
862 if (t)
863 {
864 struct socket *so;
865
866 so = t->t_in_pcb->inp_socket;
867 if ((so->so_state & SS_NOFDREF) == 0)
868 advise_user(so, error);
869 else
870 t_close(t, error);
871 }
872 }
873 break;
874
875 case PRC_GWDOWN:
876 in_gdown (&tcp, (u_long) arg);
877 break;
878
879 case PRC_REDIRECT_NET: /* icmp message */
880 case PRC_REDIRECT_HOST:
881 {
882 struct tcpcb *t;
883 register struct th *tp;
884
885 tp = (struct th *) (&((struct icmp *) arg)->ic_iphdr);
886 t = (struct tcpcb *)tcp_conn_used ((struct inpcb *) 0,
887 tp->t_src, tp->t_s.s_addr,
888 tp->t_dst, tp->t_d.s_addr);
889 if (t)
890 icmp_redirect_inp(t->t_in_pcb, (struct icmp *) arg,
891 prc_code == PRC_REDIRECT_NET ? rtnet : rthost);
892 }
893 break;
894
895 case PRC_TIMXCEED_INTRANS: /* icmp message */
896 case PRC_TIMXCEED_REASS:
897 case PRC_PARAMPROB:
898 break;
899
900 case PRC_QUENCH: /* icmp message */
901 /*
902 * See RFC 896. The idea is, when we get a source quench message on
903 * a connection we should send fewer packets. This ties in with the
904 * silly window syndrome whose solution is to send fewer, larger packets.
905 * Deal with quenches by altering threshold used by silly window
906 * syndrome. This is similar to acting as if the window is smaller
907 * than it actually is for deciding when to send, except that when we
908 * do, we use as much as there really is.
909 */
910 {
911 register struct th *tp;
912 struct tcpcb *t;
913
914 tp = (struct th *) (&((struct icmp *) arg)->ic_iphdr);
915 t = (struct tcpcb *)tcp_conn_used ((struct inpcb *) 0,
916 tp->t_src, tp->t_s.s_addr,
917 tp->t_dst, tp->t_d.s_addr);
918 if (t)
919 {
920 t->sws_qff -= SWS_QFF_DEC;
921 if (t->sws_qff < SWS_QFF_MIN)
922 t->sws_qff = SWS_QFF_MIN;
923 }
924 }
925 break;
926
927 case PRC_IFDOWN:
928 {
929 u_long addr;
930
931 addr = ((struct sockaddr_in *)(arg))->sin_addr.s_addr;
932 inpcb_notify(&tcp, addr, (u_long) 0, error);
933 inpcb_notify(&tcp, (u_long) 0, addr, error);
934 }
935 break;
936
937 case PRC_HOSTDEAD: /* from imp interface */
938 case PRC_HOSTUNREACH:
939 /*
940 * get same message for destination hosts and gateways.
941 */
942 {
943 u_long addr;
944
945 addr = ((struct sockaddr_in *)arg)->sin_addr.s_addr;
946 in_gdown (&tcp, addr);
947 inpcb_notify(&tcp, (u_long) 0, addr, error);
948 }
949 break;
950
951 default:
952 panic("tcp_ctlinput");
953 }
954}