xref: /freebsd/sys/netinet/ip_carp.c (revision a254d6870e88d4194390d822ab6a2dff59a83892)
108b68b0eSGleb Smirnoff /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
408b68b0eSGleb Smirnoff  * Copyright (c) 2002 Michael Shalayeff.
508b68b0eSGleb Smirnoff  * Copyright (c) 2003 Ryan McBride.
608b68b0eSGleb Smirnoff  * Copyright (c) 2011 Gleb Smirnoff <glebius@FreeBSD.org>
708b68b0eSGleb Smirnoff  * All rights reserved.
8a9771948SGleb Smirnoff  *
9a9771948SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
10a9771948SGleb Smirnoff  * modification, are permitted provided that the following conditions
11a9771948SGleb Smirnoff  * are met:
12a9771948SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
13a9771948SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
14a9771948SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
15a9771948SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
16a9771948SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
17a9771948SGleb Smirnoff  *
18a9771948SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19a9771948SGleb Smirnoff  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20a9771948SGleb Smirnoff  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21a9771948SGleb Smirnoff  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
22a9771948SGleb Smirnoff  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23a9771948SGleb Smirnoff  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24a9771948SGleb Smirnoff  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25a9771948SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26a9771948SGleb Smirnoff  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27a9771948SGleb Smirnoff  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28a9771948SGleb Smirnoff  * THE POSSIBILITY OF SUCH DAMAGE.
29a9771948SGleb Smirnoff  */
30a9771948SGleb Smirnoff 
314b421e2dSMike Silbersack #include <sys/cdefs.h>
32a9771948SGleb Smirnoff #include "opt_bpf.h"
33a9771948SGleb Smirnoff #include "opt_inet.h"
34a9771948SGleb Smirnoff #include "opt_inet6.h"
35a9771948SGleb Smirnoff 
36a9771948SGleb Smirnoff #include <sys/param.h>
37a9771948SGleb Smirnoff #include <sys/systm.h>
38773e541eSWarner Losh #include <sys/devctl.h>
3908b68b0eSGleb Smirnoff #include <sys/jail.h>
4037115154SKristof Provost #include <sys/kassert.h>
41a9771948SGleb Smirnoff #include <sys/kernel.h>
42a9771948SGleb Smirnoff #include <sys/limits.h>
43a9771948SGleb Smirnoff #include <sys/malloc.h>
44a9771948SGleb Smirnoff #include <sys/mbuf.h>
45a9771948SGleb Smirnoff #include <sys/module.h>
46acd3428bSRobert Watson #include <sys/priv.h>
47a9771948SGleb Smirnoff #include <sys/proc.h>
4808b68b0eSGleb Smirnoff #include <sys/socket.h>
4908b68b0eSGleb Smirnoff #include <sys/sockio.h>
50a9771948SGleb Smirnoff #include <sys/sysctl.h>
51a9771948SGleb Smirnoff #include <sys/syslog.h>
52f08535f8SGleb Smirnoff #include <sys/taskqueue.h>
5369edf037SAndrey V. Elsukov #include <sys/counter.h>
54a9771948SGleb Smirnoff 
55a9771948SGleb Smirnoff #include <net/ethernet.h>
56a9771948SGleb Smirnoff #include <net/if.h>
5776039bc8SGleb Smirnoff #include <net/if_var.h>
58433aaf04SRuslan Ermilov #include <net/if_dl.h>
5908b68b0eSGleb Smirnoff #include <net/if_llatbl.h>
603d0d5b21SJustin Hibbits #include <net/if_private.h>
61a9771948SGleb Smirnoff #include <net/if_types.h>
62a9771948SGleb Smirnoff #include <net/route.h>
63530c0060SRobert Watson #include <net/vnet.h>
64a9771948SGleb Smirnoff 
65a0ae8f04SBjoern A. Zeeb #if defined(INET) || defined(INET6)
66a9771948SGleb Smirnoff #include <netinet/in.h>
67a9771948SGleb Smirnoff #include <netinet/in_var.h>
68a0ae8f04SBjoern A. Zeeb #include <netinet/ip_carp.h>
6940e04359SKristof Provost #include <netinet/ip_carp_nl.h>
70a9771948SGleb Smirnoff #include <netinet/ip.h>
71a0ae8f04SBjoern A. Zeeb #include <machine/in_cksum.h>
72a0ae8f04SBjoern A. Zeeb #endif
73a0ae8f04SBjoern A. Zeeb #ifdef INET
74a9771948SGleb Smirnoff #include <netinet/ip_var.h>
75a9771948SGleb Smirnoff #include <netinet/if_ether.h>
76a9771948SGleb Smirnoff #endif
77a9771948SGleb Smirnoff 
78a9771948SGleb Smirnoff #ifdef INET6
79a9771948SGleb Smirnoff #include <netinet/icmp6.h>
80a9771948SGleb Smirnoff #include <netinet/ip6.h>
8108b68b0eSGleb Smirnoff #include <netinet6/in6_var.h>
82a9771948SGleb Smirnoff #include <netinet6/ip6_var.h>
8329da8af6SHajimu UMEMOTO #include <netinet6/scope6_var.h>
84a9771948SGleb Smirnoff #include <netinet6/nd6.h>
85a9771948SGleb Smirnoff #endif
86a9771948SGleb Smirnoff 
8740e04359SKristof Provost #include <netlink/netlink.h>
8840e04359SKristof Provost #include <netlink/netlink_ctl.h>
8940e04359SKristof Provost #include <netlink/netlink_generic.h>
9040e04359SKristof Provost #include <netlink/netlink_message_parser.h>
9140e04359SKristof Provost 
92a9771948SGleb Smirnoff #include <crypto/sha1.h>
93a9771948SGleb Smirnoff 
9408b68b0eSGleb Smirnoff static MALLOC_DEFINE(M_CARP, "CARP", "CARP addresses");
95a9771948SGleb Smirnoff 
96a9771948SGleb Smirnoff struct carp_softc {
9708b68b0eSGleb Smirnoff 	struct ifnet		*sc_carpdev;	/* Pointer to parent ifnet. */
9808b68b0eSGleb Smirnoff 	struct ifaddr		**sc_ifas;	/* Our ifaddrs. */
9937115154SKristof Provost 	carp_version_t		sc_version;	/* carp or VRRPv3 */
100*a254d687SGleb Smirnoff 	uint8_t			sc_addr[ETHER_ADDR_LEN];	/* Our link level address. */
10108b68b0eSGleb Smirnoff 	struct callout		sc_ad_tmo;	/* Advertising timeout. */
102a0ae8f04SBjoern A. Zeeb #ifdef INET
10308b68b0eSGleb Smirnoff 	struct callout		sc_md_tmo;	/* Master down timeout. */
104a0ae8f04SBjoern A. Zeeb #endif
105a9771948SGleb Smirnoff #ifdef INET6
10608b68b0eSGleb Smirnoff 	struct callout 		sc_md6_tmo;	/* XXX: Master down timeout. */
10708b68b0eSGleb Smirnoff #endif
10808b68b0eSGleb Smirnoff 	struct mtx		sc_mtx;
109a9771948SGleb Smirnoff 
11008b68b0eSGleb Smirnoff 	int			sc_vhid;
111*a254d687SGleb Smirnoff 	union {
112*a254d687SGleb Smirnoff 		struct { /* sc_version == CARP_VERSION_CARP */
11308b68b0eSGleb Smirnoff 			int		sc_advskew;
11408b68b0eSGleb Smirnoff 			int		sc_advbase;
11513781800SKristof Provost 			struct in_addr	sc_carpaddr;
11613781800SKristof Provost 			struct in6_addr	sc_carpaddr6;
117*a254d687SGleb Smirnoff 			uint64_t	sc_counter;
118*a254d687SGleb Smirnoff 			bool		sc_init_counter;
119*a254d687SGleb Smirnoff #define	CARP_HMAC_PAD	64
120*a254d687SGleb Smirnoff 			unsigned char sc_key[CARP_KEY_LEN];
121*a254d687SGleb Smirnoff 			unsigned char sc_pad[CARP_HMAC_PAD];
122*a254d687SGleb Smirnoff 			SHA1_CTX sc_sha1;
12337115154SKristof Provost 		};
124*a254d687SGleb Smirnoff 		struct { /* sc_version == CARP_VERSION_VRRPv3 */
12537115154SKristof Provost 			uint8_t		sc_vrrp_prio;
12637115154SKristof Provost 			uint16_t	sc_vrrp_adv_inter;
12737115154SKristof Provost 			uint16_t	sc_vrrp_master_inter;
12837115154SKristof Provost 		};
129*a254d687SGleb Smirnoff 	};
13008b68b0eSGleb Smirnoff 	int			sc_naddrs;
13108b68b0eSGleb Smirnoff 	int			sc_naddrs6;
13208b68b0eSGleb Smirnoff 	int			sc_ifasiz;
133a9771948SGleb Smirnoff 	enum { INIT = 0, BACKUP, MASTER }	sc_state;
134a9771948SGleb Smirnoff 	int			sc_suppress;
135a9771948SGleb Smirnoff 	int			sc_sendad_errors;
136a9771948SGleb Smirnoff #define	CARP_SENDAD_MAX_ERRORS	3
137a9771948SGleb Smirnoff 	int			sc_sendad_success;
138a9771948SGleb Smirnoff #define	CARP_SENDAD_MIN_SUCCESS 3
139a9771948SGleb Smirnoff 
14008b68b0eSGleb Smirnoff 	TAILQ_ENTRY(carp_softc)	sc_list;	/* On the carp_if list. */
14108b68b0eSGleb Smirnoff 	LIST_ENTRY(carp_softc)	sc_next;	/* On the global list. */
142a9771948SGleb Smirnoff };
14308b68b0eSGleb Smirnoff 
14408b68b0eSGleb Smirnoff struct carp_if {
14508b68b0eSGleb Smirnoff #ifdef INET
14608b68b0eSGleb Smirnoff 	int	cif_naddrs;
14708b68b0eSGleb Smirnoff #endif
14808b68b0eSGleb Smirnoff #ifdef INET6
14908b68b0eSGleb Smirnoff 	int	cif_naddrs6;
15008b68b0eSGleb Smirnoff #endif
15108b68b0eSGleb Smirnoff 	TAILQ_HEAD(, carp_softc) cif_vrs;
15208b68b0eSGleb Smirnoff #ifdef INET
15308b68b0eSGleb Smirnoff 	struct ip_moptions 	 cif_imo;
15408b68b0eSGleb Smirnoff #endif
15508b68b0eSGleb Smirnoff #ifdef INET6
15608b68b0eSGleb Smirnoff 	struct ip6_moptions 	 cif_im6o;
15708b68b0eSGleb Smirnoff #endif
15808b68b0eSGleb Smirnoff 	struct ifnet	*cif_ifp;
15908b68b0eSGleb Smirnoff 	struct mtx	cif_mtx;
1600cc726f2SGleb Smirnoff 	uint32_t	cif_flags;
1610cc726f2SGleb Smirnoff #define	CIF_PROMISC	0x00000001
16208b68b0eSGleb Smirnoff };
16308b68b0eSGleb Smirnoff 
16413781800SKristof Provost /* Kernel equivalent of struct carpreq, but with more fields for new features.
16513781800SKristof Provost  * */
16613781800SKristof Provost struct carpkreq {
16713781800SKristof Provost 	int		carpr_count;
16813781800SKristof Provost 	int		carpr_vhid;
16913781800SKristof Provost 	int		carpr_state;
17013781800SKristof Provost 	int		carpr_advskew;
17113781800SKristof Provost 	int		carpr_advbase;
17213781800SKristof Provost 	unsigned char	carpr_key[CARP_KEY_LEN];
17313781800SKristof Provost 	/* Everything above this is identical to carpreq */
17413781800SKristof Provost 	struct in_addr	carpr_addr;
17513781800SKristof Provost 	struct in6_addr	carpr_addr6;
17637115154SKristof Provost 	carp_version_t	carpr_version;
17737115154SKristof Provost 	uint8_t		carpr_vrrp_priority;
17837115154SKristof Provost 	uint16_t	carpr_vrrp_adv_inter;
17913781800SKristof Provost };
18013781800SKristof Provost 
18108b68b0eSGleb Smirnoff /*
18208b68b0eSGleb Smirnoff  * Brief design of carp(4).
18308b68b0eSGleb Smirnoff  *
18408b68b0eSGleb Smirnoff  * Any carp-capable ifnet may have a list of carp softcs hanging off
18508b68b0eSGleb Smirnoff  * its ifp->if_carp pointer. Each softc represents one unique virtual
18608b68b0eSGleb Smirnoff  * host id, or vhid. The softc has a back pointer to the ifnet. All
18708b68b0eSGleb Smirnoff  * softcs are joined in a global list, which has quite limited use.
18808b68b0eSGleb Smirnoff  *
18908b68b0eSGleb Smirnoff  * Any interface address that takes part in CARP negotiation has a
19008b68b0eSGleb Smirnoff  * pointer to the softc of its vhid, ifa->ifa_carp. That could be either
19108b68b0eSGleb Smirnoff  * AF_INET or AF_INET6 address.
19208b68b0eSGleb Smirnoff  *
19308b68b0eSGleb Smirnoff  * Although, one can get the softc's backpointer to ifnet and traverse
19408b68b0eSGleb Smirnoff  * through its ifp->if_addrhead queue to find all interface addresses
19508b68b0eSGleb Smirnoff  * involved in CARP, we keep a growable array of ifaddr pointers. This
19608b68b0eSGleb Smirnoff  * allows us to avoid grabbing the IF_ADDR_LOCK() in many traversals that
19708b68b0eSGleb Smirnoff  * do calls into the network stack, thus avoiding LORs.
19808b68b0eSGleb Smirnoff  *
19908b68b0eSGleb Smirnoff  * Locking:
20008b68b0eSGleb Smirnoff  *
20108b68b0eSGleb Smirnoff  * Each softc has a lock sc_mtx. It is used to synchronise carp_input_c(),
20208b68b0eSGleb Smirnoff  * callout-driven events and ioctl()s.
20308b68b0eSGleb Smirnoff  *
20481098a01SAlexander Motin  * To traverse the list of softcs on an ifnet we use CIF_LOCK() or carp_sx.
20581098a01SAlexander Motin  * To traverse the global list we use the mutex carp_mtx.
20608b68b0eSGleb Smirnoff  *
20708b68b0eSGleb Smirnoff  * Known issues with locking:
20808b68b0eSGleb Smirnoff  *
20908b68b0eSGleb Smirnoff  * - Sending ad, we put the pointer to the softc in an mtag, and no reference
21008b68b0eSGleb Smirnoff  *   counting is done on the softc.
21108b68b0eSGleb Smirnoff  * - On module unload we may race (?) with packet processing thread
21208b68b0eSGleb Smirnoff  *   dereferencing our function pointers.
21308b68b0eSGleb Smirnoff  */
214a9771948SGleb Smirnoff 
215c5c392e7SMikolaj Golub /* Accept incoming CARP packets. */
2165f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_allow) = 1;
217c5c392e7SMikolaj Golub #define	V_carp_allow	VNET(carp_allow)
218c5c392e7SMikolaj Golub 
2190d3d234cSKristof Provost /* Set DSCP in outgoing CARP packets. */
2205f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_dscp) = 56;
2210d3d234cSKristof Provost #define	V_carp_dscp	VNET(carp_dscp)
2220d3d234cSKristof Provost 
223c5c392e7SMikolaj Golub /* Preempt slower nodes. */
2245f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_preempt) = 0;
225c5c392e7SMikolaj Golub #define	V_carp_preempt	VNET(carp_preempt)
226c5c392e7SMikolaj Golub 
227c5c392e7SMikolaj Golub /* Log level. */
2285f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_log) = 1;
229c5c392e7SMikolaj Golub #define	V_carp_log	VNET(carp_log)
230c5c392e7SMikolaj Golub 
231c5c392e7SMikolaj Golub /* Global advskew demotion. */
2325f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_demotion) = 0;
233c5c392e7SMikolaj Golub #define	V_carp_demotion	VNET(carp_demotion)
234c5c392e7SMikolaj Golub 
235c5c392e7SMikolaj Golub /* Send error demotion factor. */
2365f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_senderr_adj) = CARP_MAXSKEW;
237c5c392e7SMikolaj Golub #define	V_carp_senderr_adj	VNET(carp_senderr_adj)
238c5c392e7SMikolaj Golub 
239c5c392e7SMikolaj Golub /* Iface down demotion factor. */
2405f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_ifdown_adj) = CARP_MAXSKEW;
241c5c392e7SMikolaj Golub #define	V_carp_ifdown_adj	VNET(carp_ifdown_adj)
242c5c392e7SMikolaj Golub 
243167a3440SAlexander Motin static int carp_allow_sysctl(SYSCTL_HANDLER_ARGS);
2440d3d234cSKristof Provost static int carp_dscp_sysctl(SYSCTL_HANDLER_ARGS);
2457951008bSGleb Smirnoff static int carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS);
246a9771948SGleb Smirnoff 
24710b49b23SPawel Biernacki SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
24810b49b23SPawel Biernacki     "CARP");
249167a3440SAlexander Motin SYSCTL_PROC(_net_inet_carp, OID_AUTO, allow,
250242fa308SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE,
251ee49c5d3SBoris Lytochkin     &VNET_NAME(carp_allow), 0, carp_allow_sysctl, "I",
252167a3440SAlexander Motin     "Accept incoming CARP packets");
2530d3d234cSKristof Provost SYSCTL_PROC(_net_inet_carp, OID_AUTO, dscp,
25410b49b23SPawel Biernacki     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
25510b49b23SPawel Biernacki     0, 0, carp_dscp_sysctl, "I",
2560d3d234cSKristof Provost     "DSCP value for carp packets");
2576df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, preempt, CTLFLAG_VNET | CTLFLAG_RW,
258c5c392e7SMikolaj Golub     &VNET_NAME(carp_preempt), 0, "High-priority backup preemption mode");
2596df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, log, CTLFLAG_VNET | CTLFLAG_RW,
260c5c392e7SMikolaj Golub     &VNET_NAME(carp_log), 0, "CARP log level");
2616df8a710SGleb Smirnoff SYSCTL_PROC(_net_inet_carp, OID_AUTO, demotion,
26210b49b23SPawel Biernacki     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2637951008bSGleb Smirnoff     0, 0, carp_demote_adj_sysctl, "I",
2647951008bSGleb Smirnoff     "Adjust demotion factor (skew of advskew)");
2656df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, senderr_demotion_factor,
2666df8a710SGleb Smirnoff     CTLFLAG_VNET | CTLFLAG_RW,
267c5c392e7SMikolaj Golub     &VNET_NAME(carp_senderr_adj), 0, "Send error demotion factor adjustment");
2686df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, ifdown_demotion_factor,
2696df8a710SGleb Smirnoff     CTLFLAG_VNET | CTLFLAG_RW,
270c5c392e7SMikolaj Golub     &VNET_NAME(carp_ifdown_adj), 0,
271c5c392e7SMikolaj Golub     "Interface down demotion factor adjustment");
272f08535f8SGleb Smirnoff 
273c5c392e7SMikolaj Golub VNET_PCPUSTAT_DEFINE(struct carpstats, carpstats);
274c5c392e7SMikolaj Golub VNET_PCPUSTAT_SYSINIT(carpstats);
275c5c392e7SMikolaj Golub VNET_PCPUSTAT_SYSUNINIT(carpstats);
276c5c392e7SMikolaj Golub 
27769edf037SAndrey V. Elsukov #define	CARPSTATS_ADD(name, val)	\
278c5c392e7SMikolaj Golub     counter_u64_add(VNET(carpstats)[offsetof(struct carpstats, name) / \
27969edf037SAndrey V. Elsukov 	sizeof(uint64_t)], (val))
28069edf037SAndrey V. Elsukov #define	CARPSTATS_INC(name)		CARPSTATS_ADD(name, 1)
28169edf037SAndrey V. Elsukov 
282c5c392e7SMikolaj Golub SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats,
283c5c392e7SMikolaj Golub     carpstats, "CARP statistics (struct carpstats, netinet/ip_carp.h)");
284a9771948SGleb Smirnoff 
28508b68b0eSGleb Smirnoff #define	CARP_LOCK_INIT(sc)	mtx_init(&(sc)->sc_mtx, "carp_softc",   \
286a9771948SGleb Smirnoff 	NULL, MTX_DEF)
28708b68b0eSGleb Smirnoff #define	CARP_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
28808b68b0eSGleb Smirnoff #define	CARP_LOCK_ASSERT(sc)	mtx_assert(&(sc)->sc_mtx, MA_OWNED)
28908b68b0eSGleb Smirnoff #define	CARP_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
29008b68b0eSGleb Smirnoff #define	CARP_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
29108b68b0eSGleb Smirnoff #define	CIF_LOCK_INIT(cif)	mtx_init(&(cif)->cif_mtx, "carp_if",   \
29208b68b0eSGleb Smirnoff 	NULL, MTX_DEF)
29308b68b0eSGleb Smirnoff #define	CIF_LOCK_DESTROY(cif)	mtx_destroy(&(cif)->cif_mtx)
29408b68b0eSGleb Smirnoff #define	CIF_LOCK_ASSERT(cif)	mtx_assert(&(cif)->cif_mtx, MA_OWNED)
29508b68b0eSGleb Smirnoff #define	CIF_LOCK(cif)		mtx_lock(&(cif)->cif_mtx)
29608b68b0eSGleb Smirnoff #define	CIF_UNLOCK(cif)		mtx_unlock(&(cif)->cif_mtx)
297a9a2c40cSGleb Smirnoff #define	CIF_FREE(cif)	do {				\
2989c2cd1aaSGleb Smirnoff 		CIF_LOCK(cif);				\
299a9a2c40cSGleb Smirnoff 		if (TAILQ_EMPTY(&(cif)->cif_vrs))	\
300a9a2c40cSGleb Smirnoff 			carp_free_if(cif);		\
301a9a2c40cSGleb Smirnoff 		else					\
302a9a2c40cSGleb Smirnoff 			CIF_UNLOCK(cif);		\
303a9a2c40cSGleb Smirnoff } while (0)
304d220759bSGleb Smirnoff 
305947b7cf3SGleb Smirnoff #define	CARP_LOG(...)	do {				\
306c5c392e7SMikolaj Golub 	if (V_carp_log > 0)				\
30708b68b0eSGleb Smirnoff 		log(LOG_INFO, "carp: " __VA_ARGS__);	\
308947b7cf3SGleb Smirnoff } while (0)
3091e9e6572SGleb Smirnoff 
310947b7cf3SGleb Smirnoff #define	CARP_DEBUG(...)	do {				\
311c5c392e7SMikolaj Golub 	if (V_carp_log > 1)				\
3121e9e6572SGleb Smirnoff 		log(LOG_DEBUG, __VA_ARGS__);		\
313947b7cf3SGleb Smirnoff } while (0)
314a9771948SGleb Smirnoff 
31508b68b0eSGleb Smirnoff #define	IFNET_FOREACH_IFA(ifp, ifa)					\
316d7c5a620SMatt Macy 	CK_STAILQ_FOREACH((ifa), &(ifp)->if_addrhead, ifa_link)	\
31708b68b0eSGleb Smirnoff 		if ((ifa)->ifa_carp != NULL)
31808b68b0eSGleb Smirnoff 
31908b68b0eSGleb Smirnoff #define	CARP_FOREACH_IFA(sc, ifa)					\
32008b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);						\
32108b68b0eSGleb Smirnoff 	for (int _i = 0;						\
32208b68b0eSGleb Smirnoff 		_i < (sc)->sc_naddrs + (sc)->sc_naddrs6 &&		\
32308b68b0eSGleb Smirnoff 		((ifa) = sc->sc_ifas[_i]) != NULL;			\
32408b68b0eSGleb Smirnoff 		++_i)
32508b68b0eSGleb Smirnoff 
32608b68b0eSGleb Smirnoff #define	IFNET_FOREACH_CARP(ifp, sc)					\
32781098a01SAlexander Motin 	KASSERT(mtx_owned(&ifp->if_carp->cif_mtx) ||			\
32881098a01SAlexander Motin 	    sx_xlocked(&carp_sx), ("cif_vrs not locked"));		\
32908b68b0eSGleb Smirnoff 	TAILQ_FOREACH((sc), &(ifp)->if_carp->cif_vrs, sc_list)
33008b68b0eSGleb Smirnoff 
331f08535f8SGleb Smirnoff #define	DEMOTE_ADVSKEW(sc)					\
332c5c392e7SMikolaj Golub     (((sc)->sc_advskew + V_carp_demotion > CARP_MAXSKEW) ?	\
3331019354bSMarius Halden     CARP_MAXSKEW :						\
3341019354bSMarius Halden         (((sc)->sc_advskew + V_carp_demotion < 0) ?		\
3351019354bSMarius Halden         0 : ((sc)->sc_advskew + V_carp_demotion)))
336f08535f8SGleb Smirnoff 
33713781800SKristof Provost static void	carp_input_c(struct mbuf *, struct carp_header *, sa_family_t, int);
33837115154SKristof Provost static void	vrrp_input_c(struct mbuf *, int, sa_family_t, int, int, uint16_t);
33908b68b0eSGleb Smirnoff static struct carp_softc
340*a254d687SGleb Smirnoff 		*carp_alloc(struct ifnet *, carp_version_t, int);
34108b68b0eSGleb Smirnoff static void	carp_destroy(struct carp_softc *);
34208b68b0eSGleb Smirnoff static struct carp_if
34308b68b0eSGleb Smirnoff 		*carp_alloc_if(struct ifnet *);
34408b68b0eSGleb Smirnoff static void	carp_free_if(struct carp_if *);
345d01641e2SWill Andrews static void	carp_set_state(struct carp_softc *, int, const char* reason);
34608b68b0eSGleb Smirnoff static void	carp_sc_state(struct carp_softc *);
34708b68b0eSGleb Smirnoff static void	carp_setrun(struct carp_softc *, sa_family_t);
3485c1f0f6dSGleb Smirnoff static void	carp_master_down(void *);
349d01641e2SWill Andrews static void	carp_master_down_locked(struct carp_softc *,
350d01641e2SWill Andrews     		    const char* reason);
35108b68b0eSGleb Smirnoff static void	carp_send_ad_locked(struct carp_softc *);
35237115154SKristof Provost static void	vrrp_send_ad_locked(struct carp_softc *);
35308b68b0eSGleb Smirnoff static void	carp_addroute(struct carp_softc *);
3542512b096SGleb Smirnoff static void	carp_ifa_addroute(struct ifaddr *);
35508b68b0eSGleb Smirnoff static void	carp_delroute(struct carp_softc *);
3562512b096SGleb Smirnoff static void	carp_ifa_delroute(struct ifaddr *);
357f08535f8SGleb Smirnoff static void	carp_send_ad_all(void *, int);
358f08535f8SGleb Smirnoff static void	carp_demote_adj(int, char *);
359a9771948SGleb Smirnoff 
36008b68b0eSGleb Smirnoff static LIST_HEAD(, carp_softc) carp_list;
361d92d54d5SGleb Smirnoff static struct mtx carp_mtx;
36293d4534cSGleb Smirnoff static struct sx carp_sx;
363f08535f8SGleb Smirnoff static struct task carp_sendall_task =
364f08535f8SGleb Smirnoff     TASK_INITIALIZER(0, carp_send_ad_all, NULL);
365a9771948SGleb Smirnoff 
36640e04359SKristof Provost static int
36740e04359SKristof Provost carp_is_supported_if(if_t ifp)
36840e04359SKristof Provost {
36940e04359SKristof Provost 	if (ifp == NULL)
37040e04359SKristof Provost 		return (ENXIO);
37140e04359SKristof Provost 
37240e04359SKristof Provost 	switch (ifp->if_type) {
37340e04359SKristof Provost 	case IFT_ETHER:
37440e04359SKristof Provost 	case IFT_L2VLAN:
37540e04359SKristof Provost 	case IFT_BRIDGE:
37640e04359SKristof Provost 		break;
37740e04359SKristof Provost 	default:
37840e04359SKristof Provost 		return (EOPNOTSUPP);
37940e04359SKristof Provost 	}
38040e04359SKristof Provost 
38140e04359SKristof Provost 	return (0);
38240e04359SKristof Provost }
38340e04359SKristof Provost 
3845c1f0f6dSGleb Smirnoff static void
385a9771948SGleb Smirnoff carp_hmac_prepare(struct carp_softc *sc)
386a9771948SGleb Smirnoff {
38737115154SKristof Provost 	uint8_t version = CARP_VERSION_CARP, type = CARP_ADVERTISEMENT;
38808b68b0eSGleb Smirnoff 	uint8_t vhid = sc->sc_vhid & 0xff;
389a9771948SGleb Smirnoff 	struct ifaddr *ifa;
3901ead26d4SMax Laier 	int i, found;
3911ead26d4SMax Laier #ifdef INET
3921ead26d4SMax Laier 	struct in_addr last, cur, in;
3931ead26d4SMax Laier #endif
394a9771948SGleb Smirnoff #ifdef INET6
3951ead26d4SMax Laier 	struct in6_addr last6, cur6, in6;
396a9771948SGleb Smirnoff #endif
397a9771948SGleb Smirnoff 
39808b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
399*a254d687SGleb Smirnoff 	MPASS(sc->sc_version == CARP_VERSION_CARP);
400d220759bSGleb Smirnoff 
40108b68b0eSGleb Smirnoff 	/* Compute ipad from key. */
402a9771948SGleb Smirnoff 	bzero(sc->sc_pad, sizeof(sc->sc_pad));
403a9771948SGleb Smirnoff 	bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key));
404a9771948SGleb Smirnoff 	for (i = 0; i < sizeof(sc->sc_pad); i++)
405a9771948SGleb Smirnoff 		sc->sc_pad[i] ^= 0x36;
406a9771948SGleb Smirnoff 
40708b68b0eSGleb Smirnoff 	/* Precompute first part of inner hash. */
408a9771948SGleb Smirnoff 	SHA1Init(&sc->sc_sha1);
409a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad));
410a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version));
411a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type));
412a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid));
413a9771948SGleb Smirnoff #ifdef INET
4141ead26d4SMax Laier 	cur.s_addr = 0;
4151ead26d4SMax Laier 	do {
4161ead26d4SMax Laier 		found = 0;
4171ead26d4SMax Laier 		last = cur;
4181ead26d4SMax Laier 		cur.s_addr = 0xffffffff;
41908b68b0eSGleb Smirnoff 		CARP_FOREACH_IFA(sc, ifa) {
4201ead26d4SMax Laier 			in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
4211ead26d4SMax Laier 			if (ifa->ifa_addr->sa_family == AF_INET &&
4221ead26d4SMax Laier 			    ntohl(in.s_addr) > ntohl(last.s_addr) &&
4231ead26d4SMax Laier 			    ntohl(in.s_addr) < ntohl(cur.s_addr)) {
4241ead26d4SMax Laier 				cur.s_addr = in.s_addr;
4251ead26d4SMax Laier 				found++;
426a9771948SGleb Smirnoff 			}
4271ead26d4SMax Laier 		}
4281ead26d4SMax Laier 		if (found)
4291ead26d4SMax Laier 			SHA1Update(&sc->sc_sha1, (void *)&cur, sizeof(cur));
4301ead26d4SMax Laier 	} while (found);
431a9771948SGleb Smirnoff #endif /* INET */
432a9771948SGleb Smirnoff #ifdef INET6
4331ead26d4SMax Laier 	memset(&cur6, 0, sizeof(cur6));
4341ead26d4SMax Laier 	do {
4351ead26d4SMax Laier 		found = 0;
4361ead26d4SMax Laier 		last6 = cur6;
4371ead26d4SMax Laier 		memset(&cur6, 0xff, sizeof(cur6));
43808b68b0eSGleb Smirnoff 		CARP_FOREACH_IFA(sc, ifa) {
439a9771948SGleb Smirnoff 			in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
4401ead26d4SMax Laier 			if (IN6_IS_SCOPE_EMBED(&in6))
4411ead26d4SMax Laier 				in6.s6_addr16[1] = 0;
4421ead26d4SMax Laier 			if (ifa->ifa_addr->sa_family == AF_INET6 &&
4431ead26d4SMax Laier 			    memcmp(&in6, &last6, sizeof(in6)) > 0 &&
4441ead26d4SMax Laier 			    memcmp(&in6, &cur6, sizeof(in6)) < 0) {
4451ead26d4SMax Laier 				cur6 = in6;
4461ead26d4SMax Laier 				found++;
447a9771948SGleb Smirnoff 			}
448a9771948SGleb Smirnoff 		}
4491ead26d4SMax Laier 		if (found)
4501ead26d4SMax Laier 			SHA1Update(&sc->sc_sha1, (void *)&cur6, sizeof(cur6));
4511ead26d4SMax Laier 	} while (found);
452a9771948SGleb Smirnoff #endif /* INET6 */
453a9771948SGleb Smirnoff 
454a9771948SGleb Smirnoff 	/* convert ipad to opad */
455a9771948SGleb Smirnoff 	for (i = 0; i < sizeof(sc->sc_pad); i++)
456a9771948SGleb Smirnoff 		sc->sc_pad[i] ^= 0x36 ^ 0x5c;
457a9771948SGleb Smirnoff }
458a9771948SGleb Smirnoff 
4595c1f0f6dSGleb Smirnoff static void
46008b68b0eSGleb Smirnoff carp_hmac_generate(struct carp_softc *sc, uint32_t counter[2],
461a9771948SGleb Smirnoff     unsigned char md[20])
462a9771948SGleb Smirnoff {
463a9771948SGleb Smirnoff 	SHA1_CTX sha1ctx;
464a9771948SGleb Smirnoff 
46508b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
46608b68b0eSGleb Smirnoff 
467a9771948SGleb Smirnoff 	/* fetch first half of inner hash */
468a9771948SGleb Smirnoff 	bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx));
469a9771948SGleb Smirnoff 
470a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter));
471a9771948SGleb Smirnoff 	SHA1Final(md, &sha1ctx);
472a9771948SGleb Smirnoff 
473a9771948SGleb Smirnoff 	/* outer hash */
474a9771948SGleb Smirnoff 	SHA1Init(&sha1ctx);
475a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad));
476a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, md, 20);
477a9771948SGleb Smirnoff 	SHA1Final(md, &sha1ctx);
478a9771948SGleb Smirnoff }
479a9771948SGleb Smirnoff 
4805c1f0f6dSGleb Smirnoff static int
48108b68b0eSGleb Smirnoff carp_hmac_verify(struct carp_softc *sc, uint32_t counter[2],
482a9771948SGleb Smirnoff     unsigned char md[20])
483a9771948SGleb Smirnoff {
484a9771948SGleb Smirnoff 	unsigned char md2[20];
485a9771948SGleb Smirnoff 
48608b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
487d220759bSGleb Smirnoff 
488a9771948SGleb Smirnoff 	carp_hmac_generate(sc, counter, md2);
489a9771948SGleb Smirnoff 
490a9771948SGleb Smirnoff 	return (bcmp(md, md2, sizeof(md2)));
491a9771948SGleb Smirnoff }
492a9771948SGleb Smirnoff 
49337115154SKristof Provost static int
49437115154SKristof Provost vrrp_checksum_verify(struct mbuf *m, int off, int len, uint16_t phdrcksum)
49537115154SKristof Provost {
49637115154SKristof Provost 	uint16_t cksum;
49737115154SKristof Provost 
49837115154SKristof Provost 	/*
49937115154SKristof Provost 	 * Note that VRRPv3 checksums are different from CARP checksums.
50037115154SKristof Provost 	 * Carp just calculates the checksum over the packet.
50137115154SKristof Provost 	 * VRRPv3 includes the pseudo-header checksum as well.
50237115154SKristof Provost 	 */
50337115154SKristof Provost 	cksum = in_cksum_skip(m, off + len, off);
50437115154SKristof Provost 	cksum -= phdrcksum;
50537115154SKristof Provost 
50637115154SKristof Provost 	return (cksum);
50737115154SKristof Provost }
50837115154SKristof Provost 
509a9771948SGleb Smirnoff /*
510a9771948SGleb Smirnoff  * process input packet.
511a9771948SGleb Smirnoff  * we have rearranged checks order compared to the rfc,
512a9771948SGleb Smirnoff  * but it seems more efficient this way or not possible otherwise.
513a9771948SGleb Smirnoff  */
514a0ae8f04SBjoern A. Zeeb #ifdef INET
51578b1fc05SGleb Smirnoff static int
5168f5a8818SKevin Lo carp_input(struct mbuf **mp, int *offp, int proto)
517a9771948SGleb Smirnoff {
5188f5a8818SKevin Lo 	struct mbuf *m = *mp;
519a9771948SGleb Smirnoff 	struct ip *ip = mtod(m, struct ip *);
52037115154SKristof Provost 	struct vrrpv3_header *vh;
52137115154SKristof Provost 	int iplen;
52237115154SKristof Provost 	int minlen;
52337115154SKristof Provost 	int totlen;
524a9771948SGleb Smirnoff 
5258f5a8818SKevin Lo 	iplen = *offp;
5268f5a8818SKevin Lo 	*mp = NULL;
5278f5a8818SKevin Lo 
5286bf65bcfSRobert Watson 	CARPSTATS_INC(carps_ipackets);
529a9771948SGleb Smirnoff 
530c5c392e7SMikolaj Golub 	if (!V_carp_allow) {
531a9771948SGleb Smirnoff 		m_freem(m);
5328f5a8818SKevin Lo 		return (IPPROTO_DONE);
533a9771948SGleb Smirnoff 	}
534a9771948SGleb Smirnoff 
535a9771948SGleb Smirnoff 	iplen = ip->ip_hl << 2;
53637115154SKristof Provost 	totlen = ntohs(ip->ip_len);
537a9771948SGleb Smirnoff 
53837115154SKristof Provost 	/* Ensure we have enough header to figure out the version. */
53937115154SKristof Provost 	if (m->m_pkthdr.len < iplen + sizeof(*vh)) {
5406bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badlen);
54137115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < sizeof(struct vrrpv3_header) "
54208b68b0eSGleb Smirnoff 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
543511a6d5eSKristof Provost 		    if_name(m->m_pkthdr.rcvif));
544a9771948SGleb Smirnoff 		m_freem(m);
5458f5a8818SKevin Lo 		return (IPPROTO_DONE);
546a9771948SGleb Smirnoff 	}
547a9771948SGleb Smirnoff 
54837115154SKristof Provost 	if (iplen + sizeof(*vh) < m->m_len) {
54937115154SKristof Provost 		if ((m = m_pullup(m, iplen + sizeof(*vh))) == NULL) {
5506bf65bcfSRobert Watson 			CARPSTATS_INC(carps_hdrops);
55137115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
5528f5a8818SKevin Lo 			return (IPPROTO_DONE);
553a9771948SGleb Smirnoff 		}
554a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
555a9771948SGleb Smirnoff 	}
55637115154SKristof Provost 	vh = (struct vrrpv3_header *)((char *)ip + iplen);
557a9771948SGleb Smirnoff 
55837115154SKristof Provost 	switch (vh->vrrp_version) {
55937115154SKristof Provost 	case CARP_VERSION_CARP:
56037115154SKristof Provost 		minlen = sizeof(struct carp_header);
56137115154SKristof Provost 		break;
56237115154SKristof Provost 	case CARP_VERSION_VRRPv3:
56337115154SKristof Provost 		minlen = sizeof(struct vrrpv3_header);
56437115154SKristof Provost 		break;
56537115154SKristof Provost 	default:
56637115154SKristof Provost 		CARPSTATS_INC(carps_badver);
56737115154SKristof Provost 		CARP_DEBUG("%s: unsupported version %d on %s\n", __func__,
56837115154SKristof Provost 		    vh->vrrp_version, if_name(m->m_pkthdr.rcvif));
56937115154SKristof Provost 		m_freem(m);
57037115154SKristof Provost 		return (IPPROTO_DONE);
57137115154SKristof Provost 	}
57237115154SKristof Provost 
57337115154SKristof Provost 	/* And now check the length again but with the real minimal length. */
57437115154SKristof Provost 	if (m->m_pkthdr.len < iplen + minlen) {
5756bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badlen);
57637115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < %d "
57737115154SKristof Provost 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
57837115154SKristof Provost 		    iplen + minlen,
579511a6d5eSKristof Provost 		    if_name(m->m_pkthdr.rcvif));
580a9771948SGleb Smirnoff 		m_freem(m);
5818f5a8818SKevin Lo 		return (IPPROTO_DONE);
582a9771948SGleb Smirnoff 	}
583a9771948SGleb Smirnoff 
58437115154SKristof Provost 	if (iplen + minlen < m->m_len) {
58537115154SKristof Provost 		if ((m = m_pullup(m, iplen + minlen)) == NULL) {
5866bf65bcfSRobert Watson 			CARPSTATS_INC(carps_hdrops);
58737115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
5888f5a8818SKevin Lo 			return (IPPROTO_DONE);
589a9771948SGleb Smirnoff 		}
590a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
59137115154SKristof Provost 		vh = (struct vrrpv3_header *)((char *)ip + iplen);
59237115154SKristof Provost 	}
593a9771948SGleb Smirnoff 
594*a254d687SGleb Smirnoff 	switch (vh->vrrp_version) {
595*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP: {
596*a254d687SGleb Smirnoff 		struct carp_header *ch;
59737115154SKristof Provost 
598a9771948SGleb Smirnoff 		/* verify the CARP checksum */
599a9771948SGleb Smirnoff 		m->m_data += iplen;
60037115154SKristof Provost 		if (in_cksum(m, totlen - iplen)) {
6016bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badsum);
60208b68b0eSGleb Smirnoff 			CARP_DEBUG("%s: checksum failed on %s\n", __func__,
603511a6d5eSKristof Provost 			    if_name(m->m_pkthdr.rcvif));
604a9771948SGleb Smirnoff 			m_freem(m);
605*a254d687SGleb Smirnoff 			break;
606a9771948SGleb Smirnoff 		}
607a9771948SGleb Smirnoff 		m->m_data -= iplen;
608*a254d687SGleb Smirnoff 		ch = (struct carp_header *)((char *)ip + iplen);
60913781800SKristof Provost 		carp_input_c(m, ch, AF_INET, ip->ip_ttl);
61037115154SKristof Provost 		break;
61137115154SKristof Provost 	}
612*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3: {
613*a254d687SGleb Smirnoff 		uint16_t phdrcksum;
614*a254d687SGleb Smirnoff 
615*a254d687SGleb Smirnoff 		phdrcksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
616*a254d687SGleb Smirnoff 		    htonl((u_short)(totlen - iplen) + ip->ip_p));
617*a254d687SGleb Smirnoff 		vrrp_input_c(m, iplen, AF_INET, ip->ip_ttl, totlen - iplen,
618*a254d687SGleb Smirnoff 		    phdrcksum);
61937115154SKristof Provost 		break;
620*a254d687SGleb Smirnoff 	}
62137115154SKristof Provost 	default:
62237115154SKristof Provost 		KASSERT(false, ("Unsupported version %d", vh->vrrp_version));
62337115154SKristof Provost 	}
62437115154SKristof Provost 
6258f5a8818SKevin Lo 	return (IPPROTO_DONE);
626a9771948SGleb Smirnoff }
627a0ae8f04SBjoern A. Zeeb #endif
628a9771948SGleb Smirnoff 
629a9771948SGleb Smirnoff #ifdef INET6
63078b1fc05SGleb Smirnoff static int
631a9771948SGleb Smirnoff carp6_input(struct mbuf **mp, int *offp, int proto)
632a9771948SGleb Smirnoff {
633a9771948SGleb Smirnoff 	struct mbuf *m = *mp;
634a9771948SGleb Smirnoff 	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
63537115154SKristof Provost 	struct vrrpv3_header *vh;
63637115154SKristof Provost 	u_int len, minlen;
637a9771948SGleb Smirnoff 
6386bf65bcfSRobert Watson 	CARPSTATS_INC(carps_ipackets6);
639a9771948SGleb Smirnoff 
640c5c392e7SMikolaj Golub 	if (!V_carp_allow) {
641a9771948SGleb Smirnoff 		m_freem(m);
642a9771948SGleb Smirnoff 		return (IPPROTO_DONE);
643a9771948SGleb Smirnoff 	}
644a9771948SGleb Smirnoff 
645a9771948SGleb Smirnoff 	/* check if received on a valid carp interface */
646a9771948SGleb Smirnoff 	if (m->m_pkthdr.rcvif->if_carp == NULL) {
6476bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badif);
64808b68b0eSGleb Smirnoff 		CARP_DEBUG("%s: packet received on non-carp interface: %s\n",
649511a6d5eSKristof Provost 		    __func__, if_name(m->m_pkthdr.rcvif));
650a9771948SGleb Smirnoff 		m_freem(m);
651a9771948SGleb Smirnoff 		return (IPPROTO_DONE);
652a9771948SGleb Smirnoff 	}
653a9771948SGleb Smirnoff 
65437115154SKristof Provost 	if (m->m_len < *offp + sizeof(*vh)) {
655a9771948SGleb Smirnoff 		len = m->m_len;
65637115154SKristof Provost 		m = m_pullup(m, *offp + sizeof(*vh));
65763abacc2SBjoern A. Zeeb 		if (m == NULL) {
6586bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badlen);
65908b68b0eSGleb Smirnoff 			CARP_DEBUG("%s: packet size %u too small\n", __func__, len);
660a9771948SGleb Smirnoff 			return (IPPROTO_DONE);
661a9771948SGleb Smirnoff 		}
66213781800SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
663a4adf6ccSBjoern A. Zeeb 	}
66437115154SKristof Provost 	vh = (struct vrrpv3_header *)(mtod(m, char *) + *offp);
66537115154SKristof Provost 
66637115154SKristof Provost 	switch (vh->vrrp_version) {
66737115154SKristof Provost 	case CARP_VERSION_CARP:
66837115154SKristof Provost 		minlen = sizeof(struct carp_header);
66937115154SKristof Provost 		break;
67037115154SKristof Provost 	case CARP_VERSION_VRRPv3:
67137115154SKristof Provost 		minlen = sizeof(struct vrrpv3_header);
67237115154SKristof Provost 		break;
67337115154SKristof Provost 	default:
67437115154SKristof Provost 		CARPSTATS_INC(carps_badver);
67537115154SKristof Provost 		CARP_DEBUG("%s: unsupported version %d on %s\n", __func__,
67637115154SKristof Provost 		    vh->vrrp_version, if_name(m->m_pkthdr.rcvif));
67737115154SKristof Provost 		m_freem(m);
67837115154SKristof Provost 		return (IPPROTO_DONE);
67937115154SKristof Provost 	}
68037115154SKristof Provost 
68137115154SKristof Provost 	/* And now check the length again but with the real minimal length. */
68237115154SKristof Provost 	if (m->m_pkthdr.len < sizeof(*ip6) + minlen) {
68337115154SKristof Provost 		CARPSTATS_INC(carps_badlen);
68437115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < %zd "
68537115154SKristof Provost 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
68637115154SKristof Provost 		    sizeof(*ip6) + minlen,
68737115154SKristof Provost 		    if_name(m->m_pkthdr.rcvif));
68837115154SKristof Provost 		m_freem(m);
68937115154SKristof Provost 		return (IPPROTO_DONE);
69037115154SKristof Provost 	}
69137115154SKristof Provost 
69237115154SKristof Provost 	if (sizeof (*ip6) + minlen < m->m_len) {
69337115154SKristof Provost 		if ((m = m_pullup(m, sizeof(*ip6) + minlen)) == NULL) {
69437115154SKristof Provost 			CARPSTATS_INC(carps_hdrops);
69537115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
69637115154SKristof Provost 			return (IPPROTO_DONE);
69737115154SKristof Provost 		}
69837115154SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
69937115154SKristof Provost 		vh = (struct vrrpv3_header *)mtodo(m, sizeof(*ip6));
70037115154SKristof Provost 	}
70137115154SKristof Provost 
702*a254d687SGleb Smirnoff 	switch (vh->vrrp_version) {
703*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP: {
704*a254d687SGleb Smirnoff 		struct carp_header *ch;
705a9771948SGleb Smirnoff 
706a9771948SGleb Smirnoff 		/* verify the CARP checksum */
707a9771948SGleb Smirnoff 		m->m_data += *offp;
70837115154SKristof Provost 		if (in_cksum(m, sizeof(struct carp_header))) {
7096bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badsum);
71008b68b0eSGleb Smirnoff 			CARP_DEBUG("%s: checksum failed, on %s\n", __func__,
711511a6d5eSKristof Provost 			    if_name(m->m_pkthdr.rcvif));
712a9771948SGleb Smirnoff 			m_freem(m);
713*a254d687SGleb Smirnoff 			break;
714a9771948SGleb Smirnoff 		}
715a9771948SGleb Smirnoff 		m->m_data -= *offp;
716*a254d687SGleb Smirnoff 		ch = (struct carp_header *)((char *)ip6 + sizeof(*ip6));
71713781800SKristof Provost 		carp_input_c(m, ch, AF_INET6, ip6->ip6_hlim);
71837115154SKristof Provost 		break;
71937115154SKristof Provost 	}
720*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3: {
721*a254d687SGleb Smirnoff 		uint16_t phdrcksum;
722*a254d687SGleb Smirnoff 
723*a254d687SGleb Smirnoff 		phdrcksum = in6_cksum_pseudo(ip6, ntohs(ip6->ip6_plen),
724*a254d687SGleb Smirnoff 		    ip6->ip6_nxt, 0);
725*a254d687SGleb Smirnoff 		vrrp_input_c(m, sizeof(*ip6), AF_INET6, ip6->ip6_hlim,
726*a254d687SGleb Smirnoff 		    ntohs(ip6->ip6_plen), phdrcksum);
72737115154SKristof Provost 		break;
728*a254d687SGleb Smirnoff 	}
72937115154SKristof Provost 	default:
73037115154SKristof Provost 		KASSERT(false, ("Unsupported version %d", vh->vrrp_version));
73137115154SKristof Provost 	}
732a9771948SGleb Smirnoff 	return (IPPROTO_DONE);
733a9771948SGleb Smirnoff }
734a9771948SGleb Smirnoff #endif /* INET6 */
735a9771948SGleb Smirnoff 
7368151740cSJosh Paetzel /*
7378151740cSJosh Paetzel  * This routine should not be necessary at all, but some switches
7388151740cSJosh Paetzel  * (VMWare ESX vswitches) can echo our own packets back at us,
7398151740cSJosh Paetzel  * and we must ignore them or they will cause us to drop out of
7408151740cSJosh Paetzel  * MASTER mode.
7418151740cSJosh Paetzel  *
7428151740cSJosh Paetzel  * We cannot catch all cases of network loops.  Instead, what we
7438151740cSJosh Paetzel  * do here is catch any packet that arrives with a carp header
7448151740cSJosh Paetzel  * with a VHID of 0, that comes from an address that is our own.
7458151740cSJosh Paetzel  * These packets are by definition "from us" (even if they are from
7468151740cSJosh Paetzel  * a misconfigured host that is pretending to be us).
7478151740cSJosh Paetzel  *
7488151740cSJosh Paetzel  * The VHID test is outside this mini-function.
7498151740cSJosh Paetzel  */
7508151740cSJosh Paetzel static int
75137115154SKristof Provost carp_source_is_self(const struct mbuf *m, struct ifaddr *ifa, sa_family_t af)
7528151740cSJosh Paetzel {
753cfff8d3dSEnji Cooper #ifdef INET
7548151740cSJosh Paetzel 	struct ip *ip4;
7558151740cSJosh Paetzel 	struct in_addr in4;
756cfff8d3dSEnji Cooper #endif
757cfff8d3dSEnji Cooper #ifdef INET6
7588151740cSJosh Paetzel 	struct ip6_hdr *ip6;
7598151740cSJosh Paetzel 	struct in6_addr in6;
760cfff8d3dSEnji Cooper #endif
7618151740cSJosh Paetzel 
7628151740cSJosh Paetzel 	switch (af) {
763cfff8d3dSEnji Cooper #ifdef INET
7648151740cSJosh Paetzel 	case AF_INET:
7658151740cSJosh Paetzel 		ip4 = mtod(m, struct ip *);
7668151740cSJosh Paetzel 		in4 = ifatoia(ifa)->ia_addr.sin_addr;
7678151740cSJosh Paetzel 		return (in4.s_addr == ip4->ip_src.s_addr);
768cfff8d3dSEnji Cooper #endif
769cfff8d3dSEnji Cooper #ifdef INET6
7708151740cSJosh Paetzel 	case AF_INET6:
7718151740cSJosh Paetzel 		ip6 = mtod(m, struct ip6_hdr *);
7728151740cSJosh Paetzel 		in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
7738151740cSJosh Paetzel 		return (memcmp(&in6, &ip6->ip6_src, sizeof(in6)) == 0);
774cfff8d3dSEnji Cooper #endif
775cfff8d3dSEnji Cooper 	default:
7768151740cSJosh Paetzel 		break;
7778151740cSJosh Paetzel 	}
7788151740cSJosh Paetzel 	return (0);
7798151740cSJosh Paetzel }
7808151740cSJosh Paetzel 
78137115154SKristof Provost static struct ifaddr *
78237115154SKristof Provost carp_find_ifa(const struct mbuf *m, sa_family_t af, uint8_t vhid)
783a9771948SGleb Smirnoff {
784a9771948SGleb Smirnoff 	struct ifnet *ifp = m->m_pkthdr.rcvif;
7858151740cSJosh Paetzel 	struct ifaddr *ifa, *match;
7868151740cSJosh Paetzel 	int error;
787a9771948SGleb Smirnoff 
788b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
789b8a6e03fSGleb Smirnoff 
7908151740cSJosh Paetzel 	/*
7918151740cSJosh Paetzel 	 * Verify that the VHID is valid on the receiving interface.
7928151740cSJosh Paetzel 	 *
7938151740cSJosh Paetzel 	 * There should be just one match.  If there are none
7948151740cSJosh Paetzel 	 * the VHID is not valid and we drop the packet.  If
7958151740cSJosh Paetzel 	 * there are multiple VHID matches, take just the first
7968151740cSJosh Paetzel 	 * one, for compatibility with previous code.  While we're
7978151740cSJosh Paetzel 	 * scanning, check for obvious loops in the network topology
7988151740cSJosh Paetzel 	 * (these should never happen, and as noted above, we may
7998151740cSJosh Paetzel 	 * miss real loops; this is just a double-check).
8008151740cSJosh Paetzel 	 */
8018151740cSJosh Paetzel 	error = 0;
8028151740cSJosh Paetzel 	match = NULL;
8038151740cSJosh Paetzel 	IFNET_FOREACH_IFA(ifp, ifa) {
8048151740cSJosh Paetzel 		if (match == NULL && ifa->ifa_carp != NULL &&
8058151740cSJosh Paetzel 		    ifa->ifa_addr->sa_family == af &&
80637115154SKristof Provost 		    ifa->ifa_carp->sc_vhid == vhid)
8078151740cSJosh Paetzel 			match = ifa;
80837115154SKristof Provost 		if (vhid == 0 && carp_source_is_self(m, ifa, af))
8098151740cSJosh Paetzel 			error = ELOOP;
81008b68b0eSGleb Smirnoff 	}
8118151740cSJosh Paetzel 	ifa = error ? NULL : match;
8128151740cSJosh Paetzel 	if (ifa != NULL)
8138151740cSJosh Paetzel 		ifa_ref(ifa);
814d220759bSGleb Smirnoff 
81508b68b0eSGleb Smirnoff 	if (ifa == NULL) {
8168151740cSJosh Paetzel 		if (error == ELOOP) {
8178151740cSJosh Paetzel 			CARP_DEBUG("dropping looped packet on interface %s\n",
818511a6d5eSKristof Provost 			    if_name(ifp));
8198151740cSJosh Paetzel 			CARPSTATS_INC(carps_badif);	/* ??? */
8208151740cSJosh Paetzel 		} else {
8216bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badvhid);
8228151740cSJosh Paetzel 		}
82337115154SKristof Provost 	}
82437115154SKristof Provost 
82537115154SKristof Provost 	return (ifa);
82637115154SKristof Provost }
82737115154SKristof Provost 
82837115154SKristof Provost static void
82937115154SKristof Provost carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af, int ttl)
83037115154SKristof Provost {
83137115154SKristof Provost 	struct ifnet *ifp = m->m_pkthdr.rcvif;
83237115154SKristof Provost 	struct ifaddr *ifa;
83337115154SKristof Provost 	struct carp_softc *sc;
83437115154SKristof Provost 	uint64_t tmp_counter;
83537115154SKristof Provost 	struct timeval sc_tv, ch_tv;
83637115154SKristof Provost 	bool multicast = false;
83737115154SKristof Provost 
83837115154SKristof Provost 	NET_EPOCH_ASSERT();
839cda57d95SGleb Smirnoff 	MPASS(ch->carp_version == CARP_VERSION_CARP);
84037115154SKristof Provost 
84137115154SKristof Provost 	ifa = carp_find_ifa(m, af, ch->carp_vhid);
84237115154SKristof Provost 	if (ifa == NULL) {
843a9771948SGleb Smirnoff 		m_freem(m);
844a9771948SGleb Smirnoff 		return;
845a9771948SGleb Smirnoff 	}
846a9771948SGleb Smirnoff 
84737115154SKristof Provost 	sc = ifa->ifa_carp;
84837115154SKristof Provost 	CARP_LOCK(sc);
84937115154SKristof Provost 
850a9771948SGleb Smirnoff 	/* verify the CARP version. */
851cda57d95SGleb Smirnoff 	if (sc->sc_version != CARP_VERSION_CARP) {
85237115154SKristof Provost 		CARP_UNLOCK(sc);
85337115154SKristof Provost 
8546bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badver);
855511a6d5eSKristof Provost 		CARP_DEBUG("%s: invalid version %d\n", if_name(ifp),
8561e9e6572SGleb Smirnoff 		    ch->carp_version);
85708b68b0eSGleb Smirnoff 		ifa_free(ifa);
858a9771948SGleb Smirnoff 		m_freem(m);
859a9771948SGleb Smirnoff 		return;
860a9771948SGleb Smirnoff 	}
861a9771948SGleb Smirnoff 
86213781800SKristof Provost 	if (ifa->ifa_addr->sa_family == AF_INET) {
86313781800SKristof Provost 		multicast = IN_MULTICAST(sc->sc_carpaddr.s_addr);
86413781800SKristof Provost 	} else {
86513781800SKristof Provost 		multicast = IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6);
86613781800SKristof Provost 	}
86708b68b0eSGleb Smirnoff 	ifa_free(ifa);
86808b68b0eSGleb Smirnoff 
86913781800SKristof Provost 	/* verify that the IP TTL is 255, but only if we're not in unicast mode. */
87013781800SKristof Provost 	if (multicast && ttl != CARP_DFLTTL) {
87113781800SKristof Provost 		CARPSTATS_INC(carps_badttl);
87213781800SKristof Provost 		CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
873511a6d5eSKristof Provost 		    ttl, if_name(m->m_pkthdr.rcvif));
87413781800SKristof Provost 		goto out;
87513781800SKristof Provost 	}
87613781800SKristof Provost 
877a9771948SGleb Smirnoff 	if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) {
8786bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badauth);
87908b68b0eSGleb Smirnoff 		CARP_DEBUG("%s: incorrect hash for VHID %u@%s\n", __func__,
880511a6d5eSKristof Provost 		    sc->sc_vhid, if_name(ifp));
88108b68b0eSGleb Smirnoff 		goto out;
882a9771948SGleb Smirnoff 	}
883a9771948SGleb Smirnoff 
884a9771948SGleb Smirnoff 	tmp_counter = ntohl(ch->carp_counter[0]);
885a9771948SGleb Smirnoff 	tmp_counter = tmp_counter<<32;
886a9771948SGleb Smirnoff 	tmp_counter += ntohl(ch->carp_counter[1]);
887a9771948SGleb Smirnoff 
888a9771948SGleb Smirnoff 	/* XXX Replay protection goes here */
889a9771948SGleb Smirnoff 
890*a254d687SGleb Smirnoff 	sc->sc_init_counter = false;
891a9771948SGleb Smirnoff 	sc->sc_counter = tmp_counter;
892a9771948SGleb Smirnoff 
893a9771948SGleb Smirnoff 	sc_tv.tv_sec = sc->sc_advbase;
894f08535f8SGleb Smirnoff 	sc_tv.tv_usec = DEMOTE_ADVSKEW(sc) * 1000000 / 256;
895a9771948SGleb Smirnoff 	ch_tv.tv_sec = ch->carp_advbase;
896a9771948SGleb Smirnoff 	ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256;
897a9771948SGleb Smirnoff 
898a9771948SGleb Smirnoff 	switch (sc->sc_state) {
899a9771948SGleb Smirnoff 	case INIT:
900a9771948SGleb Smirnoff 		break;
901a9771948SGleb Smirnoff 	case MASTER:
902a9771948SGleb Smirnoff 		/*
903a9771948SGleb Smirnoff 		 * If we receive an advertisement from a master who's going to
904a9771948SGleb Smirnoff 		 * be more frequent than us, go into BACKUP state.
905a9771948SGleb Smirnoff 		 */
906a9771948SGleb Smirnoff 		if (timevalcmp(&sc_tv, &ch_tv, >) ||
907a9771948SGleb Smirnoff 		    timevalcmp(&sc_tv, &ch_tv, ==)) {
908a9771948SGleb Smirnoff 			callout_stop(&sc->sc_ad_tmo);
909d01641e2SWill Andrews 			carp_set_state(sc, BACKUP,
910d01641e2SWill Andrews 			    "more frequent advertisement received");
911a9771948SGleb Smirnoff 			carp_setrun(sc, 0);
91208b68b0eSGleb Smirnoff 			carp_delroute(sc);
913a9771948SGleb Smirnoff 		}
914a9771948SGleb Smirnoff 		break;
915a9771948SGleb Smirnoff 	case BACKUP:
916a9771948SGleb Smirnoff 		/*
917a9771948SGleb Smirnoff 		 * If we're pre-empting masters who advertise slower than us,
918a9771948SGleb Smirnoff 		 * and this one claims to be slower, treat him as down.
919a9771948SGleb Smirnoff 		 */
920c5c392e7SMikolaj Golub 		if (V_carp_preempt && timevalcmp(&sc_tv, &ch_tv, <)) {
921d01641e2SWill Andrews 			carp_master_down_locked(sc,
922d01641e2SWill Andrews 			    "preempting a slower master");
923a9771948SGleb Smirnoff 			break;
924a9771948SGleb Smirnoff 		}
925a9771948SGleb Smirnoff 
926a9771948SGleb Smirnoff 		/*
927a9771948SGleb Smirnoff 		 *  If the master is going to advertise at such a low frequency
928a9771948SGleb Smirnoff 		 *  that he's guaranteed to time out, we'd might as well just
929a9771948SGleb Smirnoff 		 *  treat him as timed out now.
930a9771948SGleb Smirnoff 		 */
931a9771948SGleb Smirnoff 		sc_tv.tv_sec = sc->sc_advbase * 3;
932a9771948SGleb Smirnoff 		if (timevalcmp(&sc_tv, &ch_tv, <)) {
933d01641e2SWill Andrews 			carp_master_down_locked(sc, "master will time out");
934a9771948SGleb Smirnoff 			break;
935a9771948SGleb Smirnoff 		}
936a9771948SGleb Smirnoff 
937a9771948SGleb Smirnoff 		/*
938a9771948SGleb Smirnoff 		 * Otherwise, we reset the counter and wait for the next
939a9771948SGleb Smirnoff 		 * advertisement.
940a9771948SGleb Smirnoff 		 */
941a9771948SGleb Smirnoff 		carp_setrun(sc, af);
942a9771948SGleb Smirnoff 		break;
943a9771948SGleb Smirnoff 	}
944a9771948SGleb Smirnoff 
94508b68b0eSGleb Smirnoff out:
94608b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
947a9771948SGleb Smirnoff 	m_freem(m);
948a9771948SGleb Smirnoff }
949a9771948SGleb Smirnoff 
95037115154SKristof Provost static void
95137115154SKristof Provost vrrp_input_c(struct mbuf *m, int off, sa_family_t af, int ttl,
95237115154SKristof Provost     int len, uint16_t phdrcksum)
95337115154SKristof Provost {
95437115154SKristof Provost 	struct vrrpv3_header *vh = mtodo(m, off);
95537115154SKristof Provost 	struct ifnet *ifp = m->m_pkthdr.rcvif;
95637115154SKristof Provost 	struct ifaddr *ifa;
95737115154SKristof Provost 	struct carp_softc *sc;
95837115154SKristof Provost 
95937115154SKristof Provost 	NET_EPOCH_ASSERT();
960cda57d95SGleb Smirnoff 	MPASS(vh->vrrp_version == CARP_VERSION_VRRPv3);
96137115154SKristof Provost 
96237115154SKristof Provost 	ifa = carp_find_ifa(m, af, vh->vrrp_vrtid);
96337115154SKristof Provost 	if (ifa == NULL) {
96437115154SKristof Provost 		m_freem(m);
96537115154SKristof Provost 		return;
96637115154SKristof Provost 	}
96737115154SKristof Provost 
96837115154SKristof Provost 	sc = ifa->ifa_carp;
96937115154SKristof Provost 	CARP_LOCK(sc);
97037115154SKristof Provost 
97137115154SKristof Provost 	ifa_free(ifa);
97237115154SKristof Provost 
97337115154SKristof Provost 	/* verify the CARP version. */
974cda57d95SGleb Smirnoff 	if (sc->sc_version != CARP_VERSION_VRRPv3) {
97537115154SKristof Provost 		CARP_UNLOCK(sc);
97637115154SKristof Provost 
97737115154SKristof Provost 		CARPSTATS_INC(carps_badver);
97837115154SKristof Provost 		CARP_DEBUG("%s: invalid version %d\n", if_name(ifp),
97937115154SKristof Provost 		    vh->vrrp_version);
98037115154SKristof Provost 		m_freem(m);
98137115154SKristof Provost 		return;
98237115154SKristof Provost 	}
98337115154SKristof Provost 
98437115154SKristof Provost 	/* verify that the IP TTL is 255. */
98537115154SKristof Provost 	if (ttl != CARP_DFLTTL) {
98637115154SKristof Provost 		CARPSTATS_INC(carps_badttl);
98737115154SKristof Provost 		CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
98837115154SKristof Provost 		    ttl, if_name(m->m_pkthdr.rcvif));
98937115154SKristof Provost 		goto out;
99037115154SKristof Provost 	}
99137115154SKristof Provost 
99237115154SKristof Provost 	if (vrrp_checksum_verify(m, off, len, phdrcksum)) {
99337115154SKristof Provost 		CARPSTATS_INC(carps_badsum);
99437115154SKristof Provost 		CARP_DEBUG("%s: incorrect checksum for VRID %u@%s\n", __func__,
99537115154SKristof Provost 		    sc->sc_vhid, if_name(ifp));
99637115154SKristof Provost 		goto out;
99737115154SKristof Provost 	}
99837115154SKristof Provost 
99937115154SKristof Provost 	/* RFC9568, 7.1 Receiving VRRP packets. */
100037115154SKristof Provost 	if (sc->sc_vrrp_prio == 255) {
100137115154SKristof Provost 		CARP_DEBUG("%s: our priority is 255. Ignore peer announcement.\n",
100237115154SKristof Provost 		    __func__);
100337115154SKristof Provost 		goto out;
100437115154SKristof Provost 	}
100537115154SKristof Provost 
100637115154SKristof Provost 	/* XXX TODO Check IP address payload. */
100737115154SKristof Provost 
100837115154SKristof Provost 	sc->sc_vrrp_master_inter = ntohs(vh->vrrp_max_adver_int);
100937115154SKristof Provost 
101037115154SKristof Provost 	switch (sc->sc_state) {
101137115154SKristof Provost 	case INIT:
101237115154SKristof Provost 		break;
101337115154SKristof Provost 	case MASTER:
101437115154SKristof Provost 		/*
101537115154SKristof Provost 		 * If we receive an advertisement from a master who's going to
101637115154SKristof Provost 		 * be more frequent than us, go into BACKUP state.
101737115154SKristof Provost 		 * Same if the peer has a higher priority than us.
101837115154SKristof Provost 		 */
101937115154SKristof Provost 		if (ntohs(vh->vrrp_max_adver_int) < sc->sc_vrrp_adv_inter ||
102037115154SKristof Provost 		    vh->vrrp_priority > sc->sc_vrrp_prio) {
102137115154SKristof Provost 			callout_stop(&sc->sc_ad_tmo);
102237115154SKristof Provost 			carp_set_state(sc, BACKUP,
102337115154SKristof Provost 			    "more frequent advertisement received");
102437115154SKristof Provost 			carp_setrun(sc, 0);
102537115154SKristof Provost 			carp_delroute(sc);
102637115154SKristof Provost 		}
102737115154SKristof Provost 		break;
102837115154SKristof Provost 	case BACKUP:
102937115154SKristof Provost 		/*
103037115154SKristof Provost 		 * If we're pre-empting masters who advertise slower than us,
103137115154SKristof Provost 		 * and this one claims to be slower, treat him as down.
103237115154SKristof Provost 		 */
103337115154SKristof Provost 		if (V_carp_preempt && (ntohs(vh->vrrp_max_adver_int) > sc->sc_vrrp_adv_inter
103437115154SKristof Provost 		    || vh->vrrp_priority < sc->sc_vrrp_prio)) {
103537115154SKristof Provost 			carp_master_down_locked(sc,
103637115154SKristof Provost 			    "preempting a slower master");
103737115154SKristof Provost 			break;
103837115154SKristof Provost 		}
103937115154SKristof Provost 
104037115154SKristof Provost 		/*
104137115154SKristof Provost 		 * Otherwise, we reset the counter and wait for the next
104237115154SKristof Provost 		 * advertisement.
104337115154SKristof Provost 		 */
104437115154SKristof Provost 		carp_setrun(sc, af);
104537115154SKristof Provost 		break;
104637115154SKristof Provost 	}
104737115154SKristof Provost 
104837115154SKristof Provost out:
104937115154SKristof Provost 	CARP_UNLOCK(sc);
105037115154SKristof Provost 	m_freem(m);
105137115154SKristof Provost }
105237115154SKristof Provost 
105337115154SKristof Provost static int
105437115154SKristof Provost carp_tag(struct carp_softc *sc, struct mbuf *m)
105537115154SKristof Provost {
105637115154SKristof Provost 	struct m_tag *mtag;
105737115154SKristof Provost 
105837115154SKristof Provost 	/* Tag packet for carp_output */
1059601438fbSGleb Smirnoff 	if ((mtag = m_tag_get(PACKET_TAG_CARP, sizeof(sc->sc_vhid),
106037115154SKristof Provost 	    M_NOWAIT)) == NULL) {
106137115154SKristof Provost 		m_freem(m);
106237115154SKristof Provost 		CARPSTATS_INC(carps_onomem);
106337115154SKristof Provost 		return (ENOMEM);
106437115154SKristof Provost 	}
1065601438fbSGleb Smirnoff 	bcopy(&sc->sc_vhid, mtag + 1, sizeof(sc->sc_vhid));
106637115154SKristof Provost 	m_tag_prepend(m, mtag);
106737115154SKristof Provost 
106837115154SKristof Provost 	return (0);
106937115154SKristof Provost }
107037115154SKristof Provost 
1071601438fbSGleb Smirnoff static void
1072a9771948SGleb Smirnoff carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch)
1073a9771948SGleb Smirnoff {
1074a9771948SGleb Smirnoff 
1075601438fbSGleb Smirnoff 	MPASS(sc->sc_version == CARP_VERSION_CARP);
1076601438fbSGleb Smirnoff 
1077a9771948SGleb Smirnoff 	if (sc->sc_init_counter) {
1078a9771948SGleb Smirnoff 		/* this could also be seconds since unix epoch */
1079a9771948SGleb Smirnoff 		sc->sc_counter = arc4random();
1080a9771948SGleb Smirnoff 		sc->sc_counter = sc->sc_counter << 32;
1081a9771948SGleb Smirnoff 		sc->sc_counter += arc4random();
1082a9771948SGleb Smirnoff 	} else
1083a9771948SGleb Smirnoff 		sc->sc_counter++;
1084a9771948SGleb Smirnoff 
1085a9771948SGleb Smirnoff 	ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff);
1086a9771948SGleb Smirnoff 	ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff);
1087a9771948SGleb Smirnoff 
1088a9771948SGleb Smirnoff 	carp_hmac_generate(sc, ch->carp_counter, ch->carp_md);
1089a9771948SGleb Smirnoff }
1090a9771948SGleb Smirnoff 
10915ee92cbdSGleb Smirnoff static inline void
10925ee92cbdSGleb Smirnoff send_ad_locked(struct carp_softc *sc)
10935ee92cbdSGleb Smirnoff {
10945ee92cbdSGleb Smirnoff 	switch (sc->sc_version) {
10955ee92cbdSGleb Smirnoff 	case CARP_VERSION_CARP:
10965ee92cbdSGleb Smirnoff 		carp_send_ad_locked(sc);
10975ee92cbdSGleb Smirnoff 		break;
10985ee92cbdSGleb Smirnoff 	case CARP_VERSION_VRRPv3:
10995ee92cbdSGleb Smirnoff 		vrrp_send_ad_locked(sc);
11005ee92cbdSGleb Smirnoff 		break;
11015ee92cbdSGleb Smirnoff 	}
11025ee92cbdSGleb Smirnoff }
11035ee92cbdSGleb Smirnoff 
1104f08535f8SGleb Smirnoff /*
1105f08535f8SGleb Smirnoff  * To avoid LORs and possible recursions this function shouldn't
1106f08535f8SGleb Smirnoff  * be called directly, but scheduled via taskqueue.
1107f08535f8SGleb Smirnoff  */
11085c1f0f6dSGleb Smirnoff static void
1109f08535f8SGleb Smirnoff carp_send_ad_all(void *ctx __unused, int pending __unused)
1110a9771948SGleb Smirnoff {
1111d92d54d5SGleb Smirnoff 	struct carp_softc *sc;
11121d126e9bSKristof Provost 	struct epoch_tracker et;
1113a9771948SGleb Smirnoff 
11141d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
1115d92d54d5SGleb Smirnoff 	mtx_lock(&carp_mtx);
111608b68b0eSGleb Smirnoff 	LIST_FOREACH(sc, &carp_list, sc_next)
1117f08535f8SGleb Smirnoff 		if (sc->sc_state == MASTER) {
111808b68b0eSGleb Smirnoff 			CARP_LOCK(sc);
1119afdbac98SGleb Smirnoff 			CURVNET_SET(sc->sc_carpdev->if_vnet);
11205ee92cbdSGleb Smirnoff 			send_ad_locked(sc);
1121afdbac98SGleb Smirnoff 			CURVNET_RESTORE();
112208b68b0eSGleb Smirnoff 			CARP_UNLOCK(sc);
1123a9771948SGleb Smirnoff 		}
1124d92d54d5SGleb Smirnoff 	mtx_unlock(&carp_mtx);
11251d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1126a9771948SGleb Smirnoff }
1127a9771948SGleb Smirnoff 
1128afdbac98SGleb Smirnoff /* Send a periodic advertisement, executed in callout context. */
11295c1f0f6dSGleb Smirnoff static void
11305ee92cbdSGleb Smirnoff carp_callout(void *v)
1131a9771948SGleb Smirnoff {
1132d220759bSGleb Smirnoff 	struct carp_softc *sc = v;
11331d126e9bSKristof Provost 	struct epoch_tracker et;
1134d220759bSGleb Smirnoff 
11351d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
113608b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1137afdbac98SGleb Smirnoff 	CURVNET_SET(sc->sc_carpdev->if_vnet);
11385ee92cbdSGleb Smirnoff 	send_ad_locked(sc);
1139afdbac98SGleb Smirnoff 	CURVNET_RESTORE();
114008b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
11411d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1142d220759bSGleb Smirnoff }
1143d220759bSGleb Smirnoff 
1144d220759bSGleb Smirnoff static void
11454a2dd8d4SGleb Smirnoff carp_send_ad_error(struct carp_softc *sc, int error)
11464a2dd8d4SGleb Smirnoff {
11474a2dd8d4SGleb Smirnoff 
11489a8cf950SGleb Smirnoff 	/*
11496bce41a3SGordon Bergling 	 * We track errors and successful sends with this logic:
11509a8cf950SGleb Smirnoff 	 * - Any error resets success counter to 0.
11519a8cf950SGleb Smirnoff 	 * - MAX_ERRORS triggers demotion.
11529a8cf950SGleb Smirnoff 	 * - MIN_SUCCESS successes resets error counter to 0.
11539a8cf950SGleb Smirnoff 	 * - MIN_SUCCESS reverts demotion, if it was triggered before.
11549a8cf950SGleb Smirnoff 	 */
11554a2dd8d4SGleb Smirnoff 	if (error) {
11564a2dd8d4SGleb Smirnoff 		if (sc->sc_sendad_errors < INT_MAX)
11574a2dd8d4SGleb Smirnoff 			sc->sc_sendad_errors++;
11584a2dd8d4SGleb Smirnoff 		if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) {
11594a2dd8d4SGleb Smirnoff 			static const char fmt[] = "send error %d on %s";
11604a2dd8d4SGleb Smirnoff 			char msg[sizeof(fmt) + IFNAMSIZ];
11614a2dd8d4SGleb Smirnoff 
1162511a6d5eSKristof Provost 			sprintf(msg, fmt, error, if_name(sc->sc_carpdev));
11634a2dd8d4SGleb Smirnoff 			carp_demote_adj(V_carp_senderr_adj, msg);
11644a2dd8d4SGleb Smirnoff 		}
11654a2dd8d4SGleb Smirnoff 		sc->sc_sendad_success = 0;
11669a8cf950SGleb Smirnoff 	} else if (sc->sc_sendad_errors > 0) {
11679a8cf950SGleb Smirnoff 		if (++sc->sc_sendad_success >= CARP_SENDAD_MIN_SUCCESS) {
11689a8cf950SGleb Smirnoff 			if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
11694a2dd8d4SGleb Smirnoff 				static const char fmt[] = "send ok on %s";
11704a2dd8d4SGleb Smirnoff 				char msg[sizeof(fmt) + IFNAMSIZ];
11714a2dd8d4SGleb Smirnoff 
1172511a6d5eSKristof Provost 				sprintf(msg, fmt, if_name(sc->sc_carpdev));
11734a2dd8d4SGleb Smirnoff 				carp_demote_adj(-V_carp_senderr_adj, msg);
11749a8cf950SGleb Smirnoff 			}
11754a2dd8d4SGleb Smirnoff 			sc->sc_sendad_errors = 0;
11769a8cf950SGleb Smirnoff 		}
11774a2dd8d4SGleb Smirnoff 	}
11784a2dd8d4SGleb Smirnoff }
11794a2dd8d4SGleb Smirnoff 
11808151740cSJosh Paetzel /*
11818151740cSJosh Paetzel  * Pick the best ifaddr on the given ifp for sending CARP
11828151740cSJosh Paetzel  * advertisements.
11838151740cSJosh Paetzel  *
11848151740cSJosh Paetzel  * "Best" here is defined by ifa_preferred().  This function is much
11858151740cSJosh Paetzel  * much like ifaof_ifpforaddr() except that we just use ifa_preferred().
11868151740cSJosh Paetzel  *
11878151740cSJosh Paetzel  * (This could be simplified to return the actual address, except that
11888151740cSJosh Paetzel  * it has a different format in AF_INET and AF_INET6.)
11898151740cSJosh Paetzel  */
11908151740cSJosh Paetzel static struct ifaddr *
11918151740cSJosh Paetzel carp_best_ifa(int af, struct ifnet *ifp)
11928151740cSJosh Paetzel {
11938151740cSJosh Paetzel 	struct ifaddr *ifa, *best;
11948151740cSJosh Paetzel 
1195b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1196b8a6e03fSGleb Smirnoff 
11978151740cSJosh Paetzel 	if (af >= AF_MAX)
11988151740cSJosh Paetzel 		return (NULL);
11998151740cSJosh Paetzel 	best = NULL;
1200d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
12018151740cSJosh Paetzel 		if (ifa->ifa_addr->sa_family == af &&
12028151740cSJosh Paetzel 		    (best == NULL || ifa_preferred(best, ifa)))
12038151740cSJosh Paetzel 			best = ifa;
12048151740cSJosh Paetzel 	}
12058151740cSJosh Paetzel 	if (best != NULL)
12068151740cSJosh Paetzel 		ifa_ref(best);
12078151740cSJosh Paetzel 	return (best);
12088151740cSJosh Paetzel }
12098151740cSJosh Paetzel 
12104a2dd8d4SGleb Smirnoff static void
1211d220759bSGleb Smirnoff carp_send_ad_locked(struct carp_softc *sc)
1212d220759bSGleb Smirnoff {
1213a9771948SGleb Smirnoff 	struct carp_header ch;
1214a9771948SGleb Smirnoff 	struct timeval tv;
121508b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
1216a9771948SGleb Smirnoff 	struct carp_header *ch_ptr;
1217a9771948SGleb Smirnoff 	struct mbuf *m;
121808b68b0eSGleb Smirnoff 	int len, advskew;
1219a9771948SGleb Smirnoff 
12201d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
122108b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
12225ee92cbdSGleb Smirnoff 	MPASS(sc->sc_version == CARP_VERSION_CARP);
122337115154SKristof Provost 
1224f08535f8SGleb Smirnoff 	advskew = DEMOTE_ADVSKEW(sc);
122508b68b0eSGleb Smirnoff 	tv.tv_sec = sc->sc_advbase;
1226a9771948SGleb Smirnoff 	tv.tv_usec = advskew * 1000000 / 256;
1227a9771948SGleb Smirnoff 
122837115154SKristof Provost 	ch.carp_version = CARP_VERSION_CARP;
1229a9771948SGleb Smirnoff 	ch.carp_type = CARP_ADVERTISEMENT;
1230a9771948SGleb Smirnoff 	ch.carp_vhid = sc->sc_vhid;
123108b68b0eSGleb Smirnoff 	ch.carp_advbase = sc->sc_advbase;
1232a9771948SGleb Smirnoff 	ch.carp_advskew = advskew;
1233a9771948SGleb Smirnoff 	ch.carp_authlen = 7;	/* XXX DEFINE */
1234a9771948SGleb Smirnoff 	ch.carp_pad1 = 0;	/* must be zero */
1235a9771948SGleb Smirnoff 	ch.carp_cksum = 0;
1236a9771948SGleb Smirnoff 
123708b68b0eSGleb Smirnoff 	/* XXXGL: OpenBSD picks first ifaddr with needed family. */
123808b68b0eSGleb Smirnoff 
1239a9771948SGleb Smirnoff #ifdef INET
124008b68b0eSGleb Smirnoff 	if (sc->sc_naddrs) {
1241a9771948SGleb Smirnoff 		struct ip *ip;
1242a9771948SGleb Smirnoff 
1243dc4ad05eSGleb Smirnoff 		m = m_gethdr(M_NOWAIT, MT_DATA);
1244a9771948SGleb Smirnoff 		if (m == NULL) {
12456bf65bcfSRobert Watson 			CARPSTATS_INC(carps_onomem);
1246891122d1SGleb Smirnoff 			goto resched;
1247a9771948SGleb Smirnoff 		}
1248a9771948SGleb Smirnoff 		len = sizeof(*ip) + sizeof(ch);
1249a9771948SGleb Smirnoff 		m->m_pkthdr.len = len;
1250a9771948SGleb Smirnoff 		m->m_pkthdr.rcvif = NULL;
1251a9771948SGleb Smirnoff 		m->m_len = len;
1252ed6a66caSRobert Watson 		M_ALIGN(m, m->m_len);
125313781800SKristof Provost 		if (IN_MULTICAST(sc->sc_carpaddr.s_addr))
1254a9771948SGleb Smirnoff 			m->m_flags |= M_MCAST;
1255a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
1256a9771948SGleb Smirnoff 		ip->ip_v = IPVERSION;
1257a9771948SGleb Smirnoff 		ip->ip_hl = sizeof(*ip) >> 2;
12580d3d234cSKristof Provost 		ip->ip_tos = V_carp_dscp << IPTOS_DSCP_OFFSET;
12598f134647SGleb Smirnoff 		ip->ip_len = htons(len);
12608f134647SGleb Smirnoff 		ip->ip_off = htons(IP_DF);
1261a9771948SGleb Smirnoff 		ip->ip_ttl = CARP_DFLTTL;
1262a9771948SGleb Smirnoff 		ip->ip_p = IPPROTO_CARP;
1263a9771948SGleb Smirnoff 		ip->ip_sum = 0;
12646d947416SGleb Smirnoff 		ip_fillid(ip);
126508b68b0eSGleb Smirnoff 
12668151740cSJosh Paetzel 		ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
126708b68b0eSGleb Smirnoff 		if (ifa != NULL) {
126808b68b0eSGleb Smirnoff 			ip->ip_src.s_addr =
126908b68b0eSGleb Smirnoff 			    ifatoia(ifa)->ia_addr.sin_addr.s_addr;
127008b68b0eSGleb Smirnoff 			ifa_free(ifa);
127108b68b0eSGleb Smirnoff 		} else
127208b68b0eSGleb Smirnoff 			ip->ip_src.s_addr = 0;
127313781800SKristof Provost 		ip->ip_dst = sc->sc_carpaddr;
1274a9771948SGleb Smirnoff 
1275a9771948SGleb Smirnoff 		ch_ptr = (struct carp_header *)(&ip[1]);
1276a9771948SGleb Smirnoff 		bcopy(&ch, ch_ptr, sizeof(ch));
1277601438fbSGleb Smirnoff 		carp_prepare_ad(m, sc, ch_ptr);
1278601438fbSGleb Smirnoff 		if (IN_MULTICAST(ntohl(sc->sc_carpaddr.s_addr)) &&
1279601438fbSGleb Smirnoff 		    carp_tag(sc, m) != 0)
1280891122d1SGleb Smirnoff 			goto resched;
1281a9771948SGleb Smirnoff 
1282a9771948SGleb Smirnoff 		m->m_data += sizeof(*ip);
1283c4d06976SGleb Smirnoff 		ch_ptr->carp_cksum = in_cksum(m, len - sizeof(*ip));
1284a9771948SGleb Smirnoff 		m->m_data -= sizeof(*ip);
1285a9771948SGleb Smirnoff 
12866bf65bcfSRobert Watson 		CARPSTATS_INC(carps_opackets);
1287a9771948SGleb Smirnoff 
12884a2dd8d4SGleb Smirnoff 		carp_send_ad_error(sc, ip_output(m, NULL, NULL, IP_RAWOUTPUT,
12894a2dd8d4SGleb Smirnoff 		    &sc->sc_carpdev->if_carp->cif_imo, NULL));
1290a9771948SGleb Smirnoff 	}
1291a9771948SGleb Smirnoff #endif /* INET */
1292a9771948SGleb Smirnoff #ifdef INET6
129308b68b0eSGleb Smirnoff 	if (sc->sc_naddrs6) {
1294a9771948SGleb Smirnoff 		struct ip6_hdr *ip6;
1295a9771948SGleb Smirnoff 
1296dc4ad05eSGleb Smirnoff 		m = m_gethdr(M_NOWAIT, MT_DATA);
1297a9771948SGleb Smirnoff 		if (m == NULL) {
12986bf65bcfSRobert Watson 			CARPSTATS_INC(carps_onomem);
1299891122d1SGleb Smirnoff 			goto resched;
1300a9771948SGleb Smirnoff 		}
1301a9771948SGleb Smirnoff 		len = sizeof(*ip6) + sizeof(ch);
1302a9771948SGleb Smirnoff 		m->m_pkthdr.len = len;
1303a9771948SGleb Smirnoff 		m->m_pkthdr.rcvif = NULL;
1304a9771948SGleb Smirnoff 		m->m_len = len;
1305ed6a66caSRobert Watson 		M_ALIGN(m, m->m_len);
1306a9771948SGleb Smirnoff 		ip6 = mtod(m, struct ip6_hdr *);
1307a9771948SGleb Smirnoff 		bzero(ip6, sizeof(*ip6));
1308a9771948SGleb Smirnoff 		ip6->ip6_vfc |= IPV6_VERSION;
13090d3d234cSKristof Provost 		/* Traffic class isn't defined in ip6 struct instead
13100d3d234cSKristof Provost 		 * it gets offset into flowid field */
13110d3d234cSKristof Provost 		ip6->ip6_flow |= htonl(V_carp_dscp << (IPV6_FLOWLABEL_LEN +
13120d3d234cSKristof Provost 		    IPTOS_DSCP_OFFSET));
1313a9771948SGleb Smirnoff 		ip6->ip6_hlim = CARP_DFLTTL;
1314a9771948SGleb Smirnoff 		ip6->ip6_nxt = IPPROTO_CARP;
1315a9771948SGleb Smirnoff 
131608b68b0eSGleb Smirnoff 		/* set the source address */
13178151740cSJosh Paetzel 		ifa = carp_best_ifa(AF_INET6, sc->sc_carpdev);
131808b68b0eSGleb Smirnoff 		if (ifa != NULL) {
131908b68b0eSGleb Smirnoff 			bcopy(IFA_IN6(ifa), &ip6->ip6_src,
132008b68b0eSGleb Smirnoff 			    sizeof(struct in6_addr));
132108b68b0eSGleb Smirnoff 			ifa_free(ifa);
132208b68b0eSGleb Smirnoff 		} else
132308b68b0eSGleb Smirnoff 			/* This should never happen with IPv6. */
132408b68b0eSGleb Smirnoff 			bzero(&ip6->ip6_src, sizeof(struct in6_addr));
132508b68b0eSGleb Smirnoff 
132608b68b0eSGleb Smirnoff 		/* Set the multicast destination. */
132713781800SKristof Provost 		memcpy(&ip6->ip6_dst, &sc->sc_carpaddr6, sizeof(ip6->ip6_dst));
1328c2c28c0fSKristof Provost 		if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
1329c2c28c0fSKristof Provost 		    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) {
13307002145dSBjoern A. Zeeb 			if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
13317002145dSBjoern A. Zeeb 				m_freem(m);
1332e81ab876SGleb Smirnoff 				CARP_DEBUG("%s: in6_setscope failed\n", __func__);
1333891122d1SGleb Smirnoff 				goto resched;
13347002145dSBjoern A. Zeeb 			}
133513781800SKristof Provost 		}
1336a9771948SGleb Smirnoff 
1337a9771948SGleb Smirnoff 		ch_ptr = (struct carp_header *)(&ip6[1]);
1338a9771948SGleb Smirnoff 		bcopy(&ch, ch_ptr, sizeof(ch));
1339601438fbSGleb Smirnoff 		carp_prepare_ad(m, sc, ch_ptr);
1340601438fbSGleb Smirnoff 		if (IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6) &&
1341601438fbSGleb Smirnoff 		    carp_tag(sc, m) != 0)
1342891122d1SGleb Smirnoff 			goto resched;
1343a9771948SGleb Smirnoff 
1344a9771948SGleb Smirnoff 		m->m_data += sizeof(*ip6);
1345c4d06976SGleb Smirnoff 		ch_ptr->carp_cksum = in_cksum(m, len - sizeof(*ip6));
1346a9771948SGleb Smirnoff 		m->m_data -= sizeof(*ip6);
1347a9771948SGleb Smirnoff 
13486bf65bcfSRobert Watson 		CARPSTATS_INC(carps_opackets6);
1349a9771948SGleb Smirnoff 
13504a2dd8d4SGleb Smirnoff 		carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0,
13514a2dd8d4SGleb Smirnoff 		    &sc->sc_carpdev->if_carp->cif_im6o, NULL, NULL));
1352a9771948SGleb Smirnoff 	}
1353a9771948SGleb Smirnoff #endif /* INET6 */
1354a9771948SGleb Smirnoff 
1355891122d1SGleb Smirnoff resched:
13565ee92cbdSGleb Smirnoff 	callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), carp_callout, sc);
135708b68b0eSGleb Smirnoff }
1358a9771948SGleb Smirnoff 
135908b68b0eSGleb Smirnoff static void
136037115154SKristof Provost vrrp_send_ad_locked(struct carp_softc *sc)
136137115154SKristof Provost {
136237115154SKristof Provost 	struct vrrpv3_header *vh_ptr;
136337115154SKristof Provost 	struct ifaddr *ifa;
136437115154SKristof Provost 	struct mbuf *m;
136537115154SKristof Provost 	int len;
136637115154SKristof Provost 	struct vrrpv3_header vh = {
136737115154SKristof Provost 	    .vrrp_version = CARP_VERSION_VRRPv3,
136837115154SKristof Provost 	    .vrrp_type = VRRP_TYPE_ADVERTISEMENT,
136937115154SKristof Provost 	    .vrrp_vrtid = sc->sc_vhid,
137037115154SKristof Provost 	    .vrrp_priority = sc->sc_vrrp_prio,
137137115154SKristof Provost 	    .vrrp_count_addr = 0,
137237115154SKristof Provost 	    .vrrp_max_adver_int = htons(sc->sc_vrrp_adv_inter),
137337115154SKristof Provost 	    .vrrp_checksum = 0,
137437115154SKristof Provost 	};
137537115154SKristof Provost 
137637115154SKristof Provost 	NET_EPOCH_ASSERT();
137737115154SKristof Provost 	CARP_LOCK_ASSERT(sc);
137837115154SKristof Provost 	MPASS(sc->sc_version == CARP_VERSION_VRRPv3);
137937115154SKristof Provost 
138037115154SKristof Provost #ifdef INET
138137115154SKristof Provost 	if (sc->sc_naddrs) {
138237115154SKristof Provost 		struct ip *ip;
138337115154SKristof Provost 
138437115154SKristof Provost 		m = m_gethdr(M_NOWAIT, MT_DATA);
138537115154SKristof Provost 		if (m == NULL) {
138637115154SKristof Provost 			CARPSTATS_INC(carps_onomem);
138737115154SKristof Provost 			goto resched;
138837115154SKristof Provost 		}
138937115154SKristof Provost 		len = sizeof(*ip) + sizeof(vh);
139037115154SKristof Provost 		m->m_pkthdr.len = len;
139137115154SKristof Provost 		m->m_pkthdr.rcvif = NULL;
139237115154SKristof Provost 		m->m_len = len;
139337115154SKristof Provost 		M_ALIGN(m, m->m_len);
139437115154SKristof Provost 		m->m_flags |= M_MCAST;
139537115154SKristof Provost 		ip = mtod(m, struct ip *);
139637115154SKristof Provost 		ip->ip_v = IPVERSION;
139737115154SKristof Provost 		ip->ip_hl = sizeof(*ip) >> 2;
139837115154SKristof Provost 		ip->ip_tos = V_carp_dscp << IPTOS_DSCP_OFFSET;
139937115154SKristof Provost 		ip->ip_off = htons(IP_DF);
140037115154SKristof Provost 		ip->ip_ttl = CARP_DFLTTL;
140137115154SKristof Provost 		ip->ip_p = IPPROTO_CARP;
140237115154SKristof Provost 		ip->ip_sum = 0;
140337115154SKristof Provost 		ip_fillid(ip);
140437115154SKristof Provost 
140537115154SKristof Provost 		ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
140637115154SKristof Provost 		if (ifa != NULL) {
140737115154SKristof Provost 			ip->ip_src.s_addr =
140837115154SKristof Provost 			    ifatoia(ifa)->ia_addr.sin_addr.s_addr;
140937115154SKristof Provost 			ifa_free(ifa);
141037115154SKristof Provost 		} else
141137115154SKristof Provost 			ip->ip_src.s_addr = 0;
141237115154SKristof Provost 		ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP);
141337115154SKristof Provost 
141437115154SKristof Provost 		/* Include the IP addresses in the announcement. */
141537115154SKristof Provost 		for (int i = 0; i < (sc->sc_naddrs + sc->sc_naddrs6); i++) {
141637115154SKristof Provost 			struct sockaddr_in *in;
141737115154SKristof Provost 
141837115154SKristof Provost 			MPASS(sc->sc_ifas[i] != NULL);
141937115154SKristof Provost 			if (sc->sc_ifas[i]->ifa_addr->sa_family != AF_INET)
142037115154SKristof Provost 				continue;
142137115154SKristof Provost 
142237115154SKristof Provost 			in = (struct sockaddr_in *)sc->sc_ifas[i]->ifa_addr;
142337115154SKristof Provost 
142437115154SKristof Provost 			if (m_append(m, sizeof(in->sin_addr),
142537115154SKristof Provost 			    (caddr_t)&in->sin_addr) != 1) {
142637115154SKristof Provost 				m_freem(m);
142737115154SKristof Provost 				goto resched;
142837115154SKristof Provost 			}
142937115154SKristof Provost 
143037115154SKristof Provost 			vh.vrrp_count_addr++;
143137115154SKristof Provost 			len += sizeof(in->sin_addr);
143237115154SKristof Provost 		}
143337115154SKristof Provost 		ip->ip_len = htons(len);
143437115154SKristof Provost 
143537115154SKristof Provost 		vh_ptr = (struct vrrpv3_header *)mtodo(m, sizeof(*ip));
143637115154SKristof Provost 		bcopy(&vh, vh_ptr, sizeof(vh));
143737115154SKristof Provost 
143837115154SKristof Provost 		vh_ptr->vrrp_checksum = in_pseudo(ip->ip_src.s_addr,
143937115154SKristof Provost 		    ip->ip_dst.s_addr,
144037115154SKristof Provost 		    htonl((uint16_t)(len - sizeof(*ip)) + ip->ip_p));
144137115154SKristof Provost 		vh_ptr->vrrp_checksum = in_cksum_skip(m, len, sizeof(*ip));
144237115154SKristof Provost 
144337115154SKristof Provost 		if (carp_tag(sc, m))
144437115154SKristof Provost 			goto resched;
144537115154SKristof Provost 
144637115154SKristof Provost 		CARPSTATS_INC(carps_opackets);
144737115154SKristof Provost 
144837115154SKristof Provost 		carp_send_ad_error(sc, ip_output(m, NULL, NULL, IP_RAWOUTPUT,
144937115154SKristof Provost 		    &sc->sc_carpdev->if_carp->cif_imo, NULL));
145037115154SKristof Provost 	}
145137115154SKristof Provost #endif
145237115154SKristof Provost #ifdef INET6
145337115154SKristof Provost 	if (sc->sc_naddrs6) {
145437115154SKristof Provost 		struct ip6_hdr *ip6;
145537115154SKristof Provost 
145637115154SKristof Provost 		m = m_gethdr(M_NOWAIT, MT_DATA);
145737115154SKristof Provost 		if (m == NULL) {
145837115154SKristof Provost 			CARPSTATS_INC(carps_onomem);
145937115154SKristof Provost 			goto resched;
146037115154SKristof Provost 		}
146137115154SKristof Provost 		len = sizeof(*ip6) + sizeof(vh);
146237115154SKristof Provost 		m->m_pkthdr.len = len;
146337115154SKristof Provost 		m->m_pkthdr.rcvif = NULL;
146437115154SKristof Provost 		m->m_len = len;
146537115154SKristof Provost 		M_ALIGN(m, m->m_len);
146637115154SKristof Provost 		m->m_flags |= M_MCAST;
146737115154SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
146837115154SKristof Provost 		bzero(ip6, sizeof(*ip6));
146937115154SKristof Provost 		ip6->ip6_vfc |= IPV6_VERSION;
147037115154SKristof Provost 		/* Traffic class isn't defined in ip6 struct instead
147137115154SKristof Provost 		 * it gets offset into flowid field */
147237115154SKristof Provost 		ip6->ip6_flow |= htonl(V_carp_dscp << (IPV6_FLOWLABEL_LEN +
147337115154SKristof Provost 		    IPTOS_DSCP_OFFSET));
147437115154SKristof Provost 		ip6->ip6_hlim = CARP_DFLTTL;
147537115154SKristof Provost 		ip6->ip6_nxt = IPPROTO_CARP;
147637115154SKristof Provost 
147737115154SKristof Provost 		/* set the source address */
147837115154SKristof Provost 		ifa = carp_best_ifa(AF_INET6, sc->sc_carpdev);
147937115154SKristof Provost 		if (ifa != NULL) {
148037115154SKristof Provost 			bcopy(IFA_IN6(ifa), &ip6->ip6_src,
148137115154SKristof Provost 			    sizeof(struct in6_addr));
148237115154SKristof Provost 			ifa_free(ifa);
148337115154SKristof Provost 		} else
148437115154SKristof Provost 			/* This should never happen with IPv6. */
148537115154SKristof Provost 			bzero(&ip6->ip6_src, sizeof(struct in6_addr));
148637115154SKristof Provost 
148737115154SKristof Provost 		/* Set the multicast destination. */
148837115154SKristof Provost 		bzero(&ip6->ip6_dst, sizeof(ip6->ip6_dst));
148937115154SKristof Provost 		ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
149037115154SKristof Provost 		ip6->ip6_dst.s6_addr8[15] = 0x12;
149137115154SKristof Provost 
149237115154SKristof Provost 		/* Include the IP addresses in the announcement. */
149337115154SKristof Provost 		len = sizeof(vh);
149437115154SKristof Provost 		for (int i = 0; i < (sc->sc_naddrs + sc->sc_naddrs6); i++) {
149537115154SKristof Provost 			struct sockaddr_in6 *in6;
149637115154SKristof Provost 
149737115154SKristof Provost 			MPASS(sc->sc_ifas[i] != NULL);
149837115154SKristof Provost 			if (sc->sc_ifas[i]->ifa_addr->sa_family != AF_INET6)
149937115154SKristof Provost 				continue;
150037115154SKristof Provost 
150137115154SKristof Provost 			in6 = (struct sockaddr_in6 *)sc->sc_ifas[i]->ifa_addr;
150237115154SKristof Provost 
150337115154SKristof Provost 			if (m_append(m, sizeof(in6->sin6_addr),
150437115154SKristof Provost 			    (char *)&in6->sin6_addr) != 1) {
150537115154SKristof Provost 				m_freem(m);
150637115154SKristof Provost 				goto resched;
150737115154SKristof Provost 			}
150837115154SKristof Provost 
150937115154SKristof Provost 			vh.vrrp_count_addr++;
151037115154SKristof Provost 			len += sizeof(in6->sin6_addr);
151137115154SKristof Provost 		}
151237115154SKristof Provost 		ip6->ip6_plen = htonl(len);
151337115154SKristof Provost 
151437115154SKristof Provost 		vh_ptr = (struct vrrpv3_header *)mtodo(m, sizeof(*ip6));
151537115154SKristof Provost 		bcopy(&vh, vh_ptr, sizeof(vh));
151637115154SKristof Provost 
151737115154SKristof Provost 		vh_ptr->vrrp_checksum = in6_cksum_pseudo(ip6, len, ip6->ip6_nxt, 0);
151837115154SKristof Provost 		vh_ptr->vrrp_checksum = in_cksum_skip(m, len + sizeof(*ip6), sizeof(*ip6));
151937115154SKristof Provost 
152037115154SKristof Provost 		if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
152137115154SKristof Provost 			m_freem(m);
152237115154SKristof Provost 			CARP_DEBUG("%s: in6_setscope failed\n", __func__);
152337115154SKristof Provost 			goto resched;
152437115154SKristof Provost 		}
152537115154SKristof Provost 
152637115154SKristof Provost 		if (carp_tag(sc, m))
152737115154SKristof Provost 			goto resched;
152837115154SKristof Provost 		CARPSTATS_INC(carps_opackets6);
152937115154SKristof Provost 
153037115154SKristof Provost 		carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0,
153137115154SKristof Provost 		    &sc->sc_carpdev->if_carp->cif_im6o, NULL, NULL));
153237115154SKristof Provost 	}
153337115154SKristof Provost #endif
153437115154SKristof Provost 
153537115154SKristof Provost resched:
153637115154SKristof Provost 	callout_reset(&sc->sc_ad_tmo, sc->sc_vrrp_adv_inter * hz / 100,
15375ee92cbdSGleb Smirnoff 	    carp_callout, sc);
153837115154SKristof Provost }
153937115154SKristof Provost 
154037115154SKristof Provost static void
154108b68b0eSGleb Smirnoff carp_addroute(struct carp_softc *sc)
154208b68b0eSGleb Smirnoff {
154308b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
154408b68b0eSGleb Smirnoff 
154508b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa)
15462512b096SGleb Smirnoff 		carp_ifa_addroute(ifa);
15472512b096SGleb Smirnoff }
15482512b096SGleb Smirnoff 
15492512b096SGleb Smirnoff static void
15502512b096SGleb Smirnoff carp_ifa_addroute(struct ifaddr *ifa)
15512512b096SGleb Smirnoff {
15522512b096SGleb Smirnoff 
155308b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
155408b68b0eSGleb Smirnoff #ifdef INET
155508b68b0eSGleb Smirnoff 	case AF_INET:
1556130aebbaSAlexander V. Chernikov 		in_addprefix(ifatoia(ifa));
155708b68b0eSGleb Smirnoff 		ifa_add_loopback_route(ifa,
155808b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia(ifa)->ia_addr);
155908b68b0eSGleb Smirnoff 		break;
156008b68b0eSGleb Smirnoff #endif
156108b68b0eSGleb Smirnoff #ifdef INET6
156208b68b0eSGleb Smirnoff 	case AF_INET6:
156308b68b0eSGleb Smirnoff 		ifa_add_loopback_route(ifa,
156408b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia6(ifa)->ia_addr);
1565f6b84910SAlexander V. Chernikov 		nd6_add_ifa_lle(ifatoia6(ifa));
156608b68b0eSGleb Smirnoff 		break;
156708b68b0eSGleb Smirnoff #endif
156808b68b0eSGleb Smirnoff 	}
156908b68b0eSGleb Smirnoff }
157008b68b0eSGleb Smirnoff 
157108b68b0eSGleb Smirnoff static void
157208b68b0eSGleb Smirnoff carp_delroute(struct carp_softc *sc)
157308b68b0eSGleb Smirnoff {
157408b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
157508b68b0eSGleb Smirnoff 
157608b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa)
15772512b096SGleb Smirnoff 		carp_ifa_delroute(ifa);
15782512b096SGleb Smirnoff }
15792512b096SGleb Smirnoff 
15802512b096SGleb Smirnoff static void
15812512b096SGleb Smirnoff carp_ifa_delroute(struct ifaddr *ifa)
15822512b096SGleb Smirnoff {
15832512b096SGleb Smirnoff 
158408b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
158508b68b0eSGleb Smirnoff #ifdef INET
158608b68b0eSGleb Smirnoff 	case AF_INET:
158708b68b0eSGleb Smirnoff 		ifa_del_loopback_route(ifa,
158808b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia(ifa)->ia_addr);
158908b68b0eSGleb Smirnoff 		in_scrubprefix(ifatoia(ifa), LLE_STATIC);
159008b68b0eSGleb Smirnoff 		break;
159108b68b0eSGleb Smirnoff #endif
159208b68b0eSGleb Smirnoff #ifdef INET6
159308b68b0eSGleb Smirnoff 	case AF_INET6:
159408b68b0eSGleb Smirnoff 		ifa_del_loopback_route(ifa,
159508b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia6(ifa)->ia_addr);
15963e7a2321SAlexander V. Chernikov 		nd6_rem_ifa_lle(ifatoia6(ifa), 1);
159708b68b0eSGleb Smirnoff 		break;
159808b68b0eSGleb Smirnoff #endif
159908b68b0eSGleb Smirnoff 	}
1600a9771948SGleb Smirnoff }
1601a9771948SGleb Smirnoff 
160224421c1cSGleb Smirnoff int
160324421c1cSGleb Smirnoff carp_master(struct ifaddr *ifa)
160424421c1cSGleb Smirnoff {
160524421c1cSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
160624421c1cSGleb Smirnoff 
160724421c1cSGleb Smirnoff 	return (sc->sc_state == MASTER);
160824421c1cSGleb Smirnoff }
160924421c1cSGleb Smirnoff 
1610a0ae8f04SBjoern A. Zeeb #ifdef INET
1611a9771948SGleb Smirnoff /*
1612a9771948SGleb Smirnoff  * Broadcast a gratuitous ARP request containing
1613a9771948SGleb Smirnoff  * the virtual router MAC address for each IP address
1614a9771948SGleb Smirnoff  * associated with the virtual router.
1615a9771948SGleb Smirnoff  */
16165c1f0f6dSGleb Smirnoff static void
1617a9771948SGleb Smirnoff carp_send_arp(struct carp_softc *sc)
1618a9771948SGleb Smirnoff {
1619a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1620d6e82913SSteven Hartland 	struct in_addr addr;
1621a9771948SGleb Smirnoff 
16221d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
16231d126e9bSKristof Provost 
16241c302b58SAlexander V. Chernikov 	CARP_FOREACH_IFA(sc, ifa) {
16251c302b58SAlexander V. Chernikov 		if (ifa->ifa_addr->sa_family != AF_INET)
16261c302b58SAlexander V. Chernikov 			continue;
1627d6e82913SSteven Hartland 		addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1628*a254d687SGleb Smirnoff 		arp_announce_ifaddr(sc->sc_carpdev, addr, sc->sc_addr);
16291c302b58SAlexander V. Chernikov 	}
1630a9771948SGleb Smirnoff }
163108b68b0eSGleb Smirnoff 
163208b68b0eSGleb Smirnoff int
163308b68b0eSGleb Smirnoff carp_iamatch(struct ifaddr *ifa, uint8_t **enaddr)
163408b68b0eSGleb Smirnoff {
163508b68b0eSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
163608b68b0eSGleb Smirnoff 
163708b68b0eSGleb Smirnoff 	if (sc->sc_state == MASTER) {
1638*a254d687SGleb Smirnoff 		*enaddr = sc->sc_addr;
163908b68b0eSGleb Smirnoff 		return (1);
164008b68b0eSGleb Smirnoff 	}
164108b68b0eSGleb Smirnoff 
164208b68b0eSGleb Smirnoff 	return (0);
1643a9771948SGleb Smirnoff }
1644a0ae8f04SBjoern A. Zeeb #endif
1645a9771948SGleb Smirnoff 
1646a9771948SGleb Smirnoff #ifdef INET6
16475c1f0f6dSGleb Smirnoff static void
1648a9771948SGleb Smirnoff carp_send_na(struct carp_softc *sc)
1649a9771948SGleb Smirnoff {
1650d6e82913SSteven Hartland 	static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
1651a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1652d6e82913SSteven Hartland 	struct in6_addr *in6;
1653a9771948SGleb Smirnoff 
165408b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa) {
1655d6e82913SSteven Hartland 		if (ifa->ifa_addr->sa_family != AF_INET6)
1656a9771948SGleb Smirnoff 			continue;
1657a9771948SGleb Smirnoff 
1658d6e82913SSteven Hartland 		in6 = IFA_IN6(ifa);
1659d6e82913SSteven Hartland 		nd6_na_output(sc->sc_carpdev, &mcast, in6,
1660d6e82913SSteven Hartland 		    ND_NA_FLAG_OVERRIDE, 1, NULL);
1661d6e82913SSteven Hartland 		DELAY(1000);	/* XXX */
1662a9771948SGleb Smirnoff 	}
1663a9771948SGleb Smirnoff }
1664a9771948SGleb Smirnoff 
16658253dcabSBjoern A. Zeeb /*
16668253dcabSBjoern A. Zeeb  * Returns ifa in case it's a carp address and it is MASTER, or if the address
16678253dcabSBjoern A. Zeeb  * matches and is not a carp address.  Returns NULL otherwise.
16688253dcabSBjoern A. Zeeb  */
1669a4e53905SMax Laier struct ifaddr *
167054bfbd51SWill Andrews carp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr)
1671a9771948SGleb Smirnoff {
1672a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1673a9771948SGleb Smirnoff 
1674b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1675b8a6e03fSGleb Smirnoff 
16768253dcabSBjoern A. Zeeb 	ifa = NULL;
1677d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
16788253dcabSBjoern A. Zeeb 		if (ifa->ifa_addr->sa_family != AF_INET6)
16798253dcabSBjoern A. Zeeb 			continue;
16808253dcabSBjoern A. Zeeb 		if (!IN6_ARE_ADDR_EQUAL(taddr, IFA_IN6(ifa)))
16818253dcabSBjoern A. Zeeb 			continue;
16828253dcabSBjoern A. Zeeb 		if (ifa->ifa_carp && ifa->ifa_carp->sc_state != MASTER)
16838253dcabSBjoern A. Zeeb 			ifa = NULL;
16848253dcabSBjoern A. Zeeb 		else
16858c0fec80SRobert Watson 			ifa_ref(ifa);
16868253dcabSBjoern A. Zeeb 		break;
1687a9771948SGleb Smirnoff 	}
1688a9771948SGleb Smirnoff 
16898253dcabSBjoern A. Zeeb 	return (ifa);
1690a9771948SGleb Smirnoff }
1691a9771948SGleb Smirnoff 
1692dad68fc3SBjoern A. Zeeb char *
169354bfbd51SWill Andrews carp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr)
1694a9771948SGleb Smirnoff {
1695a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1696a9771948SGleb Smirnoff 
1697b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1698b8a6e03fSGleb Smirnoff 
169908b68b0eSGleb Smirnoff 	IFNET_FOREACH_IFA(ifp, ifa)
170008b68b0eSGleb Smirnoff 		if (ifa->ifa_addr->sa_family == AF_INET6 &&
170108b68b0eSGleb Smirnoff 		    IN6_ARE_ADDR_EQUAL(taddr, IFA_IN6(ifa))) {
170208b68b0eSGleb Smirnoff 			struct carp_softc *sc = ifa->ifa_carp;
170308b68b0eSGleb Smirnoff 			struct m_tag *mtag;
170408b68b0eSGleb Smirnoff 
1705a9771948SGleb Smirnoff 			mtag = m_tag_get(PACKET_TAG_CARP,
1706a856ddc6SGleb Smirnoff 			    sizeof(struct carp_softc *), M_NOWAIT);
170708b68b0eSGleb Smirnoff 			if (mtag == NULL)
170808b68b0eSGleb Smirnoff 				/* Better a bit than nothing. */
1709*a254d687SGleb Smirnoff 				return (sc->sc_addr);
171008b68b0eSGleb Smirnoff 
1711eaf151c4SGleb Smirnoff 			bcopy(&sc, mtag + 1, sizeof(sc));
1712a9771948SGleb Smirnoff 			m_tag_prepend(m, mtag);
1713a9771948SGleb Smirnoff 
1714*a254d687SGleb Smirnoff 			return (sc->sc_addr);
1715a9771948SGleb Smirnoff 		}
1716a9771948SGleb Smirnoff 
1717a9771948SGleb Smirnoff 	return (NULL);
1718a9771948SGleb Smirnoff }
171908b68b0eSGleb Smirnoff #endif /* INET6 */
1720a9771948SGleb Smirnoff 
172108b68b0eSGleb Smirnoff int
172254bfbd51SWill Andrews carp_forus(struct ifnet *ifp, u_char *dhost)
1723a9771948SGleb Smirnoff {
172408b68b0eSGleb Smirnoff 	struct carp_softc *sc;
172508b68b0eSGleb Smirnoff 	uint8_t *ena = dhost;
1726a9771948SGleb Smirnoff 
1727a9771948SGleb Smirnoff 	if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1)
172808b68b0eSGleb Smirnoff 		return (0);
1729a9771948SGleb Smirnoff 
173008b68b0eSGleb Smirnoff 	CIF_LOCK(ifp->if_carp);
173108b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc) {
17328c3fbf3cSAlexander Motin 		/*
17338c3fbf3cSAlexander Motin 		 * CARP_LOCK() is not here, since would protect nothing, but
17348c3fbf3cSAlexander Motin 		 * cause deadlock with if_bridge, calling this under its lock.
17358c3fbf3cSAlexander Motin 		 */
1736*a254d687SGleb Smirnoff 		if (sc->sc_state == MASTER && !bcmp(dhost, sc->sc_addr,
173708b68b0eSGleb Smirnoff 		    ETHER_ADDR_LEN)) {
173808b68b0eSGleb Smirnoff 			CIF_UNLOCK(ifp->if_carp);
173908b68b0eSGleb Smirnoff 			return (1);
1740a9771948SGleb Smirnoff 		}
174108b68b0eSGleb Smirnoff 	}
174208b68b0eSGleb Smirnoff 	CIF_UNLOCK(ifp->if_carp);
1743a9771948SGleb Smirnoff 
174408b68b0eSGleb Smirnoff 	return (0);
1745a9771948SGleb Smirnoff }
1746a9771948SGleb Smirnoff 
1747afdbac98SGleb Smirnoff /* Master down timeout event, executed in callout context. */
17485c1f0f6dSGleb Smirnoff static void
1749a9771948SGleb Smirnoff carp_master_down(void *v)
1750a9771948SGleb Smirnoff {
1751a9771948SGleb Smirnoff 	struct carp_softc *sc = v;
17521d126e9bSKristof Provost 	struct epoch_tracker et;
1753a9771948SGleb Smirnoff 
17541d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
175508b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
175608b68b0eSGleb Smirnoff 
1757afdbac98SGleb Smirnoff 	CURVNET_SET(sc->sc_carpdev->if_vnet);
175808b68b0eSGleb Smirnoff 	if (sc->sc_state == BACKUP) {
1759d01641e2SWill Andrews 		carp_master_down_locked(sc, "master timed out");
176008b68b0eSGleb Smirnoff 	}
1761afdbac98SGleb Smirnoff 	CURVNET_RESTORE();
176208b68b0eSGleb Smirnoff 
176308b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
17641d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1765d220759bSGleb Smirnoff }
1766d220759bSGleb Smirnoff 
1767d220759bSGleb Smirnoff static void
1768d01641e2SWill Andrews carp_master_down_locked(struct carp_softc *sc, const char *reason)
1769d220759bSGleb Smirnoff {
177008b68b0eSGleb Smirnoff 
17711d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
177208b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1773d220759bSGleb Smirnoff 
1774a9771948SGleb Smirnoff 	switch (sc->sc_state) {
1775a9771948SGleb Smirnoff 	case BACKUP:
1776d01641e2SWill Andrews 		carp_set_state(sc, MASTER, reason);
17775ee92cbdSGleb Smirnoff 		send_ad_locked(sc);
1778a0ae8f04SBjoern A. Zeeb #ifdef INET
1779a9771948SGleb Smirnoff 		carp_send_arp(sc);
1780a0ae8f04SBjoern A. Zeeb #endif
1781a9771948SGleb Smirnoff #ifdef INET6
1782a9771948SGleb Smirnoff 		carp_send_na(sc);
178308b68b0eSGleb Smirnoff #endif
1784a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
178508b68b0eSGleb Smirnoff 		carp_addroute(sc);
178608b68b0eSGleb Smirnoff 		break;
178708b68b0eSGleb Smirnoff 	case INIT:
178808b68b0eSGleb Smirnoff 	case MASTER:
178908b68b0eSGleb Smirnoff #ifdef INVARIANTS
179008b68b0eSGleb Smirnoff 		panic("carp: VHID %u@%s: master_down event in %s state\n",
179108b68b0eSGleb Smirnoff 		    sc->sc_vhid,
1792511a6d5eSKristof Provost 		    if_name(sc->sc_carpdev),
179308b68b0eSGleb Smirnoff 		    sc->sc_state ? "MASTER" : "INIT");
179408b68b0eSGleb Smirnoff #endif
1795a9771948SGleb Smirnoff 		break;
1796a9771948SGleb Smirnoff 	}
1797a9771948SGleb Smirnoff }
1798a9771948SGleb Smirnoff 
1799a9771948SGleb Smirnoff /*
1800a9771948SGleb Smirnoff  * When in backup state, af indicates whether to reset the master down timer
1801a9771948SGleb Smirnoff  * for v4 or v6. If it's set to zero, reset the ones which are already pending.
1802a9771948SGleb Smirnoff  */
18035c1f0f6dSGleb Smirnoff static void
1804a9771948SGleb Smirnoff carp_setrun(struct carp_softc *sc, sa_family_t af)
1805a9771948SGleb Smirnoff {
1806a9771948SGleb Smirnoff 	struct timeval tv;
180737115154SKristof Provost 	int timeout;
1808a9771948SGleb Smirnoff 
180908b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1810d220759bSGleb Smirnoff 
181108b68b0eSGleb Smirnoff 	if ((sc->sc_carpdev->if_flags & IFF_UP) == 0 ||
181208b68b0eSGleb Smirnoff 	    sc->sc_carpdev->if_link_state != LINK_STATE_UP ||
1813167a3440SAlexander Motin 	    (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) ||
1814167a3440SAlexander Motin 	    !V_carp_allow)
1815a9771948SGleb Smirnoff 		return;
1816a9771948SGleb Smirnoff 
1817a9771948SGleb Smirnoff 	switch (sc->sc_state) {
1818a9771948SGleb Smirnoff 	case INIT:
1819d01641e2SWill Andrews 		carp_set_state(sc, BACKUP, "initialization complete");
1820a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
1821a9771948SGleb Smirnoff 		break;
1822a9771948SGleb Smirnoff 	case BACKUP:
1823a9771948SGleb Smirnoff 		callout_stop(&sc->sc_ad_tmo);
182437115154SKristof Provost 
1825*a254d687SGleb Smirnoff 		switch (sc->sc_version) {
1826*a254d687SGleb Smirnoff 		case CARP_VERSION_CARP:
1827a9771948SGleb Smirnoff 			tv.tv_sec = 3 * sc->sc_advbase;
1828a9771948SGleb Smirnoff 			tv.tv_usec = sc->sc_advskew * 1000000 / 256;
182937115154SKristof Provost 			timeout = tvtohz(&tv);
1830*a254d687SGleb Smirnoff 			break;
1831*a254d687SGleb Smirnoff 		case CARP_VERSION_VRRPv3:
183237115154SKristof Provost 			/* skew time */
1833*a254d687SGleb Smirnoff 			timeout = (256 - sc->sc_vrrp_prio) *
1834*a254d687SGleb Smirnoff 			    sc->sc_vrrp_master_inter / 256;
183537115154SKristof Provost 			timeout += (3 * sc->sc_vrrp_master_inter);
183637115154SKristof Provost 			timeout *= hz;
1837*a254d687SGleb Smirnoff 			timeout /= 100; /* master interval is in centiseconds */
1838*a254d687SGleb Smirnoff 			break;
183937115154SKristof Provost 		}
1840a9771948SGleb Smirnoff 		switch (af) {
1841a9771948SGleb Smirnoff #ifdef INET
1842a9771948SGleb Smirnoff 		case AF_INET:
184337115154SKristof Provost 			callout_reset(&sc->sc_md_tmo, timeout,
1844a9771948SGleb Smirnoff 			    carp_master_down, sc);
1845a9771948SGleb Smirnoff 			break;
184608b68b0eSGleb Smirnoff #endif
1847a9771948SGleb Smirnoff #ifdef INET6
1848a9771948SGleb Smirnoff 		case AF_INET6:
184937115154SKristof Provost 			callout_reset(&sc->sc_md6_tmo, timeout,
1850a9771948SGleb Smirnoff 			    carp_master_down, sc);
1851a9771948SGleb Smirnoff 			break;
185208b68b0eSGleb Smirnoff #endif
1853a9771948SGleb Smirnoff 		default:
185408b68b0eSGleb Smirnoff #ifdef INET
1855a9771948SGleb Smirnoff 			if (sc->sc_naddrs)
185637115154SKristof Provost 				callout_reset(&sc->sc_md_tmo, timeout,
1857a9771948SGleb Smirnoff 				    carp_master_down, sc);
185808b68b0eSGleb Smirnoff #endif
185908b68b0eSGleb Smirnoff #ifdef INET6
1860a9771948SGleb Smirnoff 			if (sc->sc_naddrs6)
186137115154SKristof Provost 				callout_reset(&sc->sc_md6_tmo, timeout,
1862a9771948SGleb Smirnoff 				    carp_master_down, sc);
186308b68b0eSGleb Smirnoff #endif
1864a9771948SGleb Smirnoff 			break;
1865a9771948SGleb Smirnoff 		}
1866a9771948SGleb Smirnoff 		break;
1867a9771948SGleb Smirnoff 	case MASTER:
1868*a254d687SGleb Smirnoff 		switch (sc->sc_version) {
1869*a254d687SGleb Smirnoff 		case CARP_VERSION_CARP:
1870a9771948SGleb Smirnoff 			tv.tv_sec = sc->sc_advbase;
1871a9771948SGleb Smirnoff 			tv.tv_usec = sc->sc_advskew * 1000000 / 256;
1872a9771948SGleb Smirnoff 			callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
18735ee92cbdSGleb Smirnoff 			    carp_callout, sc);
1874*a254d687SGleb Smirnoff 			break;
1875*a254d687SGleb Smirnoff 		case CARP_VERSION_VRRPv3:
187637115154SKristof Provost 			callout_reset(&sc->sc_ad_tmo,
187737115154SKristof Provost 			    sc->sc_vrrp_adv_inter * hz / 100,
18785ee92cbdSGleb Smirnoff 			    carp_callout, sc);
1879*a254d687SGleb Smirnoff 			break;
188037115154SKristof Provost 		}
1881a9771948SGleb Smirnoff 		break;
1882a9771948SGleb Smirnoff 	}
1883a9771948SGleb Smirnoff }
1884a9771948SGleb Smirnoff 
188508b68b0eSGleb Smirnoff /*
188608b68b0eSGleb Smirnoff  * Setup multicast structures.
188708b68b0eSGleb Smirnoff  */
18885c1f0f6dSGleb Smirnoff static int
1889a9a2c40cSGleb Smirnoff carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
1890a9771948SGleb Smirnoff {
1891a9a2c40cSGleb Smirnoff 	struct ifnet *ifp = cif->cif_ifp;
189208b68b0eSGleb Smirnoff 	int error = 0;
189308b68b0eSGleb Smirnoff 
189408b68b0eSGleb Smirnoff 	switch (sa) {
189508b68b0eSGleb Smirnoff #ifdef INET
189608b68b0eSGleb Smirnoff 	case AF_INET:
189708b68b0eSGleb Smirnoff 	    {
189808b68b0eSGleb Smirnoff 		struct ip_moptions *imo = &cif->cif_imo;
189959854ecfSHans Petter Selasky 		struct in_mfilter *imf;
1900a9771948SGleb Smirnoff 		struct in_addr addr;
1901a9771948SGleb Smirnoff 
190259854ecfSHans Petter Selasky 		if (ip_mfilter_first(&imo->imo_head) != NULL)
1903a9771948SGleb Smirnoff 			return (0);
1904a9771948SGleb Smirnoff 
190559854ecfSHans Petter Selasky 		imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
190659854ecfSHans Petter Selasky 		ip_mfilter_init(&imo->imo_head);
190708b68b0eSGleb Smirnoff 		imo->imo_multicast_vif = -1;
1908a9771948SGleb Smirnoff 
1909a9771948SGleb Smirnoff 		addr.s_addr = htonl(INADDR_CARP_GROUP);
191008b68b0eSGleb Smirnoff 		if ((error = in_joingroup(ifp, &addr, NULL,
191159854ecfSHans Petter Selasky 		    &imf->imf_inm)) != 0) {
191259854ecfSHans Petter Selasky 			ip_mfilter_free(imf);
191308b68b0eSGleb Smirnoff 			break;
19142d9cfabaSRobert Watson 		}
191559854ecfSHans Petter Selasky 
191659854ecfSHans Petter Selasky 		ip_mfilter_insert(&imo->imo_head, imf);
1917a9771948SGleb Smirnoff 		imo->imo_multicast_ifp = ifp;
1918a9771948SGleb Smirnoff 		imo->imo_multicast_ttl = CARP_DFLTTL;
1919a9771948SGleb Smirnoff 		imo->imo_multicast_loop = 0;
1920a9771948SGleb Smirnoff 		break;
1921a9771948SGleb Smirnoff 	   }
192208b68b0eSGleb Smirnoff #endif
192308b68b0eSGleb Smirnoff #ifdef INET6
192408b68b0eSGleb Smirnoff 	case AF_INET6:
192508b68b0eSGleb Smirnoff 	    {
192608b68b0eSGleb Smirnoff 		struct ip6_moptions *im6o = &cif->cif_im6o;
192759854ecfSHans Petter Selasky 		struct in6_mfilter *im6f[2];
192808b68b0eSGleb Smirnoff 		struct in6_addr in6;
192933cde130SBruce M Simpson 
193059854ecfSHans Petter Selasky 		if (ip6_mfilter_first(&im6o->im6o_head))
193108b68b0eSGleb Smirnoff 			return (0);
193208b68b0eSGleb Smirnoff 
193359854ecfSHans Petter Selasky 		im6f[0] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
193459854ecfSHans Petter Selasky 		im6f[1] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
193559854ecfSHans Petter Selasky 
193659854ecfSHans Petter Selasky 		ip6_mfilter_init(&im6o->im6o_head);
193708b68b0eSGleb Smirnoff 		im6o->im6o_multicast_hlim = CARP_DFLTTL;
1938a9771948SGleb Smirnoff 		im6o->im6o_multicast_ifp = ifp;
1939a9771948SGleb Smirnoff 
194008b68b0eSGleb Smirnoff 		/* Join IPv6 CARP multicast group. */
1941a1f7e5f8SHajimu UMEMOTO 		bzero(&in6, sizeof(in6));
1942a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr16[0] = htons(0xff02);
1943a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr8[15] = 0x12;
194408b68b0eSGleb Smirnoff 		if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
194559854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
194659854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
194708b68b0eSGleb Smirnoff 			break;
194808b68b0eSGleb Smirnoff 		}
194959854ecfSHans Petter Selasky 		if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[0]->im6f_in6m, 0)) != 0) {
195059854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
195159854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
195208b68b0eSGleb Smirnoff 			break;
195308b68b0eSGleb Smirnoff 		}
1954a9771948SGleb Smirnoff 
195508b68b0eSGleb Smirnoff 		/* Join solicited multicast address. */
1956a1f7e5f8SHajimu UMEMOTO 		bzero(&in6, sizeof(in6));
1957a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr16[0] = htons(0xff02);
1958a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr32[1] = 0;
1959a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr32[2] = htonl(1);
196008b68b0eSGleb Smirnoff 		in6.s6_addr32[3] = 0;
1961a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr8[12] = 0xff;
196259854ecfSHans Petter Selasky 
196308b68b0eSGleb Smirnoff 		if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
196459854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
196559854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
196608b68b0eSGleb Smirnoff 			break;
196708b68b0eSGleb Smirnoff 		}
196859854ecfSHans Petter Selasky 
196959854ecfSHans Petter Selasky 		if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[1]->im6f_in6m, 0)) != 0) {
197059854ecfSHans Petter Selasky 			in6_leavegroup(im6f[0]->im6f_in6m, NULL);
197159854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
197259854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
197308b68b0eSGleb Smirnoff 			break;
197408b68b0eSGleb Smirnoff 		}
197559854ecfSHans Petter Selasky 		ip6_mfilter_insert(&im6o->im6o_head, im6f[0]);
197659854ecfSHans Petter Selasky 		ip6_mfilter_insert(&im6o->im6o_head, im6f[1]);
1977a9771948SGleb Smirnoff 		break;
1978a9771948SGleb Smirnoff 	    }
1979a9771948SGleb Smirnoff #endif
198008b68b0eSGleb Smirnoff 	}
198108b68b0eSGleb Smirnoff 
198208b68b0eSGleb Smirnoff 	return (error);
1983a9771948SGleb Smirnoff }
1984a9771948SGleb Smirnoff 
1985a9771948SGleb Smirnoff /*
198608b68b0eSGleb Smirnoff  * Free multicast structures.
1987a9771948SGleb Smirnoff  */
19885c1f0f6dSGleb Smirnoff static void
1989a9a2c40cSGleb Smirnoff carp_multicast_cleanup(struct carp_if *cif, sa_family_t sa)
1990a9771948SGleb Smirnoff {
199159854ecfSHans Petter Selasky #ifdef INET
199259854ecfSHans Petter Selasky 	struct ip_moptions *imo = &cif->cif_imo;
199359854ecfSHans Petter Selasky 	struct in_mfilter *imf;
199459854ecfSHans Petter Selasky #endif
199559854ecfSHans Petter Selasky #ifdef INET6
199659854ecfSHans Petter Selasky 	struct ip6_moptions *im6o = &cif->cif_im6o;
199759854ecfSHans Petter Selasky 	struct in6_mfilter *im6f;
199859854ecfSHans Petter Selasky #endif
19999c2cd1aaSGleb Smirnoff 	sx_assert(&carp_sx, SA_XLOCKED);
20009c2cd1aaSGleb Smirnoff 
200108b68b0eSGleb Smirnoff 	switch (sa) {
200208b68b0eSGleb Smirnoff #ifdef INET
200308b68b0eSGleb Smirnoff 	case AF_INET:
200459854ecfSHans Petter Selasky 		if (cif->cif_naddrs != 0)
200559854ecfSHans Petter Selasky 			break;
200608b68b0eSGleb Smirnoff 
200759854ecfSHans Petter Selasky 		while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
200859854ecfSHans Petter Selasky 			ip_mfilter_remove(&imo->imo_head, imf);
200959854ecfSHans Petter Selasky 			in_leavegroup(imf->imf_inm, NULL);
201059854ecfSHans Petter Selasky 			ip_mfilter_free(imf);
201108b68b0eSGleb Smirnoff 		}
201208b68b0eSGleb Smirnoff 		break;
2013a9771948SGleb Smirnoff #endif
201408b68b0eSGleb Smirnoff #ifdef INET6
201508b68b0eSGleb Smirnoff 	case AF_INET6:
201659854ecfSHans Petter Selasky 		if (cif->cif_naddrs6 != 0)
201759854ecfSHans Petter Selasky 			break;
201808b68b0eSGleb Smirnoff 
201959854ecfSHans Petter Selasky 		while ((im6f = ip6_mfilter_first(&im6o->im6o_head)) != NULL) {
202059854ecfSHans Petter Selasky 			ip6_mfilter_remove(&im6o->im6o_head, im6f);
202159854ecfSHans Petter Selasky 			in6_leavegroup(im6f->im6f_in6m, NULL);
202259854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f);
202308b68b0eSGleb Smirnoff 		}
202408b68b0eSGleb Smirnoff 		break;
202508b68b0eSGleb Smirnoff #endif
202608b68b0eSGleb Smirnoff 	}
2027a9771948SGleb Smirnoff }
2028a9771948SGleb Smirnoff 
2029a9771948SGleb Smirnoff int
203047e8d432SGleb Smirnoff carp_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa)
2031a9771948SGleb Smirnoff {
2032a9771948SGleb Smirnoff 	struct m_tag *mtag;
2033601438fbSGleb Smirnoff 	int vhid;
2034a9771948SGleb Smirnoff 
2035a9771948SGleb Smirnoff 	if (!sa)
2036a9771948SGleb Smirnoff 		return (0);
2037a9771948SGleb Smirnoff 
2038a9771948SGleb Smirnoff 	switch (sa->sa_family) {
2039a9771948SGleb Smirnoff #ifdef INET
2040a9771948SGleb Smirnoff 	case AF_INET:
2041a9771948SGleb Smirnoff 		break;
204208b68b0eSGleb Smirnoff #endif
2043a9771948SGleb Smirnoff #ifdef INET6
2044a9771948SGleb Smirnoff 	case AF_INET6:
2045a9771948SGleb Smirnoff 		break;
204608b68b0eSGleb Smirnoff #endif
2047a9771948SGleb Smirnoff 	default:
2048a9771948SGleb Smirnoff 		return (0);
2049a9771948SGleb Smirnoff 	}
2050a9771948SGleb Smirnoff 
2051a9771948SGleb Smirnoff 	mtag = m_tag_find(m, PACKET_TAG_CARP, NULL);
2052a9771948SGleb Smirnoff 	if (mtag == NULL)
2053a9771948SGleb Smirnoff 		return (0);
2054a9771948SGleb Smirnoff 
2055601438fbSGleb Smirnoff 	bcopy(mtag + 1, &vhid, sizeof(vhid));
205613781800SKristof Provost 
205708b68b0eSGleb Smirnoff 	/* Set the source MAC address to the Virtual Router MAC Address. */
2058a9771948SGleb Smirnoff 	switch (ifp->if_type) {
2059630481bbSYaroslav Tykhiy 	case IFT_ETHER:
20609ca1fe0dSGleb Smirnoff 	case IFT_BRIDGE:
2061630481bbSYaroslav Tykhiy 	case IFT_L2VLAN: {
2062a9771948SGleb Smirnoff 			struct ether_header *eh;
2063a9771948SGleb Smirnoff 
2064a9771948SGleb Smirnoff 			eh = mtod(m, struct ether_header *);
2065a9771948SGleb Smirnoff 			eh->ether_shost[0] = 0;
2066a9771948SGleb Smirnoff 			eh->ether_shost[1] = 0;
2067a9771948SGleb Smirnoff 			eh->ether_shost[2] = 0x5e;
2068a9771948SGleb Smirnoff 			eh->ether_shost[3] = 0;
2069a9771948SGleb Smirnoff 			eh->ether_shost[4] = 1;
2070601438fbSGleb Smirnoff 			eh->ether_shost[5] = vhid;
2071a9771948SGleb Smirnoff 		}
2072a9771948SGleb Smirnoff 		break;
2073a9771948SGleb Smirnoff 	default:
207408b68b0eSGleb Smirnoff 		printf("%s: carp is not supported for the %d interface type\n",
2075511a6d5eSKristof Provost 		    if_name(ifp), ifp->if_type);
2076a9771948SGleb Smirnoff 		return (EOPNOTSUPP);
2077a9771948SGleb Smirnoff 	}
2078a9771948SGleb Smirnoff 
2079a9771948SGleb Smirnoff 	return (0);
2080a9771948SGleb Smirnoff }
2081a9771948SGleb Smirnoff 
208208b68b0eSGleb Smirnoff static struct carp_softc*
2083*a254d687SGleb Smirnoff carp_alloc(struct ifnet *ifp, carp_version_t version, int vhid)
2084a9771948SGleb Smirnoff {
208508b68b0eSGleb Smirnoff 	struct carp_softc *sc;
208608b68b0eSGleb Smirnoff 	struct carp_if *cif;
2087d220759bSGleb Smirnoff 
208881098a01SAlexander Motin 	sx_assert(&carp_sx, SA_XLOCKED);
208981098a01SAlexander Motin 
20900cc726f2SGleb Smirnoff 	if ((cif = ifp->if_carp) == NULL)
209108b68b0eSGleb Smirnoff 		cif = carp_alloc_if(ifp);
2092a9771948SGleb Smirnoff 
2093*a254d687SGleb Smirnoff 	sc = malloc(sizeof(*sc), M_CARP, M_WAITOK);
2094*a254d687SGleb Smirnoff 	*sc = (struct carp_softc ){
2095*a254d687SGleb Smirnoff 		.sc_vhid = vhid,
2096*a254d687SGleb Smirnoff 		.sc_version = version,
2097*a254d687SGleb Smirnoff 		.sc_state = INIT,
2098*a254d687SGleb Smirnoff 		.sc_carpdev = ifp,
2099*a254d687SGleb Smirnoff 		.sc_ifasiz = sizeof(struct ifaddr *),
2100*a254d687SGleb Smirnoff 		.sc_addr = { 0, 0, 0x5e, 0, 1, vhid },
2101*a254d687SGleb Smirnoff 	};
210208b68b0eSGleb Smirnoff 	sc->sc_ifas = malloc(sc->sc_ifasiz, M_CARP, M_WAITOK|M_ZERO);
210308b68b0eSGleb Smirnoff 
2104*a254d687SGleb Smirnoff 	switch (version) {
2105*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
2106*a254d687SGleb Smirnoff 		sc->sc_advbase = CARP_DFLTINTV;
2107*a254d687SGleb Smirnoff 		sc->sc_init_counter = true;
210813781800SKristof Provost 		sc->sc_carpaddr.s_addr = htonl(INADDR_CARP_GROUP);
210913781800SKristof Provost 		sc->sc_carpaddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
211013781800SKristof Provost 		sc->sc_carpaddr6.s6_addr8[15] = 0x12;
2111*a254d687SGleb Smirnoff 		break;
2112*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
211337115154SKristof Provost 		sc->sc_vrrp_adv_inter = 100;
211437115154SKristof Provost 		sc->sc_vrrp_master_inter = sc->sc_vrrp_adv_inter;
211537115154SKristof Provost 		sc->sc_vrrp_prio = 100;
2116*a254d687SGleb Smirnoff 		break;
2117*a254d687SGleb Smirnoff 	}
211837115154SKristof Provost 
211908b68b0eSGleb Smirnoff 	CARP_LOCK_INIT(sc);
212008b68b0eSGleb Smirnoff #ifdef INET
212108b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_md_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
212208b68b0eSGleb Smirnoff #endif
212308b68b0eSGleb Smirnoff #ifdef INET6
212408b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_md6_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
212508b68b0eSGleb Smirnoff #endif
212608b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_ad_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
212708b68b0eSGleb Smirnoff 
212808b68b0eSGleb Smirnoff 	CIF_LOCK(cif);
212908b68b0eSGleb Smirnoff 	TAILQ_INSERT_TAIL(&cif->cif_vrs, sc, sc_list);
213008b68b0eSGleb Smirnoff 	CIF_UNLOCK(cif);
213108b68b0eSGleb Smirnoff 
213208b68b0eSGleb Smirnoff 	mtx_lock(&carp_mtx);
213308b68b0eSGleb Smirnoff 	LIST_INSERT_HEAD(&carp_list, sc, sc_next);
213408b68b0eSGleb Smirnoff 	mtx_unlock(&carp_mtx);
213508b68b0eSGleb Smirnoff 
213608b68b0eSGleb Smirnoff 	return (sc);
213708b68b0eSGleb Smirnoff }
213808b68b0eSGleb Smirnoff 
21399c2cd1aaSGleb Smirnoff static void
214008b68b0eSGleb Smirnoff carp_grow_ifas(struct carp_softc *sc)
214108b68b0eSGleb Smirnoff {
214208b68b0eSGleb Smirnoff 	struct ifaddr **new;
214308b68b0eSGleb Smirnoff 
21449c2cd1aaSGleb Smirnoff 	new = malloc(sc->sc_ifasiz * 2, M_CARP, M_WAITOK | M_ZERO);
21459c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
214608b68b0eSGleb Smirnoff 	bcopy(sc->sc_ifas, new, sc->sc_ifasiz);
214708b68b0eSGleb Smirnoff 	free(sc->sc_ifas, M_CARP);
214808b68b0eSGleb Smirnoff 	sc->sc_ifas = new;
214908b68b0eSGleb Smirnoff 	sc->sc_ifasiz *= 2;
21509c2cd1aaSGleb Smirnoff 	CARP_UNLOCK(sc);
215108b68b0eSGleb Smirnoff }
215208b68b0eSGleb Smirnoff 
215308b68b0eSGleb Smirnoff static void
215408b68b0eSGleb Smirnoff carp_destroy(struct carp_softc *sc)
215508b68b0eSGleb Smirnoff {
215608b68b0eSGleb Smirnoff 	struct ifnet *ifp = sc->sc_carpdev;
215708b68b0eSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
215808b68b0eSGleb Smirnoff 
21599c2cd1aaSGleb Smirnoff 	sx_assert(&carp_sx, SA_XLOCKED);
2160a9a2c40cSGleb Smirnoff 
21619c2cd1aaSGleb Smirnoff 	if (sc->sc_suppress)
21629c2cd1aaSGleb Smirnoff 		carp_demote_adj(-V_carp_ifdown_adj, "vhid removed");
21639c2cd1aaSGleb Smirnoff 	CARP_UNLOCK(sc);
21649c2cd1aaSGleb Smirnoff 
21659c2cd1aaSGleb Smirnoff 	CIF_LOCK(cif);
216608b68b0eSGleb Smirnoff 	TAILQ_REMOVE(&cif->cif_vrs, sc, sc_list);
21679c2cd1aaSGleb Smirnoff 	CIF_UNLOCK(cif);
216808b68b0eSGleb Smirnoff 
216908b68b0eSGleb Smirnoff 	mtx_lock(&carp_mtx);
217008b68b0eSGleb Smirnoff 	LIST_REMOVE(sc, sc_next);
217108b68b0eSGleb Smirnoff 	mtx_unlock(&carp_mtx);
217208b68b0eSGleb Smirnoff 
217308b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_ad_tmo);
217408b68b0eSGleb Smirnoff #ifdef INET
217508b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_md_tmo);
217608b68b0eSGleb Smirnoff #endif
217708b68b0eSGleb Smirnoff #ifdef INET6
217808b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_md6_tmo);
217908b68b0eSGleb Smirnoff #endif
218008b68b0eSGleb Smirnoff 	CARP_LOCK_DESTROY(sc);
218108b68b0eSGleb Smirnoff 
218208b68b0eSGleb Smirnoff 	free(sc->sc_ifas, M_CARP);
218308b68b0eSGleb Smirnoff 	free(sc, M_CARP);
218408b68b0eSGleb Smirnoff }
218508b68b0eSGleb Smirnoff 
218608b68b0eSGleb Smirnoff static struct carp_if*
218708b68b0eSGleb Smirnoff carp_alloc_if(struct ifnet *ifp)
2188a9771948SGleb Smirnoff {
218954bfbd51SWill Andrews 	struct carp_if *cif;
21900cc726f2SGleb Smirnoff 	int error;
2191d220759bSGleb Smirnoff 
219208b68b0eSGleb Smirnoff 	cif = malloc(sizeof(*cif), M_CARP, M_WAITOK|M_ZERO);
219308b68b0eSGleb Smirnoff 
21940cc726f2SGleb Smirnoff 	if ((error = ifpromisc(ifp, 1)) != 0)
21950cc726f2SGleb Smirnoff 		printf("%s: ifpromisc(%s) failed: %d\n",
2196511a6d5eSKristof Provost 		    __func__, if_name(ifp), error);
21970cc726f2SGleb Smirnoff 	else
21980cc726f2SGleb Smirnoff 		cif->cif_flags |= CIF_PROMISC;
219908b68b0eSGleb Smirnoff 
220008b68b0eSGleb Smirnoff 	CIF_LOCK_INIT(cif);
220108b68b0eSGleb Smirnoff 	cif->cif_ifp = ifp;
220208b68b0eSGleb Smirnoff 	TAILQ_INIT(&cif->cif_vrs);
220308b68b0eSGleb Smirnoff 
2204137f91e8SJohn Baldwin 	IF_ADDR_WLOCK(ifp);
220508b68b0eSGleb Smirnoff 	ifp->if_carp = cif;
220608b68b0eSGleb Smirnoff 	if_ref(ifp);
2207137f91e8SJohn Baldwin 	IF_ADDR_WUNLOCK(ifp);
220808b68b0eSGleb Smirnoff 
220908b68b0eSGleb Smirnoff 	return (cif);
2210d220759bSGleb Smirnoff }
2211d220759bSGleb Smirnoff 
2212d220759bSGleb Smirnoff static void
221308b68b0eSGleb Smirnoff carp_free_if(struct carp_if *cif)
221408b68b0eSGleb Smirnoff {
221508b68b0eSGleb Smirnoff 	struct ifnet *ifp = cif->cif_ifp;
221608b68b0eSGleb Smirnoff 
221708b68b0eSGleb Smirnoff 	CIF_LOCK_ASSERT(cif);
221808b68b0eSGleb Smirnoff 	KASSERT(TAILQ_EMPTY(&cif->cif_vrs), ("%s: softc list not empty",
221908b68b0eSGleb Smirnoff 	    __func__));
222008b68b0eSGleb Smirnoff 
2221137f91e8SJohn Baldwin 	IF_ADDR_WLOCK(ifp);
222208b68b0eSGleb Smirnoff 	ifp->if_carp = NULL;
2223137f91e8SJohn Baldwin 	IF_ADDR_WUNLOCK(ifp);
222408b68b0eSGleb Smirnoff 
222508b68b0eSGleb Smirnoff 	CIF_LOCK_DESTROY(cif);
222608b68b0eSGleb Smirnoff 
22270cc726f2SGleb Smirnoff 	if (cif->cif_flags & CIF_PROMISC)
222808b68b0eSGleb Smirnoff 		ifpromisc(ifp, 0);
22291f6addd9SMikolaj Golub 	if_rele(ifp);
223008b68b0eSGleb Smirnoff 
223108b68b0eSGleb Smirnoff 	free(cif, M_CARP);
223208b68b0eSGleb Smirnoff }
223308b68b0eSGleb Smirnoff 
223440e04359SKristof Provost static bool
223540e04359SKristof Provost carp_carprcp(void *arg, struct carp_softc *sc, int priv)
223608b68b0eSGleb Smirnoff {
223740e04359SKristof Provost 	struct carpreq *carpr = arg;
223808b68b0eSGleb Smirnoff 
223908b68b0eSGleb Smirnoff 	CARP_LOCK(sc);
224008b68b0eSGleb Smirnoff 	carpr->carpr_state = sc->sc_state;
224108b68b0eSGleb Smirnoff 	carpr->carpr_vhid = sc->sc_vhid;
2242*a254d687SGleb Smirnoff 	switch (sc->sc_version) {
2243*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
224408b68b0eSGleb Smirnoff 		carpr->carpr_advbase = sc->sc_advbase;
224508b68b0eSGleb Smirnoff 		carpr->carpr_advskew = sc->sc_advskew;
224608b68b0eSGleb Smirnoff 		if (priv)
2247*a254d687SGleb Smirnoff 			bcopy(sc->sc_key, carpr->carpr_key,
2248*a254d687SGleb Smirnoff 			    sizeof(carpr->carpr_key));
224908b68b0eSGleb Smirnoff 		else
225008b68b0eSGleb Smirnoff 			bzero(carpr->carpr_key, sizeof(carpr->carpr_key));
2251*a254d687SGleb Smirnoff 		break;
2252*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
2253*a254d687SGleb Smirnoff 		break;
2254*a254d687SGleb Smirnoff 	}
225508b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
225640e04359SKristof Provost 
225740e04359SKristof Provost 	return (true);
225808b68b0eSGleb Smirnoff }
225908b68b0eSGleb Smirnoff 
226040e04359SKristof Provost static int
226113781800SKristof Provost carp_ioctl_set(if_t ifp, struct carpkreq *carpr)
226208b68b0eSGleb Smirnoff {
226349cad3daSZhenlei Huang 	struct epoch_tracker et;
226408b68b0eSGleb Smirnoff 	struct carp_softc *sc = NULL;
226540e04359SKristof Provost 	int error = 0;
226608b68b0eSGleb Smirnoff 
2267*a254d687SGleb Smirnoff 	if (carpr->carpr_vhid <= 0 || carpr->carpr_vhid > CARP_MAXVHID)
2268*a254d687SGleb Smirnoff 		return (EINVAL);
2269*a254d687SGleb Smirnoff 
2270*a254d687SGleb Smirnoff 	switch (carpr->carpr_version) {
2271*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
2272*a254d687SGleb Smirnoff 		if (carpr->carpr_advbase != 0 && (carpr->carpr_advbase > 255 ||
2273*a254d687SGleb Smirnoff 		    carpr->carpr_advbase < CARP_DFLTINTV))
2274*a254d687SGleb Smirnoff 			return (EINVAL);
2275*a254d687SGleb Smirnoff 		if (carpr->carpr_advskew < 0 || carpr->carpr_advskew >= 255)
2276*a254d687SGleb Smirnoff 			return (EINVAL);
2277*a254d687SGleb Smirnoff 		break;
2278*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
2279*a254d687SGleb Smirnoff 		/* XXXGL: shouldn't we check anything? */
2280*a254d687SGleb Smirnoff 		break;
2281*a254d687SGleb Smirnoff 	default:
228240e04359SKristof Provost 		return (EINVAL);
228308b68b0eSGleb Smirnoff 	}
228408b68b0eSGleb Smirnoff 
228508b68b0eSGleb Smirnoff 	if (ifp->if_carp) {
228608b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
228740e04359SKristof Provost 			if (sc->sc_vhid == carpr->carpr_vhid)
228808b68b0eSGleb Smirnoff 				break;
228908b68b0eSGleb Smirnoff 	}
2290*a254d687SGleb Smirnoff 
2291*a254d687SGleb Smirnoff 	if (sc == NULL)
2292*a254d687SGleb Smirnoff 		sc = carp_alloc(ifp, carpr->carpr_version, carpr->carpr_vhid);
2293*a254d687SGleb Smirnoff 	else if (sc->sc_version != carpr->carpr_version)
2294*a254d687SGleb Smirnoff 		return (EINVAL);
2295*a254d687SGleb Smirnoff 
229608b68b0eSGleb Smirnoff 	CARP_LOCK(sc);
2297*a254d687SGleb Smirnoff 	switch (sc->sc_version) {
2298*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
2299*a254d687SGleb Smirnoff 		if (carpr->carpr_advbase != 0)
230040e04359SKristof Provost 			sc->sc_advbase = carpr->carpr_advbase;
230140e04359SKristof Provost 		sc->sc_advskew = carpr->carpr_advskew;
230213781800SKristof Provost 		if (carpr->carpr_addr.s_addr != INADDR_ANY)
230313781800SKristof Provost 			sc->sc_carpaddr = carpr->carpr_addr;
230413781800SKristof Provost 		if (!IN6_IS_ADDR_UNSPECIFIED(&carpr->carpr_addr6)) {
230513781800SKristof Provost 			memcpy(&sc->sc_carpaddr6, &carpr->carpr_addr6,
230613781800SKristof Provost 			    sizeof(sc->sc_carpaddr6));
230713781800SKristof Provost 		}
230840e04359SKristof Provost 		if (carpr->carpr_key[0] != '\0') {
230940e04359SKristof Provost 			bcopy(carpr->carpr_key, sc->sc_key, sizeof(sc->sc_key));
231008b68b0eSGleb Smirnoff 			carp_hmac_prepare(sc);
231108b68b0eSGleb Smirnoff 		}
2312*a254d687SGleb Smirnoff 		break;
2313*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
231437115154SKristof Provost 		if (carpr->carpr_vrrp_priority != 0)
231537115154SKristof Provost 			sc->sc_vrrp_prio = carpr->carpr_vrrp_priority;
231637115154SKristof Provost 		if (carpr->carpr_vrrp_adv_inter)
231737115154SKristof Provost 			sc->sc_vrrp_adv_inter = carpr->carpr_vrrp_adv_inter;
2318*a254d687SGleb Smirnoff 		break;
2319*a254d687SGleb Smirnoff 	}
232037115154SKristof Provost 
232108b68b0eSGleb Smirnoff 	if (sc->sc_state != INIT &&
232240e04359SKristof Provost 	    carpr->carpr_state != sc->sc_state) {
232340e04359SKristof Provost 		switch (carpr->carpr_state) {
232408b68b0eSGleb Smirnoff 		case BACKUP:
232508b68b0eSGleb Smirnoff 			callout_stop(&sc->sc_ad_tmo);
2326369a6708SWill Andrews 			carp_set_state(sc, BACKUP,
2327369a6708SWill Andrews 			    "user requested via ifconfig");
232808b68b0eSGleb Smirnoff 			carp_setrun(sc, 0);
232908b68b0eSGleb Smirnoff 			carp_delroute(sc);
233008b68b0eSGleb Smirnoff 			break;
233108b68b0eSGleb Smirnoff 		case MASTER:
233249cad3daSZhenlei Huang 			NET_EPOCH_ENTER(et);
2333369a6708SWill Andrews 			carp_master_down_locked(sc,
2334369a6708SWill Andrews 			    "user requested via ifconfig");
233549cad3daSZhenlei Huang 			NET_EPOCH_EXIT(et);
233608b68b0eSGleb Smirnoff 			break;
233708b68b0eSGleb Smirnoff 		default:
233808b68b0eSGleb Smirnoff 			break;
233908b68b0eSGleb Smirnoff 		}
234008b68b0eSGleb Smirnoff 	}
234140e04359SKristof Provost 	CARP_UNLOCK(sc);
234240e04359SKristof Provost 
234340e04359SKristof Provost 	return (error);
234440e04359SKristof Provost }
234540e04359SKristof Provost 
234640e04359SKristof Provost static int
234740e04359SKristof Provost carp_ioctl_get(if_t ifp, struct ucred *cred, struct carpreq *carpr,
234840e04359SKristof Provost     bool (*outfn)(void *, struct carp_softc *, int), void *arg)
234908b68b0eSGleb Smirnoff {
235008b68b0eSGleb Smirnoff 	int priveleged;
235140e04359SKristof Provost 	struct carp_softc *sc;
235208b68b0eSGleb Smirnoff 
235340e04359SKristof Provost 	if (carpr->carpr_vhid < 0 || carpr->carpr_vhid > CARP_MAXVHID)
235440e04359SKristof Provost 		return (EINVAL);
235540e04359SKristof Provost 	if (carpr->carpr_count < 1)
235640e04359SKristof Provost 		return (EMSGSIZE);
235740e04359SKristof Provost 	if (ifp->if_carp == NULL)
235840e04359SKristof Provost 		return (ENOENT);
235908b68b0eSGleb Smirnoff 
236040e04359SKristof Provost 	priveleged = (priv_check_cred(cred, PRIV_NETINET_CARP) == 0);
236140e04359SKristof Provost 	if (carpr->carpr_vhid != 0) {
236208b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
236340e04359SKristof Provost 			if (sc->sc_vhid == carpr->carpr_vhid)
236408b68b0eSGleb Smirnoff 				break;
236540e04359SKristof Provost 		if (sc == NULL)
236640e04359SKristof Provost 			return (ENOENT);
236740e04359SKristof Provost 
236840e04359SKristof Provost 		if (! outfn(arg, sc, priveleged))
236940e04359SKristof Provost 			return (ENOMEM);
237040e04359SKristof Provost 		carpr->carpr_count = 1;
237108b68b0eSGleb Smirnoff 	} else  {
237240e04359SKristof Provost 		int count;
237308b68b0eSGleb Smirnoff 
237408b68b0eSGleb Smirnoff 		count = 0;
237508b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
237608b68b0eSGleb Smirnoff 			count++;
237708b68b0eSGleb Smirnoff 
237840e04359SKristof Provost 		if (count > carpr->carpr_count)
237940e04359SKristof Provost 			return (EMSGSIZE);
238040e04359SKristof Provost 
238140e04359SKristof Provost 		IFNET_FOREACH_CARP(ifp, sc) {
238240e04359SKristof Provost 			if (! outfn(arg, sc, priveleged))
238340e04359SKristof Provost 				return (ENOMEM);
238440e04359SKristof Provost 			carpr->carpr_count = count;
238540e04359SKristof Provost 		}
238608b68b0eSGleb Smirnoff 	}
238708b68b0eSGleb Smirnoff 
238840e04359SKristof Provost 	return (0);
238940e04359SKristof Provost }
239040e04359SKristof Provost 
239140e04359SKristof Provost int
239240e04359SKristof Provost carp_ioctl(struct ifreq *ifr, u_long cmd, struct thread *td)
239340e04359SKristof Provost {
239440e04359SKristof Provost 	struct carpreq carpr;
2395*a254d687SGleb Smirnoff 	struct carpkreq carprk = {
2396*a254d687SGleb Smirnoff 		.carpr_version = CARP_VERSION_CARP,
2397*a254d687SGleb Smirnoff 	};
239840e04359SKristof Provost 	struct ifnet *ifp;
239940e04359SKristof Provost 	int error = 0;
240040e04359SKristof Provost 
240140e04359SKristof Provost 	if ((error = copyin(ifr_data_get_ptr(ifr), &carpr, sizeof carpr)))
240240e04359SKristof Provost 		return (error);
240340e04359SKristof Provost 
240440e04359SKristof Provost 	ifp = ifunit_ref(ifr->ifr_name);
240540e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
240640e04359SKristof Provost 		goto out;
240740e04359SKristof Provost 
240840e04359SKristof Provost 	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
240940e04359SKristof Provost 		error = EADDRNOTAVAIL;
241040e04359SKristof Provost 		goto out;
241140e04359SKristof Provost 	}
241240e04359SKristof Provost 
241340e04359SKristof Provost 	sx_xlock(&carp_sx);
241440e04359SKristof Provost 	switch (cmd) {
241540e04359SKristof Provost 	case SIOCSVH:
241640e04359SKristof Provost 		if ((error = priv_check(td, PRIV_NETINET_CARP)))
241740e04359SKristof Provost 			break;
241840e04359SKristof Provost 
241913781800SKristof Provost 		memcpy(&carprk, &carpr, sizeof(carpr));
242013781800SKristof Provost 		error = carp_ioctl_set(ifp, &carprk);
242140e04359SKristof Provost 		break;
242240e04359SKristof Provost 
242340e04359SKristof Provost 	case SIOCGVH:
242440e04359SKristof Provost 		error = carp_ioctl_get(ifp, td->td_ucred, &carpr,
242540e04359SKristof Provost 		    carp_carprcp, &carpr);
242640e04359SKristof Provost 		if (error == 0) {
2427541d96aaSBrooks Davis 			error = copyout(&carpr,
242840e04359SKristof Provost 			    (char *)ifr_data_get_ptr(ifr),
242940e04359SKristof Provost 			    carpr.carpr_count * sizeof(carpr));
243008b68b0eSGleb Smirnoff 		}
243108b68b0eSGleb Smirnoff 		break;
243208b68b0eSGleb Smirnoff 	default:
243308b68b0eSGleb Smirnoff 		error = EINVAL;
243408b68b0eSGleb Smirnoff 	}
243593d4534cSGleb Smirnoff 	sx_xunlock(&carp_sx);
243608b68b0eSGleb Smirnoff 
243708b68b0eSGleb Smirnoff out:
243840e04359SKristof Provost 	if (ifp != NULL)
243908b68b0eSGleb Smirnoff 		if_rele(ifp);
244008b68b0eSGleb Smirnoff 
244108b68b0eSGleb Smirnoff 	return (error);
244208b68b0eSGleb Smirnoff }
244308b68b0eSGleb Smirnoff 
244408b68b0eSGleb Smirnoff static int
244508b68b0eSGleb Smirnoff carp_get_vhid(struct ifaddr *ifa)
244608b68b0eSGleb Smirnoff {
244708b68b0eSGleb Smirnoff 
244808b68b0eSGleb Smirnoff 	if (ifa == NULL || ifa->ifa_carp == NULL)
244908b68b0eSGleb Smirnoff 		return (0);
245008b68b0eSGleb Smirnoff 
245108b68b0eSGleb Smirnoff 	return (ifa->ifa_carp->sc_vhid);
245208b68b0eSGleb Smirnoff }
245308b68b0eSGleb Smirnoff 
245408b68b0eSGleb Smirnoff int
245508b68b0eSGleb Smirnoff carp_attach(struct ifaddr *ifa, int vhid)
245608b68b0eSGleb Smirnoff {
245708b68b0eSGleb Smirnoff 	struct ifnet *ifp = ifa->ifa_ifp;
2458a9a2c40cSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
245908b68b0eSGleb Smirnoff 	struct carp_softc *sc;
246008b68b0eSGleb Smirnoff 	int index, error;
246108b68b0eSGleb Smirnoff 
24629c2cd1aaSGleb Smirnoff 	KASSERT(ifa->ifa_carp == NULL, ("%s: ifa %p attached", __func__, ifa));
246308b68b0eSGleb Smirnoff 
246408b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
246508b68b0eSGleb Smirnoff #ifdef INET
246608b68b0eSGleb Smirnoff 	case AF_INET:
246708b68b0eSGleb Smirnoff #endif
246808b68b0eSGleb Smirnoff #ifdef INET6
246908b68b0eSGleb Smirnoff 	case AF_INET6:
247008b68b0eSGleb Smirnoff #endif
247108b68b0eSGleb Smirnoff 		break;
247208b68b0eSGleb Smirnoff 	default:
247308b68b0eSGleb Smirnoff 		return (EPROTOTYPE);
247408b68b0eSGleb Smirnoff 	}
247508b68b0eSGleb Smirnoff 
24769c2cd1aaSGleb Smirnoff 	sx_xlock(&carp_sx);
24779c2cd1aaSGleb Smirnoff 	if (ifp->if_carp == NULL) {
24789c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
24799c2cd1aaSGleb Smirnoff 		return (ENOPROTOOPT);
24809c2cd1aaSGleb Smirnoff 	}
24819c2cd1aaSGleb Smirnoff 
248208b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc)
248308b68b0eSGleb Smirnoff 		if (sc->sc_vhid == vhid)
248408b68b0eSGleb Smirnoff 			break;
2485a9a2c40cSGleb Smirnoff 	if (sc == NULL) {
24869c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
248708b68b0eSGleb Smirnoff 		return (ENOENT);
2488a9a2c40cSGleb Smirnoff 	}
248908b68b0eSGleb Smirnoff 
2490a9a2c40cSGleb Smirnoff 	error = carp_multicast_setup(cif, ifa->ifa_addr->sa_family);
2491a9a2c40cSGleb Smirnoff 	if (error) {
2492a9a2c40cSGleb Smirnoff 		CIF_FREE(cif);
24939c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
249408b68b0eSGleb Smirnoff 		return (error);
2495a9a2c40cSGleb Smirnoff 	}
249608b68b0eSGleb Smirnoff 
249708b68b0eSGleb Smirnoff 	index = sc->sc_naddrs + sc->sc_naddrs6 + 1;
249808b68b0eSGleb Smirnoff 	if (index > sc->sc_ifasiz / sizeof(struct ifaddr *))
24999c2cd1aaSGleb Smirnoff 		carp_grow_ifas(sc);
250008b68b0eSGleb Smirnoff 
250108b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
250208b68b0eSGleb Smirnoff #ifdef INET
250308b68b0eSGleb Smirnoff 	case AF_INET:
2504a9a2c40cSGleb Smirnoff 		cif->cif_naddrs++;
250508b68b0eSGleb Smirnoff 		sc->sc_naddrs++;
250608b68b0eSGleb Smirnoff 		break;
250708b68b0eSGleb Smirnoff #endif
250808b68b0eSGleb Smirnoff #ifdef INET6
250908b68b0eSGleb Smirnoff 	case AF_INET6:
2510a9a2c40cSGleb Smirnoff 		cif->cif_naddrs6++;
251108b68b0eSGleb Smirnoff 		sc->sc_naddrs6++;
251208b68b0eSGleb Smirnoff 		break;
251308b68b0eSGleb Smirnoff #endif
251408b68b0eSGleb Smirnoff 	}
251508b68b0eSGleb Smirnoff 
251608b68b0eSGleb Smirnoff 	ifa_ref(ifa);
25179c2cd1aaSGleb Smirnoff 
25189c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
251908b68b0eSGleb Smirnoff 	sc->sc_ifas[index - 1] = ifa;
252008b68b0eSGleb Smirnoff 	ifa->ifa_carp = sc;
2521*a254d687SGleb Smirnoff 	if (sc->sc_version == CARP_VERSION_CARP)
252208b68b0eSGleb Smirnoff 		carp_hmac_prepare(sc);
252308b68b0eSGleb Smirnoff 	carp_sc_state(sc);
252408b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
25259c2cd1aaSGleb Smirnoff 
25269c2cd1aaSGleb Smirnoff 	sx_xunlock(&carp_sx);
252708b68b0eSGleb Smirnoff 
252808b68b0eSGleb Smirnoff 	return (0);
252908b68b0eSGleb Smirnoff }
253008b68b0eSGleb Smirnoff 
253108b68b0eSGleb Smirnoff void
2532338e227aSLuiz Otavio O Souza carp_detach(struct ifaddr *ifa, bool keep_cif)
253308b68b0eSGleb Smirnoff {
2534a9a2c40cSGleb Smirnoff 	struct ifnet *ifp = ifa->ifa_ifp;
2535a9a2c40cSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
253608b68b0eSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
253708b68b0eSGleb Smirnoff 	int i, index;
253808b68b0eSGleb Smirnoff 
253908b68b0eSGleb Smirnoff 	KASSERT(sc != NULL, ("%s: %p not attached", __func__, ifa));
254008b68b0eSGleb Smirnoff 
25419c2cd1aaSGleb Smirnoff 	sx_xlock(&carp_sx);
254208b68b0eSGleb Smirnoff 
25439c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
254408b68b0eSGleb Smirnoff 	/* Shift array. */
254508b68b0eSGleb Smirnoff 	index = sc->sc_naddrs + sc->sc_naddrs6;
254608b68b0eSGleb Smirnoff 	for (i = 0; i < index; i++)
254708b68b0eSGleb Smirnoff 		if (sc->sc_ifas[i] == ifa)
254808b68b0eSGleb Smirnoff 			break;
254908b68b0eSGleb Smirnoff 	KASSERT(i < index, ("%s: %p no backref", __func__, ifa));
255008b68b0eSGleb Smirnoff 	for (; i < index - 1; i++)
255108b68b0eSGleb Smirnoff 		sc->sc_ifas[i] = sc->sc_ifas[i+1];
255208b68b0eSGleb Smirnoff 	sc->sc_ifas[index - 1] = NULL;
255308b68b0eSGleb Smirnoff 
255408b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
255508b68b0eSGleb Smirnoff #ifdef INET
255608b68b0eSGleb Smirnoff 	case AF_INET:
2557a9a2c40cSGleb Smirnoff 		cif->cif_naddrs--;
255808b68b0eSGleb Smirnoff 		sc->sc_naddrs--;
255908b68b0eSGleb Smirnoff 		break;
256008b68b0eSGleb Smirnoff #endif
256108b68b0eSGleb Smirnoff #ifdef INET6
256208b68b0eSGleb Smirnoff 	case AF_INET6:
2563a9a2c40cSGleb Smirnoff 		cif->cif_naddrs6--;
256408b68b0eSGleb Smirnoff 		sc->sc_naddrs6--;
256508b68b0eSGleb Smirnoff 		break;
256608b68b0eSGleb Smirnoff #endif
256708b68b0eSGleb Smirnoff 	}
256808b68b0eSGleb Smirnoff 
25692512b096SGleb Smirnoff 	carp_ifa_delroute(ifa);
2570a9a2c40cSGleb Smirnoff 	carp_multicast_cleanup(cif, ifa->ifa_addr->sa_family);
257108b68b0eSGleb Smirnoff 
257208b68b0eSGleb Smirnoff 	ifa->ifa_carp = NULL;
257308b68b0eSGleb Smirnoff 	ifa_free(ifa);
257408b68b0eSGleb Smirnoff 
2575*a254d687SGleb Smirnoff 	if (sc->sc_version == CARP_VERSION_CARP)
257608b68b0eSGleb Smirnoff 		carp_hmac_prepare(sc);
257708b68b0eSGleb Smirnoff 	carp_sc_state(sc);
257808b68b0eSGleb Smirnoff 
2579338e227aSLuiz Otavio O Souza 	if (!keep_cif && sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0)
258008b68b0eSGleb Smirnoff 		carp_destroy(sc);
25819c2cd1aaSGleb Smirnoff 	else
258208b68b0eSGleb Smirnoff 		CARP_UNLOCK(sc);
25839c2cd1aaSGleb Smirnoff 
2584338e227aSLuiz Otavio O Souza 	if (!keep_cif)
25859c2cd1aaSGleb Smirnoff 		CIF_FREE(cif);
25869c2cd1aaSGleb Smirnoff 
25879c2cd1aaSGleb Smirnoff 	sx_xunlock(&carp_sx);
258808b68b0eSGleb Smirnoff }
258908b68b0eSGleb Smirnoff 
259008b68b0eSGleb Smirnoff static void
2591d01641e2SWill Andrews carp_set_state(struct carp_softc *sc, int state, const char *reason)
259208b68b0eSGleb Smirnoff {
259308b68b0eSGleb Smirnoff 
259408b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
259508b68b0eSGleb Smirnoff 
259608b68b0eSGleb Smirnoff 	if (sc->sc_state != state) {
259708b68b0eSGleb Smirnoff 		const char *carp_states[] = { CARP_STATES };
259808b68b0eSGleb Smirnoff 		char subsys[IFNAMSIZ+5];
259908b68b0eSGleb Smirnoff 
260008b68b0eSGleb Smirnoff 		snprintf(subsys, IFNAMSIZ+5, "%u@%s", sc->sc_vhid,
2601511a6d5eSKristof Provost 		    if_name(sc->sc_carpdev));
2602d01641e2SWill Andrews 
2603d01641e2SWill Andrews 		CARP_LOG("%s: %s -> %s (%s)\n", subsys,
2604d01641e2SWill Andrews 		    carp_states[sc->sc_state], carp_states[state], reason);
2605d01641e2SWill Andrews 
2606d01641e2SWill Andrews 		sc->sc_state = state;
2607d01641e2SWill Andrews 
260808b68b0eSGleb Smirnoff 		devctl_notify("CARP", subsys, carp_states[state], NULL);
260908b68b0eSGleb Smirnoff 	}
261008b68b0eSGleb Smirnoff }
261108b68b0eSGleb Smirnoff 
261208b68b0eSGleb Smirnoff static void
261308b68b0eSGleb Smirnoff carp_linkstate(struct ifnet *ifp)
2614d220759bSGleb Smirnoff {
2615d220759bSGleb Smirnoff 	struct carp_softc *sc;
2616d220759bSGleb Smirnoff 
261708b68b0eSGleb Smirnoff 	CIF_LOCK(ifp->if_carp);
261808b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc) {
261908b68b0eSGleb Smirnoff 		CARP_LOCK(sc);
262008b68b0eSGleb Smirnoff 		carp_sc_state(sc);
262108b68b0eSGleb Smirnoff 		CARP_UNLOCK(sc);
262208b68b0eSGleb Smirnoff 	}
262308b68b0eSGleb Smirnoff 	CIF_UNLOCK(ifp->if_carp);
26244cb39345SGleb Smirnoff }
26254cb39345SGleb Smirnoff 
26264cb39345SGleb Smirnoff static void
262708b68b0eSGleb Smirnoff carp_sc_state(struct carp_softc *sc)
26284cb39345SGleb Smirnoff {
262908b68b0eSGleb Smirnoff 
263008b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
26314cb39345SGleb Smirnoff 
2632e8c34a71SGleb Smirnoff 	if (sc->sc_carpdev->if_link_state != LINK_STATE_UP ||
2633167a3440SAlexander Motin 	    !(sc->sc_carpdev->if_flags & IFF_UP) ||
2634167a3440SAlexander Motin 	    !V_carp_allow) {
2635a9771948SGleb Smirnoff 		callout_stop(&sc->sc_ad_tmo);
263608b68b0eSGleb Smirnoff #ifdef INET
2637a9771948SGleb Smirnoff 		callout_stop(&sc->sc_md_tmo);
263808b68b0eSGleb Smirnoff #endif
263908b68b0eSGleb Smirnoff #ifdef INET6
2640a9771948SGleb Smirnoff 		callout_stop(&sc->sc_md6_tmo);
264108b68b0eSGleb Smirnoff #endif
2642bb269f3aSWill Andrews 		carp_set_state(sc, INIT, "hardware interface down");
2643a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
2644600bf006SAndrey V. Elsukov 		carp_delroute(sc);
2645f08535f8SGleb Smirnoff 		if (!sc->sc_suppress)
2646c5c392e7SMikolaj Golub 			carp_demote_adj(V_carp_ifdown_adj, "interface down");
2647a9771948SGleb Smirnoff 		sc->sc_suppress = 1;
2648a9771948SGleb Smirnoff 	} else {
2649bb269f3aSWill Andrews 		carp_set_state(sc, INIT, "hardware interface up");
2650a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
2651a9771948SGleb Smirnoff 		if (sc->sc_suppress)
2652c5c392e7SMikolaj Golub 			carp_demote_adj(-V_carp_ifdown_adj, "interface up");
2653a9771948SGleb Smirnoff 		sc->sc_suppress = 0;
2654a9771948SGleb Smirnoff 	}
2655a9771948SGleb Smirnoff }
2656a9771948SGleb Smirnoff 
2657f08535f8SGleb Smirnoff static void
2658f08535f8SGleb Smirnoff carp_demote_adj(int adj, char *reason)
2659f08535f8SGleb Smirnoff {
2660c5c392e7SMikolaj Golub 	atomic_add_int(&V_carp_demotion, adj);
2661c5c392e7SMikolaj Golub 	CARP_LOG("demoted by %d to %d (%s)\n", adj, V_carp_demotion, reason);
2662f08535f8SGleb Smirnoff 	taskqueue_enqueue(taskqueue_swi, &carp_sendall_task);
2663f08535f8SGleb Smirnoff }
266408b68b0eSGleb Smirnoff 
26657951008bSGleb Smirnoff static int
2666167a3440SAlexander Motin carp_allow_sysctl(SYSCTL_HANDLER_ARGS)
2667167a3440SAlexander Motin {
2668167a3440SAlexander Motin 	int new, error;
2669167a3440SAlexander Motin 	struct carp_softc *sc;
2670167a3440SAlexander Motin 
2671167a3440SAlexander Motin 	new = V_carp_allow;
2672167a3440SAlexander Motin 	error = sysctl_handle_int(oidp, &new, 0, req);
2673167a3440SAlexander Motin 	if (error || !req->newptr)
2674167a3440SAlexander Motin 		return (error);
2675167a3440SAlexander Motin 
2676167a3440SAlexander Motin 	if (V_carp_allow != new) {
2677167a3440SAlexander Motin 		V_carp_allow = new;
2678167a3440SAlexander Motin 
2679167a3440SAlexander Motin 		mtx_lock(&carp_mtx);
2680167a3440SAlexander Motin 		LIST_FOREACH(sc, &carp_list, sc_next) {
2681167a3440SAlexander Motin 			CARP_LOCK(sc);
2682167a3440SAlexander Motin 			if (curvnet == sc->sc_carpdev->if_vnet)
2683167a3440SAlexander Motin 				carp_sc_state(sc);
2684167a3440SAlexander Motin 			CARP_UNLOCK(sc);
2685167a3440SAlexander Motin 		}
2686167a3440SAlexander Motin 		mtx_unlock(&carp_mtx);
2687167a3440SAlexander Motin 	}
2688167a3440SAlexander Motin 
2689167a3440SAlexander Motin 	return (0);
2690167a3440SAlexander Motin }
2691167a3440SAlexander Motin 
2692167a3440SAlexander Motin static int
26930d3d234cSKristof Provost carp_dscp_sysctl(SYSCTL_HANDLER_ARGS)
26940d3d234cSKristof Provost {
26950d3d234cSKristof Provost 	int new, error;
26960d3d234cSKristof Provost 
26970d3d234cSKristof Provost 	new = V_carp_dscp;
26980d3d234cSKristof Provost 	error = sysctl_handle_int(oidp, &new, 0, req);
26990d3d234cSKristof Provost 	if (error || !req->newptr)
27000d3d234cSKristof Provost 		return (error);
27010d3d234cSKristof Provost 
27020d3d234cSKristof Provost 	if (new < 0 || new > 63)
27030d3d234cSKristof Provost 		return (EINVAL);
27040d3d234cSKristof Provost 
27050d3d234cSKristof Provost 	V_carp_dscp = new;
27060d3d234cSKristof Provost 
27070d3d234cSKristof Provost 	return (0);
27080d3d234cSKristof Provost }
27090d3d234cSKristof Provost 
27100d3d234cSKristof Provost static int
27117951008bSGleb Smirnoff carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS)
27127951008bSGleb Smirnoff {
27137951008bSGleb Smirnoff 	int new, error;
27147951008bSGleb Smirnoff 
2715c5c392e7SMikolaj Golub 	new = V_carp_demotion;
27167951008bSGleb Smirnoff 	error = sysctl_handle_int(oidp, &new, 0, req);
27177951008bSGleb Smirnoff 	if (error || !req->newptr)
27187951008bSGleb Smirnoff 		return (error);
27197951008bSGleb Smirnoff 
27207951008bSGleb Smirnoff 	carp_demote_adj(new, "sysctl");
27217951008bSGleb Smirnoff 
27227951008bSGleb Smirnoff 	return (0);
27237951008bSGleb Smirnoff }
27247951008bSGleb Smirnoff 
272540e04359SKristof Provost static int
272640e04359SKristof Provost nlattr_get_carp_key(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
272740e04359SKristof Provost {
272840e04359SKristof Provost 	if (__predict_false(NLA_DATA_LEN(nla) > CARP_KEY_LEN))
272940e04359SKristof Provost 		return (EINVAL);
273040e04359SKristof Provost 
273140e04359SKristof Provost 	memcpy(target, NLA_DATA_CONST(nla), NLA_DATA_LEN(nla));
273240e04359SKristof Provost 	return (0);
273340e04359SKristof Provost }
273440e04359SKristof Provost 
273540e04359SKristof Provost struct carp_nl_send_args {
273640e04359SKristof Provost 	struct nlmsghdr *hdr;
273740e04359SKristof Provost 	struct nl_pstate *npt;
273840e04359SKristof Provost };
273940e04359SKristof Provost 
274040e04359SKristof Provost static bool
274140e04359SKristof Provost carp_nl_send(void *arg, struct carp_softc *sc, int priv)
274240e04359SKristof Provost {
274340e04359SKristof Provost 	struct carp_nl_send_args *nlsa = arg;
274440e04359SKristof Provost 	struct nlmsghdr *hdr = nlsa->hdr;
274540e04359SKristof Provost 	struct nl_pstate *npt = nlsa->npt;
274640e04359SKristof Provost 	struct nl_writer *nw = npt->nw;
274740e04359SKristof Provost 	struct genlmsghdr *ghdr_new;
274840e04359SKristof Provost 
274940e04359SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
275040e04359SKristof Provost 		nlmsg_abort(nw);
275140e04359SKristof Provost 		return (false);
275240e04359SKristof Provost 	}
275340e04359SKristof Provost 
275440e04359SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
275540e04359SKristof Provost 	if (ghdr_new == NULL) {
275640e04359SKristof Provost 		nlmsg_abort(nw);
275740e04359SKristof Provost 		return (false);
275840e04359SKristof Provost 	}
275940e04359SKristof Provost 
276040e04359SKristof Provost 	ghdr_new->cmd = CARP_NL_CMD_GET;
276140e04359SKristof Provost 	ghdr_new->version = 0;
276240e04359SKristof Provost 	ghdr_new->reserved = 0;
276340e04359SKristof Provost 
276440e04359SKristof Provost 	CARP_LOCK(sc);
276540e04359SKristof Provost 
276640e04359SKristof Provost 	nlattr_add_u32(nw, CARP_NL_VHID, sc->sc_vhid);
276740e04359SKristof Provost 	nlattr_add_u32(nw, CARP_NL_STATE, sc->sc_state);
2768*a254d687SGleb Smirnoff 	nlattr_add_u8(nw, CARP_NL_VERSION, sc->sc_version);
2769*a254d687SGleb Smirnoff 	switch (sc->sc_version) {
2770*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
277140e04359SKristof Provost 		nlattr_add_s32(nw, CARP_NL_ADVBASE, sc->sc_advbase);
277240e04359SKristof Provost 		nlattr_add_s32(nw, CARP_NL_ADVSKEW, sc->sc_advskew);
277313781800SKristof Provost 		nlattr_add_in_addr(nw, CARP_NL_ADDR, &sc->sc_carpaddr);
277413781800SKristof Provost 		nlattr_add_in6_addr(nw, CARP_NL_ADDR6, &sc->sc_carpaddr6);
277540e04359SKristof Provost 		if (priv)
2776*a254d687SGleb Smirnoff 			nlattr_add(nw, CARP_NL_KEY, sizeof(sc->sc_key),
2777*a254d687SGleb Smirnoff 			    sc->sc_key);
2778*a254d687SGleb Smirnoff 		break;
2779*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
2780*a254d687SGleb Smirnoff 		nlattr_add_u8(nw, CARP_NL_VRRP_PRIORITY, sc->sc_vrrp_prio);
2781*a254d687SGleb Smirnoff 		nlattr_add_u16(nw, CARP_NL_VRRP_ADV_INTER,
2782*a254d687SGleb Smirnoff 		    sc->sc_vrrp_adv_inter);
2783*a254d687SGleb Smirnoff 		break;
2784*a254d687SGleb Smirnoff 	}
278540e04359SKristof Provost 
278640e04359SKristof Provost 	CARP_UNLOCK(sc);
278740e04359SKristof Provost 
278840e04359SKristof Provost 	if (! nlmsg_end(nw)) {
278940e04359SKristof Provost 		nlmsg_abort(nw);
279040e04359SKristof Provost 		return (false);
279140e04359SKristof Provost 	}
279240e04359SKristof Provost 
279340e04359SKristof Provost 	return (true);
279440e04359SKristof Provost }
279540e04359SKristof Provost 
279640e04359SKristof Provost struct nl_carp_parsed {
279740e04359SKristof Provost 	unsigned int	ifindex;
279828921c4fSKristof Provost 	char		*ifname;
279940e04359SKristof Provost 	uint32_t	state;
280040e04359SKristof Provost 	uint32_t	vhid;
280140e04359SKristof Provost 	int32_t		advbase;
280240e04359SKristof Provost 	int32_t		advskew;
280340e04359SKristof Provost 	char		key[CARP_KEY_LEN];
280413781800SKristof Provost 	struct in_addr	addr;
280513781800SKristof Provost 	struct in6_addr	addr6;
280637115154SKristof Provost 	carp_version_t	version;
280737115154SKristof Provost 	uint8_t		vrrp_prio;
280837115154SKristof Provost 	uint16_t	vrrp_adv_inter;
280940e04359SKristof Provost };
281040e04359SKristof Provost 
281140e04359SKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
281240e04359SKristof Provost #define	_OUT(_field)	offsetof(struct nl_carp_parsed, _field)
281340e04359SKristof Provost 
281440e04359SKristof Provost static const struct nlattr_parser nla_p_set[] = {
281540e04359SKristof Provost 	{ .type = CARP_NL_VHID, .off = _OUT(vhid), .cb = nlattr_get_uint32 },
281640e04359SKristof Provost 	{ .type = CARP_NL_STATE, .off = _OUT(state), .cb = nlattr_get_uint32 },
281740e04359SKristof Provost 	{ .type = CARP_NL_ADVBASE, .off = _OUT(advbase), .cb = nlattr_get_uint32 },
281840e04359SKristof Provost 	{ .type = CARP_NL_ADVSKEW, .off = _OUT(advskew), .cb = nlattr_get_uint32 },
281940e04359SKristof Provost 	{ .type = CARP_NL_KEY, .off = _OUT(key), .cb = nlattr_get_carp_key },
282040e04359SKristof Provost 	{ .type = CARP_NL_IFINDEX, .off = _OUT(ifindex), .cb = nlattr_get_uint32 },
282113781800SKristof Provost 	{ .type = CARP_NL_ADDR, .off = _OUT(addr), .cb = nlattr_get_in_addr },
282213781800SKristof Provost 	{ .type = CARP_NL_ADDR6, .off = _OUT(addr6), .cb = nlattr_get_in6_addr },
282328921c4fSKristof Provost 	{ .type = CARP_NL_IFNAME, .off = _OUT(ifname), .cb = nlattr_get_string },
282437115154SKristof Provost 	{ .type = CARP_NL_VERSION, .off = _OUT(version), .cb = nlattr_get_uint8 },
282537115154SKristof Provost 	{ .type = CARP_NL_VRRP_PRIORITY, .off = _OUT(vrrp_prio), .cb = nlattr_get_uint8 },
282637115154SKristof Provost 	{ .type = CARP_NL_VRRP_ADV_INTER, .off = _OUT(vrrp_adv_inter), .cb = nlattr_get_uint16 },
282740e04359SKristof Provost };
282840e04359SKristof Provost static const struct nlfield_parser nlf_p_set[] = {
282940e04359SKristof Provost };
283040e04359SKristof Provost NL_DECLARE_PARSER(carp_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
283140e04359SKristof Provost #undef _IN
283240e04359SKristof Provost #undef _OUT
283340e04359SKristof Provost 
283440e04359SKristof Provost 
283540e04359SKristof Provost static int
283640e04359SKristof Provost carp_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
283740e04359SKristof Provost {
283840e04359SKristof Provost 	struct nl_carp_parsed attrs = { };
283940e04359SKristof Provost 	struct carp_nl_send_args args;
284040e04359SKristof Provost 	struct carpreq carpr = { };
284140e04359SKristof Provost 	struct epoch_tracker et;
284228921c4fSKristof Provost 	if_t ifp = NULL;
284340e04359SKristof Provost 	int error;
284440e04359SKristof Provost 
284540e04359SKristof Provost 	error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
284640e04359SKristof Provost 	if (error != 0)
284740e04359SKristof Provost 		return (error);
284840e04359SKristof Provost 
284940e04359SKristof Provost 	NET_EPOCH_ENTER(et);
285028921c4fSKristof Provost 	if (attrs.ifname != NULL)
285128921c4fSKristof Provost 		ifp = ifunit_ref(attrs.ifname);
285228921c4fSKristof Provost 	else if (attrs.ifindex != 0)
285340e04359SKristof Provost 		ifp = ifnet_byindex_ref(attrs.ifindex);
285440e04359SKristof Provost 	NET_EPOCH_EXIT(et);
285540e04359SKristof Provost 
285640e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
285740e04359SKristof Provost 		goto out;
285840e04359SKristof Provost 
285940e04359SKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
286040e04359SKristof Provost 	args.hdr = hdr;
286140e04359SKristof Provost 	args.npt = npt;
286240e04359SKristof Provost 
286340e04359SKristof Provost 	carpr.carpr_vhid = attrs.vhid;
286440e04359SKristof Provost 	carpr.carpr_count = CARP_MAXVHID;
286540e04359SKristof Provost 
286640e04359SKristof Provost 	sx_xlock(&carp_sx);
286740e04359SKristof Provost 	error = carp_ioctl_get(ifp, nlp_get_cred(npt->nlp), &carpr,
286840e04359SKristof Provost 	    carp_nl_send, &args);
286940e04359SKristof Provost 	sx_xunlock(&carp_sx);
287040e04359SKristof Provost 
287140e04359SKristof Provost 	if (! nlmsg_end_dump(npt->nw, error, hdr))
287240e04359SKristof Provost 		error = ENOMEM;
287340e04359SKristof Provost 
287440e04359SKristof Provost out:
287540e04359SKristof Provost 	if (ifp != NULL)
287640e04359SKristof Provost 		if_rele(ifp);
287740e04359SKristof Provost 
287840e04359SKristof Provost 	return (error);
287940e04359SKristof Provost }
288040e04359SKristof Provost 
288140e04359SKristof Provost static int
288240e04359SKristof Provost carp_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
288340e04359SKristof Provost {
288440e04359SKristof Provost 	struct nl_carp_parsed attrs = { };
288513781800SKristof Provost 	struct carpkreq carpr;
288640e04359SKristof Provost 	struct epoch_tracker et;
288728921c4fSKristof Provost 	if_t ifp = NULL;
288840e04359SKristof Provost 	int error;
288940e04359SKristof Provost 
289040e04359SKristof Provost 	error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
289140e04359SKristof Provost 	if (error != 0)
289240e04359SKristof Provost 		return (error);
289340e04359SKristof Provost 
289440e04359SKristof Provost 	if (attrs.vhid <= 0 || attrs.vhid > CARP_MAXVHID)
289540e04359SKristof Provost 		return (EINVAL);
289640e04359SKristof Provost 	if (attrs.state > CARP_MAXSTATE)
289740e04359SKristof Provost 		return (EINVAL);
2898*a254d687SGleb Smirnoff 	if (attrs.version == 0)	/* compat with pre-VRRPv3 */
2899*a254d687SGleb Smirnoff 		attrs.version = CARP_VERSION_CARP;
2900*a254d687SGleb Smirnoff 	switch (attrs.version) {
2901*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
290240e04359SKristof Provost 		if (attrs.advbase < 0 || attrs.advskew < 0)
290340e04359SKristof Provost 			return (EINVAL);
290440e04359SKristof Provost 		if (attrs.advbase > 255)
290540e04359SKristof Provost 			return (EINVAL);
290640e04359SKristof Provost 		if (attrs.advskew >= 255)
290740e04359SKristof Provost 			return (EINVAL);
2908*a254d687SGleb Smirnoff 		break;
2909*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
291037115154SKristof Provost 		if (attrs.vrrp_adv_inter > VRRP_MAX_INTERVAL)
291137115154SKristof Provost 			return (EINVAL);
2912*a254d687SGleb Smirnoff 		break;
2913*a254d687SGleb Smirnoff 	default:
2914*a254d687SGleb Smirnoff 		return (EINVAL);
2915*a254d687SGleb Smirnoff 	}
291640e04359SKristof Provost 
291740e04359SKristof Provost 	NET_EPOCH_ENTER(et);
291828921c4fSKristof Provost 	if (attrs.ifname != NULL)
291928921c4fSKristof Provost 		ifp = ifunit_ref(attrs.ifname);
292028921c4fSKristof Provost 	else if (attrs.ifindex != 0)
292140e04359SKristof Provost 		ifp = ifnet_byindex_ref(attrs.ifindex);
292240e04359SKristof Provost 	NET_EPOCH_EXIT(et);
292340e04359SKristof Provost 
292440e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
292540e04359SKristof Provost 		goto out;
292640e04359SKristof Provost 
292740e04359SKristof Provost 	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
292840e04359SKristof Provost 		error = EADDRNOTAVAIL;
292940e04359SKristof Provost 		goto out;
293040e04359SKristof Provost 	}
293140e04359SKristof Provost 
293240e04359SKristof Provost 	carpr.carpr_count = 1;
293340e04359SKristof Provost 	carpr.carpr_vhid = attrs.vhid;
293440e04359SKristof Provost 	carpr.carpr_state = attrs.state;
2935*a254d687SGleb Smirnoff 	carpr.carpr_version = attrs.version;
2936*a254d687SGleb Smirnoff 	switch (attrs.version) {
2937*a254d687SGleb Smirnoff 	case CARP_VERSION_CARP:
293840e04359SKristof Provost 		carpr.carpr_advbase = attrs.advbase;
293940e04359SKristof Provost 		carpr.carpr_advskew = attrs.advskew;
294013781800SKristof Provost 		carpr.carpr_addr = attrs.addr;
294113781800SKristof Provost 		carpr.carpr_addr6 = attrs.addr6;
2942*a254d687SGleb Smirnoff 		memcpy(&carpr.carpr_key, &attrs.key, sizeof(attrs.key));
2943*a254d687SGleb Smirnoff 		break;
2944*a254d687SGleb Smirnoff 	case CARP_VERSION_VRRPv3:
294537115154SKristof Provost 		carpr.carpr_vrrp_priority = attrs.vrrp_prio;
294637115154SKristof Provost 		carpr.carpr_vrrp_adv_inter = attrs.vrrp_adv_inter;
2947*a254d687SGleb Smirnoff 		break;
2948*a254d687SGleb Smirnoff 	}
294940e04359SKristof Provost 
295040e04359SKristof Provost 	sx_xlock(&carp_sx);
295140e04359SKristof Provost 	error = carp_ioctl_set(ifp, &carpr);
295240e04359SKristof Provost 	sx_xunlock(&carp_sx);
295340e04359SKristof Provost 
295440e04359SKristof Provost out:
295540e04359SKristof Provost 	if (ifp != NULL)
295640e04359SKristof Provost 		if_rele(ifp);
295740e04359SKristof Provost 
295840e04359SKristof Provost 	return (error);
295940e04359SKristof Provost }
296040e04359SKristof Provost 
296140e04359SKristof Provost static const struct nlhdr_parser *all_parsers[] = {
296240e04359SKristof Provost 	&carp_parser
296340e04359SKristof Provost };
296440e04359SKristof Provost 
296540e04359SKristof Provost static const struct genl_cmd carp_cmds[] = {
296640e04359SKristof Provost 	{
296740e04359SKristof Provost 		.cmd_num = CARP_NL_CMD_GET,
296840e04359SKristof Provost 		.cmd_name = "SIOCGVH",
296940e04359SKristof Provost 		.cmd_cb = carp_nl_get,
297040e04359SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP |
297140e04359SKristof Provost 		    GENL_CMD_CAP_HASPOL,
297240e04359SKristof Provost 	},
297340e04359SKristof Provost 	{
297440e04359SKristof Provost 		.cmd_num = CARP_NL_CMD_SET,
297540e04359SKristof Provost 		.cmd_name = "SIOCSVH",
297640e04359SKristof Provost 		.cmd_cb = carp_nl_set,
297740e04359SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
297840e04359SKristof Provost 		.cmd_priv = PRIV_NETINET_CARP,
297940e04359SKristof Provost 	},
298040e04359SKristof Provost };
298140e04359SKristof Provost 
298240e04359SKristof Provost static void
298340e04359SKristof Provost carp_nl_register(void)
298440e04359SKristof Provost {
298540e04359SKristof Provost 	bool ret __diagused;
298640e04359SKristof Provost 	int family_id __diagused;
298740e04359SKristof Provost 
298840e04359SKristof Provost 	NL_VERIFY_PARSERS(all_parsers);
298940e04359SKristof Provost 	family_id = genl_register_family(CARP_NL_FAMILY_NAME, 0, 2,
299040e04359SKristof Provost 	    CARP_NL_CMD_MAX);
299140e04359SKristof Provost 	MPASS(family_id != 0);
299240e04359SKristof Provost 
299340e04359SKristof Provost 	ret = genl_register_cmds(CARP_NL_FAMILY_NAME, carp_cmds,
299440e04359SKristof Provost 	    NL_ARRAY_LEN(carp_cmds));
299540e04359SKristof Provost 	MPASS(ret);
299640e04359SKristof Provost }
299740e04359SKristof Provost 
299840e04359SKristof Provost static void
299940e04359SKristof Provost carp_nl_unregister(void)
300040e04359SKristof Provost {
300140e04359SKristof Provost 	genl_unregister_family(CARP_NL_FAMILY_NAME);
300240e04359SKristof Provost }
300340e04359SKristof Provost 
300454bfbd51SWill Andrews static void
300554bfbd51SWill Andrews carp_mod_cleanup(void)
3006a9771948SGleb Smirnoff {
300754bfbd51SWill Andrews 
300840e04359SKristof Provost 	carp_nl_unregister();
300940e04359SKristof Provost 
301054bfbd51SWill Andrews #ifdef INET
301115249f73SWill Andrews 	(void)ipproto_unregister(IPPROTO_CARP);
301254bfbd51SWill Andrews 	carp_iamatch_p = NULL;
301354bfbd51SWill Andrews #endif
301454bfbd51SWill Andrews #ifdef INET6
301515249f73SWill Andrews 	(void)ip6proto_unregister(IPPROTO_CARP);
301654bfbd51SWill Andrews 	carp_iamatch6_p = NULL;
301754bfbd51SWill Andrews 	carp_macmatch6_p = NULL;
301854bfbd51SWill Andrews #endif
301908b68b0eSGleb Smirnoff 	carp_ioctl_p = NULL;
302008b68b0eSGleb Smirnoff 	carp_attach_p = NULL;
302108b68b0eSGleb Smirnoff 	carp_detach_p = NULL;
302208b68b0eSGleb Smirnoff 	carp_get_vhid_p = NULL;
302354bfbd51SWill Andrews 	carp_linkstate_p = NULL;
302454bfbd51SWill Andrews 	carp_forus_p = NULL;
302554bfbd51SWill Andrews 	carp_output_p = NULL;
3026f08535f8SGleb Smirnoff 	carp_demote_adj_p = NULL;
302724421c1cSGleb Smirnoff 	carp_master_p = NULL;
3028f08535f8SGleb Smirnoff 	mtx_unlock(&carp_mtx);
3029f08535f8SGleb Smirnoff 	taskqueue_drain(taskqueue_swi, &carp_sendall_task);
303054bfbd51SWill Andrews 	mtx_destroy(&carp_mtx);
303193d4534cSGleb Smirnoff 	sx_destroy(&carp_sx);
303254bfbd51SWill Andrews }
303354bfbd51SWill Andrews 
3034ee49c5d3SBoris Lytochkin static void
3035ee49c5d3SBoris Lytochkin ipcarp_sysinit(void)
3036ee49c5d3SBoris Lytochkin {
3037ee49c5d3SBoris Lytochkin 
3038ee49c5d3SBoris Lytochkin 	/* Load allow as tunable so to postpone carp start after module load */
3039ee49c5d3SBoris Lytochkin 	TUNABLE_INT_FETCH("net.inet.carp.allow", &V_carp_allow);
3040ee49c5d3SBoris Lytochkin }
3041ee49c5d3SBoris Lytochkin VNET_SYSINIT(ip_carp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipcarp_sysinit, NULL);
3042ee49c5d3SBoris Lytochkin 
304354bfbd51SWill Andrews static int
304454bfbd51SWill Andrews carp_mod_load(void)
304554bfbd51SWill Andrews {
304615249f73SWill Andrews 	int err;
304754bfbd51SWill Andrews 
3048d92d54d5SGleb Smirnoff 	mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF);
304993d4534cSGleb Smirnoff 	sx_init(&carp_sx, "carp_sx");
305008b68b0eSGleb Smirnoff 	LIST_INIT(&carp_list);
305108b68b0eSGleb Smirnoff 	carp_get_vhid_p = carp_get_vhid;
305254bfbd51SWill Andrews 	carp_forus_p = carp_forus;
305354bfbd51SWill Andrews 	carp_output_p = carp_output;
305408b68b0eSGleb Smirnoff 	carp_linkstate_p = carp_linkstate;
305508b68b0eSGleb Smirnoff 	carp_ioctl_p = carp_ioctl;
305608b68b0eSGleb Smirnoff 	carp_attach_p = carp_attach;
305708b68b0eSGleb Smirnoff 	carp_detach_p = carp_detach;
3058f08535f8SGleb Smirnoff 	carp_demote_adj_p = carp_demote_adj;
305924421c1cSGleb Smirnoff 	carp_master_p = carp_master;
306054bfbd51SWill Andrews #ifdef INET6
306154bfbd51SWill Andrews 	carp_iamatch6_p = carp_iamatch6;
306254bfbd51SWill Andrews 	carp_macmatch6_p = carp_macmatch6;
306378b1fc05SGleb Smirnoff 	err = ip6proto_register(IPPROTO_CARP, carp6_input, NULL);
306415249f73SWill Andrews 	if (err) {
306515249f73SWill Andrews 		printf("carp: error %d registering with INET6\n", err);
306615249f73SWill Andrews 		carp_mod_cleanup();
30676baf7a24SGleb Smirnoff 		return (err);
306815249f73SWill Andrews 	}
306954bfbd51SWill Andrews #endif
307054bfbd51SWill Andrews #ifdef INET
307154bfbd51SWill Andrews 	carp_iamatch_p = carp_iamatch;
307278b1fc05SGleb Smirnoff 	err = ipproto_register(IPPROTO_CARP, carp_input, NULL);
307315249f73SWill Andrews 	if (err) {
307415249f73SWill Andrews 		printf("carp: error %d registering with INET\n", err);
307515249f73SWill Andrews 		carp_mod_cleanup();
30766baf7a24SGleb Smirnoff 		return (err);
307715249f73SWill Andrews 	}
307854bfbd51SWill Andrews #endif
307940e04359SKristof Provost 
308040e04359SKristof Provost 	carp_nl_register();
308140e04359SKristof Provost 
308208b68b0eSGleb Smirnoff 	return (0);
308354bfbd51SWill Andrews }
3084a9771948SGleb Smirnoff 
308554bfbd51SWill Andrews static int
308654bfbd51SWill Andrews carp_modevent(module_t mod, int type, void *data)
308754bfbd51SWill Andrews {
308854bfbd51SWill Andrews 	switch (type) {
308954bfbd51SWill Andrews 	case MOD_LOAD:
309054bfbd51SWill Andrews 		return carp_mod_load();
309154bfbd51SWill Andrews 		/* NOTREACHED */
3092a9771948SGleb Smirnoff 	case MOD_UNLOAD:
309308b68b0eSGleb Smirnoff 		mtx_lock(&carp_mtx);
309408b68b0eSGleb Smirnoff 		if (LIST_EMPTY(&carp_list))
309554bfbd51SWill Andrews 			carp_mod_cleanup();
309608b68b0eSGleb Smirnoff 		else {
309708b68b0eSGleb Smirnoff 			mtx_unlock(&carp_mtx);
309854bfbd51SWill Andrews 			return (EBUSY);
309908b68b0eSGleb Smirnoff 		}
3100a9771948SGleb Smirnoff 		break;
3101a9771948SGleb Smirnoff 
3102a9771948SGleb Smirnoff 	default:
31030fa08018SGleb Smirnoff 		return (EINVAL);
3104a9771948SGleb Smirnoff 	}
3105a9771948SGleb Smirnoff 
31060fa08018SGleb Smirnoff 	return (0);
3107a9771948SGleb Smirnoff }
3108a9771948SGleb Smirnoff 
3109a9771948SGleb Smirnoff static moduledata_t carp_mod = {
3110a9771948SGleb Smirnoff 	"carp",
3111a9771948SGleb Smirnoff 	carp_modevent,
31129823d527SKevin Lo 	0
3113a9771948SGleb Smirnoff };
3114a9771948SGleb Smirnoff 
3115e24fa11dSWill Andrews DECLARE_MODULE(carp, carp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
3116