source: rtems/cpukit/libnetworking/netinet/tcp_usrreq.c @ 657e6c93

5
Last change on this file since 657e6c93 was 657e6c93, checked in by Christian Mauderer <Christian.Mauderer@…>, on 06/24/16 at 05:57:17

libnetworking: Import current <netinet/in.h>

Import the <netinet/in.h> from current FreeBSD. This allows to build
some current software (e.g. libressl).

Add legacy support like

  • prototype for in_cksum(),
  • IPPORT_USERRESERVED,
  • deprecated IPCTL_RT* defines,
  • ip_fw_chk_t and ip_fw_ctl_t,
  • ip_nat_... (IP NAT hooks), and
  • IP_NAT option for get/setsockopt()

to new <rtems/rtems_netinet_in.h>.

  • Property mode set to 100644
File size: 19.7 KB
Line 
1/*
2 * Copyright (c) 1982, 1986, 1988, 1993
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *      From: @(#)tcp_usrreq.c  8.2 (Berkeley) 1/3/94
30 * $FreeBSD: src/sys/netinet/tcp_usrreq.c,v 1.120 2005/05/01 14:01:38 rwatson Exp $
31 */
32 
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include "opt_tcpdebug.h"
39
40#include <sys/param.h>
41#include <sys/queue.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/sysctl.h>
45#include <sys/malloc.h>
46#include <sys/mbuf.h>
47#include <sys/socket.h>
48#include <sys/socketvar.h>
49#include <sys/protosw.h>
50#include <errno.h>
51#include <sys/stat.h>
52
53#include <net/if.h>
54#include <net/route.h>
55
56#include <netinet/in.h>
57#include <rtems/rtems_netinet_in.h>
58#include <netinet/in_systm.h>
59#include <netinet/ip.h>
60#include <netinet/in_pcb.h>
61#include <netinet/in_var.h>
62#include <netinet/ip_var.h>
63#include <netinet/tcp.h>
64#include <netinet/tcp_fsm.h>
65#include <netinet/tcp_seq.h>
66#include <netinet/tcp_timer.h>
67#include <netinet/tcp_var.h>
68#include <netinet/tcpip.h>
69#ifdef TCPDEBUG
70#include <netinet/tcp_debug.h>
71#endif
72
73/*
74 * TCP protocol interface to socket abstraction.
75 */
76extern  char *tcpstates[];
77
78static int      tcp_attach(struct socket *);
79static int      tcp_connect(struct tcpcb *, struct mbuf *);
80static struct tcpcb *
81                tcp_disconnect(struct tcpcb *);
82static struct tcpcb *
83                tcp_usrclosed(struct tcpcb *);
84
85#ifdef TCPDEBUG
86#define TCPDEBUG0       int ostate
87#define TCPDEBUG1()     ostate = tp ? tp->t_state : 0
88#define TCPDEBUG2(req)  if (tp && (so->so_options & SO_DEBUG)) \
89                                tcp_trace(TA_USER, ostate, tp, 0, req)
90#else
91#define TCPDEBUG0
92#define TCPDEBUG1()
93#define TCPDEBUG2(req)
94#endif
95
96/*
97 * TCP attaches to socket via pru_attach(), reserving space,
98 * and an internet control block.
99 */
100static int
101tcp_usr_attach(struct socket *so, intptr_t proto)
102{
103        int s = splnet();
104        int error;
105        struct inpcb *inp = sotoinpcb(so);
106        struct tcpcb *tp = 0;
107        TCPDEBUG0;
108
109        TCPDEBUG1();
110        if (inp) {
111                error = EISCONN;
112                goto out;
113        }
114
115        error = tcp_attach(so);
116        if (error)
117                goto out;
118
119        if ((so->so_options & SO_LINGER) && so->so_linger == 0)
120                so->so_linger = TCP_LINGERTIME * hz;
121        tp = sototcpcb(so);
122out:
123        TCPDEBUG2(PRU_ATTACH);
124        splx(s);
125        return error;
126}
127
128/*
129 * pru_detach() detaches the TCP protocol from the socket.
130 * If the protocol state is non-embryonic, then can't
131 * do this directly: have to initiate a pru_disconnect(),
132 * which may finish later; embryonic TCB's can just
133 * be discarded here.
134 */
135static int
136tcp_usr_detach(struct socket *so)
137{
138        int s = splnet();
139        int error = 0;
140        struct inpcb *inp = sotoinpcb(so);
141        struct tcpcb *tp;
142        TCPDEBUG0;
143
144        if (inp == 0) {
145                splx(s);
146                return EINVAL;  /* XXX */
147        }
148        tp = intotcpcb(inp);
149        TCPDEBUG1();
150        if (tp->t_state > TCPS_LISTEN)
151                tp = tcp_disconnect(tp);
152        else
153                tp = tcp_close(tp);
154
155        TCPDEBUG2(PRU_DETACH);
156        splx(s);
157        return error;
158}
159
160#define COMMON_START()  TCPDEBUG0; \
161                        do { \
162                                     if (inp == 0) { \
163                                             splx(s); \
164                                             return EINVAL; \
165                                     } \
166                                     tp = intotcpcb(inp); \
167                                     TCPDEBUG1(); \
168                     } while(0)
169                             
170#define COMMON_END(req) out: TCPDEBUG2(req); splx(s); return error; goto out
171
172
173/*
174 * Give the socket an address.
175 */
176static int
177tcp_usr_bind(struct socket *so, struct mbuf *nam)
178{
179        int s = splnet();
180        int error = 0;
181        struct inpcb *inp = sotoinpcb(so);
182        struct tcpcb *tp;
183        struct sockaddr_in *sinp;
184
185        COMMON_START();
186
187        /*
188         * Must check for multicast addresses and disallow binding
189         * to them.
190         */
191        sinp = mtod(nam, struct sockaddr_in *);
192        if (sinp->sin_family == AF_INET &&
193            IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
194                error = EAFNOSUPPORT;
195                goto out;
196        }
197        error = in_pcbbind(inp, nam);
198        if (error)
199                goto out;
200        COMMON_END(PRU_BIND);
201
202}
203
204/*
205 * Prepare to accept connections.
206 */
207static int
208tcp_usr_listen(struct socket *so)
209{
210        int s = splnet();
211        int error = 0;
212        struct inpcb *inp = sotoinpcb(so);
213        struct tcpcb *tp;
214
215        COMMON_START();
216        if (inp->inp_lport == 0)
217                error = in_pcbbind(inp, NULL);
218        if (error == 0)
219                tp->t_state = TCPS_LISTEN;
220        COMMON_END(PRU_LISTEN);
221}
222
223/*
224 * Initiate connection to peer.
225 * Create a template for use in transmissions on this connection.
226 * Enter SYN_SENT state, and mark socket as connecting.
227 * Start keep-alive timer, and seed output sequence space.
228 * Send initial segment on connection.
229 */
230static int
231tcp_usr_connect(struct socket *so, struct mbuf *nam)
232{
233        int s = splnet();
234        int error = 0;
235        struct inpcb *inp = sotoinpcb(so);
236        struct tcpcb *tp;
237        struct sockaddr_in *sinp;
238
239        COMMON_START();
240
241        /*
242         * Must disallow TCP ``connections'' to multicast addresses.
243         */
244        sinp = mtod(nam, struct sockaddr_in *);
245        if (sinp->sin_family == AF_INET
246            && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
247                error = EAFNOSUPPORT;
248                goto out;
249        }
250
251        if ((error = tcp_connect(tp, nam)) != 0)
252                goto out;
253        error = tcp_output(tp);
254        COMMON_END(PRU_CONNECT);
255}
256
257/*
258 * Initiate disconnect from peer.
259 * If connection never passed embryonic stage, just drop;
260 * else if don't need to let data drain, then can just drop anyways,
261 * else have to begin TCP shutdown process: mark socket disconnecting,
262 * drain unread data, state switch to reflect user close, and
263 * send segment (e.g. FIN) to peer.  Socket will be really disconnected
264 * when peer sends FIN and acks ours.
265 *
266 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB.
267 */
268static int
269tcp_usr_disconnect(struct socket *so)
270{
271        int s = splnet();
272        int error = 0;
273        struct inpcb *inp = sotoinpcb(so);
274        struct tcpcb *tp;
275
276        COMMON_START();
277        tp = tcp_disconnect(tp);
278        COMMON_END(PRU_DISCONNECT);
279}
280
281/*
282 * Accept a connection.  Essentially all the work is
283 * done at higher levels; just return the address
284 * of the peer, storing through addr.
285 */
286static int
287tcp_usr_accept(struct socket *so, struct mbuf *nam)
288{
289        int s = splnet();
290        int error = 0;
291        struct inpcb *inp = sotoinpcb(so);
292        struct tcpcb *tp;
293
294        COMMON_START();
295        in_setpeeraddr(inp, nam);
296        COMMON_END(PRU_ACCEPT);
297}
298
299/*
300 * Mark the connection as being incapable of further output.
301 */
302static int
303tcp_usr_shutdown(struct socket *so)
304{
305        int s = splnet();
306        int error = 0;
307        struct inpcb *inp = sotoinpcb(so);
308        struct tcpcb *tp;
309
310        COMMON_START();
311        socantsendmore(so);
312        tp = tcp_usrclosed(tp);
313        if (tp)
314                error = tcp_output(tp);
315        COMMON_END(PRU_SHUTDOWN);
316}
317
318/*
319 * After a receive, possibly send window update to peer.
320 */
321static int
322tcp_usr_rcvd(struct socket *so, intptr_t flags)
323{
324        int s = splnet();
325        int error = 0;
326        struct inpcb *inp = sotoinpcb(so);
327        struct tcpcb *tp;
328
329        COMMON_START();
330        tcp_output(tp);
331        COMMON_END(PRU_RCVD);
332}
333
334/*
335 * Do a send by putting data in output queue and updating urgent
336 * marker if URG set.  Possibly send more data.
337 */
338static int
339tcp_usr_send(struct socket *so, int flags, struct mbuf *m, struct mbuf *nam,
340             struct mbuf *control)
341{
342        int s = splnet();
343        int error = 0;
344        struct inpcb *inp = sotoinpcb(so);
345        struct tcpcb *tp;
346
347        COMMON_START();
348        if (control && control->m_len) {
349                m_freem(control); /* XXX shouldn't caller do this??? */
350                if (m)
351                        m_freem(m);
352                error = EINVAL;
353                goto out;
354        }
355
356        if(!(flags & PRUS_OOB)) {
357                sbappend(&so->so_snd, m);
358                if (nam && tp->t_state < TCPS_SYN_SENT) {
359                        /*
360                         * Do implied connect if not yet connected,
361                         * initialize window to default value, and
362                         * initialize maxseg/maxopd using peer's cached
363                         * MSS.
364                         */
365                        error = tcp_connect(tp, nam);
366                        if (error)
367                                goto out;
368                        tp->snd_wnd = TTCP_CLIENT_SND_WND;
369                        tcp_mss(tp, -1);
370                }
371
372                if (flags & PRUS_EOF) {
373                        /*
374                         * Close the send side of the connection after
375                         * the data is sent.
376                         */
377                        socantsendmore(so);
378                        tp = tcp_usrclosed(tp);
379                }
380                if (tp != NULL)
381                        error = tcp_output(tp);
382        } else {
383                if (sbspace(&so->so_snd) < -512) {
384                        m_freem(m);
385                        error = ENOBUFS;
386                        goto out;
387                }
388                /*
389                 * According to RFC961 (Assigned Protocols),
390                 * the urgent pointer points to the last octet
391                 * of urgent data.  We continue, however,
392                 * to consider it to indicate the first octet
393                 * of data past the urgent section.
394                 * Otherwise, snd_up should be one lower.
395                 */
396                sbappend(&so->so_snd, m);
397                if (nam && tp->t_state < TCPS_SYN_SENT) {
398                        /*
399                         * Do implied connect if not yet connected,
400                         * initialize window to default value, and
401                         * initialize maxseg/maxopd using peer's cached
402                         * MSS.
403                         */
404                        error = tcp_connect(tp, nam);
405                        if (error)
406                                goto out;
407                        tp->snd_wnd = TTCP_CLIENT_SND_WND;
408                        tcp_mss(tp, -1);
409                }
410                tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
411                tp->t_force = 1;
412                error = tcp_output(tp);
413                tp->t_force = 0;
414        }
415        COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB :
416                   ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND));
417}
418
419/*
420 * Abort the TCP.
421 */
422static int
423tcp_usr_abort(struct socket *so)
424{
425        int s = splnet();
426        int error = 0;
427        struct inpcb *inp = sotoinpcb(so);
428        struct tcpcb *tp;
429
430        COMMON_START();
431        tp = tcp_drop(tp, ECONNABORTED);
432        COMMON_END(PRU_ABORT);
433}
434
435/*
436 * Fill in st_bklsize for fstat() operations on a socket.
437 */
438static int
439tcp_usr_sense(struct socket *so, struct stat *sb)
440{
441        int s = splnet();
442
443        sb->st_blksize = so->so_snd.sb_hiwat;
444        splx(s);
445        return 0;
446}
447
448/*
449 * Receive out-of-band data.
450 */
451static int
452tcp_usr_rcvoob(struct socket *so, struct mbuf *m, intptr_t flags)
453{
454        int s = splnet();
455        int error = 0;
456        struct inpcb *inp = sotoinpcb(so);
457        struct tcpcb *tp;
458
459        COMMON_START();
460        if ((so->so_oobmark == 0 &&
461             (so->so_state & SS_RCVATMARK) == 0) ||
462            so->so_options & SO_OOBINLINE ||
463            tp->t_oobflags & TCPOOB_HADDATA) {
464                error = EINVAL;
465                goto out;
466        }
467        if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) {
468                error = EWOULDBLOCK;
469                goto out;
470        }
471        m->m_len = 1;
472        *mtod(m, caddr_t) = tp->t_iobc;
473        if ((flags & MSG_PEEK) == 0)
474                tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA);
475        COMMON_END(PRU_RCVOOB);
476}
477
478static int
479tcp_usr_sockaddr(struct socket *so, struct mbuf *nam)
480{
481        int s = splnet();
482        int error = 0;
483        struct inpcb *inp = sotoinpcb(so);
484        struct tcpcb *tp;
485
486        COMMON_START();
487        in_setsockaddr(inp, nam);
488        COMMON_END(PRU_SOCKADDR);
489}
490
491static int
492tcp_usr_peeraddr(struct socket *so, struct mbuf *nam)
493{
494        int s = splnet();
495        int error = 0;
496        struct inpcb *inp = sotoinpcb(so);
497        struct tcpcb *tp;
498
499        COMMON_START();
500        in_setpeeraddr(inp, nam);
501        COMMON_END(PRU_PEERADDR);
502}
503
504/*
505 * XXX - this should just be a call to in_control, but we need to get
506 * the types worked out.
507 */
508static int
509tcp_usr_control(struct socket *so, intptr_t cmd, caddr_t arg, struct ifnet *ifp)
510{
511        return in_control(so, cmd, arg, ifp);
512}
513
514/* xxx - should be const */
515struct pr_usrreqs tcp_usrreqs = {
516        tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind,
517        tcp_usr_connect, pru_connect2_notsupp, tcp_usr_control, tcp_usr_detach,
518        tcp_usr_disconnect, tcp_usr_listen, tcp_usr_peeraddr, tcp_usr_rcvd,
519        tcp_usr_rcvoob, tcp_usr_send, tcp_usr_sense, tcp_usr_shutdown,
520        tcp_usr_sockaddr
521};
522
523/*
524 * Common subroutine to open a TCP connection to remote host specified
525 * by struct sockaddr_in in mbuf *nam.  Call in_pcbbind to assign a local
526 * port number if needed.  Call in_pcbladdr to do the routing and to choose
527 * a local host address (interface).  If there is an existing incarnation
528 * of the same connection in TIME-WAIT state and if the remote host was
529 * sending CC options and if the connection duration was < MSL, then
530 * truncate the previous TIME-WAIT state and proceed.
531 * Initialize connection parameters and enter SYN-SENT state.
532 */
533static int
534tcp_connect(struct tcpcb *tp, struct mbuf *nam)
535{
536        struct inpcb *inp = tp->t_inpcb, *oinp;
537        struct socket *so = inp->inp_socket;
538        struct tcpcb *otp;
539        struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
540        struct sockaddr_in *ifaddr;
541        int error;
542        struct rmxp_tao *taop;
543        struct rmxp_tao tao_noncached;
544
545        if (inp->inp_lport == 0) {
546                error = in_pcbbind(inp, NULL);
547                if (error)
548                        return error;
549        }
550
551        /*
552         * Cannot simply call in_pcbconnect, because there might be an
553         * earlier incarnation of this same connection still in
554         * TIME_WAIT state, creating an ADDRINUSE error.
555         */
556        error = in_pcbladdr(inp, nam, &ifaddr);
557        if (error)
558                return error;
559        oinp = in_pcblookuphash(inp->inp_pcbinfo,
560            sin->sin_addr, sin->sin_port,
561            inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr
562                                                : ifaddr->sin_addr,
563            inp->inp_lport,  0);
564        if (oinp) {
565                if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
566                otp->t_state == TCPS_TIME_WAIT &&
567                    otp->t_duration < TCPTV_MSL &&
568                    (otp->t_flags & TF_RCVD_CC))
569                        otp = tcp_close(otp);
570                else
571                        return EADDRINUSE;
572        }
573        if (inp->inp_laddr.s_addr == INADDR_ANY)
574                inp->inp_laddr = ifaddr->sin_addr;
575        inp->inp_faddr = sin->sin_addr;
576        inp->inp_fport = sin->sin_port;
577        in_pcbrehash(inp);
578
579        tp->t_template = tcp_template(tp);
580        if (tp->t_template == 0) {
581                in_pcbdisconnect(inp);
582                return ENOBUFS;
583        }
584
585        /* Compute window scaling to request.  */
586        while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
587            (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
588                tp->request_r_scale++;
589
590        soisconnecting(so);
591        tcpstat.tcps_connattempt++;
592        tp->t_state = TCPS_SYN_SENT;
593        tp->t_timer[TCPT_KEEP] = tcp_keepinit;
594        tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
595        tcp_sendseqinit(tp);
596
597        /*
598         * Generate a CC value for this connection and
599         * check whether CC or CCnew should be used.
600         */
601        if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) {
602                taop = &tao_noncached;
603                bzero(taop, sizeof(*taop));
604        }
605
606        tp->cc_send = CC_INC(tcp_ccgen);
607        if (taop->tao_ccsent != 0 &&
608            CC_GEQ(tp->cc_send, taop->tao_ccsent)) {
609                taop->tao_ccsent = tp->cc_send;
610        } else {
611                taop->tao_ccsent = 0;
612                tp->t_flags |= TF_SENDCCNEW;
613        }
614
615        return 0;
616}
617
618int
619tcp_ctloutput(int op, struct socket *so, int level, int optname,
620    struct mbuf **mp)
621{
622        int error = 0, s;
623        struct inpcb *inp;
624        register struct tcpcb *tp;
625        register struct mbuf *m;
626        register int i;
627
628        s = splnet();
629        inp = sotoinpcb(so);
630        if (inp == NULL) {
631                splx(s);
632                if (op == PRCO_SETOPT && *mp)
633                        (void) m_free(*mp);
634                return (ECONNRESET);
635        }
636        if (level != IPPROTO_TCP) {
637                error = ip_ctloutput(op, so, level, optname, mp);
638                splx(s);
639                return (error);
640        }
641        tp = intotcpcb(inp);
642
643        switch (op) {
644
645        case PRCO_SETOPT:
646                m = *mp;
647                switch (optname) {
648
649                case TCP_NODELAY:
650                        if (m == NULL || m->m_len < sizeof (int))
651                                error = EINVAL;
652                        else if (*mtod(m, int *))
653                                tp->t_flags |= TF_NODELAY;
654                        else
655                                tp->t_flags &= ~TF_NODELAY;
656                        break;
657
658                case TCP_MAXSEG:
659                        if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg)
660                                tp->t_maxseg = i;
661                        else
662                                error = EINVAL;
663                        break;
664
665                case TCP_NOOPT:
666                        if (m == NULL || m->m_len < sizeof (int))
667                                error = EINVAL;
668                        else if (*mtod(m, int *))
669                                tp->t_flags |= TF_NOOPT;
670                        else
671                                tp->t_flags &= ~TF_NOOPT;
672                        break;
673
674                case TCP_NOPUSH:
675                        if (m == NULL || m->m_len < sizeof (int))
676                                error = EINVAL;
677                        else if (*mtod(m, int *))
678                                tp->t_flags |= TF_NOPUSH;
679                        else
680                                tp->t_flags &= ~TF_NOPUSH;
681                        break;
682
683                default:
684                        error = ENOPROTOOPT;
685                        break;
686                }
687                if (m)
688                        (void) m_free(m);
689                break;
690
691        case PRCO_GETOPT:
692                *mp = m = m_get(M_WAIT, MT_SOOPTS);
693                m->m_len = sizeof(int);
694
695                switch (optname) {
696                case TCP_NODELAY:
697                        *mtod(m, int *) = tp->t_flags & TF_NODELAY;
698                        break;
699                case TCP_MAXSEG:
700                        *mtod(m, int *) = tp->t_maxseg;
701                        break;
702                case TCP_NOOPT:
703                        *mtod(m, int *) = tp->t_flags & TF_NOOPT;
704                        break;
705                case TCP_NOPUSH:
706                        *mtod(m, int *) = tp->t_flags & TF_NOPUSH;
707                        break;
708                default:
709                        error = ENOPROTOOPT;
710                        break;
711                }
712                break;
713        }
714        splx(s);
715        return (error);
716}
717
718/*
719 * tcp_sendspace and tcp_recvspace are the default send and receive window
720 * sizes, respectively.  These are obsolescent (this information should
721 * be set by the route).
722 */
723u_long  tcp_sendspace = 1024*16;
724SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace,
725        CTLFLAG_RW, &tcp_sendspace , 0, "");
726u_long  tcp_recvspace = 1024*16;
727SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace,
728        CTLFLAG_RW, &tcp_recvspace , 0, "");
729
730#if defined(__rtems__)
731void rtems_set_tcp_buffer_sizes(u_long sendspace, u_long recvspace)
732{
733    if ( sendspace != 0 )
734      tcp_sendspace = sendspace;
735    if ( recvspace != 0 )
736      tcp_recvspace = recvspace;
737}
738#endif
739
740/*
741 * Attach TCP protocol to socket, allocating
742 * internet protocol control block, tcp control block,
743 * bufer space, and entering LISTEN state if to accept connections.
744 */
745static int
746tcp_attach(struct socket *so)
747{
748        register struct tcpcb *tp;
749        struct inpcb *inp;
750        int error;
751
752        if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
753                error = soreserve(so, tcp_sendspace, tcp_recvspace);
754                if (error)
755                        return (error);
756        }
757        error = in_pcballoc(so, &tcbinfo);
758        if (error)
759                return (error);
760        inp = sotoinpcb(so);
761        tp = tcp_newtcpcb(inp);
762        if (tp == 0) {
763                int nofd = so->so_state & SS_NOFDREF;   /* XXX */
764
765                so->so_state &= ~SS_NOFDREF;    /* don't free the socket yet */
766                in_pcbdetach(inp);
767                so->so_state |= nofd;
768                return (ENOBUFS);
769        }
770        tp->t_state = TCPS_CLOSED;
771        return (0);
772}
773
774/*
775 * Initiate (or continue) disconnect.
776 * If embryonic state, just send reset (once).
777 * If in ``let data drain'' option and linger null, just drop.
778 * Otherwise (hard), mark socket disconnecting and drop
779 * current input data; switch states based on user close, and
780 * send segment to peer (with FIN).
781 */
782static struct tcpcb *
783tcp_disconnect(struct tcpcb *tp)
784{
785        struct socket *so = tp->t_inpcb->inp_socket;
786
787        if (tp->t_state < TCPS_ESTABLISHED)
788                tp = tcp_close(tp);
789        else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
790                tp = tcp_drop(tp, 0);
791        else {
792                soisdisconnecting(so);
793                sbflush(&so->so_rcv);
794                tp = tcp_usrclosed(tp);
795                if (tp)
796                        (void) tcp_output(tp);
797        }
798        return (tp);
799}
800
801/*
802 * User issued close, and wish to trail through shutdown states:
803 * if never received SYN, just forget it.  If got a SYN from peer,
804 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
805 * If already got a FIN from peer, then almost done; go to LAST_ACK
806 * state.  In all other cases, have already sent FIN to peer (e.g.
807 * after PRU_SHUTDOWN), and just have to play tedious game waiting
808 * for peer to send FIN or not respond to keep-alives, etc.
809 * We can let the user exit from the close as soon as the FIN is acked.
810 */
811static struct tcpcb *
812tcp_usrclosed(struct tcpcb *tp)
813{
814
815        switch (tp->t_state) {
816
817        case TCPS_CLOSED:
818        case TCPS_LISTEN:
819                tp->t_state = TCPS_CLOSED;
820                tp = tcp_close(tp);
821                break;
822
823        case TCPS_SYN_SENT:
824        case TCPS_SYN_RECEIVED:
825                tp->t_flags |= TF_NEEDFIN;
826                break;
827
828        case TCPS_ESTABLISHED:
829                tp->t_state = TCPS_FIN_WAIT_1;
830                break;
831
832        case TCPS_CLOSE_WAIT:
833                tp->t_state = TCPS_LAST_ACK;
834                break;
835        }
836        if (tp && tp->t_state >= TCPS_FIN_WAIT_2) {
837                soisdisconnected(tp->t_inpcb->inp_socket);
838                /* To prevent the connection hanging in FIN_WAIT_2 forever. */
839                if (tp->t_state == TCPS_FIN_WAIT_2)
840                        tp->t_timer[TCPT_2MSL] = tcp_maxidle;
841        }
842        return (tp);
843}
Note: See TracBrowser for help on using the repository browser.