diff options
author | shin <shin@FreeBSD.org> | 2000-01-09 19:17:30 +0000 |
---|---|---|
committer | shin <shin@FreeBSD.org> | 2000-01-09 19:17:30 +0000 |
commit | 3bdc213839894a0b030b1dbcd75627552b53f9e0 (patch) | |
tree | 791258919d385ab19f9452b82289317ac87b7b82 /sys/netinet/tcp_usrreq.c | |
parent | 7db2a1ade5cd3959a2fb9d19a12f9791a6a31b66 (diff) | |
download | FreeBSD-src-3bdc213839894a0b030b1dbcd75627552b53f9e0.zip FreeBSD-src-3bdc213839894a0b030b1dbcd75627552b53f9e0.tar.gz |
tcp updates to support IPv6.
also a small patch to sys/nfs/nfs_socket.c, as max_hdr size change.
Reviewed by: freebsd-arch, cvs-committers
Obtained from: KAME project
Diffstat (limited to 'sys/netinet/tcp_usrreq.c')
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 288 |
1 files changed, 287 insertions, 1 deletions
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index d3aea36..7ae34e9 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -35,6 +35,7 @@ */ #include "opt_ipsec.h" +#include "opt_inet6.h" #include "opt_tcpdebug.h" #include <sys/param.h> @@ -42,6 +43,9 @@ #include <sys/kernel.h> #include <sys/sysctl.h> #include <sys/mbuf.h> +#ifdef INET6 +#include <sys/domain.h> +#endif /* INET6 */ #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/protosw.h> @@ -51,9 +55,18 @@ #include <netinet/in.h> #include <netinet/in_systm.h> +#ifdef INET6 +#include <netinet/ip6.h> +#endif #include <netinet/in_pcb.h> +#ifdef INET6 +#include <netinet6/in6_pcb.h> +#endif #include <netinet/in_var.h> #include <netinet/ip_var.h> +#ifdef INET6 +#include <netinet6/ip6_var.h> +#endif #include <netinet/tcp.h> #include <netinet/tcp_fsm.h> #include <netinet/tcp_seq.h> @@ -76,6 +89,10 @@ extern char *tcpstates[]; /* XXX ??? */ static int tcp_attach __P((struct socket *, struct proc *)); static int tcp_connect __P((struct tcpcb *, struct sockaddr *, struct proc *)); +#ifdef INET6 +static int tcp6_connect __P((struct tcpcb *, struct sockaddr *, + struct proc *)); +#endif /* INET6 */ static struct tcpcb * tcp_disconnect __P((struct tcpcb *)); static struct tcpcb * @@ -85,7 +102,7 @@ static struct tcpcb * #define TCPDEBUG0 int ostate #define TCPDEBUG1() ostate = tp ? tp->t_state : 0 #define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ - tcp_trace(TA_USER, ostate, tp, 0, req) + tcp_trace(TA_USER, ostate, tp, 0, 0, req) #else #define TCPDEBUG0 #define TCPDEBUG1() @@ -197,6 +214,51 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) } +#ifdef INET6 +static int +tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + int s = splnet(); + int error = 0; + struct inpcb *inp = sotoinpcb(so); + struct tcpcb *tp; + struct sockaddr_in6 *sin6p; + + COMMON_START(); + + /* + * Must check for multicast addresses and disallow binding + * to them. + */ + sin6p = (struct sockaddr_in6 *)nam; + if (sin6p->sin6_family == AF_INET6 && + IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { + error = EAFNOSUPPORT; + goto out; + } + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; + if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_BINDV6ONLY) == NULL) { + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) + inp->inp_vflag |= INP_IPV4; + else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + struct sockaddr_in sin; + + in6_sin6_2_sin(&sin, sin6p); + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; + error = in_pcbbind(inp, (struct sockaddr *)&sin, p); + goto out; + } + } + error = in6_pcbbind(inp, nam, p); + if (error) + goto out; + COMMON_END(PRU_BIND); +} +#endif /* INET6 */ + /* * Prepare to accept connections. */ @@ -216,6 +278,29 @@ tcp_usr_listen(struct socket *so, struct proc *p) COMMON_END(PRU_LISTEN); } +#ifdef INET6 +static int +tcp6_usr_listen(struct socket *so, struct proc *p) +{ + int s = splnet(); + int error = 0; + struct inpcb *inp = sotoinpcb(so); + struct tcpcb *tp; + + COMMON_START(); + if (inp->inp_lport == 0) { + inp->inp_vflag &= ~INP_IPV4; + if (ip6_mapped_addr_on && + (inp->inp_flags & IN6P_BINDV6ONLY) == NULL) + inp->inp_vflag |= INP_IPV4; + error = in6_pcbbind(inp, (struct sockaddr *)0, p); + } + if (error == 0) + tp->t_state = TCPS_LISTEN; + COMMON_END(PRU_LISTEN); +} +#endif /* INET6 */ + /* * Initiate connection to peer. * Create a template for use in transmissions on this connection. @@ -252,6 +337,49 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) COMMON_END(PRU_CONNECT); } +#ifdef INET6 +static int +tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +{ + int s = splnet(); + int error = 0; + struct inpcb *inp = sotoinpcb(so); + struct tcpcb *tp; + struct sockaddr_in6 *sin6p; + + COMMON_START(); + + /* + * Must disallow TCP ``connections'' to multicast addresses. + */ + sin6p = (struct sockaddr_in6 *)nam; + if (sin6p->sin6_family == AF_INET6 + && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { + error = EAFNOSUPPORT; + goto out; + } + + if (ip6_mapped_addr_on && + IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { + struct sockaddr_in sin; + + in6_sin6_2_sin(&sin, sin6p); + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; + if ((error = tcp_connect(tp, (struct sockaddr *)&sin, p)) != 0) + goto out; + error = tcp_output(tp); + goto out; + } + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; + if ((error = tcp6_connect(tp, nam, p)) != 0) + goto out; + error = tcp_output(tp); + COMMON_END(PRU_CONNECT); +} +#endif /* INET6 */ + /* * Initiate disconnect from peer. * If connection never passed embryonic stage, just drop; @@ -294,6 +422,20 @@ tcp_usr_accept(struct socket *so, struct sockaddr **nam) COMMON_END(PRU_ACCEPT); } +#ifdef INET6 +static int +tcp6_usr_accept(struct socket *so, struct sockaddr **nam) +{ + int s = splnet(); + int error = 0; + struct inpcb *inp = sotoinpcb(so); + struct tcpcb *tp; + + COMMON_START(); + in6_mapped_peeraddr(so, nam); + COMMON_END(PRU_ACCEPT); +} +#endif /* INET6 */ /* * Mark the connection as being incapable of further output. */ @@ -344,6 +486,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; +#ifdef INET6 + int isipv6; +#endif TCPDEBUG0; if (inp == NULL) { @@ -361,6 +506,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, TCPDEBUG1(); goto out; } +#ifdef INET6 + isipv6 = nam && nam->sa_family == AF_INET6; +#endif /* INET6 */ tp = intotcpcb(inp); TCPDEBUG1(); if (control) { @@ -383,6 +531,11 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, * initialize maxseg/maxopd using peer's cached * MSS. */ +#ifdef INET6 + if (isipv6) + error = tcp6_connect(tp, nam, p); + else +#endif /* INET6 */ error = tcp_connect(tp, nam, p); if (error) goto out; @@ -427,6 +580,11 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m, * initialize maxseg/maxopd using peer's cached * MSS. */ +#ifdef INET6 + if (isipv6) + error = tcp6_connect(tp, nam, p); + else +#endif /* INET6 */ error = tcp_connect(tp, nam, p); if (error) goto out; @@ -497,6 +655,16 @@ struct pr_usrreqs tcp_usrreqs = { in_setsockaddr, sosend, soreceive, sopoll }; +#ifdef INET6 +struct pr_usrreqs tcp6_usrreqs = { + tcp_usr_abort, tcp6_usr_accept, tcp_usr_attach, tcp6_usr_bind, + tcp6_usr_connect, pru_connect2_notsupp, in6_control, tcp_usr_detach, + tcp_usr_disconnect, tcp6_usr_listen, in6_mapped_peeraddr, tcp_usr_rcvd, + tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, + in6_mapped_sockaddr, sosend, soreceive, sopoll +}; +#endif /* INET6 */ + /* * Common subroutine to open a TCP connection to remote host specified * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local @@ -595,6 +763,99 @@ tcp_connect(tp, nam, p) return 0; } +#ifdef INET6 +static int +tcp6_connect(tp, nam, p) + register struct tcpcb *tp; + struct sockaddr *nam; + struct proc *p; +{ + struct inpcb *inp = tp->t_inpcb, *oinp; + struct socket *so = inp->inp_socket; + struct tcpcb *otp; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; + struct in6_addr *addr6; + struct rmxp_tao *taop; + struct rmxp_tao tao_noncached; + int error; + + if (inp->inp_lport == 0) { + error = in6_pcbbind(inp, (struct sockaddr *)0, p); + if (error) + return error; + } + + /* + * Cannot simply call in_pcbconnect, because there might be an + * earlier incarnation of this same connection still in + * TIME_WAIT state, creating an ADDRINUSE error. + */ + error = in6_pcbladdr(inp, nam, &addr6); + if (error) + return error; + oinp = in6_pcblookup_hash(inp->inp_pcbinfo, + &sin6->sin6_addr, sin6->sin6_port, + IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) + ? addr6 + : &inp->in6p_laddr, + inp->inp_lport, 0, NULL); + if (oinp) { + if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && + otp->t_state == TCPS_TIME_WAIT && + (ticks - otp->t_starttime) < tcp_msl && + (otp->t_flags & TF_RCVD_CC)) + otp = tcp_close(otp); + else + return EADDRINUSE; + } + if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) + inp->in6p_laddr = *addr6; + inp->in6p_faddr = sin6->sin6_addr; + inp->inp_fport = sin6->sin6_port; + if ((sin6->sin6_flowinfo & IPV6_FLOWINFO_MASK) != NULL) + inp->in6p_flowinfo = sin6->sin6_flowinfo; + in_pcbrehash(inp); + + tp->t_template = tcp_template(tp); + if (tp->t_template == 0) { + in6_pcbdisconnect(inp); + return ENOBUFS; + } + + /* Compute window scaling to request. */ + while (tp->request_r_scale < TCP_MAX_WINSHIFT && + (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) + tp->request_r_scale++; + + soisconnecting(so); + tcpstat.tcps_connattempt++; + tp->t_state = TCPS_SYN_SENT; + callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp); + tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + + /* + * Generate a CC value for this connection and + * check whether CC or CCnew should be used. + */ + if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { + taop = &tao_noncached; + bzero(taop, sizeof(*taop)); + } + + tp->cc_send = CC_INC(tcp_ccgen); + if (taop->tao_ccsent != 0 && + CC_GEQ(tp->cc_send, taop->tao_ccsent)) { + taop->tao_ccsent = tp->cc_send; + } else { + taop->tao_ccsent = 0; + tp->t_flags |= TF_SENDCCNEW; + } + + return 0; +} +#endif /* INET6 */ + /* * The new sockopt interface makes it possible for us to block in the * copyin/out step (if we take a page fault). Taking a page fault at @@ -619,6 +880,11 @@ tcp_ctloutput(so, sopt) return (ECONNRESET); } if (sopt->sopt_level != IPPROTO_TCP) { +#ifdef INET6 + if (INP_CHECK_SOCKAF(so, AF_INET6)) + error = ip6_ctloutput(so, sopt); + else +#endif /* INET6 */ error = ip_ctloutput(so, sopt); splx(s); return (error); @@ -726,6 +992,9 @@ tcp_attach(so, p) register struct tcpcb *tp; struct inpcb *inp; int error; +#ifdef INET6 + int isipv6 = INP_CHECK_SOCKAF(so, AF_INET6) != NULL; +#endif if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { error = soreserve(so, tcp_sendspace, tcp_recvspace); @@ -739,16 +1008,33 @@ tcp_attach(so, p) #ifdef IPSEC error = ipsec_init_policy(so, &inp->inp_sp); if (error) { +#ifdef INET6 + if (isipv6) + in6_pcbdetach(inp); + else +#endif in_pcbdetach(inp); return (error); } #endif /*IPSEC*/ +#ifdef INET6 + if (isipv6) { + inp->inp_vflag |= INP_IPV6; + inp->in6p_hops = -1; /* use kernel default */ + } + else +#endif inp->inp_vflag |= INP_IPV4; tp = tcp_newtcpcb(inp); if (tp == 0) { int nofd = so->so_state & SS_NOFDREF; /* XXX */ so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ +#ifdef INET6 + if (isipv6) + in6_pcbdetach(inp); + else +#endif in_pcbdetach(inp); so->so_state |= nofd; return (ENOBUFS); |