summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoriedowse <iedowse@FreeBSD.org>2002-10-21 13:55:50 +0000
committeriedowse <iedowse@FreeBSD.org>2002-10-21 13:55:50 +0000
commita5bc5c7b7ede3570b7cb9530c8d8f2847945f0fa (patch)
treeafd766ec15eff4244ba3cea699f2b6825cd18fbc
parentda47f8f7ff907d670fb37bf616bfe3505a92288d (diff)
downloadFreeBSD-src-a5bc5c7b7ede3570b7cb9530c8d8f2847945f0fa.zip
FreeBSD-src-a5bc5c7b7ede3570b7cb9530c8d8f2847945f0fa.tar.gz
Replace in_pcbladdr() with a more generic inner subroutine for
in_pcbconnect() called in_pcbconnect_setup(). This version performs all of the functions of in_pcbconnect() except for the final committing of changes to the PCB. In the case of an EADDRINUSE error it can also provide to the caller the PCB of the duplicate connection, avoiding an extra in_pcblookup_hash() lookup in tcp_connect(). This change will allow the "temporary connect" hack in udp_output() to be removed and is part of the preparation for adding the IP_SENDSRCADDR control message. Discussed on: -net Approved by: re
-rw-r--r--sys/netinet/in_pcb.c205
-rw-r--r--sys/netinet/in_pcb.h5
-rw-r--r--sys/netinet/tcp_usrreq.c26
3 files changed, 136 insertions, 100 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 5ea538a..e7992d5 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 7f26114..7cbcd3d 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 491529b..143a20c 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);
OpenPOWER on IntegriCloud