Date: Sat, 3 Jul 2004 09:09:00 +0100 From: Bruce M Simpson <bms@spc.org> To: freebsd-net@freebsd.org Cc: freebsd-gnats-submit@freebsd.org Subject: Re: kern/38554: changing interface ipaddress doesn't seem to work Message-ID: <20040703080900.GL97102@empiric.dek.spc.org>
next in thread | raw e-mail | index | archive | help
--jQIvE3yXcK9X9HBh Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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 --jQIvE3yXcK9X9HBh Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="archie-current.diff" 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); --jQIvE3yXcK9X9HBh--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040703080900.GL97102>