kern/38554: changing interface ipaddress doesn't seem to work
Bruce M Simpson
bms at spc.org
Sat Jul 3 01:09:05 PDT 2004
Please review the attached patch (which is a reworking of Archie's
patch for -CURRENT). When the underlying IP address is changed,
wildcard-bound UDP sockets which are temporarily bound locally for
a sendto() (by userland apps such as ntp, syslogd etc) will begin
using the new IP address, whilst apps using TCP (ssh, Quagga bgpd) will
error out with EADDRINUSE.
I would appreciate any feedback on our adopting this behaviour (which
strikes me as similar to that of Solaris and a few other OSes).
BMS
-------------- next part --------------
Index: in.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in.c,v
retrieving revision 1.75
diff -u -p -r1.75 in.c
--- in.c 7 Apr 2004 20:46:13 -0000 1.75
+++ in.c 3 Jul 2004 07:40:19 -0000
@@ -420,6 +420,11 @@ in_control(so, cmd, data, ifp, td)
*/
in_ifadown(&ia->ia_ifa, 1);
/*
+ * Mark the interface address as no longer valid.
+ * Sockets that are bound to it should notice.
+ */
+ ia->ia_ifa.ifa_flags |= RTF_REJECT;
+ /*
* XXX horrible hack to detect that we are being called
* from if_detach()
*/
Index: in_pcb.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.c,v
retrieving revision 1.150
diff -u -p -r1.150 in_pcb.c
--- in_pcb.c 16 Jun 2004 10:02:36 -0000 1.150
+++ in_pcb.c 3 Jul 2004 07:47:54 -0000
@@ -53,6 +53,7 @@
#include <net/if.h>
#include <net/if_types.h>
#include <net/route.h>
+#include <net/net_osdep.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
@@ -228,14 +229,17 @@ in_pcbbind(inp, nam, cred)
anonport = inp->inp_lport == 0 && (nam == NULL ||
((struct sockaddr_in *)nam)->sin_port == 0);
error = in_pcbbind_setup(inp, nam, &inp->inp_laddr.s_addr,
- &inp->inp_lport, cred);
+ &inp->inp_lport, &inp->inp_locia, cred);
if (error)
return (error);
if (in_pcbinshash(inp) != 0) {
inp->inp_laddr.s_addr = INADDR_ANY;
inp->inp_lport = 0;
+ inp->inp_locia = NULL;
return (EAGAIN);
}
+ if (inp->inp_locia != NULL)
+ IFAREF(&inp->inp_locia->ia_ifa);
if (anonport)
inp->inp_flags |= INP_ANONPORT;
return (0);
@@ -251,17 +255,19 @@ in_pcbbind(inp, nam, cred)
* On error, the values of *laddrp and *lportp are not changed.
*/
int
-in_pcbbind_setup(inp, nam, laddrp, lportp, cred)
+in_pcbbind_setup(inp, nam, laddrp, lportp, iap, cred)
struct inpcb *inp;
struct sockaddr *nam;
in_addr_t *laddrp;
u_short *lportp;
+ struct in_ifaddr **iap;
struct ucred *cred;
{
struct socket *so = inp->inp_socket;
unsigned short *lastport;
struct sockaddr_in *sin;
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
+ struct in_ifaddr *ia = NULL;
struct in_addr laddr;
u_short lport = 0;
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
@@ -312,7 +318,8 @@ in_pcbbind_setup(inp, nam, laddrp, lport
} else if (sin->sin_addr.s_addr != INADDR_ANY) {
sin->sin_port = 0; /* yech... */
bzero(&sin->sin_zero, sizeof(sin->sin_zero));
- if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
+ if ((ia = (struct in_ifaddr *)ifa_ifwithaddr(
+ (struct sockaddr *)sin)) == 0)
return (EADDRNOTAVAIL);
}
laddr = sin->sin_addr;
@@ -451,6 +458,8 @@ in_pcbbind_setup(inp, nam, laddrp, lport
return (EINVAL);
*laddrp = laddr.s_addr;
*lportp = lport;
+ if (iap != NULL)
+ *iap = ia;
return (0);
}
@@ -468,13 +477,14 @@ in_pcbconnect(inp, nam, cred)
{
u_short lport, fport;
in_addr_t laddr, faddr;
+ struct in_ifaddr *locia;
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, cred);
+ NULL, &locia, cred);
if (error)
return (error);
@@ -492,6 +502,9 @@ in_pcbconnect(inp, nam, cred)
/* Commit the remaining changes. */
inp->inp_lport = lport;
inp->inp_laddr.s_addr = laddr;
+ inp->inp_locia = locia;
+ if (inp->inp_locia != NULL)
+ IFAREF(&inp->inp_locia->ia_ifa);
inp->inp_faddr.s_addr = faddr;
inp->inp_fport = fport;
in_pcbrehash(inp);
@@ -509,7 +522,9 @@ in_pcbconnect(inp, nam, cred)
* 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.
+ * the connect. If iap is not NULL, *iap is set to the interface
+ * address corresponding to *laddrp, if any, but no new reference
+ * to it has been added.
*
* On success, *faddrp and *fportp will be set to the remote address
* and port. These are not updated in the error case.
@@ -520,7 +535,7 @@ in_pcbconnect(inp, nam, cred)
* is set to NULL.
*/
int
-in_pcbconnect_setup(inp, nam, laddrp, lportp, faddrp, fportp, oinpp, cred)
+in_pcbconnect_setup(inp, nam, laddrp, lportp, faddrp, fportp, oinpp, iap, cred)
register struct inpcb *inp;
struct sockaddr *nam;
in_addr_t *laddrp;
@@ -528,10 +543,11 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
in_addr_t *faddrp;
u_short *fportp;
struct inpcb **oinpp;
+ struct in_ifaddr **iap;
struct ucred *cred;
{
struct sockaddr_in *sin = (struct sockaddr_in *)nam;
- struct in_ifaddr *ia;
+ struct in_ifaddr *ia = NULL;
struct sockaddr_in sa;
struct ucred *socred;
struct inpcb *oinp;
@@ -558,7 +574,7 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
sa.sin_len = sizeof(sa);
sa.sin_family = AF_INET;
error = in_pcbbind_setup(inp, (struct sockaddr *)&sa,
- &laddr.s_addr, &lport, cred);
+ &laddr.s_addr, &lport, &ia, cred);
if (error)
return (error);
}
@@ -648,7 +664,7 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
}
if (lport == 0) {
error = in_pcbbind_setup(inp, NULL, &laddr.s_addr, &lport,
- cred);
+ &ia, cred);
if (error)
return (error);
}
@@ -656,6 +672,8 @@ in_pcbconnect_setup(inp, nam, laddrp, lp
*lportp = lport;
*faddrp = faddr.s_addr;
*fportp = fport;
+ if (iap != NULL)
+ *iap = ia;
return (0);
}
@@ -694,6 +712,8 @@ in_pcbdetach(inp)
so->so_pcb = 0;
sotryfree(so);
}
+ if (inp->inp_locia != NULL)
+ IFAFREE(&inp->inp_locia->ia_ifa);
if (inp->inp_options)
(void)m_free(inp->inp_options);
ip_freemoptions(inp->inp_moptions);
Index: in_pcb.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.73
diff -u -p -r1.73 in_pcb.h
--- in_pcb.h 24 Jun 2004 02:01:48 -0000 1.73
+++ in_pcb.h 3 Jul 2004 07:49:32 -0000
@@ -71,6 +71,7 @@ struct in_addr_4in6 {
struct in_endpoints {
u_int16_t ie_fport; /* foreign port */
u_int16_t ie_lport; /* local port */
+ struct in_ifaddr *ie_locia; /* locally bound address */
/* protocol dependent part, local and foreign addr */
union {
/* foreign host table entry */
@@ -102,6 +103,7 @@ struct in_conninfo {
#define inc_isipv6 inc_flags /* temp compatability */
#define inc_fport inc_ie.ie_fport
#define inc_lport inc_ie.ie_lport
+#define inc_locia inc_ie.ie_locia
#define inc_faddr inc_ie.ie_faddr
#define inc_laddr inc_ie.ie_laddr
#define inc6_faddr inc_ie.ie6_faddr
@@ -142,6 +144,7 @@ struct inpcb {
} inp_depend4;
#define inp_fport inp_inc.inc_fport
#define inp_lport inp_inc.inc_lport
+#define inp_locia inp_inc.inc_locia
#define inp_faddr inp_inc.inc_faddr
#define inp_laddr inp_inc.inc_laddr
#define inp_ip_tos inp_depend4.inp4_ip_tos
@@ -340,6 +343,8 @@ struct inpcbinfo { /* XXX documentation
#define INP_CHECK_SOCKAF(so, af) (INP_SOCKAF(so) == af)
#ifdef _KERNEL
+struct in_ifaddr;
+
extern int ipport_lowfirstauto;
extern int ipport_lowlastauto;
extern int ipport_firstauto;
@@ -351,11 +356,11 @@ void in_pcbpurgeif0(struct inpcbinfo *,
int in_pcballoc(struct socket *, struct inpcbinfo *, const char *);
int in_pcbbind(struct inpcb *, struct sockaddr *, struct ucred *);
int in_pcbbind_setup(struct inpcb *, struct sockaddr *, in_addr_t *,
- u_short *, struct ucred *);
+ u_short *, struct in_ifaddr **, struct ucred *);
int in_pcbconnect(struct inpcb *, struct sockaddr *, struct ucred *);
int in_pcbconnect_setup(struct inpcb *, struct sockaddr *, in_addr_t *,
u_short *, in_addr_t *, u_short *, struct inpcb **,
- struct ucred *);
+ struct in_ifaddr **, struct ucred *);
void in_pcbdetach(struct inpcb *);
void in_pcbdisconnect(struct inpcb *);
int in_pcbinshash(struct inpcb *);
Index: tcp_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_output.c,v
retrieving revision 1.95
diff -u -p -r1.95 tcp_output.c
--- tcp_output.c 23 Jun 2004 21:04:37 -0000 1.95
+++ tcp_output.c 3 Jul 2004 07:40:19 -0000
@@ -51,12 +51,15 @@
#include <sys/sysctl.h>
#include <net/route.h>
+#include <net/if.h>
+#include <net/if_var.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
#ifdef INET6
#include <netinet6/in6_pcb.h>
#include <netinet/ip6.h>
@@ -804,6 +807,16 @@ send:
}
/*
+ * Check that our local (source) IP address is still valid.
+ */
+ if (tp->t_inpcb->inp_locia != NULL
+ && (tp->t_inpcb->inp_locia->ia_ifa.ifa_flags & RTF_REJECT) != 0) {
+ error = EADDRNOTAVAIL;
+ m_freem(m);
+ goto out;
+ }
+
+ /*
* Fill in fields, remembering maximum advertised
* window for use in delaying messages about window sizes.
* If resending a FIN, be sure not to use a new sequence number.
Index: tcp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.103
diff -u -p -r1.103 tcp_usrreq.c
--- tcp_usrreq.c 26 Jun 2004 17:50:50 -0000 1.103
+++ tcp_usrreq.c 3 Jul 2004 07:51:41 -0000
@@ -51,6 +51,7 @@
#include <sys/jail.h>
#include <net/if.h>
+#include <net/net_osdep.h>
#include <net/route.h>
#include <netinet/in.h>
@@ -817,6 +818,7 @@ tcp_connect(tp, nam, td)
struct socket *so = inp->inp_socket;
struct tcptw *otw;
struct rmxp_tao tao;
+ struct in_ifaddr *locia;
struct in_addr laddr;
u_short lport;
int error;
@@ -836,8 +838,10 @@ tcp_connect(tp, nam, td)
*/
laddr = inp->inp_laddr;
lport = inp->inp_lport;
+ locia = inp->inp_locia;
error = in_pcbconnect_setup(inp, nam, &laddr.s_addr, &lport,
- &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, td->td_ucred);
+ &inp->inp_faddr.s_addr, &inp->inp_fport, &oinp, &locia,
+ td->td_ucred);
if (error && oinp == NULL)
return error;
if (oinp) {
@@ -852,6 +856,11 @@ tcp_connect(tp, nam, td)
return EADDRINUSE;
}
inp->inp_laddr = laddr;
+ if (inp->inp_locia != NULL)
+ IFAFREE(&inp->inp_locia->ia_ifa);
+ inp->inp_locia = locia;
+ if (inp->inp_locia != NULL)
+ IFAREF(&inp->inp_locia->ia_ifa);
in_pcbrehash(inp);
/* Compute window scaling to request. */
Index: udp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.156
diff -u -p -r1.156 udp_usrreq.c
--- udp_usrreq.c 26 Jun 2004 19:10:39 -0000 1.156
+++ udp_usrreq.c 3 Jul 2004 07:52:53 -0000
@@ -724,6 +724,7 @@ udp_output(inp, m, addr, control, td)
{
register struct udpiphdr *ui;
register int len = m->m_pkthdr.len;
+ struct in_ifaddr *locia;
struct in_addr faddr, laddr;
struct cmsghdr *cm;
struct sockaddr_in *sin, src;
@@ -792,13 +793,14 @@ udp_output(inp, m, addr, control, td)
goto release;
laddr = inp->inp_laddr;
lport = inp->inp_lport;
+ locia = inp->inp_locia;
if (src.sin_addr.s_addr != INADDR_ANY) {
if (lport == 0) {
error = EINVAL;
goto release;
}
error = in_pcbbind_setup(inp, (struct sockaddr *)&src,
- &laddr.s_addr, &lport, td->td_ucred);
+ &laddr.s_addr, &lport, &locia, td->td_ucred);
if (error)
goto release;
}
@@ -812,7 +814,7 @@ udp_output(inp, m, addr, control, td)
goto release;
}
error = in_pcbconnect_setup(inp, addr, &laddr.s_addr, &lport,
- &faddr.s_addr, &fport, NULL, td->td_ucred);
+ &faddr.s_addr, &fport, NULL, &locia, td->td_ucred);
if (error)
goto release;
@@ -835,6 +837,15 @@ udp_output(inp, m, addr, control, td)
goto release;
}
}
+
+ /*
+ * Check that the local (source) IP address is valid.
+ */
+ if (locia != NULL && (locia->ia_ifa.ifa_flags & RTF_REJECT) != 0) {
+ error = EADDRNOTAVAIL;
+ goto release;
+ }
+
/*
* Calculate data length and get a mbuf
* for UDP and IP headers.
@@ -1054,6 +1065,10 @@ udp_disconnect(struct socket *so)
s = splnet();
in_pcbdisconnect(inp);
inp->inp_laddr.s_addr = INADDR_ANY;
+ if (inp->inp_locia != NULL) {
+ IFAFREE(&inp->inp_locia->ia_ifa);
+ inp->inp_locia = NULL;
+ }
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&udbinfo);
splx(s);
More information about the freebsd-net
mailing list