From ded5357984b1ce3d00b141005e695e1113a1169b Mon Sep 17 00:00:00 2001 From: Luiz Otavio O Souza Date: Tue, 20 Dec 2016 09:18:17 -0600 Subject: Fix a bug where existing CARP alias addresses cannot be changed. When a existent address is delete with carp_detach() if it is the last address for that CARP vhid, the CARP vhid will be destroyed and the subsequent carp_attach() to add the new IP will fail. Ticket #6892 (cherry picked from commit 77805aa5fa51dbd2ed0b6c363c6235c892caee76) --- sys/net/if.c | 2 +- sys/netinet/in.c | 13 +++++++------ sys/netinet/ip_carp.c | 7 ++++--- sys/netinet/ip_carp.h | 4 ++-- sys/netinet6/in6.c | 4 ++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/sys/net/if.c b/sys/net/if.c index 85fe24e..6e897d0 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -144,7 +144,7 @@ int (*carp_output_p)(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa); int (*carp_ioctl_p)(struct ifreq *, u_long, struct thread *); int (*carp_attach_p)(struct ifaddr *, int); -void (*carp_detach_p)(struct ifaddr *); +void (*carp_detach_p)(struct ifaddr *, bool); #endif #ifdef INET int (*carp_iamatch_p)(struct ifaddr *, uint8_t **); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index f42375b..4e4108f 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -71,7 +71,7 @@ __FBSDID("$FreeBSD$"); #include static int in_aifaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); -static int in_difaddr_ioctl(caddr_t, struct ifnet *, struct thread *); +static int in_difaddr_ioctl(u_long, caddr_t, struct ifnet *, struct thread *); static void in_socktrim(struct sockaddr_in *); static void in_purgemaddrs(struct ifnet *); @@ -245,7 +245,7 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, break; case SIOCDIFADDR: sx_xlock(&in_control_sx); - error = in_difaddr_ioctl(data, ifp, td); + error = in_difaddr_ioctl(cmd, data, ifp, td); sx_xunlock(&in_control_sx); return (error); case OSIOCAIFADDR: /* 9.x compat */ @@ -390,7 +390,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) IF_ADDR_RUNLOCK(ifp); if (ia != NULL) - (void )in_difaddr_ioctl(data, ifp, td); + (void )in_difaddr_ioctl(cmd, data, ifp, td); ifa = ifa_alloc(sizeof(struct in_ifaddr), M_WAITOK); ia = (struct in_ifaddr *)ifa; @@ -528,7 +528,7 @@ fail2: fail1: if (ia->ia_ifa.ifa_carp) - (*carp_detach_p)(&ia->ia_ifa); + (*carp_detach_p)(&ia->ia_ifa, true); IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); @@ -545,7 +545,7 @@ fail1: } static int -in_difaddr_ioctl(caddr_t data, struct ifnet *ifp, struct thread *td) +in_difaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { const struct ifreq *ifr = (struct ifreq *)data; const struct sockaddr_in *addr = (const struct sockaddr_in *) @@ -618,7 +618,8 @@ in_difaddr_ioctl(caddr_t data, struct ifnet *ifp, struct thread *td) in_ifadown(&ia->ia_ifa, 1); if (ia->ia_ifa.ifa_carp) - (*carp_detach_p)(&ia->ia_ifa); + (*carp_detach_p)(&ia->ia_ifa, + (cmd == SIOCDIFADDR) ? true : false); /* * If this is the last IPv4 address configured on this diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index b6af698..86e6a10 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1880,7 +1880,7 @@ carp_attach(struct ifaddr *ifa, int vhid) } void -carp_detach(struct ifaddr *ifa) +carp_detach(struct ifaddr *ifa, bool destroy) { struct ifnet *ifp = ifa->ifa_ifp; struct carp_if *cif = ifp->if_carp; @@ -1926,12 +1926,13 @@ carp_detach(struct ifaddr *ifa) carp_hmac_prepare(sc); carp_sc_state(sc); - if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) + if (destroy && sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) carp_destroy(sc); else CARP_UNLOCK(sc); - CIF_FREE(cif); + if (destroy) + CIF_FREE(cif); sx_xunlock(&carp_sx); } diff --git a/sys/netinet/ip_carp.h b/sys/netinet/ip_carp.h index 5b7e506..9c6edf6 100644 --- a/sys/netinet/ip_carp.h +++ b/sys/netinet/ip_carp.h @@ -138,7 +138,7 @@ struct carpreq { #ifdef _KERNEL int carp_ioctl(struct ifreq *, u_long, struct thread *); int carp_attach(struct ifaddr *, int); -void carp_detach(struct ifaddr *); +void carp_detach(struct ifaddr *, bool); void carp_carpdev_state(struct ifnet *); int carp_input(struct mbuf **, int *, int); int carp6_input (struct mbuf **, int *, int); @@ -154,7 +154,7 @@ int carp_forus(struct ifnet *, u_char *); /* net/if.c */ extern int (*carp_ioctl_p)(struct ifreq *, u_long, struct thread *); extern int (*carp_attach_p)(struct ifaddr *, int); -extern void (*carp_detach_p)(struct ifaddr *); +extern void (*carp_detach_p)(struct ifaddr *, bool); extern void (*carp_linkstate_p)(struct ifnet *); extern void (*carp_demote_adj_p)(int, char *); extern int (*carp_master_p)(struct ifaddr *); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 4df3564..d473995 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -624,7 +624,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, */ if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) { if (carp_attached) - (*carp_detach_p)(&ia->ia_ifa); + (*carp_detach_p)(&ia->ia_ifa, false); goto out; } } @@ -1245,7 +1245,7 @@ in6_purgeaddr(struct ifaddr *ifa) int plen, error; if (ifa->ifa_carp) - (*carp_detach_p)(ifa); + (*carp_detach_p)(ifa, true); /* * Remove the loopback route to the interface address. -- cgit v1.1