diff options
| -rw-r--r-- | sys/netinet/in_pcb.c | 205 | ||||
| -rw-r--r-- | sys/netinet/in_pcb.h | 5 | ||||
| -rw-r--r-- | sys/netinet/tcp_usrreq.c | 26 |
3 files changed, 136 insertions, 100 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 5ea538a31652..e7992d59acd9 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -404,32 +404,110 @@ in_pcbbind_setup(inp, nam, laddrp, lportp, td) } /* - * Transform old in_pcbconnect() into an inner subroutine for new - * in_pcbconnect(): Do some validity-checking on the remote - * address (in mbuf 'nam') and then determine local host address - * (i.e., which interface) to use to access that remote host. - * - * This preserves definition of in_pcbconnect(), while supporting a - * slightly different version for T/TCP. (This is more than - * a bit of a kludge, but cleaning up the internal interfaces would - * have forced minor changes in every protocol). + * Connect from a socket to a specified address. + * Both address and port must be specified in argument sin. + * If don't have a local address for this socket yet, + * then pick one. */ +int +in_pcbconnect(inp, nam, td) + register struct inpcb *inp; + struct sockaddr *nam; + struct thread *td; +{ + u_short lport, fport; + in_addr_t laddr, faddr; + int anonport, error; + + lport = inp->inp_lport; + laddr = inp->inp_laddr.s_addr; + anonport = (lport == 0); + error = in_pcbconnect_setup(inp, nam, &laddr, &lport, &faddr, &fport, + NULL, td); + if (error) + return (error); + + /* Do the initial binding of the local address if required. */ + if (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0) { + inp->inp_lport = lport; + inp->inp_laddr.s_addr = laddr; + if (in_pcbinshash(inp) != 0) { + inp->inp_laddr.s_addr = INADDR_ANY; + inp->inp_lport = 0; + return (EAGAIN); + } + } + + /* Commit the remaining changes. */ + inp->inp_lport = lport; + inp->inp_laddr.s_addr = laddr; + inp->inp_faddr.s_addr = faddr; + inp->inp_fport = fport; + in_pcbrehash(inp); + if (anonport) + inp->inp_flags |= INP_ANONPORT; + return (0); +} +/* + * Set up for a connect from a socket to the specified address. + * On entry, *laddrp and *lportp should contain the current local + * address and port for the PCB; these are updated to the values + * that should be placed in inp_laddr and inp_lport to complete + * the connect. + * + * On success, *faddrp and *fportp will be set to the remote address + * and port. These are not updated in the error case. + * + * If the operation fails because the connection already exists, + * *oinpp will be set to the PCB of that connection so that the + * caller can decide to override it. In all other cases, *oinpp + * is set to NULL. + */ int -in_pcbladdr(inp, nam, plocal_sin) +in_pcbconnect_setup(inp, nam, laddrp, lportp, faddrp, fportp, oinpp, td) register struct inpcb *inp; struct sockaddr *nam; - struct sockaddr_in **plocal_sin; + in_addr_t *laddrp; + u_short *lportp; + in_addr_t *faddrp; + u_short *fportp; + struct inpcb **oinpp; + struct thread *td; { + struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct in_ifaddr *ia; - register struct sockaddr_in *sin = (struct sockaddr_in *)nam; + struct sockaddr_in sa; + struct ucred *cred; + struct inpcb *oinp; + struct in_addr laddr, faddr; + u_short lport, fport; + int error; + if (oinpp != NULL) + *oinpp = NULL; if (nam->sa_len != sizeof (*sin)) return (EINVAL); if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); if (sin->sin_port == 0) return (EADDRNOTAVAIL); + laddr.s_addr = *laddrp; + lport = *lportp; + faddr = sin->sin_addr; + fport = sin->sin_port; + cred = inp->inp_socket->so_cred; + if (laddr.s_addr == INADDR_ANY && jailed(cred)) { + bzero(&sa, sizeof(sa)); + sa.sin_addr.s_addr = htonl(prison_getip(cred)); + sa.sin_len = sizeof(sa); + sa.sin_family = AF_INET; + error = in_pcbbind_setup(inp, (struct sockaddr *)&sa, + &laddr.s_addr, &lport, td); + if (error) + return (error); + } + if (!TAILQ_EMPTY(&in_ifaddrhead)) { /* * If the destination address is INADDR_ANY, @@ -438,13 +516,15 @@ in_pcbladdr(inp, nam, plocal_sin) * and the primary interface supports broadcast, * choose the broadcast address for that interface. */ - if (sin->sin_addr.s_addr == INADDR_ANY) - sin->sin_addr = IA_SIN(TAILQ_FIRST(&in_ifaddrhead))->sin_addr; - else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && - (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST)) - sin->sin_addr = satosin(&TAILQ_FIRST(&in_ifaddrhead)->ia_broadaddr)->sin_addr; + if (faddr.s_addr == INADDR_ANY) + faddr = IA_SIN(TAILQ_FIRST(&in_ifaddrhead))->sin_addr; + else if (faddr.s_addr == (u_long)INADDR_BROADCAST && + (TAILQ_FIRST(&in_ifaddrhead)->ia_ifp->if_flags & + IFF_BROADCAST)) + faddr = satosin(&TAILQ_FIRST( + &in_ifaddrhead)->ia_broadaddr)->sin_addr; } - if (inp->inp_laddr.s_addr == INADDR_ANY) { + if (laddr.s_addr == INADDR_ANY) { register struct route *ro; ia = (struct in_ifaddr *)0; @@ -457,8 +537,7 @@ in_pcbladdr(inp, nam, plocal_sin) ro = &inp->inp_route; if (ro->ro_rt && (ro->ro_dst.sa_family != AF_INET || - satosin(&ro->ro_dst)->sin_addr.s_addr != - sin->sin_addr.s_addr || + satosin(&ro->ro_dst)->sin_addr.s_addr != faddr.s_addr || inp->inp_socket->so_options & SO_DONTROUTE)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; @@ -470,8 +549,7 @@ in_pcbladdr(inp, nam, plocal_sin) bzero(&ro->ro_dst, sizeof(struct sockaddr_in)); ro->ro_dst.sa_family = AF_INET; ro->ro_dst.sa_len = sizeof(struct sockaddr_in); - ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = - sin->sin_addr; + ((struct sockaddr_in *)&ro->ro_dst)->sin_addr = faddr; rtalloc(ro); } /* @@ -483,13 +561,14 @@ in_pcbladdr(inp, nam, plocal_sin) if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) ia = ifatoia(ro->ro_rt->rt_ifa); if (ia == 0) { - u_short fport = sin->sin_port; + bzero(&sa, sizeof(sa)); + sa.sin_addr = faddr; + sa.sin_len = sizeof(sa); + sa.sin_family = AF_INET; - sin->sin_port = 0; - ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); + ia = ifatoia(ifa_ifwithdstaddr(sintosa(&sa))); if (ia == 0) - ia = ifatoia(ifa_ifwithnet(sintosa(sin))); - sin->sin_port = fport; + ia = ifatoia(ifa_ifwithnet(sintosa(&sa))); if (ia == 0) ia = TAILQ_FIRST(&in_ifaddrhead); if (ia == 0) @@ -500,7 +579,7 @@ in_pcbladdr(inp, nam, plocal_sin) * interface has been set as a multicast option, use the * address of that interface as our source address. */ - if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && + if (IN_MULTICAST(ntohl(faddr.s_addr)) && inp->inp_moptions != NULL) { struct ip_moptions *imo; struct ifnet *ifp; @@ -515,67 +594,25 @@ in_pcbladdr(inp, nam, plocal_sin) return (EADDRNOTAVAIL); } } - /* - * Don't do pcblookup call here; return interface in plocal_sin - * and exit to caller, that will do the lookup. - */ - *plocal_sin = &ia->ia_addr; - - } - return(0); -} - -/* - * Outer subroutine: - * Connect from a socket to a specified address. - * Both address and port must be specified in argument sin. - * If don't have a local address for this socket yet, - * then pick one. - */ -int -in_pcbconnect(inp, nam, td) - register struct inpcb *inp; - struct sockaddr *nam; - struct thread *td; -{ - struct sockaddr_in *ifaddr; - struct sockaddr_in *sin = (struct sockaddr_in *)nam; - struct sockaddr_in sa; - struct ucred *cred; - int error; - - cred = inp->inp_socket->so_cred; - if (inp->inp_laddr.s_addr == INADDR_ANY && jailed(cred)) { - bzero(&sa, sizeof (sa)); - sa.sin_addr.s_addr = htonl(prison_getip(cred)); - sa.sin_len=sizeof (sa); - sa.sin_family = AF_INET; - error = in_pcbbind(inp, (struct sockaddr *)&sa, td); - if (error) - return (error); + laddr = ia->ia_addr.sin_addr; } - /* - * Call inner routine, to assign local interface address. - */ - if ((error = in_pcbladdr(inp, nam, &ifaddr)) != 0) - return(error); - if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, - inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, - inp->inp_lport, 0, NULL) != NULL) { + oinp = in_pcblookup_hash(inp->inp_pcbinfo, faddr, fport, laddr, lport, + 0, NULL); + if (oinp != NULL) { + if (oinpp != NULL) + *oinpp = oinp; return (EADDRINUSE); } - if (inp->inp_laddr.s_addr == INADDR_ANY) { - if (inp->inp_lport == 0) { - error = in_pcbbind(inp, (struct sockaddr *)0, td); - if (error) - return (error); - } - inp->inp_laddr = ifaddr->sin_addr; + if (lport == 0) { + error = in_pcbbind_setup(inp, NULL, &laddr.s_addr, &lport, td); + if (error) + return (error); } - inp->inp_faddr = sin->sin_addr; - inp->inp_fport = sin->sin_port; - in_pcbrehash(inp); + *laddrp = laddr.s_addr; + *lportp = lport; + *faddrp = faddr.s_addr; + *fportp = fport; return (0); } diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 7f2611426c19..7cbcd3d7a402 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -331,11 +331,12 @@ int in_pcbbind(struct inpcb *, struct sockaddr *, struct thread *); int in_pcbbind_setup(struct inpcb *, struct sockaddr *, in_addr_t *, u_short *, struct thread *); int in_pcbconnect(struct inpcb *, struct sockaddr *, struct thread *); +int in_pcbconnect_setup(struct inpcb *, struct sockaddr *, in_addr_t *, + u_short *, in_addr_t *, u_short *, struct inpcb **, + struct thread *); void in_pcbdetach(struct inpcb *); void in_pcbdisconnect(struct inpcb *); int in_pcbinshash(struct inpcb *); -int in_pcbladdr(struct inpcb *, struct sockaddr *, - struct sockaddr_in **); struct inpcb * in_pcblookup_local(struct inpcbinfo *, struct in_addr, u_int, int); diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 491529b1bfad..143a20c55ecd 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -832,10 +832,10 @@ struct pr_usrreqs tcp6_usrreqs = { /* * 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 - * port number if needed. Call in_pcbladdr to do the routing and to choose - * a local host address (interface). If there is an existing incarnation - * of the same connection in TIME-WAIT state and if the remote host was - * sending CC options and if the connection duration was < MSL, then + * port number if needed. Call in_pcbconnect_setup to do the routing and + * to choose a local host address (interface). If there is an existing + * incarnation of the same connection in TIME-WAIT state and if the remote + * host was sending CC options and if the connection duration was < MSL, then * truncate the previous TIME-WAIT state and proceed. * Initialize connection parameters and enter SYN-SENT state. */ @@ -849,9 +849,10 @@ tcp_connect(tp, nam, td) struct socket *so = inp->inp_socket; struct tcpcb *otp; struct sockaddr_in *sin = (struct sockaddr_in *)nam; - struct sockaddr_in *ifaddr; struct rmxp_tao *taop; struct rmxp_tao tao_noncached; + struct in_addr laddr; + u_short lport; int error; if (inp->inp_lport == 0) { @@ -865,14 +866,12 @@ tcp_connect(tp, nam, td) * earlier incarnation of this same connection still in * TIME_WAIT state, creating an ADDRINUSE error. */ - error = in_pcbladdr(inp, nam, &ifaddr); - if (error) + laddr = inp->inp_laddr; + lport = inp->inp_lport; + error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport, + &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td); + if (error && oinp == NULL) return error; - oinp = in_pcblookup_hash(inp->inp_pcbinfo, - sin->sin_addr, sin->sin_port, - inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr - : ifaddr->sin_addr, - inp->inp_lport, 0, NULL); if (oinp) { if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && otp->t_state == TCPS_TIME_WAIT && @@ -882,8 +881,7 @@ tcp_connect(tp, nam, td) else return EADDRINUSE; } - if (inp->inp_laddr.s_addr == INADDR_ANY) - inp->inp_laddr = ifaddr->sin_addr; + inp->inp_laddr = laddr; inp->inp_faddr = sin->sin_addr; inp->inp_fport = sin->sin_port; in_pcbrehash(inp); |
