Skip to content

Commit

Permalink
Fix a bug where existing CARP alias addresses cannot be changed.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
loos-br committed Dec 20, 2016
1 parent 7a35aeb commit 77805aa
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 14 deletions.
2 changes: 1 addition & 1 deletion sys/net/if.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 **);
Expand Down
13 changes: 7 additions & 6 deletions sys/netinet/in.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/udp_var.h>

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 *);
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -526,7 +526,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)

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);
Expand All @@ -543,7 +543,7 @@ in_aifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td)
}

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 *)
Expand Down Expand Up @@ -616,7 +616,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
Expand Down
7 changes: 4 additions & 3 deletions sys/netinet/ip_carp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions sys/netinet/ip_carp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 *);
Expand Down
4 changes: 2 additions & 2 deletions sys/netinet6/in6.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,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;
}
}
Expand Down Expand Up @@ -1238,7 +1238,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.
Expand Down

0 comments on commit 77805aa

Please sign in to comment.