Skip to content

Commit

Permalink
After the in_control() changes in r257692, an existing address is
Browse files Browse the repository at this point in the history
(intentionally) deleted first and then completely added again (so all the
events, announces and hooks are given a chance to run).

This cause an issue with CARP where the existing CARP data structure is
removed together with the last address for a given VHID, which will cause
a subsequent fail when the address is later re-added.

This change fixes this issue by adding a new flag to keep the CARP data
structure when an address is not being removed.

There was an additional issue with IPv6 CARP addresses, where the CARP data
structure would never be removed after a change and lead to VHIDs which
cannot be destroyed.

Reviewed by:	glebius
Obtained from:	pfSense
MFC after:	2 weeks
Sponsored by:	Rubicon Communications, LLC (Netgate)
  • Loading branch information
loos-br committed Jan 25, 2017
1 parent b4ef14b commit 5bd3158
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 15 deletions.
2 changes: 1 addition & 1 deletion sys/net/if.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,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 @@ -528,7 +528,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, false);

IF_ADDR_WLOCK(ifp);
TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link);
Expand All @@ -545,7 +545,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 @@ -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) ? false : true);

/*
* 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 @@ -1969,7 +1969,7 @@ carp_attach(struct ifaddr *ifa, int vhid)
}

void
carp_detach(struct ifaddr *ifa)
carp_detach(struct ifaddr *ifa, bool keep_cif)
{
struct ifnet *ifp = ifa->ifa_ifp;
struct carp_if *cif = ifp->if_carp;
Expand Down Expand Up @@ -2015,12 +2015,13 @@ carp_detach(struct ifaddr *ifa)
carp_hmac_prepare(sc);
carp_sc_state(sc);

if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0)
if (!keep_cif && sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0)
carp_destroy(sc);
else
CARP_UNLOCK(sc);

CIF_FREE(cif);
if (!keep_cif)
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
9 changes: 6 additions & 3 deletions sys/netinet6/in6.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
*/
if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
goto out;
if (ia != NULL)
if (ia != NULL) {
if (ia->ia_ifa.ifa_carp)
(*carp_detach_p)(&ia->ia_ifa, true);
ifa_free(&ia->ia_ifa);
}
if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr))
== NULL) {
/*
Expand Down Expand Up @@ -622,7 +625,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 @@ -1243,7 +1246,7 @@ in6_purgeaddr(struct ifaddr *ifa)
int plen, error;

if (ifa->ifa_carp)
(*carp_detach_p)(ifa);
(*carp_detach_p)(ifa, false);

/*
* Remove the loopback route to the interface address.
Expand Down

0 comments on commit 5bd3158

Please sign in to comment.