2005-02-22 13:04:05 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
|
|
|
|
* Copyright (c) 2003 Ryan McBride. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
|
|
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2007-10-07 20:44:24 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
#include "opt_bpf.h"
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_inet6.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/limits.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/time.h>
|
2006-11-06 13:42:10 +00:00
|
|
|
#include <sys/priv.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
#include <sys/proc.h>
|
2010-08-11 00:51:50 +00:00
|
|
|
#include <sys/protosw.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/signalvar.h>
|
|
|
|
#include <sys/filio.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
|
|
|
|
#include <machine/stdarg.h>
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/fddi.h>
|
|
|
|
#include <net/iso88025.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_clone.h>
|
2005-11-14 12:50:23 +00:00
|
|
|
#include <net/if_dl.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
#include <net/if_types.h>
|
|
|
|
#include <net/route.h>
|
2009-08-01 19:26:27 +00:00
|
|
|
#include <net/vnet.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/ip_var.h>
|
|
|
|
#include <netinet/if_ether.h>
|
|
|
|
#include <machine/in_cksum.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
#include <netinet/ip6.h>
|
2010-08-11 00:51:50 +00:00
|
|
|
#include <netinet6/ip6protosw.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
#include <netinet6/ip6_var.h>
|
2005-07-25 12:36:43 +00:00
|
|
|
#include <netinet6/scope6_var.h>
|
2005-02-22 13:04:05 +00:00
|
|
|
#include <netinet6/nd6.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <crypto/sha1.h>
|
|
|
|
#include <netinet/ip_carp.h>
|
|
|
|
|
|
|
|
#define CARP_IFNAME "carp"
|
|
|
|
static MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces");
|
|
|
|
SYSCTL_DECL(_net_inet_carp);
|
|
|
|
|
|
|
|
struct carp_softc {
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *sc_ifp; /* Interface clue */
|
2005-03-01 10:59:14 +00:00
|
|
|
struct ifnet *sc_carpdev; /* Pointer to parent interface */
|
2005-02-22 13:04:05 +00:00
|
|
|
struct in_ifaddr *sc_ia; /* primary iface address */
|
|
|
|
struct ip_moptions sc_imo;
|
|
|
|
#ifdef INET6
|
|
|
|
struct in6_ifaddr *sc_ia6; /* primary iface address v6 */
|
|
|
|
struct ip6_moptions sc_im6o;
|
|
|
|
#endif /* INET6 */
|
|
|
|
TAILQ_ENTRY(carp_softc) sc_list;
|
|
|
|
|
|
|
|
enum { INIT = 0, BACKUP, MASTER } sc_state;
|
|
|
|
|
|
|
|
int sc_flags_backup;
|
|
|
|
int sc_suppress;
|
|
|
|
|
|
|
|
int sc_sendad_errors;
|
|
|
|
#define CARP_SENDAD_MAX_ERRORS 3
|
|
|
|
int sc_sendad_success;
|
|
|
|
#define CARP_SENDAD_MIN_SUCCESS 3
|
|
|
|
|
|
|
|
int sc_vhid;
|
|
|
|
int sc_advskew;
|
|
|
|
int sc_naddrs;
|
|
|
|
int sc_naddrs6;
|
|
|
|
int sc_advbase; /* seconds */
|
|
|
|
int sc_init_counter;
|
|
|
|
u_int64_t sc_counter;
|
|
|
|
|
|
|
|
/* authentication */
|
|
|
|
#define CARP_HMAC_PAD 64
|
|
|
|
unsigned char sc_key[CARP_KEY_LEN];
|
|
|
|
unsigned char sc_pad[CARP_HMAC_PAD];
|
|
|
|
SHA1_CTX sc_sha1;
|
|
|
|
|
|
|
|
struct callout sc_ad_tmo; /* advertisement timeout */
|
|
|
|
struct callout sc_md_tmo; /* master down timeout */
|
|
|
|
struct callout sc_md6_tmo; /* master down timeout */
|
|
|
|
|
|
|
|
LIST_ENTRY(carp_softc) sc_next; /* Interface clue */
|
|
|
|
};
|
2005-06-10 16:49:24 +00:00
|
|
|
#define SC2IFP(sc) ((sc)->sc_ifp)
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
int carp_suppress_preempt = 0;
|
|
|
|
int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */
|
2010-08-11 00:51:50 +00:00
|
|
|
SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP");
|
2005-02-22 13:04:05 +00:00
|
|
|
SYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW,
|
|
|
|
&carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets");
|
|
|
|
SYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW,
|
|
|
|
&carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode");
|
|
|
|
SYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW,
|
|
|
|
&carp_opts[CARPCTL_LOG], 0, "log bad carp packets");
|
|
|
|
SYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW,
|
|
|
|
&carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses");
|
2005-05-15 01:44:26 +00:00
|
|
|
SYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD,
|
|
|
|
&carp_suppress_preempt, 0, "Preemption is suppressed");
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
struct carpstats carpstats;
|
|
|
|
SYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW,
|
|
|
|
&carpstats, carpstats,
|
|
|
|
"CARP statistics (struct carpstats, netinet/ip_carp.h)");
|
|
|
|
|
|
|
|
struct carp_if {
|
|
|
|
TAILQ_HEAD(, carp_softc) vhif_vrs;
|
|
|
|
int vhif_nvrs;
|
|
|
|
|
|
|
|
struct ifnet *vhif_ifp;
|
|
|
|
struct mtx vhif_mtx;
|
|
|
|
};
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
#define CARP_INET 0
|
|
|
|
#define CARP_INET6 1
|
|
|
|
static int proto_reg[] = {-1, -1};
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
/* Get carp_if from softc. Valid after carp_set_addr{,6}. */
|
|
|
|
#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp)
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
/* lock per carp_if queue */
|
2005-03-01 13:14:33 +00:00
|
|
|
#define CARP_LOCK_INIT(cif) mtx_init(&(cif)->vhif_mtx, "carp_if", \
|
2005-02-22 13:04:05 +00:00
|
|
|
NULL, MTX_DEF)
|
2005-03-01 13:14:33 +00:00
|
|
|
#define CARP_LOCK_DESTROY(cif) mtx_destroy(&(cif)->vhif_mtx)
|
2005-02-22 13:04:05 +00:00
|
|
|
#define CARP_LOCK_ASSERT(cif) mtx_assert(&(cif)->vhif_mtx, MA_OWNED)
|
|
|
|
#define CARP_LOCK(cif) mtx_lock(&(cif)->vhif_mtx)
|
|
|
|
#define CARP_UNLOCK(cif) mtx_unlock(&(cif)->vhif_mtx)
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
#define CARP_SCLOCK(sc) mtx_lock(&SC2CIF(sc)->vhif_mtx)
|
|
|
|
#define CARP_SCUNLOCK(sc) mtx_unlock(&SC2CIF(sc)->vhif_mtx)
|
|
|
|
#define CARP_SCLOCK_ASSERT(sc) mtx_assert(&SC2CIF(sc)->vhif_mtx, MA_OWNED)
|
|
|
|
|
2005-02-25 10:49:47 +00:00
|
|
|
#define CARP_LOG(...) do { \
|
2005-02-25 10:09:44 +00:00
|
|
|
if (carp_opts[CARPCTL_LOG] > 0) \
|
|
|
|
log(LOG_INFO, __VA_ARGS__); \
|
2005-02-25 10:49:47 +00:00
|
|
|
} while (0)
|
2005-02-25 10:09:44 +00:00
|
|
|
|
2005-02-25 10:49:47 +00:00
|
|
|
#define CARP_DEBUG(...) do { \
|
2005-02-25 10:09:44 +00:00
|
|
|
if (carp_opts[CARPCTL_LOG] > 1) \
|
|
|
|
log(LOG_DEBUG, __VA_ARGS__); \
|
2005-02-25 10:49:47 +00:00
|
|
|
} while (0)
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void carp_hmac_prepare(struct carp_softc *);
|
|
|
|
static void carp_hmac_generate(struct carp_softc *, u_int32_t *,
|
|
|
|
unsigned char *);
|
|
|
|
static int carp_hmac_verify(struct carp_softc *, u_int32_t *,
|
|
|
|
unsigned char *);
|
|
|
|
static void carp_setroute(struct carp_softc *, int);
|
|
|
|
static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t);
|
2006-07-09 06:04:01 +00:00
|
|
|
static int carp_clone_create(struct if_clone *, int, caddr_t);
|
2005-02-26 10:33:14 +00:00
|
|
|
static void carp_clone_destroy(struct ifnet *);
|
2007-01-25 18:03:40 +00:00
|
|
|
static void carpdetach(struct carp_softc *, int);
|
2005-02-26 10:33:14 +00:00
|
|
|
static int carp_prepare_ad(struct mbuf *, struct carp_softc *,
|
|
|
|
struct carp_header *);
|
|
|
|
static void carp_send_ad_all(void);
|
|
|
|
static void carp_send_ad(void *);
|
2005-03-01 13:14:33 +00:00
|
|
|
static void carp_send_ad_locked(struct carp_softc *);
|
2005-02-26 10:33:14 +00:00
|
|
|
static void carp_send_arp(struct carp_softc *);
|
|
|
|
static void carp_master_down(void *);
|
2005-03-01 13:14:33 +00:00
|
|
|
static void carp_master_down_locked(struct carp_softc *);
|
2005-02-26 10:33:14 +00:00
|
|
|
static int carp_ioctl(struct ifnet *, u_long, caddr_t);
|
|
|
|
static int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *,
|
2009-04-16 20:30:28 +00:00
|
|
|
struct route *);
|
2005-02-26 10:33:14 +00:00
|
|
|
static void carp_start(struct ifnet *);
|
|
|
|
static void carp_setrun(struct carp_softc *, sa_family_t);
|
|
|
|
static void carp_set_state(struct carp_softc *, int);
|
|
|
|
static int carp_addrcount(struct carp_if *, struct in_ifaddr *, int);
|
2005-02-22 13:04:05 +00:00
|
|
|
enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING };
|
|
|
|
|
2010-11-24 05:24:36 +00:00
|
|
|
static void carp_multicast_cleanup(struct carp_softc *, int dofree);
|
2005-02-26 10:33:14 +00:00
|
|
|
static int carp_set_addr(struct carp_softc *, struct sockaddr_in *);
|
|
|
|
static int carp_del_addr(struct carp_softc *, struct sockaddr_in *);
|
2005-03-01 13:14:33 +00:00
|
|
|
static void carp_carpdev_state_locked(struct carp_if *);
|
2005-03-30 11:44:43 +00:00
|
|
|
static void carp_sc_state_locked(struct carp_softc *);
|
2005-02-22 13:04:05 +00:00
|
|
|
#ifdef INET6
|
2005-02-26 10:33:14 +00:00
|
|
|
static void carp_send_na(struct carp_softc *);
|
|
|
|
static int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
|
|
|
|
static int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *);
|
2010-11-24 05:24:36 +00:00
|
|
|
static void carp_multicast6_cleanup(struct carp_softc *, int dofree);
|
2005-02-22 13:04:05 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static LIST_HEAD(, carp_softc) carpif_list;
|
2005-03-01 12:36:07 +00:00
|
|
|
static struct mtx carp_mtx;
|
2005-02-22 13:04:05 +00:00
|
|
|
IFC_SIMPLE_DECLARE(carp, 0);
|
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
static eventhandler_tag if_detach_event_tag;
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
static __inline u_int16_t
|
|
|
|
carp_cksum(struct mbuf *m, int len)
|
|
|
|
{
|
|
|
|
return (in_cksum(m, len));
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_hmac_prepare(struct carp_softc *sc)
|
|
|
|
{
|
|
|
|
u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT;
|
|
|
|
u_int8_t vhid = sc->sc_vhid & 0xff;
|
|
|
|
struct ifaddr *ifa;
|
2008-06-02 18:58:07 +00:00
|
|
|
int i, found;
|
|
|
|
#ifdef INET
|
|
|
|
struct in_addr last, cur, in;
|
|
|
|
#endif
|
2005-02-22 13:04:05 +00:00
|
|
|
#ifdef INET6
|
2008-06-02 18:58:07 +00:00
|
|
|
struct in6_addr last6, cur6, in6;
|
2005-02-22 13:04:05 +00:00
|
|
|
#endif
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
|
|
|
|
/* XXX: possible race here */
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
/* compute ipad from key */
|
|
|
|
bzero(sc->sc_pad, sizeof(sc->sc_pad));
|
|
|
|
bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key));
|
|
|
|
for (i = 0; i < sizeof(sc->sc_pad); i++)
|
|
|
|
sc->sc_pad[i] ^= 0x36;
|
|
|
|
|
|
|
|
/* precompute first part of inner hash */
|
|
|
|
SHA1Init(&sc->sc_sha1);
|
|
|
|
SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad));
|
|
|
|
SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version));
|
|
|
|
SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type));
|
|
|
|
SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid));
|
|
|
|
#ifdef INET
|
2008-06-02 18:58:07 +00:00
|
|
|
cur.s_addr = 0;
|
|
|
|
do {
|
|
|
|
found = 0;
|
|
|
|
last = cur;
|
|
|
|
cur.s_addr = 0xffffffff;
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(sc));
|
2008-06-02 18:58:07 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
|
|
|
in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
|
|
|
|
if (ifa->ifa_addr->sa_family == AF_INET &&
|
|
|
|
ntohl(in.s_addr) > ntohl(last.s_addr) &&
|
|
|
|
ntohl(in.s_addr) < ntohl(cur.s_addr)) {
|
|
|
|
cur.s_addr = in.s_addr;
|
|
|
|
found++;
|
|
|
|
}
|
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(sc));
|
2008-06-02 18:58:07 +00:00
|
|
|
if (found)
|
|
|
|
SHA1Update(&sc->sc_sha1, (void *)&cur, sizeof(cur));
|
|
|
|
} while (found);
|
2005-02-22 13:04:05 +00:00
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
2008-06-02 18:58:07 +00:00
|
|
|
memset(&cur6, 0, sizeof(cur6));
|
|
|
|
do {
|
|
|
|
found = 0;
|
|
|
|
last6 = cur6;
|
|
|
|
memset(&cur6, 0xff, sizeof(cur6));
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(sc));
|
2008-06-02 18:58:07 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
2005-02-22 13:04:05 +00:00
|
|
|
in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
|
2008-06-02 18:58:07 +00:00
|
|
|
if (IN6_IS_SCOPE_EMBED(&in6))
|
|
|
|
in6.s6_addr16[1] = 0;
|
|
|
|
if (ifa->ifa_addr->sa_family == AF_INET6 &&
|
|
|
|
memcmp(&in6, &last6, sizeof(in6)) > 0 &&
|
|
|
|
memcmp(&in6, &cur6, sizeof(in6)) < 0) {
|
|
|
|
cur6 = in6;
|
|
|
|
found++;
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(sc));
|
2008-06-02 18:58:07 +00:00
|
|
|
if (found)
|
|
|
|
SHA1Update(&sc->sc_sha1, (void *)&cur6, sizeof(cur6));
|
|
|
|
} while (found);
|
2005-02-22 13:04:05 +00:00
|
|
|
#endif /* INET6 */
|
|
|
|
|
|
|
|
/* convert ipad to opad */
|
|
|
|
for (i = 0; i < sizeof(sc->sc_pad); i++)
|
|
|
|
sc->sc_pad[i] ^= 0x36 ^ 0x5c;
|
2005-03-01 13:14:33 +00:00
|
|
|
|
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2],
|
|
|
|
unsigned char md[20])
|
|
|
|
{
|
|
|
|
SHA1_CTX sha1ctx;
|
|
|
|
|
|
|
|
/* fetch first half of inner hash */
|
|
|
|
bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx));
|
|
|
|
|
|
|
|
SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter));
|
|
|
|
SHA1Final(md, &sha1ctx);
|
|
|
|
|
|
|
|
/* outer hash */
|
|
|
|
SHA1Init(&sha1ctx);
|
|
|
|
SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad));
|
|
|
|
SHA1Update(&sha1ctx, md, 20);
|
|
|
|
SHA1Final(md, &sha1ctx);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2],
|
|
|
|
unsigned char md[20])
|
|
|
|
{
|
|
|
|
unsigned char md2[20];
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_hmac_generate(sc, counter, md2);
|
|
|
|
|
|
|
|
return (bcmp(md, md2, sizeof(md2)));
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setroute(struct carp_softc *sc, int cmd)
|
|
|
|
{
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
int s;
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
s = splnet();
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
2005-03-01 13:14:33 +00:00
|
|
|
if (ifa->ifa_addr->sa_family == AF_INET &&
|
|
|
|
sc->sc_carpdev != NULL) {
|
2005-02-22 13:04:05 +00:00
|
|
|
int count = carp_addrcount(
|
2005-02-26 13:55:07 +00:00
|
|
|
(struct carp_if *)sc->sc_carpdev->if_carp,
|
2005-02-22 13:04:05 +00:00
|
|
|
ifatoia(ifa), CARP_COUNT_MASTER);
|
|
|
|
|
|
|
|
if ((cmd == RTM_ADD && count == 1) ||
|
|
|
|
(cmd == RTM_DELETE && count == 0))
|
|
|
|
rtinit(ifa, cmd, RTF_UP | RTF_HOST);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2006-07-09 06:04:01 +00:00
|
|
|
carp_clone_create(struct if_clone *ifc, int unit, caddr_t params)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
struct carp_softc *sc;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
2008-10-23 15:53:51 +00:00
|
|
|
sc = malloc(sizeof(*sc), M_CARP, M_WAITOK|M_ZERO);
|
2005-06-10 16:49:24 +00:00
|
|
|
ifp = SC2IFP(sc) = if_alloc(IFT_ETHER);
|
|
|
|
if (ifp == NULL) {
|
2008-10-23 15:53:51 +00:00
|
|
|
free(sc, M_CARP);
|
2005-06-10 16:49:24 +00:00
|
|
|
return (ENOSPC);
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
sc->sc_flags_backup = 0;
|
|
|
|
sc->sc_suppress = 0;
|
|
|
|
sc->sc_advbase = CARP_DFLTINTV;
|
|
|
|
sc->sc_vhid = -1; /* required setting */
|
|
|
|
sc->sc_advskew = 0;
|
|
|
|
sc->sc_init_counter = 1;
|
|
|
|
sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */
|
2006-07-08 00:01:01 +00:00
|
|
|
sc->sc_imo.imo_membership = (struct in_multi **)malloc(
|
|
|
|
(sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_CARP,
|
|
|
|
M_WAITOK);
|
Import rewrite of IPv4 socket multicast layer to support source-specific
and protocol-independent host mode multicast. The code is written to
accomodate IPv6, IGMPv3 and MLDv2 with only a little additional work.
This change only pertains to FreeBSD's use as a multicast end-station and
does not concern multicast routing; for an IGMPv3/MLDv2 router
implementation, consider the XORP project.
The work is based on Wilbert de Graaf's IGMPv3 code drop for FreeBSD 4.6,
which is available at: http://www.kloosterhof.com/wilbert/igmpv3.html
Summary
* IPv4 multicast socket processing is now moved out of ip_output.c
into a new module, in_mcast.c.
* The in_mcast.c module implements the IPv4 legacy any-source API in
terms of the protocol-independent source-specific API.
* Source filters are lazy allocated as the common case does not use them.
They are part of per inpcb state and are covered by the inpcb lock.
* struct ip_mreqn is now supported to allow applications to specify
multicast joins by interface index in the legacy IPv4 any-source API.
* In UDP, an incoming multicast datagram only requires that the source
port matches the 4-tuple if the socket was already bound by source port.
An unbound socket SHOULD be able to receive multicasts sent from an
ephemeral source port.
* The UDP socket multicast filter mode defaults to exclusive, that is,
sources present in the per-socket list will be blocked from delivery.
* The RFC 3678 userland functions have been added to libc: setsourcefilter,
getsourcefilter, setipv4sourcefilter, getipv4sourcefilter.
* Definitions for IGMPv3 are merged but not yet used.
* struct sockaddr_storage is now referenced from <netinet/in.h>. It
is therefore defined there if not already declared in the same way
as for the C99 types.
* The RFC 1724 hack (specify 0.0.0.0/8 addresses to IP_MULTICAST_IF
which are then interpreted as interface indexes) is now deprecated.
* A patch for the Rhyolite.com routed in the FreeBSD base system
is available in the -net archives. This only affects individuals
running RIPv1 or RIPv2 via point-to-point and/or unnumbered interfaces.
* Make IPv6 detach path similar to IPv4's in code flow; functionally same.
* Bump __FreeBSD_version to 700048; see UPDATING.
This work was financially supported by another FreeBSD committer.
Obtained from: p4://bms_netdev
Submitted by: Wilbert de Graaf (original work)
Reviewed by: rwatson (locking), silence from fenner,
net@ (but with encouragement)
2007-06-12 16:24:56 +00:00
|
|
|
sc->sc_imo.imo_mfilters = NULL;
|
2006-07-08 00:01:01 +00:00
|
|
|
sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
|
2006-09-25 11:53:54 +00:00
|
|
|
sc->sc_imo.imo_multicast_vif = -1;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
#ifdef INET6
|
|
|
|
sc->sc_im6o.im6o_membership = (struct in6_multi **)malloc(
|
|
|
|
(sizeof(struct in6_multi *) * IPV6_MIN_MEMBERSHIPS), M_CARP,
|
|
|
|
M_WAITOK);
|
|
|
|
sc->sc_im6o.im6o_mfilters = NULL;
|
|
|
|
sc->sc_im6o.im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
|
|
|
|
sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL;
|
|
|
|
#endif
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2007-07-28 07:31:30 +00:00
|
|
|
callout_init(&sc->sc_ad_tmo, CALLOUT_MPSAFE);
|
|
|
|
callout_init(&sc->sc_md_tmo, CALLOUT_MPSAFE);
|
|
|
|
callout_init(&sc->sc_md6_tmo, CALLOUT_MPSAFE);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
if_initname(ifp, CARP_IFNAME, unit);
|
|
|
|
ifp->if_mtu = ETHERMTU;
|
2005-10-26 05:57:35 +00:00
|
|
|
ifp->if_flags = IFF_LOOPBACK;
|
2005-02-22 13:04:05 +00:00
|
|
|
ifp->if_ioctl = carp_ioctl;
|
|
|
|
ifp->if_output = carp_looutput;
|
|
|
|
ifp->if_start = carp_start;
|
|
|
|
ifp->if_type = IFT_CARP;
|
|
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
|
|
|
ifp->if_hdrlen = 0;
|
|
|
|
if_attach(ifp);
|
2005-06-10 16:49:24 +00:00
|
|
|
bpfattach(SC2IFP(sc), DLT_NULL, sizeof(u_int32_t));
|
2005-03-01 12:36:07 +00:00
|
|
|
mtx_lock(&carp_mtx);
|
|
|
|
LIST_INSERT_HEAD(&carpif_list, sc, sc_next);
|
|
|
|
mtx_unlock(&carp_mtx);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_clone_destroy(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct carp_softc *sc = ifp->if_softc;
|
2006-03-21 14:29:48 +00:00
|
|
|
|
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK(sc);
|
2007-01-25 18:03:40 +00:00
|
|
|
carpdetach(sc, 1); /* Returns unlocked. */
|
2006-03-21 14:29:48 +00:00
|
|
|
|
|
|
|
mtx_lock(&carp_mtx);
|
|
|
|
LIST_REMOVE(sc, sc_next);
|
|
|
|
mtx_unlock(&carp_mtx);
|
|
|
|
bpfdetach(ifp);
|
|
|
|
if_detach(ifp);
|
|
|
|
if_free_type(ifp, IFT_ETHER);
|
2006-07-08 00:01:01 +00:00
|
|
|
free(sc->sc_imo.imo_membership, M_CARP);
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
#ifdef INET6
|
|
|
|
free(sc->sc_im6o.im6o_membership, M_CARP);
|
|
|
|
#endif
|
2006-03-21 14:29:48 +00:00
|
|
|
free(sc, M_CARP);
|
|
|
|
}
|
|
|
|
|
2007-02-02 09:39:09 +00:00
|
|
|
/*
|
|
|
|
* This function can be called on CARP interface destroy path,
|
|
|
|
* and in case of the removal of the underlying interface as
|
2010-11-24 05:24:36 +00:00
|
|
|
* well. We differentiate these two cases: in case of destruction
|
|
|
|
* of the underlying interface, we do not cleanup our multicast
|
|
|
|
* memberships, since they are already freed. But we purge pointers
|
|
|
|
* to multicast structures, since they are no longer valid, to
|
|
|
|
* avoid panic in future calls to carpdetach(). Also, we do not
|
2007-02-02 09:39:09 +00:00
|
|
|
* release the lock on return, because the function will be
|
|
|
|
* called once more, for another CARP instance on the same
|
|
|
|
* interface.
|
|
|
|
*/
|
2006-03-21 14:29:48 +00:00
|
|
|
static void
|
2007-01-25 18:03:40 +00:00
|
|
|
carpdetach(struct carp_softc *sc, int unlock)
|
2006-03-21 14:29:48 +00:00
|
|
|
{
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_if *cif;
|
2005-05-15 01:44:26 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
|
|
|
callout_stop(&sc->sc_md_tmo);
|
|
|
|
callout_stop(&sc->sc_md6_tmo);
|
|
|
|
|
2005-05-15 01:44:26 +00:00
|
|
|
if (sc->sc_suppress)
|
|
|
|
carp_suppress_preempt--;
|
|
|
|
sc->sc_suppress = 0;
|
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS)
|
|
|
|
carp_suppress_preempt--;
|
|
|
|
sc->sc_sendad_errors = 0;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
SC2IFP(sc)->if_flags &= ~IFF_UP;
|
|
|
|
carp_setrun(sc, 0);
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast_cleanup(sc, unlock);
|
2007-02-02 09:39:09 +00:00
|
|
|
#ifdef INET6
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast6_cleanup(sc, unlock);
|
2007-02-02 09:39:09 +00:00
|
|
|
#endif
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
if (sc->sc_carpdev != NULL) {
|
|
|
|
cif = (struct carp_if *)sc->sc_carpdev->if_carp;
|
|
|
|
CARP_LOCK_ASSERT(cif);
|
2005-02-22 13:04:05 +00:00
|
|
|
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
|
|
|
|
if (!--cif->vhif_nvrs) {
|
2006-03-21 14:29:48 +00:00
|
|
|
ifpromisc(sc->sc_carpdev, 0);
|
2005-02-26 13:55:07 +00:00
|
|
|
sc->sc_carpdev->if_carp = NULL;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK_DESTROY(cif);
|
2009-08-20 02:33:12 +00:00
|
|
|
free(cif, M_CARP);
|
2007-01-25 18:03:40 +00:00
|
|
|
} else if (unlock)
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
sc->sc_carpdev = NULL;
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
2006-03-21 14:29:48 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
/* Detach an interface from the carp. */
|
|
|
|
static void
|
|
|
|
carp_ifdetach(void *arg __unused, struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct carp_if *cif = (struct carp_if *)ifp->if_carp;
|
|
|
|
struct carp_softc *sc, *nextsc;
|
2007-01-25 17:58:16 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
if (cif == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: At the end of for() cycle the lock will be destroyed.
|
|
|
|
*/
|
|
|
|
CARP_LOCK(cif);
|
|
|
|
for (sc = TAILQ_FIRST(&cif->vhif_vrs); sc; sc = nextsc) {
|
|
|
|
nextsc = TAILQ_NEXT(sc, sc_list);
|
2007-01-25 18:03:40 +00:00
|
|
|
carpdetach(sc, 0);
|
2006-03-21 14:29:48 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* process input packet.
|
|
|
|
* we have rearranged checks order compared to the rfc,
|
|
|
|
* but it seems more efficient this way or not possible otherwise.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
carp_input(struct mbuf *m, int hlen)
|
|
|
|
{
|
|
|
|
struct ip *ip = mtod(m, struct ip *);
|
|
|
|
struct carp_header *ch;
|
|
|
|
int iplen, len;
|
|
|
|
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_ipackets);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (!carp_opts[CARPCTL_ALLOW]) {
|
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if received on a valid carp interface */
|
|
|
|
if (m->m_pkthdr.rcvif->if_carp == NULL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badif);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp_input: packet received on non-carp "
|
2005-02-25 11:26:39 +00:00
|
|
|
"interface: %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify that the IP TTL is 255. */
|
|
|
|
if (ip->ip_ttl != CARP_DFLTTL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badttl);
|
2010-01-08 16:14:41 +00:00
|
|
|
CARP_DEBUG("carp_input: received ttl %d != 255 on %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
ip->ip_ttl,
|
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
iplen = ip->ip_hl << 2;
|
|
|
|
|
|
|
|
if (m->m_pkthdr.len < iplen + sizeof(*ch)) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badlen);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp_input: received len %zd < "
|
2009-07-30 17:40:47 +00:00
|
|
|
"sizeof(struct carp_header) on %s\n",
|
|
|
|
m->m_len - sizeof(struct ip),
|
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iplen + sizeof(*ch) < m->m_len) {
|
|
|
|
if ((m = m_pullup(m, iplen + sizeof(*ch))) == NULL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_hdrops);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp_input: pullup failed\n");
|
2005-02-22 13:04:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
}
|
|
|
|
ch = (struct carp_header *)((char *)ip + iplen);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* verify that the received packet length is
|
|
|
|
* equal to the CARP header
|
|
|
|
*/
|
|
|
|
len = iplen + sizeof(*ch);
|
|
|
|
if (len > m->m_pkthdr.len) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badlen);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp_input: packet too short %d on %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
m->m_pkthdr.len,
|
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((m = m_pullup(m, len)) == NULL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_hdrops);
|
2005-02-22 13:04:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
ch = (struct carp_header *)((char *)ip + iplen);
|
|
|
|
|
|
|
|
/* verify the CARP checksum */
|
|
|
|
m->m_data += iplen;
|
|
|
|
if (carp_cksum(m, len - iplen)) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badsum);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp_input: checksum failed on %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m->m_data -= iplen;
|
|
|
|
|
2005-02-25 10:09:44 +00:00
|
|
|
carp_input_c(m, ch, AF_INET);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
int
|
|
|
|
carp6_input(struct mbuf **mp, int *offp, int proto)
|
|
|
|
{
|
|
|
|
struct mbuf *m = *mp;
|
|
|
|
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
struct carp_header *ch;
|
|
|
|
u_int len;
|
|
|
|
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_ipackets6);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (!carp_opts[CARPCTL_ALLOW]) {
|
|
|
|
m_freem(m);
|
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if received on a valid carp interface */
|
|
|
|
if (m->m_pkthdr.rcvif->if_carp == NULL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badif);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp6_input: packet received on non-carp "
|
2005-02-25 11:26:39 +00:00
|
|
|
"interface: %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify that the IP TTL is 255 */
|
|
|
|
if (ip6->ip6_hlim != CARP_DFLTTL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badttl);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp6_input: received ttl %d != 255 on %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
ip6->ip6_hlim,
|
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify that we have a complete carp packet */
|
|
|
|
len = m->m_len;
|
|
|
|
IP6_EXTHDR_GET(ch, struct carp_header *, m, *offp, sizeof(*ch));
|
|
|
|
if (ch == NULL) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badlen);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp6_input: packet size %u too small\n", len);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* verify the CARP checksum */
|
|
|
|
m->m_data += *offp;
|
|
|
|
if (carp_cksum(m, sizeof(*ch))) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badsum);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("carp6_input: checksum failed, on %s\n",
|
2005-02-25 10:09:44 +00:00
|
|
|
m->m_pkthdr.rcvif->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
m->m_data -= *offp;
|
|
|
|
|
2005-02-25 10:09:44 +00:00
|
|
|
carp_input_c(m, ch, AF_INET6);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (IPPROTO_DONE);
|
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-25 10:09:44 +00:00
|
|
|
carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = m->m_pkthdr.rcvif;
|
2005-02-25 10:09:44 +00:00
|
|
|
struct carp_softc *sc;
|
2005-02-22 13:04:05 +00:00
|
|
|
u_int64_t tmp_counter;
|
|
|
|
struct timeval sc_tv, ch_tv;
|
|
|
|
|
|
|
|
/* verify that the VHID is valid on the receiving interface */
|
|
|
|
CARP_LOCK(ifp->if_carp);
|
|
|
|
TAILQ_FOREACH(sc, &((struct carp_if *)ifp->if_carp)->vhif_vrs, sc_list)
|
|
|
|
if (sc->sc_vhid == ch->carp_vhid)
|
|
|
|
break;
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2005-08-09 10:20:02 +00:00
|
|
|
if (!sc || !((SC2IFP(sc)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badvhid);
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(ifp->if_carp);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
getmicrotime(&SC2IFP(sc)->if_lastchange);
|
|
|
|
SC2IFP(sc)->if_ipackets++;
|
|
|
|
SC2IFP(sc)->if_ibytes += m->m_pkthdr.len;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2006-06-02 19:59:33 +00:00
|
|
|
if (bpf_peers_present(SC2IFP(sc)->if_bpf)) {
|
2005-02-22 13:04:05 +00:00
|
|
|
struct ip *ip = mtod(m, struct ip *);
|
2005-02-28 11:54:36 +00:00
|
|
|
uint32_t af1 = af;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
/* BPF wants net byte order */
|
2005-02-28 11:54:36 +00:00
|
|
|
ip->ip_len = htons(ip->ip_len + (ip->ip_hl << 2));
|
|
|
|
ip->ip_off = htons(ip->ip_off);
|
2005-06-10 16:49:24 +00:00
|
|
|
bpf_mtap2(SC2IFP(sc)->if_bpf, &af1, sizeof(af1), m);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* verify the CARP version. */
|
|
|
|
if (ch->carp_version != CARP_VERSION) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badver);
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_ierrors++;
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(ifp->if_carp);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("%s; invalid version %d\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname,
|
2005-02-25 10:09:44 +00:00
|
|
|
ch->carp_version);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify the hash */
|
|
|
|
if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) {
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_badauth);
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_ierrors++;
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(ifp->if_carp);
|
2010-01-08 16:14:41 +00:00
|
|
|
CARP_DEBUG("%s: incorrect hash\n", SC2IFP(sc)->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp_counter = ntohl(ch->carp_counter[0]);
|
|
|
|
tmp_counter = tmp_counter<<32;
|
|
|
|
tmp_counter += ntohl(ch->carp_counter[1]);
|
|
|
|
|
|
|
|
/* XXX Replay protection goes here */
|
|
|
|
|
|
|
|
sc->sc_init_counter = 0;
|
|
|
|
sc->sc_counter = tmp_counter;
|
|
|
|
|
|
|
|
sc_tv.tv_sec = sc->sc_advbase;
|
|
|
|
if (carp_suppress_preempt && sc->sc_advskew < 240)
|
|
|
|
sc_tv.tv_usec = 240 * 1000000 / 256;
|
|
|
|
else
|
|
|
|
sc_tv.tv_usec = sc->sc_advskew * 1000000 / 256;
|
|
|
|
ch_tv.tv_sec = ch->carp_advbase;
|
|
|
|
ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256;
|
|
|
|
|
|
|
|
switch (sc->sc_state) {
|
|
|
|
case INIT:
|
|
|
|
break;
|
|
|
|
case MASTER:
|
|
|
|
/*
|
|
|
|
* If we receive an advertisement from a master who's going to
|
|
|
|
* be more frequent than us, go into BACKUP state.
|
|
|
|
*/
|
|
|
|
if (timevalcmp(&sc_tv, &ch_tv, >) ||
|
|
|
|
timevalcmp(&sc_tv, &ch_tv, ==)) {
|
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_LOG("%s: MASTER -> BACKUP "
|
2005-02-25 11:26:39 +00:00
|
|
|
"(more frequent advertisement received)\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, BACKUP);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
carp_setroute(sc, RTM_DELETE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BACKUP:
|
|
|
|
/*
|
|
|
|
* If we're pre-empting masters who advertise slower than us,
|
|
|
|
* and this one claims to be slower, treat him as down.
|
|
|
|
*/
|
|
|
|
if (carp_opts[CARPCTL_PREEMPT] &&
|
|
|
|
timevalcmp(&sc_tv, &ch_tv, <)) {
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_LOG("%s: BACKUP -> MASTER "
|
2005-02-25 11:26:39 +00:00
|
|
|
"(preempting a slower master)\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname);
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_master_down_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the master is going to advertise at such a low frequency
|
|
|
|
* that he's guaranteed to time out, we'd might as well just
|
|
|
|
* treat him as timed out now.
|
|
|
|
*/
|
|
|
|
sc_tv.tv_sec = sc->sc_advbase * 3;
|
|
|
|
if (timevalcmp(&sc_tv, &ch_tv, <)) {
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_LOG("%s: BACKUP -> MASTER "
|
2005-02-25 11:26:39 +00:00
|
|
|
"(master timed out)\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname);
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_master_down_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise, we reset the counter and wait for the next
|
|
|
|
* advertisement.
|
|
|
|
*/
|
|
|
|
carp_setrun(sc, af);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(ifp->if_carp);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
m_freem(m);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch)
|
|
|
|
{
|
|
|
|
struct m_tag *mtag;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = SC2IFP(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (sc->sc_init_counter) {
|
|
|
|
/* this could also be seconds since unix epoch */
|
|
|
|
sc->sc_counter = arc4random();
|
|
|
|
sc->sc_counter = sc->sc_counter << 32;
|
|
|
|
sc->sc_counter += arc4random();
|
|
|
|
} else
|
|
|
|
sc->sc_counter++;
|
|
|
|
|
|
|
|
ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff);
|
|
|
|
ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff);
|
|
|
|
|
|
|
|
carp_hmac_generate(sc, ch->carp_counter, ch->carp_md);
|
|
|
|
|
|
|
|
/* Tag packet for carp_output */
|
|
|
|
mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct ifnet *), M_NOWAIT);
|
|
|
|
if (mtag == NULL) {
|
|
|
|
m_freem(m);
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_oerrors++;
|
2005-02-22 13:04:05 +00:00
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
bcopy(&ifp, (caddr_t)(mtag + 1), sizeof(struct ifnet *));
|
|
|
|
m_tag_prepend(m, mtag);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_ad_all(void)
|
|
|
|
{
|
2005-03-01 12:36:07 +00:00
|
|
|
struct carp_softc *sc;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2005-03-01 12:36:07 +00:00
|
|
|
mtx_lock(&carp_mtx);
|
|
|
|
LIST_FOREACH(sc, &carpif_list, sc_next) {
|
|
|
|
if (sc->sc_carpdev == NULL)
|
2005-02-22 13:04:05 +00:00
|
|
|
continue;
|
2005-03-01 12:36:07 +00:00
|
|
|
CARP_SCLOCK(sc);
|
2005-08-09 10:20:02 +00:00
|
|
|
if ((SC2IFP(sc)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING) &&
|
2005-03-01 12:36:07 +00:00
|
|
|
sc->sc_state == MASTER)
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_send_ad_locked(sc);
|
2005-03-01 12:36:07 +00:00
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
2005-03-01 12:36:07 +00:00
|
|
|
mtx_unlock(&carp_mtx);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_ad(void *v)
|
2005-03-01 13:14:33 +00:00
|
|
|
{
|
|
|
|
struct carp_softc *sc = v;
|
|
|
|
|
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
carp_send_ad_locked(sc);
|
|
|
|
CARP_SCUNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
carp_send_ad_locked(struct carp_softc *sc)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
|
|
|
struct carp_header ch;
|
|
|
|
struct timeval tv;
|
|
|
|
struct carp_header *ch_ptr;
|
|
|
|
struct mbuf *m;
|
|
|
|
int len, advbase, advskew;
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
/* bow out if we've lost our UPness or RUNNINGuiness */
|
2005-08-09 10:20:02 +00:00
|
|
|
if (!((SC2IFP(sc)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING))) {
|
2005-02-22 13:04:05 +00:00
|
|
|
advbase = 255;
|
|
|
|
advskew = 255;
|
|
|
|
} else {
|
|
|
|
advbase = sc->sc_advbase;
|
|
|
|
if (!carp_suppress_preempt || sc->sc_advskew > 240)
|
|
|
|
advskew = sc->sc_advskew;
|
|
|
|
else
|
|
|
|
advskew = 240;
|
|
|
|
tv.tv_sec = advbase;
|
|
|
|
tv.tv_usec = advskew * 1000000 / 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
ch.carp_version = CARP_VERSION;
|
|
|
|
ch.carp_type = CARP_ADVERTISEMENT;
|
|
|
|
ch.carp_vhid = sc->sc_vhid;
|
|
|
|
ch.carp_advbase = advbase;
|
|
|
|
ch.carp_advskew = advskew;
|
|
|
|
ch.carp_authlen = 7; /* XXX DEFINE */
|
|
|
|
ch.carp_pad1 = 0; /* must be zero */
|
|
|
|
ch.carp_cksum = 0;
|
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
if (sc->sc_ia) {
|
|
|
|
struct ip *ip;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_HEADER);
|
|
|
|
if (m == NULL) {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_oerrors++;
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_onomem);
|
2005-02-22 13:04:05 +00:00
|
|
|
/* XXX maybe less ? */
|
|
|
|
if (advbase != 255 || advskew != 255)
|
|
|
|
callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
|
|
|
|
carp_send_ad, sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
len = sizeof(*ip) + sizeof(ch);
|
|
|
|
m->m_pkthdr.len = len;
|
|
|
|
m->m_pkthdr.rcvif = NULL;
|
|
|
|
m->m_len = len;
|
|
|
|
MH_ALIGN(m, m->m_len);
|
|
|
|
m->m_flags |= M_MCAST;
|
|
|
|
ip = mtod(m, struct ip *);
|
|
|
|
ip->ip_v = IPVERSION;
|
|
|
|
ip->ip_hl = sizeof(*ip) >> 2;
|
|
|
|
ip->ip_tos = IPTOS_LOWDELAY;
|
|
|
|
ip->ip_len = len;
|
|
|
|
ip->ip_id = ip_newid();
|
|
|
|
ip->ip_off = IP_DF;
|
|
|
|
ip->ip_ttl = CARP_DFLTTL;
|
|
|
|
ip->ip_p = IPPROTO_CARP;
|
|
|
|
ip->ip_sum = 0;
|
|
|
|
ip->ip_src.s_addr = sc->sc_ia->ia_addr.sin_addr.s_addr;
|
|
|
|
ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP);
|
|
|
|
|
|
|
|
ch_ptr = (struct carp_header *)(&ip[1]);
|
|
|
|
bcopy(&ch, ch_ptr, sizeof(ch));
|
|
|
|
if (carp_prepare_ad(m, sc, ch_ptr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m->m_data += sizeof(*ip);
|
|
|
|
ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip));
|
|
|
|
m->m_data -= sizeof(*ip);
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
getmicrotime(&SC2IFP(sc)->if_lastchange);
|
|
|
|
SC2IFP(sc)->if_opackets++;
|
|
|
|
SC2IFP(sc)->if_obytes += len;
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_opackets);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_oerrors++;
|
2005-02-22 13:04:05 +00:00
|
|
|
if (sc->sc_sendad_errors < INT_MAX)
|
|
|
|
sc->sc_sendad_errors++;
|
|
|
|
if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) {
|
|
|
|
carp_suppress_preempt++;
|
2005-03-01 13:14:33 +00:00
|
|
|
if (carp_suppress_preempt == 1) {
|
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_ad_all();
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
sc->sc_sendad_success = 0;
|
|
|
|
} else {
|
|
|
|
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
|
|
|
|
if (++sc->sc_sendad_success >=
|
|
|
|
CARP_SENDAD_MIN_SUCCESS) {
|
|
|
|
carp_suppress_preempt--;
|
|
|
|
sc->sc_sendad_errors = 0;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sc->sc_sendad_errors = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
if (sc->sc_ia6) {
|
|
|
|
struct ip6_hdr *ip6;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_HEADER);
|
|
|
|
if (m == NULL) {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_oerrors++;
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_onomem);
|
2005-02-22 13:04:05 +00:00
|
|
|
/* XXX maybe less ? */
|
|
|
|
if (advbase != 255 || advskew != 255)
|
|
|
|
callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
|
|
|
|
carp_send_ad, sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
len = sizeof(*ip6) + sizeof(ch);
|
|
|
|
m->m_pkthdr.len = len;
|
|
|
|
m->m_pkthdr.rcvif = NULL;
|
|
|
|
m->m_len = len;
|
|
|
|
MH_ALIGN(m, m->m_len);
|
|
|
|
m->m_flags |= M_MCAST;
|
|
|
|
ip6 = mtod(m, struct ip6_hdr *);
|
|
|
|
bzero(ip6, sizeof(*ip6));
|
|
|
|
ip6->ip6_vfc |= IPV6_VERSION;
|
|
|
|
ip6->ip6_hlim = CARP_DFLTTL;
|
|
|
|
ip6->ip6_nxt = IPPROTO_CARP;
|
|
|
|
bcopy(&sc->sc_ia6->ia_addr.sin6_addr, &ip6->ip6_src,
|
|
|
|
sizeof(struct in6_addr));
|
|
|
|
/* set the multicast destination */
|
|
|
|
|
2006-10-07 10:19:58 +00:00
|
|
|
ip6->ip6_dst.s6_addr16[0] = htons(0xff02);
|
2005-02-22 13:04:05 +00:00
|
|
|
ip6->ip6_dst.s6_addr8[15] = 0x12;
|
2006-10-07 10:19:58 +00:00
|
|
|
if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
|
|
|
|
SC2IFP(sc)->if_oerrors++;
|
|
|
|
m_freem(m);
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_DEBUG("%s: in6_setscope failed\n", __func__);
|
2006-10-07 10:19:58 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
ch_ptr = (struct carp_header *)(&ip6[1]);
|
|
|
|
bcopy(&ch, ch_ptr, sizeof(ch));
|
|
|
|
if (carp_prepare_ad(m, sc, ch_ptr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m->m_data += sizeof(*ip6);
|
|
|
|
ch_ptr->carp_cksum = carp_cksum(m, len - sizeof(*ip6));
|
|
|
|
m->m_data -= sizeof(*ip6);
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
getmicrotime(&SC2IFP(sc)->if_lastchange);
|
|
|
|
SC2IFP(sc)->if_opackets++;
|
|
|
|
SC2IFP(sc)->if_obytes += len;
|
2009-04-12 14:19:37 +00:00
|
|
|
CARPSTATS_INC(carps_opackets6);
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (ip6_output(m, NULL, NULL, 0, &sc->sc_im6o, NULL, NULL)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_oerrors++;
|
2005-02-22 13:04:05 +00:00
|
|
|
if (sc->sc_sendad_errors < INT_MAX)
|
|
|
|
sc->sc_sendad_errors++;
|
|
|
|
if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) {
|
|
|
|
carp_suppress_preempt++;
|
2005-03-01 13:14:33 +00:00
|
|
|
if (carp_suppress_preempt == 1) {
|
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_ad_all();
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
sc->sc_sendad_success = 0;
|
|
|
|
} else {
|
|
|
|
if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
|
|
|
|
if (++sc->sc_sendad_success >=
|
|
|
|
CARP_SENDAD_MIN_SUCCESS) {
|
|
|
|
carp_suppress_preempt--;
|
|
|
|
sc->sc_sendad_errors = 0;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sc->sc_sendad_errors = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
|
|
|
if (advbase != 255 || advskew != 255)
|
|
|
|
callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
|
|
|
|
carp_send_ad, sc);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Broadcast a gratuitous ARP request containing
|
|
|
|
* the virtual router MAC address for each IP address
|
|
|
|
* associated with the virtual router.
|
|
|
|
*/
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_arp(struct carp_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (ifa->ifa_addr->sa_family != AF_INET)
|
|
|
|
continue;
|
|
|
|
|
2005-11-11 16:04:59 +00:00
|
|
|
/* arprequest(sc->sc_carpdev, &in, &in, IF_LLADDR(sc->sc_ifp)); */
|
|
|
|
arp_ifinit2(sc->sc_carpdev, ifa, IF_LLADDR(sc->sc_ifp));
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
DELAY(1000); /* XXX */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET6
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_na(struct carp_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
struct in6_addr *in6;
|
|
|
|
static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (ifa->ifa_addr->sa_family != AF_INET6)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
in6 = &ifatoia6(ifa)->ia_addr.sin6_addr;
|
2005-02-26 13:55:07 +00:00
|
|
|
nd6_na_output(sc->sc_carpdev, &mcast, in6,
|
2005-02-22 13:04:05 +00:00
|
|
|
ND_NA_FLAG_OVERRIDE, 1, NULL);
|
|
|
|
DELAY(1000); /* XXX */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type)
|
|
|
|
{
|
|
|
|
struct carp_softc *vh;
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
int count = 0;
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_LOCK_ASSERT(cif);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
|
|
|
|
if ((type == CARP_COUNT_RUNNING &&
|
2005-08-09 10:20:02 +00:00
|
|
|
(SC2IFP(vh)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) ||
|
2005-02-22 13:04:05 +00:00
|
|
|
(type == CARP_COUNT_MASTER && vh->sc_state == MASTER)) {
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(vh));
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist,
|
2005-02-22 13:04:05 +00:00
|
|
|
ifa_list) {
|
|
|
|
if (ifa->ifa_addr->sa_family == AF_INET &&
|
|
|
|
ia->ia_addr.sin_addr.s_addr ==
|
|
|
|
ifatoia(ifa)->ia_addr.sin_addr.s_addr)
|
|
|
|
count++;
|
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (count);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2010-08-11 00:51:50 +00:00
|
|
|
carp_iamatch(struct ifnet *ifp, struct in_ifaddr *ia,
|
2005-02-22 13:04:05 +00:00
|
|
|
struct in_addr *isaddr, u_int8_t **enaddr)
|
|
|
|
{
|
2010-08-11 00:51:50 +00:00
|
|
|
struct carp_if *cif;
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_softc *vh;
|
|
|
|
int index, count = 0;
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
cif = ifp->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK(cif);
|
|
|
|
|
|
|
|
if (carp_opts[CARPCTL_ARPBALANCE]) {
|
|
|
|
/*
|
|
|
|
* XXX proof of concept implementation.
|
|
|
|
* We use the source ip to decide which virtual host should
|
|
|
|
* handle the request. If we're master of that virtual host,
|
|
|
|
* then we respond, otherwise, just drop the arp packet on
|
|
|
|
* the floor.
|
|
|
|
*/
|
|
|
|
count = carp_addrcount(cif, ia, CARP_COUNT_RUNNING);
|
|
|
|
if (count == 0) {
|
|
|
|
/* should never reach this */
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this should be a hash, like pf_hash() */
|
2005-07-01 08:22:13 +00:00
|
|
|
index = ntohl(isaddr->s_addr) % count;
|
2005-02-22 13:04:05 +00:00
|
|
|
count = 0;
|
|
|
|
|
|
|
|
TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
|
2005-08-09 10:20:02 +00:00
|
|
|
if ((SC2IFP(vh)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING)) {
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(vh));
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist,
|
2005-02-22 13:04:05 +00:00
|
|
|
ifa_list) {
|
|
|
|
if (ifa->ifa_addr->sa_family ==
|
|
|
|
AF_INET &&
|
|
|
|
ia->ia_addr.sin_addr.s_addr ==
|
|
|
|
ifatoia(ifa)->ia_addr.sin_addr.s_addr) {
|
|
|
|
if (count == index) {
|
|
|
|
if (vh->sc_state ==
|
|
|
|
MASTER) {
|
2005-11-11 16:04:59 +00:00
|
|
|
*enaddr = IF_LLADDR(vh->sc_ifp);
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (1);
|
|
|
|
} else {
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
|
2005-08-09 10:20:02 +00:00
|
|
|
if ((SC2IFP(vh)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) &&
|
2005-11-17 12:56:40 +00:00
|
|
|
ia->ia_ifp == SC2IFP(vh) &&
|
|
|
|
vh->sc_state == MASTER) {
|
2005-11-11 16:04:59 +00:00
|
|
|
*enaddr = IF_LLADDR(vh->sc_ifp);
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET6
|
2005-02-27 11:32:26 +00:00
|
|
|
struct ifaddr *
|
2010-08-11 00:51:50 +00:00
|
|
|
carp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
2010-08-11 00:51:50 +00:00
|
|
|
struct carp_if *cif;
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_softc *vh;
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
cif = ifp->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK(cif);
|
|
|
|
TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) {
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(vh));
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(vh)->if_addrlist, ifa_list) {
|
2005-02-22 13:04:05 +00:00
|
|
|
if (IN6_ARE_ADDR_EQUAL(taddr,
|
|
|
|
&ifatoia6(ifa)->ia_addr.sin6_addr) &&
|
2005-08-09 10:20:02 +00:00
|
|
|
(SC2IFP(vh)->if_flags & IFF_UP) &&
|
2005-11-17 12:56:40 +00:00
|
|
|
(SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) &&
|
|
|
|
vh->sc_state == MASTER) {
|
2009-06-23 20:19:09 +00:00
|
|
|
ifa_ref(ifa);
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (ifa);
|
|
|
|
}
|
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
caddr_t
|
|
|
|
carp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
|
|
|
struct m_tag *mtag;
|
2010-08-11 00:51:50 +00:00
|
|
|
struct carp_if *cif;
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_softc *sc;
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
cif = ifp->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK(cif);
|
|
|
|
TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) {
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_LOCK(SC2IFP(sc));
|
2005-06-10 16:49:24 +00:00
|
|
|
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
2005-02-22 13:04:05 +00:00
|
|
|
if (IN6_ARE_ADDR_EQUAL(taddr,
|
|
|
|
&ifatoia6(ifa)->ia_addr.sin6_addr) &&
|
2005-08-09 10:20:02 +00:00
|
|
|
(SC2IFP(sc)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(sc)->if_drv_flags & IFF_DRV_RUNNING)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = SC2IFP(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
mtag = m_tag_get(PACKET_TAG_CARP,
|
|
|
|
sizeof(struct ifnet *), M_NOWAIT);
|
|
|
|
if (mtag == NULL) {
|
|
|
|
/* better a bit than nothing */
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(sc));
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
2005-11-11 16:04:59 +00:00
|
|
|
return (IF_LLADDR(sc->sc_ifp));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
bcopy(&ifp, (caddr_t)(mtag + 1),
|
|
|
|
sizeof(struct ifnet *));
|
|
|
|
m_tag_prepend(m, mtag);
|
|
|
|
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(sc));
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
2005-11-11 16:04:59 +00:00
|
|
|
return (IF_LLADDR(sc->sc_ifp));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-26 19:05:40 +00:00
|
|
|
IF_ADDR_UNLOCK(SC2IFP(sc));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct ifnet *
|
2010-08-11 00:51:50 +00:00
|
|
|
carp_forus(struct ifnet *ifp, u_char *dhost)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
2010-08-11 00:51:50 +00:00
|
|
|
struct carp_if *cif;
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_softc *vh;
|
|
|
|
u_int8_t *ena = dhost;
|
|
|
|
|
|
|
|
if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1)
|
|
|
|
return (NULL);
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
cif = ifp->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK(cif);
|
|
|
|
TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list)
|
2005-08-09 10:20:02 +00:00
|
|
|
if ((SC2IFP(vh)->if_flags & IFF_UP) &&
|
|
|
|
(SC2IFP(vh)->if_drv_flags & IFF_DRV_RUNNING) &&
|
|
|
|
vh->sc_state == MASTER &&
|
2005-11-11 16:04:59 +00:00
|
|
|
!bcmp(dhost, IF_LLADDR(vh->sc_ifp), ETHER_ADDR_LEN)) {
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_UNLOCK(cif);
|
2005-06-10 16:49:24 +00:00
|
|
|
return (SC2IFP(vh));
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_master_down(void *v)
|
|
|
|
{
|
|
|
|
struct carp_softc *sc = v;
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
carp_master_down_locked(sc);
|
|
|
|
CARP_SCUNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
carp_master_down_locked(struct carp_softc *sc)
|
|
|
|
{
|
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
switch (sc->sc_state) {
|
|
|
|
case INIT:
|
|
|
|
printf("%s: master_down event in INIT state\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
case MASTER:
|
|
|
|
break;
|
|
|
|
case BACKUP:
|
|
|
|
carp_set_state(sc, MASTER);
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_send_ad_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_arp(sc);
|
|
|
|
#ifdef INET6
|
|
|
|
carp_send_na(sc);
|
|
|
|
#endif /* INET6 */
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
carp_setroute(sc, RTM_ADD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When in backup state, af indicates whether to reset the master down timer
|
|
|
|
* for v4 or v6. If it's set to zero, reset the ones which are already pending.
|
|
|
|
*/
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(struct carp_softc *sc, sa_family_t af)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
if (sc->sc_carpdev == NULL) {
|
|
|
|
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
return;
|
|
|
|
} else
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-06-10 16:49:24 +00:00
|
|
|
if (SC2IFP(sc)->if_flags & IFF_UP &&
|
Address an edge condition that we found at work, where the carp(4)
interface goes to issue LINK_UP, then LINK_DOWN, then LINK_UP at
cold boot. This behavior is not observed when carp(4) interface
is created slightly later, when the underlying interface is fully
up.
Before this change what happen at boot is roughly:
- ifconfig creates em0 interface;
- ifconfig clones a carp device using em0;
(em0's link state is DOWN at this point)
- carp state: INIT -> BACKUP [*]
- carp state: BACKUP -> MASTER
- [Some negotiate between em0 and switch]
- em0 kicks up link state change event
(em0's link state is now up DOWN at this point)
- do_link_state_change() -> carp_carpdev_state()
- carp state: MASTER -> INIT (via carp_set_state(sc, INIT)) [+]
- carp state: INIT -> BACKUP
- carp state: BACKUP -> MASTER
At the [*] stage, em0 did not received any broadcast message from other
node, and assume our node is the master, thus carp(4) sets the link
state to "UP" after becoming a master. At [+], the master status
is forcely set to "INIT", then an election is casted, after which our
node would actually become a master.
We believe that at the [*] stage, the master status should remain as
"INIT" since the underlying parent interface's link state is not up.
Obtained from: iXsystems, Inc.
Reported by: jpaetzel
MFC after: 2 months
2010-08-08 07:04:27 +00:00
|
|
|
sc->sc_vhid > 0 && (sc->sc_naddrs || sc->sc_naddrs6) &&
|
|
|
|
sc->sc_carpdev->if_link_state == LINK_STATE_UP)
|
2005-08-09 10:20:02 +00:00
|
|
|
SC2IFP(sc)->if_drv_flags |= IFF_DRV_RUNNING;
|
2005-02-22 13:04:05 +00:00
|
|
|
else {
|
2005-08-09 10:20:02 +00:00
|
|
|
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setroute(sc, RTM_DELETE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (sc->sc_state) {
|
|
|
|
case INIT:
|
|
|
|
if (carp_opts[CARPCTL_PREEMPT] && !carp_suppress_preempt) {
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_send_ad_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_send_arp(sc);
|
|
|
|
#ifdef INET6
|
|
|
|
carp_send_na(sc);
|
|
|
|
#endif /* INET6 */
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_LOG("%s: INIT -> MASTER (preempting)\n",
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, MASTER);
|
|
|
|
carp_setroute(sc, RTM_ADD);
|
|
|
|
} else {
|
2009-12-02 13:24:21 +00:00
|
|
|
CARP_LOG("%s: INIT -> BACKUP\n", SC2IFP(sc)->if_xname);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, BACKUP);
|
|
|
|
carp_setroute(sc, RTM_DELETE);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BACKUP:
|
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
|
|
|
tv.tv_sec = 3 * sc->sc_advbase;
|
|
|
|
tv.tv_usec = sc->sc_advskew * 1000000 / 256;
|
|
|
|
switch (af) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
callout_reset(&sc->sc_md_tmo, tvtohz(&tv),
|
|
|
|
carp_master_down, sc);
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
callout_reset(&sc->sc_md6_tmo, tvtohz(&tv),
|
|
|
|
carp_master_down, sc);
|
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
|
|
if (sc->sc_naddrs)
|
|
|
|
callout_reset(&sc->sc_md_tmo, tvtohz(&tv),
|
|
|
|
carp_master_down, sc);
|
|
|
|
if (sc->sc_naddrs6)
|
|
|
|
callout_reset(&sc->sc_md6_tmo, tvtohz(&tv),
|
|
|
|
carp_master_down, sc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MASTER:
|
|
|
|
tv.tv_sec = sc->sc_advbase;
|
|
|
|
tv.tv_usec = sc->sc_advskew * 1000000 / 256;
|
|
|
|
callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
|
|
|
|
carp_send_ad, sc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-02 09:39:09 +00:00
|
|
|
static void
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast_cleanup(struct carp_softc *sc, int dofree)
|
2006-03-21 14:29:48 +00:00
|
|
|
{
|
|
|
|
struct ip_moptions *imo = &sc->sc_imo;
|
|
|
|
u_int16_t n = imo->imo_num_memberships;
|
2007-01-25 17:58:16 +00:00
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
/* Clean up our own multicast memberships */
|
|
|
|
while (n-- > 0) {
|
|
|
|
if (imo->imo_membership[n] != NULL) {
|
2010-11-24 05:24:36 +00:00
|
|
|
if (dofree)
|
|
|
|
in_delmulti(imo->imo_membership[n]);
|
2006-03-21 14:29:48 +00:00
|
|
|
imo->imo_membership[n] = NULL;
|
|
|
|
}
|
|
|
|
}
|
Import rewrite of IPv4 socket multicast layer to support source-specific
and protocol-independent host mode multicast. The code is written to
accomodate IPv6, IGMPv3 and MLDv2 with only a little additional work.
This change only pertains to FreeBSD's use as a multicast end-station and
does not concern multicast routing; for an IGMPv3/MLDv2 router
implementation, consider the XORP project.
The work is based on Wilbert de Graaf's IGMPv3 code drop for FreeBSD 4.6,
which is available at: http://www.kloosterhof.com/wilbert/igmpv3.html
Summary
* IPv4 multicast socket processing is now moved out of ip_output.c
into a new module, in_mcast.c.
* The in_mcast.c module implements the IPv4 legacy any-source API in
terms of the protocol-independent source-specific API.
* Source filters are lazy allocated as the common case does not use them.
They are part of per inpcb state and are covered by the inpcb lock.
* struct ip_mreqn is now supported to allow applications to specify
multicast joins by interface index in the legacy IPv4 any-source API.
* In UDP, an incoming multicast datagram only requires that the source
port matches the 4-tuple if the socket was already bound by source port.
An unbound socket SHOULD be able to receive multicasts sent from an
ephemeral source port.
* The UDP socket multicast filter mode defaults to exclusive, that is,
sources present in the per-socket list will be blocked from delivery.
* The RFC 3678 userland functions have been added to libc: setsourcefilter,
getsourcefilter, setipv4sourcefilter, getipv4sourcefilter.
* Definitions for IGMPv3 are merged but not yet used.
* struct sockaddr_storage is now referenced from <netinet/in.h>. It
is therefore defined there if not already declared in the same way
as for the C99 types.
* The RFC 1724 hack (specify 0.0.0.0/8 addresses to IP_MULTICAST_IF
which are then interpreted as interface indexes) is now deprecated.
* A patch for the Rhyolite.com routed in the FreeBSD base system
is available in the -net archives. This only affects individuals
running RIPv1 or RIPv2 via point-to-point and/or unnumbered interfaces.
* Make IPv6 detach path similar to IPv4's in code flow; functionally same.
* Bump __FreeBSD_version to 700048; see UPDATING.
This work was financially supported by another FreeBSD committer.
Obtained from: p4://bms_netdev
Submitted by: Wilbert de Graaf (original work)
Reviewed by: rwatson (locking), silence from fenner,
net@ (but with encouragement)
2007-06-12 16:24:56 +00:00
|
|
|
KASSERT(imo->imo_mfilters == NULL,
|
|
|
|
("%s: imo_mfilters != NULL", __func__));
|
2006-03-21 14:29:48 +00:00
|
|
|
imo->imo_num_memberships = 0;
|
|
|
|
imo->imo_multicast_ifp = NULL;
|
2007-02-02 09:39:09 +00:00
|
|
|
}
|
2006-03-21 14:29:48 +00:00
|
|
|
|
|
|
|
#ifdef INET6
|
2007-02-02 09:39:09 +00:00
|
|
|
static void
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast6_cleanup(struct carp_softc *sc, int dofree)
|
2007-02-02 09:39:09 +00:00
|
|
|
{
|
|
|
|
struct ip6_moptions *im6o = &sc->sc_im6o;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
u_int16_t n = im6o->im6o_num_memberships;
|
2007-02-02 09:39:09 +00:00
|
|
|
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
while (n-- > 0) {
|
|
|
|
if (im6o->im6o_membership[n] != NULL) {
|
2010-11-24 05:24:36 +00:00
|
|
|
if (dofree)
|
|
|
|
in6_mc_leave(im6o->im6o_membership[n], NULL);
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
im6o->im6o_membership[n] = NULL;
|
|
|
|
}
|
2006-03-21 14:29:48 +00:00
|
|
|
}
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
KASSERT(im6o->im6o_mfilters == NULL,
|
|
|
|
("%s: im6o_mfilters != NULL", __func__));
|
|
|
|
im6o->im6o_num_memberships = 0;
|
2006-03-21 14:29:48 +00:00
|
|
|
im6o->im6o_multicast_ifp = NULL;
|
|
|
|
}
|
2007-02-02 09:39:09 +00:00
|
|
|
#endif
|
2006-03-21 14:29:48 +00:00
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct carp_if *cif;
|
|
|
|
struct in_ifaddr *ia, *ia_if;
|
|
|
|
struct ip_moptions *imo = &sc->sc_imo;
|
|
|
|
struct in_addr addr;
|
|
|
|
u_long iaddr = htonl(sin->sin_addr.s_addr);
|
|
|
|
int own, error;
|
|
|
|
|
|
|
|
if (sin->sin_addr.s_addr == 0) {
|
2005-06-10 16:49:24 +00:00
|
|
|
if (!(SC2IFP(sc)->if_flags & IFF_UP))
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
if (sc->sc_naddrs)
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2008-07-14 20:11:51 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(sc, 0);
|
2008-07-14 20:11:51 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we have to do it by hands to check we won't match on us */
|
|
|
|
ia_if = NULL; own = 0;
|
2009-06-25 11:52:33 +00:00
|
|
|
IN_IFADDR_RLOCK();
|
Commit step 1 of the vimage project, (network stack)
virtualization work done by Marko Zec (zec@).
This is the first in a series of commits over the course
of the next few weeks.
Mark all uses of global variables to be virtualized
with a V_ prefix.
Use macros to map them back to their global names for
now, so this is a NOP change only.
We hope to have caught at least 85-90% of what is needed
so we do not invalidate a lot of outstanding patches again.
Obtained from: //depot/projects/vimage-commit2/...
Reviewed by: brooks, des, ed, mav, julian,
jamie, kris, rwatson, zec, ...
(various people I forgot, different versions)
md5 (with a bit of help)
Sponsored by: NLnet Foundation, The FreeBSD Foundation
X-MFC after: never
V_Commit_Message_Reviewed_By: more people than the patch
2008-08-17 23:27:27 +00:00
|
|
|
TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
|
2005-02-22 13:04:05 +00:00
|
|
|
/* and, yeah, we need a multicast-capable iface too */
|
2005-06-10 16:49:24 +00:00
|
|
|
if (ia->ia_ifp != SC2IFP(sc) &&
|
2005-02-22 13:04:05 +00:00
|
|
|
(ia->ia_ifp->if_flags & IFF_MULTICAST) &&
|
|
|
|
(iaddr & ia->ia_subnetmask) == ia->ia_subnet) {
|
|
|
|
if (!ia_if)
|
|
|
|
ia_if = ia;
|
|
|
|
if (sin->sin_addr.s_addr ==
|
|
|
|
ia->ia_addr.sin_addr.s_addr)
|
|
|
|
own++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-25 11:52:33 +00:00
|
|
|
if (!ia_if) {
|
|
|
|
IN_IFADDR_RUNLOCK();
|
2005-02-22 13:04:05 +00:00
|
|
|
return (EADDRNOTAVAIL);
|
2009-06-25 11:52:33 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
ia = ia_if;
|
2009-06-25 11:52:33 +00:00
|
|
|
ifa_ref(&ia->ia_ifa);
|
|
|
|
IN_IFADDR_RUNLOCK();
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
ifp = ia->ia_ifp;
|
|
|
|
|
|
|
|
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 ||
|
2009-06-25 11:52:33 +00:00
|
|
|
(imo->imo_multicast_ifp && imo->imo_multicast_ifp != ifp)) {
|
|
|
|
ifa_free(&ia->ia_ifa);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (EADDRNOTAVAIL);
|
2009-06-25 11:52:33 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (imo->imo_num_memberships == 0) {
|
|
|
|
addr.s_addr = htonl(INADDR_CARP_GROUP);
|
2009-06-25 11:52:33 +00:00
|
|
|
if ((imo->imo_membership[0] = in_addmulti(&addr, ifp)) ==
|
|
|
|
NULL) {
|
|
|
|
ifa_free(&ia->ia_ifa);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (ENOBUFS);
|
2009-06-25 11:52:33 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
imo->imo_num_memberships++;
|
|
|
|
imo->imo_multicast_ifp = ifp;
|
|
|
|
imo->imo_multicast_ttl = CARP_DFLTTL;
|
|
|
|
imo->imo_multicast_loop = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifp->if_carp) {
|
|
|
|
|
2008-10-23 15:53:51 +00:00
|
|
|
cif = malloc(sizeof(*cif), M_CARP,
|
2005-02-22 13:04:05 +00:00
|
|
|
M_WAITOK|M_ZERO);
|
|
|
|
if (!cif) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if ((error = ifpromisc(ifp, 1))) {
|
2008-10-23 15:53:51 +00:00
|
|
|
free(cif, M_CARP);
|
2005-02-22 13:04:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
CARP_LOCK_INIT(cif);
|
|
|
|
CARP_LOCK(cif);
|
|
|
|
cif->vhif_ifp = ifp;
|
|
|
|
TAILQ_INIT(&cif->vhif_vrs);
|
|
|
|
ifp->if_carp = cif;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
struct carp_softc *vr;
|
|
|
|
|
|
|
|
cif = (struct carp_if *)ifp->if_carp;
|
|
|
|
CARP_LOCK(cif);
|
|
|
|
TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
|
|
|
|
if (vr != sc && vr->sc_vhid == sc->sc_vhid) {
|
|
|
|
CARP_UNLOCK(cif);
|
2008-02-07 13:18:59 +00:00
|
|
|
error = EEXIST;
|
2005-02-22 13:04:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->sc_ia = ia;
|
2005-02-26 13:55:07 +00:00
|
|
|
sc->sc_carpdev = ifp;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
{ /* XXX prevent endless loop if already in queue */
|
|
|
|
struct carp_softc *vr, *after = NULL;
|
|
|
|
int myself = 0;
|
|
|
|
cif = (struct carp_if *)ifp->if_carp;
|
|
|
|
|
|
|
|
/* XXX: cif should not change, right? So we still hold the lock */
|
|
|
|
CARP_LOCK_ASSERT(cif);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
|
|
|
|
if (vr == sc)
|
|
|
|
myself = 1;
|
|
|
|
if (vr->sc_vhid < sc->sc_vhid)
|
|
|
|
after = vr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!myself) {
|
|
|
|
/* We're trying to keep things in order */
|
|
|
|
if (after == NULL) {
|
|
|
|
TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list);
|
|
|
|
} else {
|
|
|
|
TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list);
|
|
|
|
}
|
|
|
|
cif->vhif_nvrs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_naddrs++;
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
if (own)
|
|
|
|
sc->sc_advskew = 0;
|
2005-03-30 11:44:43 +00:00
|
|
|
carp_sc_state_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(sc, 0);
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(cif);
|
2009-06-25 11:52:33 +00:00
|
|
|
ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
|
2009-06-25 11:52:33 +00:00
|
|
|
ifa_free(&ia->ia_ifa);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_del_addr(struct carp_softc *sc, struct sockaddr_in *sin)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (!--sc->sc_naddrs) {
|
2005-02-26 13:55:07 +00:00
|
|
|
struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
struct ip_moptions *imo = &sc->sc_imo;
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_LOCK(cif);
|
2005-02-22 13:04:05 +00:00
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
2005-08-09 10:20:02 +00:00
|
|
|
SC2IFP(sc)->if_flags &= ~IFF_UP;
|
|
|
|
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
|
2005-02-22 13:04:05 +00:00
|
|
|
sc->sc_vhid = -1;
|
|
|
|
in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
|
|
|
|
imo->imo_multicast_ifp = NULL;
|
|
|
|
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
|
|
|
|
if (!--cif->vhif_nvrs) {
|
2005-02-26 13:55:07 +00:00
|
|
|
sc->sc_carpdev->if_carp = NULL;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK_DESTROY(cif);
|
2009-08-20 02:33:12 +00:00
|
|
|
free(cif, M_CARP);
|
2005-02-22 13:04:05 +00:00
|
|
|
} else {
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef INET6
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct carp_if *cif;
|
|
|
|
struct in6_ifaddr *ia, *ia_if;
|
|
|
|
struct ip6_moptions *im6o = &sc->sc_im6o;
|
2005-07-25 12:31:43 +00:00
|
|
|
struct in6_addr in6;
|
2005-02-22 13:04:05 +00:00
|
|
|
int own, error;
|
|
|
|
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
error = 0;
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
if (!(SC2IFP(sc)->if_flags & IFF_UP))
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
if (sc->sc_naddrs6)
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2008-07-14 20:11:51 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(sc, 0);
|
2008-07-14 20:11:51 +00:00
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCUNLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we have to do it by hands to check we won't match on us */
|
|
|
|
ia_if = NULL; own = 0;
|
2009-06-25 16:35:28 +00:00
|
|
|
IN6_IFADDR_RLOCK();
|
2009-06-24 21:34:38 +00:00
|
|
|
TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
|
2005-02-22 13:04:05 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if ((sin6->sin6_addr.s6_addr32[i] &
|
|
|
|
ia->ia_prefixmask.sin6_addr.s6_addr32[i]) !=
|
|
|
|
(ia->ia_addr.sin6_addr.s6_addr32[i] &
|
|
|
|
ia->ia_prefixmask.sin6_addr.s6_addr32[i]))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* and, yeah, we need a multicast-capable iface too */
|
2005-06-10 16:49:24 +00:00
|
|
|
if (ia->ia_ifp != SC2IFP(sc) &&
|
2005-02-22 13:04:05 +00:00
|
|
|
(ia->ia_ifp->if_flags & IFF_MULTICAST) &&
|
|
|
|
(i == 4)) {
|
|
|
|
if (!ia_if)
|
|
|
|
ia_if = ia;
|
|
|
|
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
|
|
|
|
&ia->ia_addr.sin6_addr))
|
|
|
|
own++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-25 16:35:28 +00:00
|
|
|
if (!ia_if) {
|
|
|
|
IN6_IFADDR_RUNLOCK();
|
2005-02-22 13:04:05 +00:00
|
|
|
return (EADDRNOTAVAIL);
|
2009-06-25 16:35:28 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
ia = ia_if;
|
2009-06-25 16:35:28 +00:00
|
|
|
ifa_ref(&ia->ia_ifa);
|
|
|
|
IN6_IFADDR_RUNLOCK();
|
2005-02-22 13:04:05 +00:00
|
|
|
ifp = ia->ia_ifp;
|
|
|
|
|
|
|
|
if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0 ||
|
2009-06-25 16:35:28 +00:00
|
|
|
(im6o->im6o_multicast_ifp && im6o->im6o_multicast_ifp != ifp)) {
|
|
|
|
ifa_free(&ia->ia_ifa);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (EADDRNOTAVAIL);
|
2009-06-25 16:35:28 +00:00
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
if (!sc->sc_naddrs6) {
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
struct in6_multi *in6m;
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
im6o->im6o_multicast_ifp = ifp;
|
|
|
|
|
|
|
|
/* join CARP multicast address */
|
2005-07-25 12:31:43 +00:00
|
|
|
bzero(&in6, sizeof(in6));
|
|
|
|
in6.s6_addr16[0] = htons(0xff02);
|
|
|
|
in6.s6_addr8[15] = 0x12;
|
|
|
|
if (in6_setscope(&in6, ifp, NULL) != 0)
|
|
|
|
goto cleanup;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
in6m = NULL;
|
|
|
|
error = in6_mc_join(ifp, &in6, NULL, &in6m, 0);
|
|
|
|
if (error)
|
2005-02-22 13:04:05 +00:00
|
|
|
goto cleanup;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
im6o->im6o_membership[0] = in6m;
|
|
|
|
im6o->im6o_num_memberships++;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
/* join solicited multicast address */
|
2005-07-25 12:31:43 +00:00
|
|
|
bzero(&in6, sizeof(in6));
|
|
|
|
in6.s6_addr16[0] = htons(0xff02);
|
|
|
|
in6.s6_addr32[1] = 0;
|
|
|
|
in6.s6_addr32[2] = htonl(1);
|
|
|
|
in6.s6_addr32[3] = sin6->sin6_addr.s6_addr32[3];
|
|
|
|
in6.s6_addr8[12] = 0xff;
|
|
|
|
if (in6_setscope(&in6, ifp, NULL) != 0)
|
|
|
|
goto cleanup;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
in6m = NULL;
|
|
|
|
error = in6_mc_join(ifp, &in6, NULL, &in6m, 0);
|
|
|
|
if (error)
|
2005-02-22 13:04:05 +00:00
|
|
|
goto cleanup;
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
im6o->im6o_membership[1] = in6m;
|
|
|
|
im6o->im6o_num_memberships++;
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifp->if_carp) {
|
2008-10-23 15:53:51 +00:00
|
|
|
cif = malloc(sizeof(*cif), M_CARP,
|
2005-02-22 13:04:05 +00:00
|
|
|
M_WAITOK|M_ZERO);
|
|
|
|
if (!cif) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if ((error = ifpromisc(ifp, 1))) {
|
2008-10-23 15:53:51 +00:00
|
|
|
free(cif, M_CARP);
|
2005-02-22 13:04:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
CARP_LOCK_INIT(cif);
|
|
|
|
CARP_LOCK(cif);
|
|
|
|
cif->vhif_ifp = ifp;
|
|
|
|
TAILQ_INIT(&cif->vhif_vrs);
|
|
|
|
ifp->if_carp = cif;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
struct carp_softc *vr;
|
|
|
|
|
|
|
|
cif = (struct carp_if *)ifp->if_carp;
|
|
|
|
CARP_LOCK(cif);
|
|
|
|
TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
|
|
|
|
if (vr != sc && vr->sc_vhid == sc->sc_vhid) {
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
error = EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sc->sc_ia6 = ia;
|
2005-02-26 13:55:07 +00:00
|
|
|
sc->sc_carpdev = ifp;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
{ /* XXX prevent endless loop if already in queue */
|
|
|
|
struct carp_softc *vr, *after = NULL;
|
|
|
|
int myself = 0;
|
|
|
|
cif = (struct carp_if *)ifp->if_carp;
|
|
|
|
CARP_LOCK_ASSERT(cif);
|
|
|
|
|
|
|
|
TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list) {
|
|
|
|
if (vr == sc)
|
|
|
|
myself = 1;
|
|
|
|
if (vr->sc_vhid < sc->sc_vhid)
|
|
|
|
after = vr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!myself) {
|
|
|
|
/* We're trying to keep things in order */
|
|
|
|
if (after == NULL) {
|
|
|
|
TAILQ_INSERT_TAIL(&cif->vhif_vrs, sc, sc_list);
|
|
|
|
} else {
|
|
|
|
TAILQ_INSERT_AFTER(&cif->vhif_vrs, after, sc, sc_list);
|
|
|
|
}
|
|
|
|
cif->vhif_nvrs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_naddrs6++;
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
if (own)
|
|
|
|
sc->sc_advskew = 0;
|
2005-03-30 11:44:43 +00:00
|
|
|
carp_sc_state_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(sc, 0);
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_UNLOCK(cif);
|
2009-06-25 16:35:28 +00:00
|
|
|
ifa_free(&ia->ia_ifa); /* XXXRW: should hold reference for softc. */
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
cleanup:
|
Bite the bullet, and make the IPv6 SSM and MLDv2 mega-commit:
import from p4 bms_netdev. Summary of changes:
* Connect netinet6/in6_mcast.c to build.
The legacy KAME KPIs are mostly preserved.
* Eliminate now dead code from ip6_output.c.
Don't do mbuf bingo, we are not going to do RFC 2292 style
CMSG tricks for multicast options as they are not required
by any current IPv6 normative reference.
* Refactor transports (UDP, raw_ip6) to do own mcast filtering.
SCTP, TCP unaffected by this change.
* Add ip6_msource, in6_msource structs to in6_var.h.
* Hookup mld_ifinfo state to in6_ifextra, allocate from
domifattach path.
* Eliminate IN6_LOOKUP_MULTI(), it is no longer referenced.
Kernel consumers which need this should use in6m_lookup().
* Refactor IPv6 socket group memberships to use a vector (like IPv4).
* Update ifmcstat(8) for IPv6 SSM.
* Add witness lock order for IN6_MULTI_LOCK.
* Move IN6_MULTI_LOCK out of lower ip6_output()/ip6_input() paths.
* Introduce IP6STAT_ADD/SUB/INC/DEC as per rwatson's IPv4 cleanup.
* Update carp(4) for new IPv6 SSM KPIs.
* Virtualize ip6_mrouter socket.
Changes mostly localized to IPv6 MROUTING.
* Don't do a local group lookup in MROUTING.
* Kill unused KAME prototypes in6_purgemkludge(), in6_restoremkludge().
* Preserve KAME DAD timer jitter behaviour in MLDv1 compatibility mode.
* Bump __FreeBSD_version to 800084.
* Update UPDATING.
NOTE WELL:
* This code hasn't been tested against real MLDv2 queriers
(yet), although the on-wire protocol has been verified in Wireshark.
* There are a few unresolved issues in the socket layer APIs to
do with scope ID propagation.
* There is a LOR present in ip6_output()'s use of
in6_setscope() which needs to be resolved. See comments in mld6.c.
This is believed to be benign and can't be avoided for the moment
without re-introducing an indirect netisr.
This work was mostly derived from the IGMPv3 implementation, and
has been sponsored by a third party.
2009-04-29 19:19:13 +00:00
|
|
|
if (!sc->sc_naddrs6)
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast6_cleanup(sc, 1);
|
2009-06-25 16:35:28 +00:00
|
|
|
ifa_free(&ia->ia_ifa);
|
2005-02-22 13:04:05 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_del_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (!--sc->sc_naddrs6) {
|
2005-02-26 13:55:07 +00:00
|
|
|
struct carp_if *cif = (struct carp_if *)sc->sc_carpdev->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
CARP_LOCK(cif);
|
2005-02-22 13:04:05 +00:00
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
2005-08-09 10:20:02 +00:00
|
|
|
SC2IFP(sc)->if_flags &= ~IFF_UP;
|
|
|
|
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
|
2005-02-22 13:04:05 +00:00
|
|
|
sc->sc_vhid = -1;
|
2010-11-24 05:24:36 +00:00
|
|
|
carp_multicast6_cleanup(sc, 1);
|
2005-02-22 13:04:05 +00:00
|
|
|
TAILQ_REMOVE(&cif->vhif_vrs, sc, sc_list);
|
|
|
|
if (!--cif->vhif_nvrs) {
|
|
|
|
CARP_LOCK_DESTROY(cif);
|
2005-02-26 13:55:07 +00:00
|
|
|
sc->sc_carpdev->if_carp = NULL;
|
2009-08-20 02:33:12 +00:00
|
|
|
free(cif, M_CARP);
|
2005-02-22 13:04:05 +00:00
|
|
|
} else
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
#endif /* INET6 */
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static int
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
|
|
|
|
{
|
|
|
|
struct carp_softc *sc = ifp->if_softc, *vr;
|
|
|
|
struct carpreq carpr;
|
|
|
|
struct ifaddr *ifa;
|
|
|
|
struct ifreq *ifr;
|
|
|
|
struct ifaliasreq *ifra;
|
2005-03-01 13:14:33 +00:00
|
|
|
int locked = 0, error = 0;
|
2005-02-22 13:04:05 +00:00
|
|
|
|
|
|
|
ifa = (struct ifaddr *)addr;
|
|
|
|
ifra = (struct ifaliasreq *)addr;
|
|
|
|
ifr = (struct ifreq *)addr;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCSIFADDR:
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
bcopy(ifa->ifa_addr, ifa->ifa_dstaddr,
|
|
|
|
sizeof(struct sockaddr));
|
|
|
|
error = carp_set_addr(sc, satosin(ifa->ifa_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
error = carp_set_addr6(sc, satosin6(ifa->ifa_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
|
|
error = EAFNOSUPPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCAIFADDR:
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
bcopy(ifa->ifa_addr, ifa->ifa_dstaddr,
|
|
|
|
sizeof(struct sockaddr));
|
|
|
|
error = carp_set_addr(sc, satosin(&ifra->ifra_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
error = carp_set_addr6(sc, satosin6(&ifra->ifra_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
|
|
error = EAFNOSUPPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCDIFADDR:
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
error = carp_del_addr(sc, satosin(&ifra->ifra_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
error = carp_del_addr6(sc, satosin6(&ifra->ifra_addr));
|
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
|
|
error = EAFNOSUPPORT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSIFFLAGS:
|
2005-03-01 13:14:33 +00:00
|
|
|
if (sc->sc_carpdev) {
|
|
|
|
locked = 1;
|
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
if (sc->sc_state != INIT && !(ifr->ifr_flags & IFF_UP)) {
|
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
|
|
|
callout_stop(&sc->sc_md_tmo);
|
|
|
|
callout_stop(&sc->sc_md6_tmo);
|
|
|
|
if (sc->sc_state == MASTER)
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_send_ad_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
} else if (sc->sc_state == INIT && (ifr->ifr_flags & IFF_UP)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= IFF_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_setrun(sc, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSVH:
|
2006-11-06 13:42:10 +00:00
|
|
|
error = priv_check(curthread, PRIV_NETINET_CARP);
|
|
|
|
if (error)
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
if ((error = copyin(ifr->ifr_data, &carpr, sizeof carpr)))
|
|
|
|
break;
|
|
|
|
error = 1;
|
2005-03-01 13:14:33 +00:00
|
|
|
if (sc->sc_carpdev) {
|
|
|
|
locked = 1;
|
|
|
|
CARP_SCLOCK(sc);
|
|
|
|
}
|
2005-02-22 13:04:05 +00:00
|
|
|
if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) {
|
|
|
|
switch (carpr.carpr_state) {
|
|
|
|
case BACKUP:
|
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
|
|
|
carp_set_state(sc, BACKUP);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
carp_setroute(sc, RTM_DELETE);
|
|
|
|
break;
|
|
|
|
case MASTER:
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_master_down_locked(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (carpr.carpr_vhid > 0) {
|
|
|
|
if (carpr.carpr_vhid > 255) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2005-02-26 13:55:07 +00:00
|
|
|
if (sc->sc_carpdev) {
|
2005-02-22 13:04:05 +00:00
|
|
|
struct carp_if *cif;
|
2005-02-26 13:55:07 +00:00
|
|
|
cif = (struct carp_if *)sc->sc_carpdev->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
TAILQ_FOREACH(vr, &cif->vhif_vrs, sc_list)
|
|
|
|
if (vr != sc &&
|
2007-06-06 14:21:49 +00:00
|
|
|
vr->sc_vhid == carpr.carpr_vhid) {
|
|
|
|
error = EEXIST;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (error == EEXIST)
|
|
|
|
break;
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
sc->sc_vhid = carpr.carpr_vhid;
|
2005-11-11 16:04:59 +00:00
|
|
|
IF_LLADDR(sc->sc_ifp)[0] = 0;
|
|
|
|
IF_LLADDR(sc->sc_ifp)[1] = 0;
|
|
|
|
IF_LLADDR(sc->sc_ifp)[2] = 0x5e;
|
|
|
|
IF_LLADDR(sc->sc_ifp)[3] = 0;
|
|
|
|
IF_LLADDR(sc->sc_ifp)[4] = 1;
|
|
|
|
IF_LLADDR(sc->sc_ifp)[5] = sc->sc_vhid;
|
2005-02-22 13:04:05 +00:00
|
|
|
error--;
|
|
|
|
}
|
|
|
|
if (carpr.carpr_advbase > 0 || carpr.carpr_advskew > 0) {
|
|
|
|
if (carpr.carpr_advskew >= 255) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (carpr.carpr_advbase > 255) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sc->sc_advbase = carpr.carpr_advbase;
|
|
|
|
sc->sc_advskew = carpr.carpr_advskew;
|
|
|
|
error--;
|
|
|
|
}
|
|
|
|
bcopy(carpr.carpr_key, sc->sc_key, sizeof(sc->sc_key));
|
|
|
|
if (error > 0)
|
|
|
|
error = EINVAL;
|
|
|
|
else {
|
|
|
|
error = 0;
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCGVH:
|
2005-03-01 13:14:33 +00:00
|
|
|
/* XXX: lockless read */
|
2005-02-22 13:04:05 +00:00
|
|
|
bzero(&carpr, sizeof(carpr));
|
|
|
|
carpr.carpr_state = sc->sc_state;
|
|
|
|
carpr.carpr_vhid = sc->sc_vhid;
|
|
|
|
carpr.carpr_advbase = sc->sc_advbase;
|
|
|
|
carpr.carpr_advskew = sc->sc_advskew;
|
2006-11-06 13:42:10 +00:00
|
|
|
error = priv_check(curthread, PRIV_NETINET_CARP);
|
|
|
|
if (error == 0)
|
2005-02-22 13:04:05 +00:00
|
|
|
bcopy(sc->sc_key, carpr.carpr_key,
|
|
|
|
sizeof(carpr.carpr_key));
|
|
|
|
error = copyout(&carpr, ifr->ifr_data, sizeof(carpr));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
|
|
|
|
2005-03-01 13:14:33 +00:00
|
|
|
if (locked)
|
|
|
|
CARP_SCUNLOCK(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_hmac_prepare(sc);
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: this is looutput. We should eventually use it from there.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
carp_looutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
|
2009-04-16 20:30:28 +00:00
|
|
|
struct route *ro)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
2005-06-26 18:11:11 +00:00
|
|
|
u_int32_t af;
|
2009-04-16 20:30:28 +00:00
|
|
|
struct rtentry *rt = NULL;
|
2005-06-26 18:11:11 +00:00
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
M_ASSERTPKTHDR(m); /* check if we have the packet header */
|
|
|
|
|
2009-04-16 20:30:28 +00:00
|
|
|
if (ro != NULL)
|
|
|
|
rt = ro->ro_rt;
|
2005-02-22 13:04:05 +00:00
|
|
|
if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
|
|
|
|
m_freem(m);
|
|
|
|
return (rt->rt_flags & RTF_BLACKHOLE ? 0 :
|
|
|
|
rt->rt_flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
|
|
|
|
}
|
|
|
|
|
|
|
|
ifp->if_opackets++;
|
|
|
|
ifp->if_obytes += m->m_pkthdr.len;
|
2005-06-26 18:11:11 +00:00
|
|
|
|
|
|
|
/* BPF writes need to be handled specially. */
|
|
|
|
if (dst->sa_family == AF_UNSPEC) {
|
|
|
|
bcopy(dst->sa_data, &af, sizeof(af));
|
|
|
|
dst->sa_family = af;
|
|
|
|
}
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
#if 1 /* XXX */
|
|
|
|
switch (dst->sa_family) {
|
|
|
|
case AF_INET:
|
|
|
|
case AF_INET6:
|
|
|
|
case AF_IPX:
|
|
|
|
case AF_APPLETALK:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("carp_looutput: af=%d unexpected\n", dst->sa_family);
|
|
|
|
m_freem(m);
|
|
|
|
return (EAFNOSUPPORT);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return(if_simloop(ifp, m, dst->sa_family, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start output on carp interface. This function should never be called.
|
|
|
|
*/
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_start(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf("%s: start called\n", ifp->if_xname);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
carp_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
|
|
|
|
struct rtentry *rt)
|
|
|
|
{
|
|
|
|
struct m_tag *mtag;
|
|
|
|
struct carp_softc *sc;
|
|
|
|
struct ifnet *carp_ifp;
|
|
|
|
|
|
|
|
if (!sa)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
switch (sa->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
#ifdef INET6
|
|
|
|
case AF_INET6:
|
|
|
|
break;
|
|
|
|
#endif /* INET6 */
|
|
|
|
default:
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
mtag = m_tag_find(m, PACKET_TAG_CARP, NULL);
|
|
|
|
if (mtag == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bcopy(mtag + 1, &carp_ifp, sizeof(struct ifnet *));
|
|
|
|
sc = carp_ifp->if_softc;
|
|
|
|
|
|
|
|
/* Set the source MAC address to Virtual Router MAC Address */
|
|
|
|
switch (ifp->if_type) {
|
2005-02-28 16:19:11 +00:00
|
|
|
case IFT_ETHER:
|
|
|
|
case IFT_L2VLAN: {
|
2005-02-22 13:04:05 +00:00
|
|
|
struct ether_header *eh;
|
|
|
|
|
|
|
|
eh = mtod(m, struct ether_header *);
|
|
|
|
eh->ether_shost[0] = 0;
|
|
|
|
eh->ether_shost[1] = 0;
|
|
|
|
eh->ether_shost[2] = 0x5e;
|
|
|
|
eh->ether_shost[3] = 0;
|
|
|
|
eh->ether_shost[4] = 1;
|
|
|
|
eh->ether_shost[5] = sc->sc_vhid;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IFT_FDDI: {
|
|
|
|
struct fddi_header *fh;
|
|
|
|
|
|
|
|
fh = mtod(m, struct fddi_header *);
|
|
|
|
fh->fddi_shost[0] = 0;
|
|
|
|
fh->fddi_shost[1] = 0;
|
|
|
|
fh->fddi_shost[2] = 0x5e;
|
|
|
|
fh->fddi_shost[3] = 0;
|
|
|
|
fh->fddi_shost[4] = 1;
|
|
|
|
fh->fddi_shost[5] = sc->sc_vhid;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IFT_ISO88025: {
|
|
|
|
struct iso88025_header *th;
|
|
|
|
th = mtod(m, struct iso88025_header *);
|
|
|
|
th->iso88025_shost[0] = 3;
|
|
|
|
th->iso88025_shost[1] = 0;
|
|
|
|
th->iso88025_shost[2] = 0x40 >> (sc->sc_vhid - 1);
|
|
|
|
th->iso88025_shost[3] = 0x40000 >> (sc->sc_vhid - 1);
|
|
|
|
th->iso88025_shost[4] = 0;
|
|
|
|
th->iso88025_shost[5] = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: carp is not supported for this interface type\n",
|
|
|
|
ifp->if_xname);
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-02-26 10:33:14 +00:00
|
|
|
static void
|
2005-02-22 13:04:05 +00:00
|
|
|
carp_set_state(struct carp_softc *sc, int state)
|
|
|
|
{
|
2008-12-05 14:37:14 +00:00
|
|
|
int link_state;
|
2005-03-01 13:14:33 +00:00
|
|
|
|
|
|
|
if (sc->sc_carpdev)
|
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
if (sc->sc_state == state)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sc->sc_state = state;
|
|
|
|
switch (state) {
|
|
|
|
case BACKUP:
|
2008-12-05 14:37:14 +00:00
|
|
|
link_state = LINK_STATE_DOWN;
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
case MASTER:
|
2008-12-05 14:37:14 +00:00
|
|
|
link_state = LINK_STATE_UP;
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-12-05 14:37:14 +00:00
|
|
|
link_state = LINK_STATE_UNKNOWN;
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-12-05 14:37:14 +00:00
|
|
|
if_link_state_change(SC2IFP(sc), link_state);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-11 00:51:50 +00:00
|
|
|
carp_carpdev_state(struct ifnet *ifp)
|
2005-02-22 13:04:05 +00:00
|
|
|
{
|
2010-08-11 00:51:50 +00:00
|
|
|
struct carp_if *cif;
|
2005-03-01 13:14:33 +00:00
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
cif = ifp->if_carp;
|
2005-02-22 13:04:05 +00:00
|
|
|
CARP_LOCK(cif);
|
2005-03-01 13:14:33 +00:00
|
|
|
carp_carpdev_state_locked(cif);
|
|
|
|
CARP_UNLOCK(cif);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
carp_carpdev_state_locked(struct carp_if *cif)
|
|
|
|
{
|
|
|
|
struct carp_softc *sc;
|
|
|
|
|
2005-03-30 11:44:43 +00:00
|
|
|
TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list)
|
|
|
|
carp_sc_state_locked(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
carp_sc_state_locked(struct carp_softc *sc)
|
|
|
|
{
|
|
|
|
CARP_SCLOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
if (sc->sc_carpdev->if_link_state != LINK_STATE_UP ||
|
|
|
|
!(sc->sc_carpdev->if_flags & IFF_UP)) {
|
2005-06-10 16:49:24 +00:00
|
|
|
sc->sc_flags_backup = SC2IFP(sc)->if_flags;
|
2005-08-09 10:20:02 +00:00
|
|
|
SC2IFP(sc)->if_flags &= ~IFF_UP;
|
|
|
|
SC2IFP(sc)->if_drv_flags &= ~IFF_DRV_RUNNING;
|
2005-03-30 11:44:43 +00:00
|
|
|
callout_stop(&sc->sc_ad_tmo);
|
|
|
|
callout_stop(&sc->sc_md_tmo);
|
|
|
|
callout_stop(&sc->sc_md6_tmo);
|
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
if (!sc->sc_suppress) {
|
|
|
|
carp_suppress_preempt++;
|
|
|
|
if (carp_suppress_preempt == 1) {
|
|
|
|
CARP_SCUNLOCK(sc);
|
|
|
|
carp_send_ad_all();
|
|
|
|
CARP_SCLOCK(sc);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
}
|
2005-03-30 11:44:43 +00:00
|
|
|
sc->sc_suppress = 1;
|
|
|
|
} else {
|
2005-06-10 16:49:24 +00:00
|
|
|
SC2IFP(sc)->if_flags |= sc->sc_flags_backup;
|
2005-03-30 11:44:43 +00:00
|
|
|
carp_set_state(sc, INIT);
|
|
|
|
carp_setrun(sc, 0);
|
|
|
|
if (sc->sc_suppress)
|
|
|
|
carp_suppress_preempt--;
|
|
|
|
sc->sc_suppress = 0;
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
2005-03-30 11:44:43 +00:00
|
|
|
|
|
|
|
return;
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
2010-08-11 00:51:50 +00:00
|
|
|
#ifdef INET
|
|
|
|
extern struct domain inetdomain;
|
|
|
|
static struct protosw in_carp_protosw = {
|
|
|
|
.pr_type = SOCK_RAW,
|
|
|
|
.pr_domain = &inetdomain,
|
|
|
|
.pr_protocol = IPPROTO_CARP,
|
|
|
|
.pr_flags = PR_ATOMIC|PR_ADDR,
|
|
|
|
.pr_input = carp_input,
|
|
|
|
.pr_output = (pr_output_t *)rip_output,
|
|
|
|
.pr_ctloutput = rip_ctloutput,
|
|
|
|
.pr_usrreqs = &rip_usrreqs
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef INET6
|
|
|
|
extern struct domain inet6domain;
|
|
|
|
static struct ip6protosw in6_carp_protosw = {
|
|
|
|
.pr_type = SOCK_RAW,
|
|
|
|
.pr_domain = &inet6domain,
|
|
|
|
.pr_protocol = IPPROTO_CARP,
|
|
|
|
.pr_flags = PR_ATOMIC|PR_ADDR,
|
|
|
|
.pr_input = carp6_input,
|
|
|
|
.pr_output = rip6_output,
|
|
|
|
.pr_ctloutput = rip6_ctloutput,
|
|
|
|
.pr_usrreqs = &rip6_usrreqs
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
carp_mod_cleanup(void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (if_detach_event_tag == NULL)
|
|
|
|
return;
|
|
|
|
EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag);
|
|
|
|
if_clone_detach(&carp_cloner);
|
|
|
|
#ifdef INET
|
|
|
|
if (proto_reg[CARP_INET] == 0) {
|
2010-09-06 21:06:06 +00:00
|
|
|
(void)ipproto_unregister(IPPROTO_CARP);
|
2010-08-11 00:51:50 +00:00
|
|
|
pf_proto_unregister(PF_INET, IPPROTO_CARP, SOCK_RAW);
|
|
|
|
proto_reg[CARP_INET] = -1;
|
|
|
|
}
|
|
|
|
carp_iamatch_p = NULL;
|
|
|
|
#endif
|
|
|
|
#ifdef INET6
|
|
|
|
if (proto_reg[CARP_INET6] == 0) {
|
2010-09-06 21:06:06 +00:00
|
|
|
(void)ip6proto_unregister(IPPROTO_CARP);
|
2010-08-11 00:51:50 +00:00
|
|
|
pf_proto_unregister(PF_INET6, IPPROTO_CARP, SOCK_RAW);
|
|
|
|
proto_reg[CARP_INET6] = -1;
|
|
|
|
}
|
|
|
|
carp_iamatch6_p = NULL;
|
|
|
|
carp_macmatch6_p = NULL;
|
|
|
|
#endif
|
|
|
|
carp_linkstate_p = NULL;
|
|
|
|
carp_forus_p = NULL;
|
|
|
|
carp_output_p = NULL;
|
|
|
|
mtx_destroy(&carp_mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
carp_mod_load(void)
|
|
|
|
{
|
2010-09-06 21:06:06 +00:00
|
|
|
int err;
|
2010-08-11 00:51:50 +00:00
|
|
|
|
|
|
|
if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event,
|
|
|
|
carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY);
|
|
|
|
if (if_detach_event_tag == NULL)
|
|
|
|
return (ENOMEM);
|
|
|
|
mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF);
|
|
|
|
LIST_INIT(&carpif_list);
|
|
|
|
if_clone_attach(&carp_cloner);
|
|
|
|
carp_linkstate_p = carp_carpdev_state;
|
|
|
|
carp_forus_p = carp_forus;
|
|
|
|
carp_output_p = carp_output;
|
|
|
|
#ifdef INET6
|
|
|
|
carp_iamatch6_p = carp_iamatch6;
|
|
|
|
carp_macmatch6_p = carp_macmatch6;
|
|
|
|
proto_reg[CARP_INET6] = pf_proto_register(PF_INET6,
|
|
|
|
(struct protosw *)&in6_carp_protosw);
|
|
|
|
if (proto_reg[CARP_INET6] != 0) {
|
|
|
|
printf("carp: error %d attaching to PF_INET6\n",
|
|
|
|
proto_reg[CARP_INET6]);
|
|
|
|
carp_mod_cleanup();
|
2010-09-20 12:23:10 +00:00
|
|
|
return (proto_reg[CARP_INET6]);
|
2010-08-11 00:51:50 +00:00
|
|
|
}
|
2010-09-06 21:06:06 +00:00
|
|
|
err = ip6proto_register(IPPROTO_CARP);
|
|
|
|
if (err) {
|
|
|
|
printf("carp: error %d registering with INET6\n", err);
|
|
|
|
carp_mod_cleanup();
|
2010-09-20 12:23:10 +00:00
|
|
|
return (err);
|
2010-09-06 21:06:06 +00:00
|
|
|
}
|
2010-08-11 00:51:50 +00:00
|
|
|
#endif
|
|
|
|
#ifdef INET
|
|
|
|
carp_iamatch_p = carp_iamatch;
|
|
|
|
proto_reg[CARP_INET] = pf_proto_register(PF_INET, &in_carp_protosw);
|
|
|
|
if (proto_reg[CARP_INET] != 0) {
|
|
|
|
printf("carp: error %d attaching to PF_INET\n",
|
|
|
|
proto_reg[CARP_INET]);
|
|
|
|
carp_mod_cleanup();
|
2010-09-20 12:23:10 +00:00
|
|
|
return (proto_reg[CARP_INET]);
|
2010-08-11 00:51:50 +00:00
|
|
|
}
|
2010-09-06 21:06:06 +00:00
|
|
|
err = ipproto_register(IPPROTO_CARP);
|
|
|
|
if (err) {
|
|
|
|
printf("carp: error %d registering with INET\n", err);
|
|
|
|
carp_mod_cleanup();
|
2010-09-20 12:23:10 +00:00
|
|
|
return (err);
|
2010-09-06 21:06:06 +00:00
|
|
|
}
|
2010-08-11 00:51:50 +00:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-02-22 13:04:05 +00:00
|
|
|
static int
|
|
|
|
carp_modevent(module_t mod, int type, void *data)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case MOD_LOAD:
|
2010-08-11 00:51:50 +00:00
|
|
|
return carp_mod_load();
|
|
|
|
/* NOTREACHED */
|
2005-02-22 13:04:05 +00:00
|
|
|
case MOD_UNLOAD:
|
2010-08-11 00:51:50 +00:00
|
|
|
/*
|
|
|
|
* XXX: For now, disallow module unloading by default due to
|
|
|
|
* a race condition where a thread may dereference one of the
|
|
|
|
* function pointer hooks after the module has been
|
|
|
|
* unloaded, during processing of a packet, causing a panic.
|
|
|
|
*/
|
|
|
|
#ifdef CARPMOD_CAN_UNLOAD
|
|
|
|
carp_mod_cleanup();
|
|
|
|
#else
|
|
|
|
return (EBUSY);
|
|
|
|
#endif
|
2005-02-22 13:04:05 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2006-03-21 14:29:48 +00:00
|
|
|
return (EINVAL);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 14:29:48 +00:00
|
|
|
return (0);
|
2005-02-22 13:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static moduledata_t carp_mod = {
|
|
|
|
"carp",
|
|
|
|
carp_modevent,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2010-09-06 21:03:30 +00:00
|
|
|
DECLARE_MODULE(carp, carp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
|