xref: /freebsd/sys/netinet/ip_carp.c (revision 5ee92cbd82d0afb65bc5feabf29d8417307d38ad)
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 */
10008b68b0eSGleb Smirnoff 	struct sockaddr_dl	sc_addr;	/* 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;
11137115154SKristof Provost 	struct { /* CARP specific context */
11208b68b0eSGleb Smirnoff 		int		sc_advskew;
11308b68b0eSGleb Smirnoff 		int		sc_advbase;
11413781800SKristof Provost 		struct in_addr	sc_carpaddr;
11513781800SKristof Provost 		struct in6_addr	sc_carpaddr6;
11637115154SKristof Provost 	};
11737115154SKristof Provost 	struct { /* VRRPv3 specific context */
11837115154SKristof Provost 		uint8_t		sc_vrrp_prio;
11937115154SKristof Provost 		uint16_t	sc_vrrp_adv_inter;
12037115154SKristof Provost 		uint16_t	sc_vrrp_master_inter;
12137115154SKristof Provost 	};
12208b68b0eSGleb Smirnoff 
12308b68b0eSGleb Smirnoff 	int			sc_naddrs;
12408b68b0eSGleb Smirnoff 	int			sc_naddrs6;
12508b68b0eSGleb Smirnoff 	int			sc_ifasiz;
126a9771948SGleb Smirnoff 	enum { INIT = 0, BACKUP, MASTER }	sc_state;
127a9771948SGleb Smirnoff 	int			sc_suppress;
128a9771948SGleb Smirnoff 	int			sc_sendad_errors;
129a9771948SGleb Smirnoff #define	CARP_SENDAD_MAX_ERRORS	3
130a9771948SGleb Smirnoff 	int			sc_sendad_success;
131a9771948SGleb Smirnoff #define	CARP_SENDAD_MIN_SUCCESS 3
132a9771948SGleb Smirnoff 
133a9771948SGleb Smirnoff 	int			sc_init_counter;
13408b68b0eSGleb Smirnoff 	uint64_t		sc_counter;
135a9771948SGleb Smirnoff 
136a9771948SGleb Smirnoff 	/* authentication */
137a9771948SGleb Smirnoff #define	CARP_HMAC_PAD	64
138a9771948SGleb Smirnoff 	unsigned char sc_key[CARP_KEY_LEN];
139a9771948SGleb Smirnoff 	unsigned char sc_pad[CARP_HMAC_PAD];
140a9771948SGleb Smirnoff 	SHA1_CTX sc_sha1;
141a9771948SGleb Smirnoff 
14208b68b0eSGleb Smirnoff 	TAILQ_ENTRY(carp_softc)	sc_list;	/* On the carp_if list. */
14308b68b0eSGleb Smirnoff 	LIST_ENTRY(carp_softc)	sc_next;	/* On the global list. */
144a9771948SGleb Smirnoff };
14508b68b0eSGleb Smirnoff 
14608b68b0eSGleb Smirnoff struct carp_if {
14708b68b0eSGleb Smirnoff #ifdef INET
14808b68b0eSGleb Smirnoff 	int	cif_naddrs;
14908b68b0eSGleb Smirnoff #endif
15008b68b0eSGleb Smirnoff #ifdef INET6
15108b68b0eSGleb Smirnoff 	int	cif_naddrs6;
15208b68b0eSGleb Smirnoff #endif
15308b68b0eSGleb Smirnoff 	TAILQ_HEAD(, carp_softc) cif_vrs;
15408b68b0eSGleb Smirnoff #ifdef INET
15508b68b0eSGleb Smirnoff 	struct ip_moptions 	 cif_imo;
15608b68b0eSGleb Smirnoff #endif
15708b68b0eSGleb Smirnoff #ifdef INET6
15808b68b0eSGleb Smirnoff 	struct ip6_moptions 	 cif_im6o;
15908b68b0eSGleb Smirnoff #endif
16008b68b0eSGleb Smirnoff 	struct ifnet	*cif_ifp;
16108b68b0eSGleb Smirnoff 	struct mtx	cif_mtx;
1620cc726f2SGleb Smirnoff 	uint32_t	cif_flags;
1630cc726f2SGleb Smirnoff #define	CIF_PROMISC	0x00000001
16408b68b0eSGleb Smirnoff };
16508b68b0eSGleb Smirnoff 
16613781800SKristof Provost /* Kernel equivalent of struct carpreq, but with more fields for new features.
16713781800SKristof Provost  * */
16813781800SKristof Provost struct carpkreq {
16913781800SKristof Provost 	int		carpr_count;
17013781800SKristof Provost 	int		carpr_vhid;
17113781800SKristof Provost 	int		carpr_state;
17213781800SKristof Provost 	int		carpr_advskew;
17313781800SKristof Provost 	int		carpr_advbase;
17413781800SKristof Provost 	unsigned char	carpr_key[CARP_KEY_LEN];
17513781800SKristof Provost 	/* Everything above this is identical to carpreq */
17613781800SKristof Provost 	struct in_addr	carpr_addr;
17713781800SKristof Provost 	struct in6_addr	carpr_addr6;
17837115154SKristof Provost 	carp_version_t	carpr_version;
17937115154SKristof Provost 	uint8_t		carpr_vrrp_priority;
18037115154SKristof Provost 	uint16_t	carpr_vrrp_adv_inter;
18113781800SKristof Provost };
18213781800SKristof Provost 
18308b68b0eSGleb Smirnoff /*
18408b68b0eSGleb Smirnoff  * Brief design of carp(4).
18508b68b0eSGleb Smirnoff  *
18608b68b0eSGleb Smirnoff  * Any carp-capable ifnet may have a list of carp softcs hanging off
18708b68b0eSGleb Smirnoff  * its ifp->if_carp pointer. Each softc represents one unique virtual
18808b68b0eSGleb Smirnoff  * host id, or vhid. The softc has a back pointer to the ifnet. All
18908b68b0eSGleb Smirnoff  * softcs are joined in a global list, which has quite limited use.
19008b68b0eSGleb Smirnoff  *
19108b68b0eSGleb Smirnoff  * Any interface address that takes part in CARP negotiation has a
19208b68b0eSGleb Smirnoff  * pointer to the softc of its vhid, ifa->ifa_carp. That could be either
19308b68b0eSGleb Smirnoff  * AF_INET or AF_INET6 address.
19408b68b0eSGleb Smirnoff  *
19508b68b0eSGleb Smirnoff  * Although, one can get the softc's backpointer to ifnet and traverse
19608b68b0eSGleb Smirnoff  * through its ifp->if_addrhead queue to find all interface addresses
19708b68b0eSGleb Smirnoff  * involved in CARP, we keep a growable array of ifaddr pointers. This
19808b68b0eSGleb Smirnoff  * allows us to avoid grabbing the IF_ADDR_LOCK() in many traversals that
19908b68b0eSGleb Smirnoff  * do calls into the network stack, thus avoiding LORs.
20008b68b0eSGleb Smirnoff  *
20108b68b0eSGleb Smirnoff  * Locking:
20208b68b0eSGleb Smirnoff  *
20308b68b0eSGleb Smirnoff  * Each softc has a lock sc_mtx. It is used to synchronise carp_input_c(),
20408b68b0eSGleb Smirnoff  * callout-driven events and ioctl()s.
20508b68b0eSGleb Smirnoff  *
20681098a01SAlexander Motin  * To traverse the list of softcs on an ifnet we use CIF_LOCK() or carp_sx.
20781098a01SAlexander Motin  * To traverse the global list we use the mutex carp_mtx.
20808b68b0eSGleb Smirnoff  *
20908b68b0eSGleb Smirnoff  * Known issues with locking:
21008b68b0eSGleb Smirnoff  *
21108b68b0eSGleb Smirnoff  * - Sending ad, we put the pointer to the softc in an mtag, and no reference
21208b68b0eSGleb Smirnoff  *   counting is done on the softc.
21308b68b0eSGleb Smirnoff  * - On module unload we may race (?) with packet processing thread
21408b68b0eSGleb Smirnoff  *   dereferencing our function pointers.
21508b68b0eSGleb Smirnoff  */
216a9771948SGleb Smirnoff 
217c5c392e7SMikolaj Golub /* Accept incoming CARP packets. */
2185f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_allow) = 1;
219c5c392e7SMikolaj Golub #define	V_carp_allow	VNET(carp_allow)
220c5c392e7SMikolaj Golub 
2210d3d234cSKristof Provost /* Set DSCP in outgoing CARP packets. */
2225f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_dscp) = 56;
2230d3d234cSKristof Provost #define	V_carp_dscp	VNET(carp_dscp)
2240d3d234cSKristof Provost 
225c5c392e7SMikolaj Golub /* Preempt slower nodes. */
2265f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_preempt) = 0;
227c5c392e7SMikolaj Golub #define	V_carp_preempt	VNET(carp_preempt)
228c5c392e7SMikolaj Golub 
229c5c392e7SMikolaj Golub /* Log level. */
2305f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_log) = 1;
231c5c392e7SMikolaj Golub #define	V_carp_log	VNET(carp_log)
232c5c392e7SMikolaj Golub 
233c5c392e7SMikolaj Golub /* Global advskew demotion. */
2345f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_demotion) = 0;
235c5c392e7SMikolaj Golub #define	V_carp_demotion	VNET(carp_demotion)
236c5c392e7SMikolaj Golub 
237c5c392e7SMikolaj Golub /* Send error demotion factor. */
2385f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_senderr_adj) = CARP_MAXSKEW;
239c5c392e7SMikolaj Golub #define	V_carp_senderr_adj	VNET(carp_senderr_adj)
240c5c392e7SMikolaj Golub 
241c5c392e7SMikolaj Golub /* Iface down demotion factor. */
2425f901c92SAndrew Turner VNET_DEFINE_STATIC(int, carp_ifdown_adj) = CARP_MAXSKEW;
243c5c392e7SMikolaj Golub #define	V_carp_ifdown_adj	VNET(carp_ifdown_adj)
244c5c392e7SMikolaj Golub 
245167a3440SAlexander Motin static int carp_allow_sysctl(SYSCTL_HANDLER_ARGS);
2460d3d234cSKristof Provost static int carp_dscp_sysctl(SYSCTL_HANDLER_ARGS);
2477951008bSGleb Smirnoff static int carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS);
248a9771948SGleb Smirnoff 
24910b49b23SPawel Biernacki SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
25010b49b23SPawel Biernacki     "CARP");
251167a3440SAlexander Motin SYSCTL_PROC(_net_inet_carp, OID_AUTO, allow,
252242fa308SZhenlei Huang     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NOFETCH | CTLFLAG_MPSAFE,
253ee49c5d3SBoris Lytochkin     &VNET_NAME(carp_allow), 0, carp_allow_sysctl, "I",
254167a3440SAlexander Motin     "Accept incoming CARP packets");
2550d3d234cSKristof Provost SYSCTL_PROC(_net_inet_carp, OID_AUTO, dscp,
25610b49b23SPawel Biernacki     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
25710b49b23SPawel Biernacki     0, 0, carp_dscp_sysctl, "I",
2580d3d234cSKristof Provost     "DSCP value for carp packets");
2596df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, preempt, CTLFLAG_VNET | CTLFLAG_RW,
260c5c392e7SMikolaj Golub     &VNET_NAME(carp_preempt), 0, "High-priority backup preemption mode");
2616df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, log, CTLFLAG_VNET | CTLFLAG_RW,
262c5c392e7SMikolaj Golub     &VNET_NAME(carp_log), 0, "CARP log level");
2636df8a710SGleb Smirnoff SYSCTL_PROC(_net_inet_carp, OID_AUTO, demotion,
26410b49b23SPawel Biernacki     CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
2657951008bSGleb Smirnoff     0, 0, carp_demote_adj_sysctl, "I",
2667951008bSGleb Smirnoff     "Adjust demotion factor (skew of advskew)");
2676df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, senderr_demotion_factor,
2686df8a710SGleb Smirnoff     CTLFLAG_VNET | CTLFLAG_RW,
269c5c392e7SMikolaj Golub     &VNET_NAME(carp_senderr_adj), 0, "Send error demotion factor adjustment");
2706df8a710SGleb Smirnoff SYSCTL_INT(_net_inet_carp, OID_AUTO, ifdown_demotion_factor,
2716df8a710SGleb Smirnoff     CTLFLAG_VNET | CTLFLAG_RW,
272c5c392e7SMikolaj Golub     &VNET_NAME(carp_ifdown_adj), 0,
273c5c392e7SMikolaj Golub     "Interface down demotion factor adjustment");
274f08535f8SGleb Smirnoff 
275c5c392e7SMikolaj Golub VNET_PCPUSTAT_DEFINE(struct carpstats, carpstats);
276c5c392e7SMikolaj Golub VNET_PCPUSTAT_SYSINIT(carpstats);
277c5c392e7SMikolaj Golub VNET_PCPUSTAT_SYSUNINIT(carpstats);
278c5c392e7SMikolaj Golub 
27969edf037SAndrey V. Elsukov #define	CARPSTATS_ADD(name, val)	\
280c5c392e7SMikolaj Golub     counter_u64_add(VNET(carpstats)[offsetof(struct carpstats, name) / \
28169edf037SAndrey V. Elsukov 	sizeof(uint64_t)], (val))
28269edf037SAndrey V. Elsukov #define	CARPSTATS_INC(name)		CARPSTATS_ADD(name, 1)
28369edf037SAndrey V. Elsukov 
284c5c392e7SMikolaj Golub SYSCTL_VNET_PCPUSTAT(_net_inet_carp, OID_AUTO, stats, struct carpstats,
285c5c392e7SMikolaj Golub     carpstats, "CARP statistics (struct carpstats, netinet/ip_carp.h)");
286a9771948SGleb Smirnoff 
28708b68b0eSGleb Smirnoff #define	CARP_LOCK_INIT(sc)	mtx_init(&(sc)->sc_mtx, "carp_softc",   \
288a9771948SGleb Smirnoff 	NULL, MTX_DEF)
28908b68b0eSGleb Smirnoff #define	CARP_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->sc_mtx)
29008b68b0eSGleb Smirnoff #define	CARP_LOCK_ASSERT(sc)	mtx_assert(&(sc)->sc_mtx, MA_OWNED)
29108b68b0eSGleb Smirnoff #define	CARP_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
29208b68b0eSGleb Smirnoff #define	CARP_UNLOCK(sc)		mtx_unlock(&(sc)->sc_mtx)
29308b68b0eSGleb Smirnoff #define	CIF_LOCK_INIT(cif)	mtx_init(&(cif)->cif_mtx, "carp_if",   \
29408b68b0eSGleb Smirnoff 	NULL, MTX_DEF)
29508b68b0eSGleb Smirnoff #define	CIF_LOCK_DESTROY(cif)	mtx_destroy(&(cif)->cif_mtx)
29608b68b0eSGleb Smirnoff #define	CIF_LOCK_ASSERT(cif)	mtx_assert(&(cif)->cif_mtx, MA_OWNED)
29708b68b0eSGleb Smirnoff #define	CIF_LOCK(cif)		mtx_lock(&(cif)->cif_mtx)
29808b68b0eSGleb Smirnoff #define	CIF_UNLOCK(cif)		mtx_unlock(&(cif)->cif_mtx)
299a9a2c40cSGleb Smirnoff #define	CIF_FREE(cif)	do {				\
3009c2cd1aaSGleb Smirnoff 		CIF_LOCK(cif);				\
301a9a2c40cSGleb Smirnoff 		if (TAILQ_EMPTY(&(cif)->cif_vrs))	\
302a9a2c40cSGleb Smirnoff 			carp_free_if(cif);		\
303a9a2c40cSGleb Smirnoff 		else					\
304a9a2c40cSGleb Smirnoff 			CIF_UNLOCK(cif);		\
305a9a2c40cSGleb Smirnoff } while (0)
306d220759bSGleb Smirnoff 
307947b7cf3SGleb Smirnoff #define	CARP_LOG(...)	do {				\
308c5c392e7SMikolaj Golub 	if (V_carp_log > 0)				\
30908b68b0eSGleb Smirnoff 		log(LOG_INFO, "carp: " __VA_ARGS__);	\
310947b7cf3SGleb Smirnoff } while (0)
3111e9e6572SGleb Smirnoff 
312947b7cf3SGleb Smirnoff #define	CARP_DEBUG(...)	do {				\
313c5c392e7SMikolaj Golub 	if (V_carp_log > 1)				\
3141e9e6572SGleb Smirnoff 		log(LOG_DEBUG, __VA_ARGS__);		\
315947b7cf3SGleb Smirnoff } while (0)
316a9771948SGleb Smirnoff 
31708b68b0eSGleb Smirnoff #define	IFNET_FOREACH_IFA(ifp, ifa)					\
318d7c5a620SMatt Macy 	CK_STAILQ_FOREACH((ifa), &(ifp)->if_addrhead, ifa_link)	\
31908b68b0eSGleb Smirnoff 		if ((ifa)->ifa_carp != NULL)
32008b68b0eSGleb Smirnoff 
32108b68b0eSGleb Smirnoff #define	CARP_FOREACH_IFA(sc, ifa)					\
32208b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);						\
32308b68b0eSGleb Smirnoff 	for (int _i = 0;						\
32408b68b0eSGleb Smirnoff 		_i < (sc)->sc_naddrs + (sc)->sc_naddrs6 &&		\
32508b68b0eSGleb Smirnoff 		((ifa) = sc->sc_ifas[_i]) != NULL;			\
32608b68b0eSGleb Smirnoff 		++_i)
32708b68b0eSGleb Smirnoff 
32808b68b0eSGleb Smirnoff #define	IFNET_FOREACH_CARP(ifp, sc)					\
32981098a01SAlexander Motin 	KASSERT(mtx_owned(&ifp->if_carp->cif_mtx) ||			\
33081098a01SAlexander Motin 	    sx_xlocked(&carp_sx), ("cif_vrs not locked"));		\
33108b68b0eSGleb Smirnoff 	TAILQ_FOREACH((sc), &(ifp)->if_carp->cif_vrs, sc_list)
33208b68b0eSGleb Smirnoff 
333f08535f8SGleb Smirnoff #define	DEMOTE_ADVSKEW(sc)					\
334c5c392e7SMikolaj Golub     (((sc)->sc_advskew + V_carp_demotion > CARP_MAXSKEW) ?	\
3351019354bSMarius Halden     CARP_MAXSKEW :						\
3361019354bSMarius Halden         (((sc)->sc_advskew + V_carp_demotion < 0) ?		\
3371019354bSMarius Halden         0 : ((sc)->sc_advskew + V_carp_demotion)))
338f08535f8SGleb Smirnoff 
33913781800SKristof Provost static void	carp_input_c(struct mbuf *, struct carp_header *, sa_family_t, int);
34037115154SKristof Provost static void	vrrp_input_c(struct mbuf *, int, sa_family_t, int, int, uint16_t);
34108b68b0eSGleb Smirnoff static struct carp_softc
34208b68b0eSGleb Smirnoff 		*carp_alloc(struct ifnet *);
34308b68b0eSGleb Smirnoff static void	carp_destroy(struct carp_softc *);
34408b68b0eSGleb Smirnoff static struct carp_if
34508b68b0eSGleb Smirnoff 		*carp_alloc_if(struct ifnet *);
34608b68b0eSGleb Smirnoff static void	carp_free_if(struct carp_if *);
347d01641e2SWill Andrews static void	carp_set_state(struct carp_softc *, int, const char* reason);
34808b68b0eSGleb Smirnoff static void	carp_sc_state(struct carp_softc *);
34908b68b0eSGleb Smirnoff static void	carp_setrun(struct carp_softc *, sa_family_t);
3505c1f0f6dSGleb Smirnoff static void	carp_master_down(void *);
351d01641e2SWill Andrews static void	carp_master_down_locked(struct carp_softc *,
352d01641e2SWill Andrews     		    const char* reason);
35308b68b0eSGleb Smirnoff static void	carp_send_ad_locked(struct carp_softc *);
35437115154SKristof Provost static void	vrrp_send_ad_locked(struct carp_softc *);
35508b68b0eSGleb Smirnoff static void	carp_addroute(struct carp_softc *);
3562512b096SGleb Smirnoff static void	carp_ifa_addroute(struct ifaddr *);
35708b68b0eSGleb Smirnoff static void	carp_delroute(struct carp_softc *);
3582512b096SGleb Smirnoff static void	carp_ifa_delroute(struct ifaddr *);
359f08535f8SGleb Smirnoff static void	carp_send_ad_all(void *, int);
360f08535f8SGleb Smirnoff static void	carp_demote_adj(int, char *);
361a9771948SGleb Smirnoff 
36208b68b0eSGleb Smirnoff static LIST_HEAD(, carp_softc) carp_list;
363d92d54d5SGleb Smirnoff static struct mtx carp_mtx;
36493d4534cSGleb Smirnoff static struct sx carp_sx;
365f08535f8SGleb Smirnoff static struct task carp_sendall_task =
366f08535f8SGleb Smirnoff     TASK_INITIALIZER(0, carp_send_ad_all, NULL);
367a9771948SGleb Smirnoff 
36840e04359SKristof Provost static int
36940e04359SKristof Provost carp_is_supported_if(if_t ifp)
37040e04359SKristof Provost {
37140e04359SKristof Provost 	if (ifp == NULL)
37240e04359SKristof Provost 		return (ENXIO);
37340e04359SKristof Provost 
37440e04359SKristof Provost 	switch (ifp->if_type) {
37540e04359SKristof Provost 	case IFT_ETHER:
37640e04359SKristof Provost 	case IFT_L2VLAN:
37740e04359SKristof Provost 	case IFT_BRIDGE:
37840e04359SKristof Provost 		break;
37940e04359SKristof Provost 	default:
38040e04359SKristof Provost 		return (EOPNOTSUPP);
38140e04359SKristof Provost 	}
38240e04359SKristof Provost 
38340e04359SKristof Provost 	return (0);
38440e04359SKristof Provost }
38540e04359SKristof Provost 
3865c1f0f6dSGleb Smirnoff static void
387a9771948SGleb Smirnoff carp_hmac_prepare(struct carp_softc *sc)
388a9771948SGleb Smirnoff {
38937115154SKristof Provost 	uint8_t version = CARP_VERSION_CARP, type = CARP_ADVERTISEMENT;
39008b68b0eSGleb Smirnoff 	uint8_t vhid = sc->sc_vhid & 0xff;
391a9771948SGleb Smirnoff 	struct ifaddr *ifa;
3921ead26d4SMax Laier 	int i, found;
3931ead26d4SMax Laier #ifdef INET
3941ead26d4SMax Laier 	struct in_addr last, cur, in;
3951ead26d4SMax Laier #endif
396a9771948SGleb Smirnoff #ifdef INET6
3971ead26d4SMax Laier 	struct in6_addr last6, cur6, in6;
398a9771948SGleb Smirnoff #endif
399a9771948SGleb Smirnoff 
40008b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
401d220759bSGleb Smirnoff 
40208b68b0eSGleb Smirnoff 	/* Compute ipad from key. */
403a9771948SGleb Smirnoff 	bzero(sc->sc_pad, sizeof(sc->sc_pad));
404a9771948SGleb Smirnoff 	bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key));
405a9771948SGleb Smirnoff 	for (i = 0; i < sizeof(sc->sc_pad); i++)
406a9771948SGleb Smirnoff 		sc->sc_pad[i] ^= 0x36;
407a9771948SGleb Smirnoff 
40808b68b0eSGleb Smirnoff 	/* Precompute first part of inner hash. */
409a9771948SGleb Smirnoff 	SHA1Init(&sc->sc_sha1);
410a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad));
411a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version));
412a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type));
413a9771948SGleb Smirnoff 	SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid));
414a9771948SGleb Smirnoff #ifdef INET
4151ead26d4SMax Laier 	cur.s_addr = 0;
4161ead26d4SMax Laier 	do {
4171ead26d4SMax Laier 		found = 0;
4181ead26d4SMax Laier 		last = cur;
4191ead26d4SMax Laier 		cur.s_addr = 0xffffffff;
42008b68b0eSGleb Smirnoff 		CARP_FOREACH_IFA(sc, ifa) {
4211ead26d4SMax Laier 			in.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
4221ead26d4SMax Laier 			if (ifa->ifa_addr->sa_family == AF_INET &&
4231ead26d4SMax Laier 			    ntohl(in.s_addr) > ntohl(last.s_addr) &&
4241ead26d4SMax Laier 			    ntohl(in.s_addr) < ntohl(cur.s_addr)) {
4251ead26d4SMax Laier 				cur.s_addr = in.s_addr;
4261ead26d4SMax Laier 				found++;
427a9771948SGleb Smirnoff 			}
4281ead26d4SMax Laier 		}
4291ead26d4SMax Laier 		if (found)
4301ead26d4SMax Laier 			SHA1Update(&sc->sc_sha1, (void *)&cur, sizeof(cur));
4311ead26d4SMax Laier 	} while (found);
432a9771948SGleb Smirnoff #endif /* INET */
433a9771948SGleb Smirnoff #ifdef INET6
4341ead26d4SMax Laier 	memset(&cur6, 0, sizeof(cur6));
4351ead26d4SMax Laier 	do {
4361ead26d4SMax Laier 		found = 0;
4371ead26d4SMax Laier 		last6 = cur6;
4381ead26d4SMax Laier 		memset(&cur6, 0xff, sizeof(cur6));
43908b68b0eSGleb Smirnoff 		CARP_FOREACH_IFA(sc, ifa) {
440a9771948SGleb Smirnoff 			in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
4411ead26d4SMax Laier 			if (IN6_IS_SCOPE_EMBED(&in6))
4421ead26d4SMax Laier 				in6.s6_addr16[1] = 0;
4431ead26d4SMax Laier 			if (ifa->ifa_addr->sa_family == AF_INET6 &&
4441ead26d4SMax Laier 			    memcmp(&in6, &last6, sizeof(in6)) > 0 &&
4451ead26d4SMax Laier 			    memcmp(&in6, &cur6, sizeof(in6)) < 0) {
4461ead26d4SMax Laier 				cur6 = in6;
4471ead26d4SMax Laier 				found++;
448a9771948SGleb Smirnoff 			}
449a9771948SGleb Smirnoff 		}
4501ead26d4SMax Laier 		if (found)
4511ead26d4SMax Laier 			SHA1Update(&sc->sc_sha1, (void *)&cur6, sizeof(cur6));
4521ead26d4SMax Laier 	} while (found);
453a9771948SGleb Smirnoff #endif /* INET6 */
454a9771948SGleb Smirnoff 
455a9771948SGleb Smirnoff 	/* convert ipad to opad */
456a9771948SGleb Smirnoff 	for (i = 0; i < sizeof(sc->sc_pad); i++)
457a9771948SGleb Smirnoff 		sc->sc_pad[i] ^= 0x36 ^ 0x5c;
458a9771948SGleb Smirnoff }
459a9771948SGleb Smirnoff 
4605c1f0f6dSGleb Smirnoff static void
46108b68b0eSGleb Smirnoff carp_hmac_generate(struct carp_softc *sc, uint32_t counter[2],
462a9771948SGleb Smirnoff     unsigned char md[20])
463a9771948SGleb Smirnoff {
464a9771948SGleb Smirnoff 	SHA1_CTX sha1ctx;
465a9771948SGleb Smirnoff 
46608b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
46708b68b0eSGleb Smirnoff 
468a9771948SGleb Smirnoff 	/* fetch first half of inner hash */
469a9771948SGleb Smirnoff 	bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx));
470a9771948SGleb Smirnoff 
471a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter));
472a9771948SGleb Smirnoff 	SHA1Final(md, &sha1ctx);
473a9771948SGleb Smirnoff 
474a9771948SGleb Smirnoff 	/* outer hash */
475a9771948SGleb Smirnoff 	SHA1Init(&sha1ctx);
476a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad));
477a9771948SGleb Smirnoff 	SHA1Update(&sha1ctx, md, 20);
478a9771948SGleb Smirnoff 	SHA1Final(md, &sha1ctx);
479a9771948SGleb Smirnoff }
480a9771948SGleb Smirnoff 
4815c1f0f6dSGleb Smirnoff static int
48208b68b0eSGleb Smirnoff carp_hmac_verify(struct carp_softc *sc, uint32_t counter[2],
483a9771948SGleb Smirnoff     unsigned char md[20])
484a9771948SGleb Smirnoff {
485a9771948SGleb Smirnoff 	unsigned char md2[20];
486a9771948SGleb Smirnoff 
48708b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
488d220759bSGleb Smirnoff 
489a9771948SGleb Smirnoff 	carp_hmac_generate(sc, counter, md2);
490a9771948SGleb Smirnoff 
491a9771948SGleb Smirnoff 	return (bcmp(md, md2, sizeof(md2)));
492a9771948SGleb Smirnoff }
493a9771948SGleb Smirnoff 
49437115154SKristof Provost static int
49537115154SKristof Provost vrrp_checksum_verify(struct mbuf *m, int off, int len, uint16_t phdrcksum)
49637115154SKristof Provost {
49737115154SKristof Provost 	uint16_t cksum;
49837115154SKristof Provost 
49937115154SKristof Provost 	/*
50037115154SKristof Provost 	 * Note that VRRPv3 checksums are different from CARP checksums.
50137115154SKristof Provost 	 * Carp just calculates the checksum over the packet.
50237115154SKristof Provost 	 * VRRPv3 includes the pseudo-header checksum as well.
50337115154SKristof Provost 	 */
50437115154SKristof Provost 	cksum = in_cksum_skip(m, off + len, off);
50537115154SKristof Provost 	cksum -= phdrcksum;
50637115154SKristof Provost 
50737115154SKristof Provost 	return (cksum);
50837115154SKristof Provost }
50937115154SKristof Provost 
510a9771948SGleb Smirnoff /*
511a9771948SGleb Smirnoff  * process input packet.
512a9771948SGleb Smirnoff  * we have rearranged checks order compared to the rfc,
513a9771948SGleb Smirnoff  * but it seems more efficient this way or not possible otherwise.
514a9771948SGleb Smirnoff  */
515a0ae8f04SBjoern A. Zeeb #ifdef INET
51678b1fc05SGleb Smirnoff static int
5178f5a8818SKevin Lo carp_input(struct mbuf **mp, int *offp, int proto)
518a9771948SGleb Smirnoff {
5198f5a8818SKevin Lo 	struct mbuf *m = *mp;
520a9771948SGleb Smirnoff 	struct ip *ip = mtod(m, struct ip *);
52137115154SKristof Provost 	struct vrrpv3_header *vh;
52237115154SKristof Provost 	int iplen;
52337115154SKristof Provost 	int minlen;
52437115154SKristof Provost 	int totlen;
52537115154SKristof Provost 	uint16_t phdrcksum = 0;
526a9771948SGleb Smirnoff 
5278f5a8818SKevin Lo 	iplen = *offp;
5288f5a8818SKevin Lo 	*mp = NULL;
5298f5a8818SKevin Lo 
5306bf65bcfSRobert Watson 	CARPSTATS_INC(carps_ipackets);
531a9771948SGleb Smirnoff 
532c5c392e7SMikolaj Golub 	if (!V_carp_allow) {
533a9771948SGleb Smirnoff 		m_freem(m);
5348f5a8818SKevin Lo 		return (IPPROTO_DONE);
535a9771948SGleb Smirnoff 	}
536a9771948SGleb Smirnoff 
537a9771948SGleb Smirnoff 	iplen = ip->ip_hl << 2;
53837115154SKristof Provost 	totlen = ntohs(ip->ip_len);
539a9771948SGleb Smirnoff 
54037115154SKristof Provost 	/* Ensure we have enough header to figure out the version. */
54137115154SKristof Provost 	if (m->m_pkthdr.len < iplen + sizeof(*vh)) {
5426bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badlen);
54337115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < sizeof(struct vrrpv3_header) "
54408b68b0eSGleb Smirnoff 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
545511a6d5eSKristof Provost 		    if_name(m->m_pkthdr.rcvif));
546a9771948SGleb Smirnoff 		m_freem(m);
5478f5a8818SKevin Lo 		return (IPPROTO_DONE);
548a9771948SGleb Smirnoff 	}
549a9771948SGleb Smirnoff 
55037115154SKristof Provost 	if (iplen + sizeof(*vh) < m->m_len) {
55137115154SKristof Provost 		if ((m = m_pullup(m, iplen + sizeof(*vh))) == NULL) {
5526bf65bcfSRobert Watson 			CARPSTATS_INC(carps_hdrops);
55337115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
5548f5a8818SKevin Lo 			return (IPPROTO_DONE);
555a9771948SGleb Smirnoff 		}
556a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
557a9771948SGleb Smirnoff 	}
55837115154SKristof Provost 	vh = (struct vrrpv3_header *)((char *)ip + iplen);
559a9771948SGleb Smirnoff 
56037115154SKristof Provost 	switch (vh->vrrp_version) {
56137115154SKristof Provost 	case CARP_VERSION_CARP:
56237115154SKristof Provost 		minlen = sizeof(struct carp_header);
56337115154SKristof Provost 		break;
56437115154SKristof Provost 	case CARP_VERSION_VRRPv3:
56537115154SKristof Provost 		minlen = sizeof(struct vrrpv3_header);
56637115154SKristof Provost 		break;
56737115154SKristof Provost 	default:
56837115154SKristof Provost 		CARPSTATS_INC(carps_badver);
56937115154SKristof Provost 		CARP_DEBUG("%s: unsupported version %d on %s\n", __func__,
57037115154SKristof Provost 		    vh->vrrp_version, if_name(m->m_pkthdr.rcvif));
57137115154SKristof Provost 		m_freem(m);
57237115154SKristof Provost 		return (IPPROTO_DONE);
57337115154SKristof Provost 	}
57437115154SKristof Provost 
57537115154SKristof Provost 	/* And now check the length again but with the real minimal length. */
57637115154SKristof Provost 	if (m->m_pkthdr.len < iplen + minlen) {
5776bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badlen);
57837115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < %d "
57937115154SKristof Provost 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
58037115154SKristof Provost 		    iplen + minlen,
581511a6d5eSKristof Provost 		    if_name(m->m_pkthdr.rcvif));
582a9771948SGleb Smirnoff 		m_freem(m);
5838f5a8818SKevin Lo 		return (IPPROTO_DONE);
584a9771948SGleb Smirnoff 	}
585a9771948SGleb Smirnoff 
58637115154SKristof Provost 	if (iplen + minlen < m->m_len) {
58737115154SKristof Provost 		if ((m = m_pullup(m, iplen + minlen)) == NULL) {
5886bf65bcfSRobert Watson 			CARPSTATS_INC(carps_hdrops);
58937115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
5908f5a8818SKevin Lo 			return (IPPROTO_DONE);
591a9771948SGleb Smirnoff 		}
592a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
59337115154SKristof Provost 		vh = (struct vrrpv3_header *)((char *)ip + iplen);
59437115154SKristof Provost 	}
595a9771948SGleb Smirnoff 
59637115154SKristof Provost 	phdrcksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
59737115154SKristof Provost 	    htonl((u_short)(totlen - iplen) + ip->ip_p));
59837115154SKristof Provost 
59937115154SKristof Provost 	if (vh->vrrp_version == CARP_VERSION_CARP) {
600a9771948SGleb Smirnoff 		/* verify the CARP checksum */
601a9771948SGleb Smirnoff 		m->m_data += iplen;
60237115154SKristof Provost 		if (in_cksum(m, totlen - iplen)) {
6036bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badsum);
60408b68b0eSGleb Smirnoff 			CARP_DEBUG("%s: checksum failed on %s\n", __func__,
605511a6d5eSKristof Provost 			    if_name(m->m_pkthdr.rcvif));
606a9771948SGleb Smirnoff 			m_freem(m);
6078f5a8818SKevin Lo 			return (IPPROTO_DONE);
608a9771948SGleb Smirnoff 		}
609a9771948SGleb Smirnoff 		m->m_data -= iplen;
61037115154SKristof Provost 	}
611a9771948SGleb Smirnoff 
61237115154SKristof Provost 	switch (vh->vrrp_version) {
61337115154SKristof Provost 	case CARP_VERSION_CARP: {
61437115154SKristof Provost 		struct carp_header *ch = (struct carp_header *)((char *)ip + iplen);
61513781800SKristof Provost 		carp_input_c(m, ch, AF_INET, ip->ip_ttl);
61637115154SKristof Provost 		break;
61737115154SKristof Provost 	}
61837115154SKristof Provost 	case CARP_VERSION_VRRPv3:
61937115154SKristof Provost 		vrrp_input_c(m, iplen, AF_INET, ip->ip_ttl, totlen - iplen, phdrcksum);
62037115154SKristof Provost 		break;
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;
63737115154SKristof Provost 	uint16_t phdrcksum = 0;
638a9771948SGleb Smirnoff 
6396bf65bcfSRobert Watson 	CARPSTATS_INC(carps_ipackets6);
640a9771948SGleb Smirnoff 
641c5c392e7SMikolaj Golub 	if (!V_carp_allow) {
642a9771948SGleb Smirnoff 		m_freem(m);
643a9771948SGleb Smirnoff 		return (IPPROTO_DONE);
644a9771948SGleb Smirnoff 	}
645a9771948SGleb Smirnoff 
646a9771948SGleb Smirnoff 	/* check if received on a valid carp interface */
647a9771948SGleb Smirnoff 	if (m->m_pkthdr.rcvif->if_carp == NULL) {
6486bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badif);
64908b68b0eSGleb Smirnoff 		CARP_DEBUG("%s: packet received on non-carp interface: %s\n",
650511a6d5eSKristof Provost 		    __func__, if_name(m->m_pkthdr.rcvif));
651a9771948SGleb Smirnoff 		m_freem(m);
652a9771948SGleb Smirnoff 		return (IPPROTO_DONE);
653a9771948SGleb Smirnoff 	}
654a9771948SGleb Smirnoff 
65537115154SKristof Provost 	if (m->m_len < *offp + sizeof(*vh)) {
656a9771948SGleb Smirnoff 		len = m->m_len;
65737115154SKristof Provost 		m = m_pullup(m, *offp + sizeof(*vh));
65863abacc2SBjoern A. Zeeb 		if (m == NULL) {
6596bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badlen);
66008b68b0eSGleb Smirnoff 			CARP_DEBUG("%s: packet size %u too small\n", __func__, len);
661a9771948SGleb Smirnoff 			return (IPPROTO_DONE);
662a9771948SGleb Smirnoff 		}
66313781800SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
664a4adf6ccSBjoern A. Zeeb 	}
66537115154SKristof Provost 	vh = (struct vrrpv3_header *)(mtod(m, char *) + *offp);
66637115154SKristof Provost 
66737115154SKristof Provost 	switch (vh->vrrp_version) {
66837115154SKristof Provost 	case CARP_VERSION_CARP:
66937115154SKristof Provost 		minlen = sizeof(struct carp_header);
67037115154SKristof Provost 		break;
67137115154SKristof Provost 	case CARP_VERSION_VRRPv3:
67237115154SKristof Provost 		minlen = sizeof(struct vrrpv3_header);
67337115154SKristof Provost 		break;
67437115154SKristof Provost 	default:
67537115154SKristof Provost 		CARPSTATS_INC(carps_badver);
67637115154SKristof Provost 		CARP_DEBUG("%s: unsupported version %d on %s\n", __func__,
67737115154SKristof Provost 		    vh->vrrp_version, if_name(m->m_pkthdr.rcvif));
67837115154SKristof Provost 		m_freem(m);
67937115154SKristof Provost 		return (IPPROTO_DONE);
68037115154SKristof Provost 	}
68137115154SKristof Provost 
68237115154SKristof Provost 	/* And now check the length again but with the real minimal length. */
68337115154SKristof Provost 	if (m->m_pkthdr.len < sizeof(*ip6) + minlen) {
68437115154SKristof Provost 		CARPSTATS_INC(carps_badlen);
68537115154SKristof Provost 		CARP_DEBUG("%s: received len %zd < %zd "
68637115154SKristof Provost 		    "on %s\n", __func__, m->m_len - sizeof(struct ip),
68737115154SKristof Provost 		    sizeof(*ip6) + minlen,
68837115154SKristof Provost 		    if_name(m->m_pkthdr.rcvif));
68937115154SKristof Provost 		m_freem(m);
69037115154SKristof Provost 		return (IPPROTO_DONE);
69137115154SKristof Provost 	}
69237115154SKristof Provost 
69337115154SKristof Provost 	if (sizeof (*ip6) + minlen < m->m_len) {
69437115154SKristof Provost 		if ((m = m_pullup(m, sizeof(*ip6) + minlen)) == NULL) {
69537115154SKristof Provost 			CARPSTATS_INC(carps_hdrops);
69637115154SKristof Provost 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
69737115154SKristof Provost 			return (IPPROTO_DONE);
69837115154SKristof Provost 		}
69937115154SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
70037115154SKristof Provost 		vh = (struct vrrpv3_header *)mtodo(m, sizeof(*ip6));
70137115154SKristof Provost 	}
70237115154SKristof Provost 
70337115154SKristof Provost 	phdrcksum = in6_cksum_pseudo(ip6, ntohs(ip6->ip6_plen), ip6->ip6_nxt, 0);
704a9771948SGleb Smirnoff 
705a9771948SGleb Smirnoff 	/* verify the CARP checksum */
70637115154SKristof Provost 	if (vh->vrrp_version == CARP_VERSION_CARP) {
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);
713a9771948SGleb Smirnoff 			return (IPPROTO_DONE);
714a9771948SGleb Smirnoff 		}
715a9771948SGleb Smirnoff 		m->m_data -= *offp;
71637115154SKristof Provost 	}
717a9771948SGleb Smirnoff 
71837115154SKristof Provost 	switch (vh->vrrp_version) {
71937115154SKristof Provost 	case CARP_VERSION_CARP: {
72037115154SKristof Provost 		struct carp_header *ch = (struct carp_header *)((char *)ip6 + sizeof(*ip6));
72113781800SKristof Provost 		carp_input_c(m, ch, AF_INET6, ip6->ip6_hlim);
72237115154SKristof Provost 		break;
72337115154SKristof Provost 	}
72437115154SKristof Provost 	case CARP_VERSION_VRRPv3:
72537115154SKristof Provost 		vrrp_input_c(m, sizeof(*ip6), AF_INET6, ip6->ip6_hlim, ntohs(ip6->ip6_plen),
72637115154SKristof Provost 		    phdrcksum);
72737115154SKristof Provost 		break;
72837115154SKristof Provost 	default:
72937115154SKristof Provost 		KASSERT(false, ("Unsupported version %d", vh->vrrp_version));
73037115154SKristof Provost 	}
731a9771948SGleb Smirnoff 	return (IPPROTO_DONE);
732a9771948SGleb Smirnoff }
733a9771948SGleb Smirnoff #endif /* INET6 */
734a9771948SGleb Smirnoff 
7358151740cSJosh Paetzel /*
7368151740cSJosh Paetzel  * This routine should not be necessary at all, but some switches
7378151740cSJosh Paetzel  * (VMWare ESX vswitches) can echo our own packets back at us,
7388151740cSJosh Paetzel  * and we must ignore them or they will cause us to drop out of
7398151740cSJosh Paetzel  * MASTER mode.
7408151740cSJosh Paetzel  *
7418151740cSJosh Paetzel  * We cannot catch all cases of network loops.  Instead, what we
7428151740cSJosh Paetzel  * do here is catch any packet that arrives with a carp header
7438151740cSJosh Paetzel  * with a VHID of 0, that comes from an address that is our own.
7448151740cSJosh Paetzel  * These packets are by definition "from us" (even if they are from
7458151740cSJosh Paetzel  * a misconfigured host that is pretending to be us).
7468151740cSJosh Paetzel  *
7478151740cSJosh Paetzel  * The VHID test is outside this mini-function.
7488151740cSJosh Paetzel  */
7498151740cSJosh Paetzel static int
75037115154SKristof Provost carp_source_is_self(const struct mbuf *m, struct ifaddr *ifa, sa_family_t af)
7518151740cSJosh Paetzel {
752cfff8d3dSEnji Cooper #ifdef INET
7538151740cSJosh Paetzel 	struct ip *ip4;
7548151740cSJosh Paetzel 	struct in_addr in4;
755cfff8d3dSEnji Cooper #endif
756cfff8d3dSEnji Cooper #ifdef INET6
7578151740cSJosh Paetzel 	struct ip6_hdr *ip6;
7588151740cSJosh Paetzel 	struct in6_addr in6;
759cfff8d3dSEnji Cooper #endif
7608151740cSJosh Paetzel 
7618151740cSJosh Paetzel 	switch (af) {
762cfff8d3dSEnji Cooper #ifdef INET
7638151740cSJosh Paetzel 	case AF_INET:
7648151740cSJosh Paetzel 		ip4 = mtod(m, struct ip *);
7658151740cSJosh Paetzel 		in4 = ifatoia(ifa)->ia_addr.sin_addr;
7668151740cSJosh Paetzel 		return (in4.s_addr == ip4->ip_src.s_addr);
767cfff8d3dSEnji Cooper #endif
768cfff8d3dSEnji Cooper #ifdef INET6
7698151740cSJosh Paetzel 	case AF_INET6:
7708151740cSJosh Paetzel 		ip6 = mtod(m, struct ip6_hdr *);
7718151740cSJosh Paetzel 		in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
7728151740cSJosh Paetzel 		return (memcmp(&in6, &ip6->ip6_src, sizeof(in6)) == 0);
773cfff8d3dSEnji Cooper #endif
774cfff8d3dSEnji Cooper 	default:
7758151740cSJosh Paetzel 		break;
7768151740cSJosh Paetzel 	}
7778151740cSJosh Paetzel 	return (0);
7788151740cSJosh Paetzel }
7798151740cSJosh Paetzel 
78037115154SKristof Provost static struct ifaddr *
78137115154SKristof Provost carp_find_ifa(const struct mbuf *m, sa_family_t af, uint8_t vhid)
782a9771948SGleb Smirnoff {
783a9771948SGleb Smirnoff 	struct ifnet *ifp = m->m_pkthdr.rcvif;
7848151740cSJosh Paetzel 	struct ifaddr *ifa, *match;
7858151740cSJosh Paetzel 	int error;
786a9771948SGleb Smirnoff 
787b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
788b8a6e03fSGleb Smirnoff 
7898151740cSJosh Paetzel 	/*
7908151740cSJosh Paetzel 	 * Verify that the VHID is valid on the receiving interface.
7918151740cSJosh Paetzel 	 *
7928151740cSJosh Paetzel 	 * There should be just one match.  If there are none
7938151740cSJosh Paetzel 	 * the VHID is not valid and we drop the packet.  If
7948151740cSJosh Paetzel 	 * there are multiple VHID matches, take just the first
7958151740cSJosh Paetzel 	 * one, for compatibility with previous code.  While we're
7968151740cSJosh Paetzel 	 * scanning, check for obvious loops in the network topology
7978151740cSJosh Paetzel 	 * (these should never happen, and as noted above, we may
7988151740cSJosh Paetzel 	 * miss real loops; this is just a double-check).
7998151740cSJosh Paetzel 	 */
8008151740cSJosh Paetzel 	error = 0;
8018151740cSJosh Paetzel 	match = NULL;
8028151740cSJosh Paetzel 	IFNET_FOREACH_IFA(ifp, ifa) {
8038151740cSJosh Paetzel 		if (match == NULL && ifa->ifa_carp != NULL &&
8048151740cSJosh Paetzel 		    ifa->ifa_addr->sa_family == af &&
80537115154SKristof Provost 		    ifa->ifa_carp->sc_vhid == vhid)
8068151740cSJosh Paetzel 			match = ifa;
80737115154SKristof Provost 		if (vhid == 0 && carp_source_is_self(m, ifa, af))
8088151740cSJosh Paetzel 			error = ELOOP;
80908b68b0eSGleb Smirnoff 	}
8108151740cSJosh Paetzel 	ifa = error ? NULL : match;
8118151740cSJosh Paetzel 	if (ifa != NULL)
8128151740cSJosh Paetzel 		ifa_ref(ifa);
813d220759bSGleb Smirnoff 
81408b68b0eSGleb Smirnoff 	if (ifa == NULL) {
8158151740cSJosh Paetzel 		if (error == ELOOP) {
8168151740cSJosh Paetzel 			CARP_DEBUG("dropping looped packet on interface %s\n",
817511a6d5eSKristof Provost 			    if_name(ifp));
8188151740cSJosh Paetzel 			CARPSTATS_INC(carps_badif);	/* ??? */
8198151740cSJosh Paetzel 		} else {
8206bf65bcfSRobert Watson 			CARPSTATS_INC(carps_badvhid);
8218151740cSJosh Paetzel 		}
82237115154SKristof Provost 	}
82337115154SKristof Provost 
82437115154SKristof Provost 	return (ifa);
82537115154SKristof Provost }
82637115154SKristof Provost 
82737115154SKristof Provost static void
82837115154SKristof Provost carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af, int ttl)
82937115154SKristof Provost {
83037115154SKristof Provost 	struct ifnet *ifp = m->m_pkthdr.rcvif;
83137115154SKristof Provost 	struct ifaddr *ifa;
83237115154SKristof Provost 	struct carp_softc *sc;
83337115154SKristof Provost 	uint64_t tmp_counter;
83437115154SKristof Provost 	struct timeval sc_tv, ch_tv;
83537115154SKristof Provost 	bool multicast = false;
83637115154SKristof Provost 
83737115154SKristof Provost 	NET_EPOCH_ASSERT();
83837115154SKristof Provost 
83937115154SKristof Provost 	ifa = carp_find_ifa(m, af, ch->carp_vhid);
84037115154SKristof Provost 	if (ifa == NULL) {
841a9771948SGleb Smirnoff 		m_freem(m);
842a9771948SGleb Smirnoff 		return;
843a9771948SGleb Smirnoff 	}
844a9771948SGleb Smirnoff 
84537115154SKristof Provost 	sc = ifa->ifa_carp;
84637115154SKristof Provost 	CARP_LOCK(sc);
84737115154SKristof Provost 
848a9771948SGleb Smirnoff 	/* verify the CARP version. */
84937115154SKristof Provost 	if (ch->carp_version != CARP_VERSION_CARP ||
85037115154SKristof Provost 	    sc->sc_version != CARP_VERSION_CARP) {
85137115154SKristof Provost 		CARP_UNLOCK(sc);
85237115154SKristof Provost 
8536bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badver);
854511a6d5eSKristof Provost 		CARP_DEBUG("%s: invalid version %d\n", if_name(ifp),
8551e9e6572SGleb Smirnoff 		    ch->carp_version);
85608b68b0eSGleb Smirnoff 		ifa_free(ifa);
857a9771948SGleb Smirnoff 		m_freem(m);
858a9771948SGleb Smirnoff 		return;
859a9771948SGleb Smirnoff 	}
860a9771948SGleb Smirnoff 
86113781800SKristof Provost 	if (ifa->ifa_addr->sa_family == AF_INET) {
86213781800SKristof Provost 		multicast = IN_MULTICAST(sc->sc_carpaddr.s_addr);
86313781800SKristof Provost 	} else {
86413781800SKristof Provost 		multicast = IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6);
86513781800SKristof Provost 	}
86608b68b0eSGleb Smirnoff 	ifa_free(ifa);
86708b68b0eSGleb Smirnoff 
86813781800SKristof Provost 	/* verify that the IP TTL is 255, but only if we're not in unicast mode. */
86913781800SKristof Provost 	if (multicast && ttl != CARP_DFLTTL) {
87013781800SKristof Provost 		CARPSTATS_INC(carps_badttl);
87113781800SKristof Provost 		CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
872511a6d5eSKristof Provost 		    ttl, if_name(m->m_pkthdr.rcvif));
87313781800SKristof Provost 		goto out;
87413781800SKristof Provost 	}
87513781800SKristof Provost 
876a9771948SGleb Smirnoff 	if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) {
8776bf65bcfSRobert Watson 		CARPSTATS_INC(carps_badauth);
87808b68b0eSGleb Smirnoff 		CARP_DEBUG("%s: incorrect hash for VHID %u@%s\n", __func__,
879511a6d5eSKristof Provost 		    sc->sc_vhid, if_name(ifp));
88008b68b0eSGleb Smirnoff 		goto out;
881a9771948SGleb Smirnoff 	}
882a9771948SGleb Smirnoff 
883a9771948SGleb Smirnoff 	tmp_counter = ntohl(ch->carp_counter[0]);
884a9771948SGleb Smirnoff 	tmp_counter = tmp_counter<<32;
885a9771948SGleb Smirnoff 	tmp_counter += ntohl(ch->carp_counter[1]);
886a9771948SGleb Smirnoff 
887a9771948SGleb Smirnoff 	/* XXX Replay protection goes here */
888a9771948SGleb Smirnoff 
889a9771948SGleb Smirnoff 	sc->sc_init_counter = 0;
890a9771948SGleb Smirnoff 	sc->sc_counter = tmp_counter;
891a9771948SGleb Smirnoff 
892a9771948SGleb Smirnoff 	sc_tv.tv_sec = sc->sc_advbase;
893f08535f8SGleb Smirnoff 	sc_tv.tv_usec = DEMOTE_ADVSKEW(sc) * 1000000 / 256;
894a9771948SGleb Smirnoff 	ch_tv.tv_sec = ch->carp_advbase;
895a9771948SGleb Smirnoff 	ch_tv.tv_usec = ch->carp_advskew * 1000000 / 256;
896a9771948SGleb Smirnoff 
897a9771948SGleb Smirnoff 	switch (sc->sc_state) {
898a9771948SGleb Smirnoff 	case INIT:
899a9771948SGleb Smirnoff 		break;
900a9771948SGleb Smirnoff 	case MASTER:
901a9771948SGleb Smirnoff 		/*
902a9771948SGleb Smirnoff 		 * If we receive an advertisement from a master who's going to
903a9771948SGleb Smirnoff 		 * be more frequent than us, go into BACKUP state.
904a9771948SGleb Smirnoff 		 */
905a9771948SGleb Smirnoff 		if (timevalcmp(&sc_tv, &ch_tv, >) ||
906a9771948SGleb Smirnoff 		    timevalcmp(&sc_tv, &ch_tv, ==)) {
907a9771948SGleb Smirnoff 			callout_stop(&sc->sc_ad_tmo);
908d01641e2SWill Andrews 			carp_set_state(sc, BACKUP,
909d01641e2SWill Andrews 			    "more frequent advertisement received");
910a9771948SGleb Smirnoff 			carp_setrun(sc, 0);
91108b68b0eSGleb Smirnoff 			carp_delroute(sc);
912a9771948SGleb Smirnoff 		}
913a9771948SGleb Smirnoff 		break;
914a9771948SGleb Smirnoff 	case BACKUP:
915a9771948SGleb Smirnoff 		/*
916a9771948SGleb Smirnoff 		 * If we're pre-empting masters who advertise slower than us,
917a9771948SGleb Smirnoff 		 * and this one claims to be slower, treat him as down.
918a9771948SGleb Smirnoff 		 */
919c5c392e7SMikolaj Golub 		if (V_carp_preempt && timevalcmp(&sc_tv, &ch_tv, <)) {
920d01641e2SWill Andrews 			carp_master_down_locked(sc,
921d01641e2SWill Andrews 			    "preempting a slower master");
922a9771948SGleb Smirnoff 			break;
923a9771948SGleb Smirnoff 		}
924a9771948SGleb Smirnoff 
925a9771948SGleb Smirnoff 		/*
926a9771948SGleb Smirnoff 		 *  If the master is going to advertise at such a low frequency
927a9771948SGleb Smirnoff 		 *  that he's guaranteed to time out, we'd might as well just
928a9771948SGleb Smirnoff 		 *  treat him as timed out now.
929a9771948SGleb Smirnoff 		 */
930a9771948SGleb Smirnoff 		sc_tv.tv_sec = sc->sc_advbase * 3;
931a9771948SGleb Smirnoff 		if (timevalcmp(&sc_tv, &ch_tv, <)) {
932d01641e2SWill Andrews 			carp_master_down_locked(sc, "master will time out");
933a9771948SGleb Smirnoff 			break;
934a9771948SGleb Smirnoff 		}
935a9771948SGleb Smirnoff 
936a9771948SGleb Smirnoff 		/*
937a9771948SGleb Smirnoff 		 * Otherwise, we reset the counter and wait for the next
938a9771948SGleb Smirnoff 		 * advertisement.
939a9771948SGleb Smirnoff 		 */
940a9771948SGleb Smirnoff 		carp_setrun(sc, af);
941a9771948SGleb Smirnoff 		break;
942a9771948SGleb Smirnoff 	}
943a9771948SGleb Smirnoff 
94408b68b0eSGleb Smirnoff out:
94508b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
946a9771948SGleb Smirnoff 	m_freem(m);
947a9771948SGleb Smirnoff }
948a9771948SGleb Smirnoff 
94937115154SKristof Provost static void
95037115154SKristof Provost vrrp_input_c(struct mbuf *m, int off, sa_family_t af, int ttl,
95137115154SKristof Provost     int len, uint16_t phdrcksum)
95237115154SKristof Provost {
95337115154SKristof Provost 	struct vrrpv3_header *vh = mtodo(m, off);
95437115154SKristof Provost 	struct ifnet *ifp = m->m_pkthdr.rcvif;
95537115154SKristof Provost 	struct ifaddr *ifa;
95637115154SKristof Provost 	struct carp_softc *sc;
95737115154SKristof Provost 
95837115154SKristof Provost 	NET_EPOCH_ASSERT();
95937115154SKristof Provost 
96037115154SKristof Provost 	ifa = carp_find_ifa(m, af, vh->vrrp_vrtid);
96137115154SKristof Provost 	if (ifa == NULL) {
96237115154SKristof Provost 		m_freem(m);
96337115154SKristof Provost 		return;
96437115154SKristof Provost 	}
96537115154SKristof Provost 
96637115154SKristof Provost 	sc = ifa->ifa_carp;
96737115154SKristof Provost 	CARP_LOCK(sc);
96837115154SKristof Provost 
96937115154SKristof Provost 	ifa_free(ifa);
97037115154SKristof Provost 
97137115154SKristof Provost 	/* verify the CARP version. */
97237115154SKristof Provost 	if (vh->vrrp_version != CARP_VERSION_VRRPv3 || sc->sc_version != CARP_VERSION_VRRPv3) {
97337115154SKristof Provost 		CARP_UNLOCK(sc);
97437115154SKristof Provost 
97537115154SKristof Provost 		CARPSTATS_INC(carps_badver);
97637115154SKristof Provost 		CARP_DEBUG("%s: invalid version %d\n", if_name(ifp),
97737115154SKristof Provost 		    vh->vrrp_version);
97837115154SKristof Provost 		m_freem(m);
97937115154SKristof Provost 		return;
98037115154SKristof Provost 	}
98137115154SKristof Provost 
98237115154SKristof Provost 	/* verify that the IP TTL is 255. */
98337115154SKristof Provost 	if (ttl != CARP_DFLTTL) {
98437115154SKristof Provost 		CARPSTATS_INC(carps_badttl);
98537115154SKristof Provost 		CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
98637115154SKristof Provost 		    ttl, if_name(m->m_pkthdr.rcvif));
98737115154SKristof Provost 		goto out;
98837115154SKristof Provost 	}
98937115154SKristof Provost 
99037115154SKristof Provost 	if (vrrp_checksum_verify(m, off, len, phdrcksum)) {
99137115154SKristof Provost 		CARPSTATS_INC(carps_badsum);
99237115154SKristof Provost 		CARP_DEBUG("%s: incorrect checksum for VRID %u@%s\n", __func__,
99337115154SKristof Provost 		    sc->sc_vhid, if_name(ifp));
99437115154SKristof Provost 		goto out;
99537115154SKristof Provost 	}
99637115154SKristof Provost 
99737115154SKristof Provost 	/* RFC9568, 7.1 Receiving VRRP packets. */
99837115154SKristof Provost 	if (sc->sc_vrrp_prio == 255) {
99937115154SKristof Provost 		CARP_DEBUG("%s: our priority is 255. Ignore peer announcement.\n",
100037115154SKristof Provost 		    __func__);
100137115154SKristof Provost 		goto out;
100237115154SKristof Provost 	}
100337115154SKristof Provost 
100437115154SKristof Provost 	/* XXX TODO Check IP address payload. */
100537115154SKristof Provost 
100637115154SKristof Provost 	sc->sc_vrrp_master_inter = ntohs(vh->vrrp_max_adver_int);
100737115154SKristof Provost 
100837115154SKristof Provost 	switch (sc->sc_state) {
100937115154SKristof Provost 	case INIT:
101037115154SKristof Provost 		break;
101137115154SKristof Provost 	case MASTER:
101237115154SKristof Provost 		/*
101337115154SKristof Provost 		 * If we receive an advertisement from a master who's going to
101437115154SKristof Provost 		 * be more frequent than us, go into BACKUP state.
101537115154SKristof Provost 		 * Same if the peer has a higher priority than us.
101637115154SKristof Provost 		 */
101737115154SKristof Provost 		if (ntohs(vh->vrrp_max_adver_int) < sc->sc_vrrp_adv_inter ||
101837115154SKristof Provost 		    vh->vrrp_priority > sc->sc_vrrp_prio) {
101937115154SKristof Provost 			callout_stop(&sc->sc_ad_tmo);
102037115154SKristof Provost 			carp_set_state(sc, BACKUP,
102137115154SKristof Provost 			    "more frequent advertisement received");
102237115154SKristof Provost 			carp_setrun(sc, 0);
102337115154SKristof Provost 			carp_delroute(sc);
102437115154SKristof Provost 		}
102537115154SKristof Provost 		break;
102637115154SKristof Provost 	case BACKUP:
102737115154SKristof Provost 		/*
102837115154SKristof Provost 		 * If we're pre-empting masters who advertise slower than us,
102937115154SKristof Provost 		 * and this one claims to be slower, treat him as down.
103037115154SKristof Provost 		 */
103137115154SKristof Provost 		if (V_carp_preempt && (ntohs(vh->vrrp_max_adver_int) > sc->sc_vrrp_adv_inter
103237115154SKristof Provost 		    || vh->vrrp_priority < sc->sc_vrrp_prio)) {
103337115154SKristof Provost 			carp_master_down_locked(sc,
103437115154SKristof Provost 			    "preempting a slower master");
103537115154SKristof Provost 			break;
103637115154SKristof Provost 		}
103737115154SKristof Provost 
103837115154SKristof Provost 		/*
103937115154SKristof Provost 		 * Otherwise, we reset the counter and wait for the next
104037115154SKristof Provost 		 * advertisement.
104137115154SKristof Provost 		 */
104237115154SKristof Provost 		carp_setrun(sc, af);
104337115154SKristof Provost 		break;
104437115154SKristof Provost 	}
104537115154SKristof Provost 
104637115154SKristof Provost out:
104737115154SKristof Provost 	CARP_UNLOCK(sc);
104837115154SKristof Provost 	m_freem(m);
104937115154SKristof Provost }
105037115154SKristof Provost 
105137115154SKristof Provost static int
105237115154SKristof Provost carp_tag(struct carp_softc *sc, struct mbuf *m)
105337115154SKristof Provost {
105437115154SKristof Provost 	struct m_tag *mtag;
105537115154SKristof Provost 
105637115154SKristof Provost 	/* Tag packet for carp_output */
105737115154SKristof Provost 	if ((mtag = m_tag_get(PACKET_TAG_CARP, sizeof(struct carp_softc *),
105837115154SKristof Provost 	    M_NOWAIT)) == NULL) {
105937115154SKristof Provost 		m_freem(m);
106037115154SKristof Provost 		CARPSTATS_INC(carps_onomem);
106137115154SKristof Provost 		return (ENOMEM);
106237115154SKristof Provost 	}
106337115154SKristof Provost 	bcopy(&sc, mtag + 1, sizeof(sc));
106437115154SKristof Provost 	m_tag_prepend(m, mtag);
106537115154SKristof Provost 
106637115154SKristof Provost 	return (0);
106737115154SKristof Provost }
106837115154SKristof Provost 
10695c1f0f6dSGleb Smirnoff static int
1070a9771948SGleb Smirnoff carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, struct carp_header *ch)
1071a9771948SGleb Smirnoff {
1072a9771948SGleb Smirnoff 
1073a9771948SGleb Smirnoff 	if (sc->sc_init_counter) {
1074a9771948SGleb Smirnoff 		/* this could also be seconds since unix epoch */
1075a9771948SGleb Smirnoff 		sc->sc_counter = arc4random();
1076a9771948SGleb Smirnoff 		sc->sc_counter = sc->sc_counter << 32;
1077a9771948SGleb Smirnoff 		sc->sc_counter += arc4random();
1078a9771948SGleb Smirnoff 	} else
1079a9771948SGleb Smirnoff 		sc->sc_counter++;
1080a9771948SGleb Smirnoff 
1081a9771948SGleb Smirnoff 	ch->carp_counter[0] = htonl((sc->sc_counter>>32)&0xffffffff);
1082a9771948SGleb Smirnoff 	ch->carp_counter[1] = htonl(sc->sc_counter&0xffffffff);
1083a9771948SGleb Smirnoff 
1084a9771948SGleb Smirnoff 	carp_hmac_generate(sc, ch->carp_counter, ch->carp_md);
1085a9771948SGleb Smirnoff 
108637115154SKristof Provost 	return (carp_tag(sc, m));
1087a9771948SGleb Smirnoff }
1088a9771948SGleb Smirnoff 
1089*5ee92cbdSGleb Smirnoff static inline void
1090*5ee92cbdSGleb Smirnoff send_ad_locked(struct carp_softc *sc)
1091*5ee92cbdSGleb Smirnoff {
1092*5ee92cbdSGleb Smirnoff 	switch (sc->sc_version) {
1093*5ee92cbdSGleb Smirnoff 	case CARP_VERSION_CARP:
1094*5ee92cbdSGleb Smirnoff 		carp_send_ad_locked(sc);
1095*5ee92cbdSGleb Smirnoff 		break;
1096*5ee92cbdSGleb Smirnoff 	case CARP_VERSION_VRRPv3:
1097*5ee92cbdSGleb Smirnoff 		vrrp_send_ad_locked(sc);
1098*5ee92cbdSGleb Smirnoff 		break;
1099*5ee92cbdSGleb Smirnoff 	}
1100*5ee92cbdSGleb Smirnoff }
1101*5ee92cbdSGleb Smirnoff 
1102f08535f8SGleb Smirnoff /*
1103f08535f8SGleb Smirnoff  * To avoid LORs and possible recursions this function shouldn't
1104f08535f8SGleb Smirnoff  * be called directly, but scheduled via taskqueue.
1105f08535f8SGleb Smirnoff  */
11065c1f0f6dSGleb Smirnoff static void
1107f08535f8SGleb Smirnoff carp_send_ad_all(void *ctx __unused, int pending __unused)
1108a9771948SGleb Smirnoff {
1109d92d54d5SGleb Smirnoff 	struct carp_softc *sc;
11101d126e9bSKristof Provost 	struct epoch_tracker et;
1111a9771948SGleb Smirnoff 
11121d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
1113d92d54d5SGleb Smirnoff 	mtx_lock(&carp_mtx);
111408b68b0eSGleb Smirnoff 	LIST_FOREACH(sc, &carp_list, sc_next)
1115f08535f8SGleb Smirnoff 		if (sc->sc_state == MASTER) {
111608b68b0eSGleb Smirnoff 			CARP_LOCK(sc);
1117afdbac98SGleb Smirnoff 			CURVNET_SET(sc->sc_carpdev->if_vnet);
1118*5ee92cbdSGleb Smirnoff 			send_ad_locked(sc);
1119afdbac98SGleb Smirnoff 			CURVNET_RESTORE();
112008b68b0eSGleb Smirnoff 			CARP_UNLOCK(sc);
1121a9771948SGleb Smirnoff 		}
1122d92d54d5SGleb Smirnoff 	mtx_unlock(&carp_mtx);
11231d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1124a9771948SGleb Smirnoff }
1125a9771948SGleb Smirnoff 
1126afdbac98SGleb Smirnoff /* Send a periodic advertisement, executed in callout context. */
11275c1f0f6dSGleb Smirnoff static void
1128*5ee92cbdSGleb Smirnoff carp_callout(void *v)
1129a9771948SGleb Smirnoff {
1130d220759bSGleb Smirnoff 	struct carp_softc *sc = v;
11311d126e9bSKristof Provost 	struct epoch_tracker et;
1132d220759bSGleb Smirnoff 
11331d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
113408b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1135afdbac98SGleb Smirnoff 	CURVNET_SET(sc->sc_carpdev->if_vnet);
1136*5ee92cbdSGleb Smirnoff 	send_ad_locked(sc);
1137afdbac98SGleb Smirnoff 	CURVNET_RESTORE();
113808b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
11391d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1140d220759bSGleb Smirnoff }
1141d220759bSGleb Smirnoff 
1142d220759bSGleb Smirnoff static void
11434a2dd8d4SGleb Smirnoff carp_send_ad_error(struct carp_softc *sc, int error)
11444a2dd8d4SGleb Smirnoff {
11454a2dd8d4SGleb Smirnoff 
11469a8cf950SGleb Smirnoff 	/*
11476bce41a3SGordon Bergling 	 * We track errors and successful sends with this logic:
11489a8cf950SGleb Smirnoff 	 * - Any error resets success counter to 0.
11499a8cf950SGleb Smirnoff 	 * - MAX_ERRORS triggers demotion.
11509a8cf950SGleb Smirnoff 	 * - MIN_SUCCESS successes resets error counter to 0.
11519a8cf950SGleb Smirnoff 	 * - MIN_SUCCESS reverts demotion, if it was triggered before.
11529a8cf950SGleb Smirnoff 	 */
11534a2dd8d4SGleb Smirnoff 	if (error) {
11544a2dd8d4SGleb Smirnoff 		if (sc->sc_sendad_errors < INT_MAX)
11554a2dd8d4SGleb Smirnoff 			sc->sc_sendad_errors++;
11564a2dd8d4SGleb Smirnoff 		if (sc->sc_sendad_errors == CARP_SENDAD_MAX_ERRORS) {
11574a2dd8d4SGleb Smirnoff 			static const char fmt[] = "send error %d on %s";
11584a2dd8d4SGleb Smirnoff 			char msg[sizeof(fmt) + IFNAMSIZ];
11594a2dd8d4SGleb Smirnoff 
1160511a6d5eSKristof Provost 			sprintf(msg, fmt, error, if_name(sc->sc_carpdev));
11614a2dd8d4SGleb Smirnoff 			carp_demote_adj(V_carp_senderr_adj, msg);
11624a2dd8d4SGleb Smirnoff 		}
11634a2dd8d4SGleb Smirnoff 		sc->sc_sendad_success = 0;
11649a8cf950SGleb Smirnoff 	} else if (sc->sc_sendad_errors > 0) {
11659a8cf950SGleb Smirnoff 		if (++sc->sc_sendad_success >= CARP_SENDAD_MIN_SUCCESS) {
11669a8cf950SGleb Smirnoff 			if (sc->sc_sendad_errors >= CARP_SENDAD_MAX_ERRORS) {
11674a2dd8d4SGleb Smirnoff 				static const char fmt[] = "send ok on %s";
11684a2dd8d4SGleb Smirnoff 				char msg[sizeof(fmt) + IFNAMSIZ];
11694a2dd8d4SGleb Smirnoff 
1170511a6d5eSKristof Provost 				sprintf(msg, fmt, if_name(sc->sc_carpdev));
11714a2dd8d4SGleb Smirnoff 				carp_demote_adj(-V_carp_senderr_adj, msg);
11729a8cf950SGleb Smirnoff 			}
11734a2dd8d4SGleb Smirnoff 			sc->sc_sendad_errors = 0;
11749a8cf950SGleb Smirnoff 		}
11754a2dd8d4SGleb Smirnoff 	}
11764a2dd8d4SGleb Smirnoff }
11774a2dd8d4SGleb Smirnoff 
11788151740cSJosh Paetzel /*
11798151740cSJosh Paetzel  * Pick the best ifaddr on the given ifp for sending CARP
11808151740cSJosh Paetzel  * advertisements.
11818151740cSJosh Paetzel  *
11828151740cSJosh Paetzel  * "Best" here is defined by ifa_preferred().  This function is much
11838151740cSJosh Paetzel  * much like ifaof_ifpforaddr() except that we just use ifa_preferred().
11848151740cSJosh Paetzel  *
11858151740cSJosh Paetzel  * (This could be simplified to return the actual address, except that
11868151740cSJosh Paetzel  * it has a different format in AF_INET and AF_INET6.)
11878151740cSJosh Paetzel  */
11888151740cSJosh Paetzel static struct ifaddr *
11898151740cSJosh Paetzel carp_best_ifa(int af, struct ifnet *ifp)
11908151740cSJosh Paetzel {
11918151740cSJosh Paetzel 	struct ifaddr *ifa, *best;
11928151740cSJosh Paetzel 
1193b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1194b8a6e03fSGleb Smirnoff 
11958151740cSJosh Paetzel 	if (af >= AF_MAX)
11968151740cSJosh Paetzel 		return (NULL);
11978151740cSJosh Paetzel 	best = NULL;
1198d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
11998151740cSJosh Paetzel 		if (ifa->ifa_addr->sa_family == af &&
12008151740cSJosh Paetzel 		    (best == NULL || ifa_preferred(best, ifa)))
12018151740cSJosh Paetzel 			best = ifa;
12028151740cSJosh Paetzel 	}
12038151740cSJosh Paetzel 	if (best != NULL)
12048151740cSJosh Paetzel 		ifa_ref(best);
12058151740cSJosh Paetzel 	return (best);
12068151740cSJosh Paetzel }
12078151740cSJosh Paetzel 
12084a2dd8d4SGleb Smirnoff static void
1209d220759bSGleb Smirnoff carp_send_ad_locked(struct carp_softc *sc)
1210d220759bSGleb Smirnoff {
1211a9771948SGleb Smirnoff 	struct carp_header ch;
1212a9771948SGleb Smirnoff 	struct timeval tv;
121308b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
1214a9771948SGleb Smirnoff 	struct carp_header *ch_ptr;
1215a9771948SGleb Smirnoff 	struct mbuf *m;
121608b68b0eSGleb Smirnoff 	int len, advskew;
1217a9771948SGleb Smirnoff 
12181d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
121908b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1220*5ee92cbdSGleb Smirnoff 	MPASS(sc->sc_version == CARP_VERSION_CARP);
122137115154SKristof Provost 
1222f08535f8SGleb Smirnoff 	advskew = DEMOTE_ADVSKEW(sc);
122308b68b0eSGleb Smirnoff 	tv.tv_sec = sc->sc_advbase;
1224a9771948SGleb Smirnoff 	tv.tv_usec = advskew * 1000000 / 256;
1225a9771948SGleb Smirnoff 
122637115154SKristof Provost 	ch.carp_version = CARP_VERSION_CARP;
1227a9771948SGleb Smirnoff 	ch.carp_type = CARP_ADVERTISEMENT;
1228a9771948SGleb Smirnoff 	ch.carp_vhid = sc->sc_vhid;
122908b68b0eSGleb Smirnoff 	ch.carp_advbase = sc->sc_advbase;
1230a9771948SGleb Smirnoff 	ch.carp_advskew = advskew;
1231a9771948SGleb Smirnoff 	ch.carp_authlen = 7;	/* XXX DEFINE */
1232a9771948SGleb Smirnoff 	ch.carp_pad1 = 0;	/* must be zero */
1233a9771948SGleb Smirnoff 	ch.carp_cksum = 0;
1234a9771948SGleb Smirnoff 
123508b68b0eSGleb Smirnoff 	/* XXXGL: OpenBSD picks first ifaddr with needed family. */
123608b68b0eSGleb Smirnoff 
1237a9771948SGleb Smirnoff #ifdef INET
123808b68b0eSGleb Smirnoff 	if (sc->sc_naddrs) {
1239a9771948SGleb Smirnoff 		struct ip *ip;
1240a9771948SGleb Smirnoff 
1241dc4ad05eSGleb Smirnoff 		m = m_gethdr(M_NOWAIT, MT_DATA);
1242a9771948SGleb Smirnoff 		if (m == NULL) {
12436bf65bcfSRobert Watson 			CARPSTATS_INC(carps_onomem);
1244891122d1SGleb Smirnoff 			goto resched;
1245a9771948SGleb Smirnoff 		}
1246a9771948SGleb Smirnoff 		len = sizeof(*ip) + sizeof(ch);
1247a9771948SGleb Smirnoff 		m->m_pkthdr.len = len;
1248a9771948SGleb Smirnoff 		m->m_pkthdr.rcvif = NULL;
1249a9771948SGleb Smirnoff 		m->m_len = len;
1250ed6a66caSRobert Watson 		M_ALIGN(m, m->m_len);
125113781800SKristof Provost 		if (IN_MULTICAST(sc->sc_carpaddr.s_addr))
1252a9771948SGleb Smirnoff 			m->m_flags |= M_MCAST;
1253a9771948SGleb Smirnoff 		ip = mtod(m, struct ip *);
1254a9771948SGleb Smirnoff 		ip->ip_v = IPVERSION;
1255a9771948SGleb Smirnoff 		ip->ip_hl = sizeof(*ip) >> 2;
12560d3d234cSKristof Provost 		ip->ip_tos = V_carp_dscp << IPTOS_DSCP_OFFSET;
12578f134647SGleb Smirnoff 		ip->ip_len = htons(len);
12588f134647SGleb Smirnoff 		ip->ip_off = htons(IP_DF);
1259a9771948SGleb Smirnoff 		ip->ip_ttl = CARP_DFLTTL;
1260a9771948SGleb Smirnoff 		ip->ip_p = IPPROTO_CARP;
1261a9771948SGleb Smirnoff 		ip->ip_sum = 0;
12626d947416SGleb Smirnoff 		ip_fillid(ip);
126308b68b0eSGleb Smirnoff 
12648151740cSJosh Paetzel 		ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
126508b68b0eSGleb Smirnoff 		if (ifa != NULL) {
126608b68b0eSGleb Smirnoff 			ip->ip_src.s_addr =
126708b68b0eSGleb Smirnoff 			    ifatoia(ifa)->ia_addr.sin_addr.s_addr;
126808b68b0eSGleb Smirnoff 			ifa_free(ifa);
126908b68b0eSGleb Smirnoff 		} else
127008b68b0eSGleb Smirnoff 			ip->ip_src.s_addr = 0;
127113781800SKristof Provost 		ip->ip_dst = sc->sc_carpaddr;
1272a9771948SGleb Smirnoff 
1273a9771948SGleb Smirnoff 		ch_ptr = (struct carp_header *)(&ip[1]);
1274a9771948SGleb Smirnoff 		bcopy(&ch, ch_ptr, sizeof(ch));
1275a9771948SGleb Smirnoff 		if (carp_prepare_ad(m, sc, ch_ptr))
1276891122d1SGleb Smirnoff 			goto resched;
1277a9771948SGleb Smirnoff 
1278a9771948SGleb Smirnoff 		m->m_data += sizeof(*ip);
1279c4d06976SGleb Smirnoff 		ch_ptr->carp_cksum = in_cksum(m, len - sizeof(*ip));
1280a9771948SGleb Smirnoff 		m->m_data -= sizeof(*ip);
1281a9771948SGleb Smirnoff 
12826bf65bcfSRobert Watson 		CARPSTATS_INC(carps_opackets);
1283a9771948SGleb Smirnoff 
12844a2dd8d4SGleb Smirnoff 		carp_send_ad_error(sc, ip_output(m, NULL, NULL, IP_RAWOUTPUT,
12854a2dd8d4SGleb Smirnoff 		    &sc->sc_carpdev->if_carp->cif_imo, NULL));
1286a9771948SGleb Smirnoff 	}
1287a9771948SGleb Smirnoff #endif /* INET */
1288a9771948SGleb Smirnoff #ifdef INET6
128908b68b0eSGleb Smirnoff 	if (sc->sc_naddrs6) {
1290a9771948SGleb Smirnoff 		struct ip6_hdr *ip6;
1291a9771948SGleb Smirnoff 
1292dc4ad05eSGleb Smirnoff 		m = m_gethdr(M_NOWAIT, MT_DATA);
1293a9771948SGleb Smirnoff 		if (m == NULL) {
12946bf65bcfSRobert Watson 			CARPSTATS_INC(carps_onomem);
1295891122d1SGleb Smirnoff 			goto resched;
1296a9771948SGleb Smirnoff 		}
1297a9771948SGleb Smirnoff 		len = sizeof(*ip6) + sizeof(ch);
1298a9771948SGleb Smirnoff 		m->m_pkthdr.len = len;
1299a9771948SGleb Smirnoff 		m->m_pkthdr.rcvif = NULL;
1300a9771948SGleb Smirnoff 		m->m_len = len;
1301ed6a66caSRobert Watson 		M_ALIGN(m, m->m_len);
1302a9771948SGleb Smirnoff 		ip6 = mtod(m, struct ip6_hdr *);
1303a9771948SGleb Smirnoff 		bzero(ip6, sizeof(*ip6));
1304a9771948SGleb Smirnoff 		ip6->ip6_vfc |= IPV6_VERSION;
13050d3d234cSKristof Provost 		/* Traffic class isn't defined in ip6 struct instead
13060d3d234cSKristof Provost 		 * it gets offset into flowid field */
13070d3d234cSKristof Provost 		ip6->ip6_flow |= htonl(V_carp_dscp << (IPV6_FLOWLABEL_LEN +
13080d3d234cSKristof Provost 		    IPTOS_DSCP_OFFSET));
1309a9771948SGleb Smirnoff 		ip6->ip6_hlim = CARP_DFLTTL;
1310a9771948SGleb Smirnoff 		ip6->ip6_nxt = IPPROTO_CARP;
1311a9771948SGleb Smirnoff 
131208b68b0eSGleb Smirnoff 		/* set the source address */
13138151740cSJosh Paetzel 		ifa = carp_best_ifa(AF_INET6, sc->sc_carpdev);
131408b68b0eSGleb Smirnoff 		if (ifa != NULL) {
131508b68b0eSGleb Smirnoff 			bcopy(IFA_IN6(ifa), &ip6->ip6_src,
131608b68b0eSGleb Smirnoff 			    sizeof(struct in6_addr));
131708b68b0eSGleb Smirnoff 			ifa_free(ifa);
131808b68b0eSGleb Smirnoff 		} else
131908b68b0eSGleb Smirnoff 			/* This should never happen with IPv6. */
132008b68b0eSGleb Smirnoff 			bzero(&ip6->ip6_src, sizeof(struct in6_addr));
132108b68b0eSGleb Smirnoff 
132208b68b0eSGleb Smirnoff 		/* Set the multicast destination. */
132313781800SKristof Provost 		memcpy(&ip6->ip6_dst, &sc->sc_carpaddr6, sizeof(ip6->ip6_dst));
1324c2c28c0fSKristof Provost 		if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
1325c2c28c0fSKristof Provost 		    IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) {
13267002145dSBjoern A. Zeeb 			if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
13277002145dSBjoern A. Zeeb 				m_freem(m);
1328e81ab876SGleb Smirnoff 				CARP_DEBUG("%s: in6_setscope failed\n", __func__);
1329891122d1SGleb Smirnoff 				goto resched;
13307002145dSBjoern A. Zeeb 			}
133113781800SKristof Provost 		}
1332a9771948SGleb Smirnoff 
1333a9771948SGleb Smirnoff 		ch_ptr = (struct carp_header *)(&ip6[1]);
1334a9771948SGleb Smirnoff 		bcopy(&ch, ch_ptr, sizeof(ch));
1335a9771948SGleb Smirnoff 		if (carp_prepare_ad(m, sc, ch_ptr))
1336891122d1SGleb Smirnoff 			goto resched;
1337a9771948SGleb Smirnoff 
1338a9771948SGleb Smirnoff 		m->m_data += sizeof(*ip6);
1339c4d06976SGleb Smirnoff 		ch_ptr->carp_cksum = in_cksum(m, len - sizeof(*ip6));
1340a9771948SGleb Smirnoff 		m->m_data -= sizeof(*ip6);
1341a9771948SGleb Smirnoff 
13426bf65bcfSRobert Watson 		CARPSTATS_INC(carps_opackets6);
1343a9771948SGleb Smirnoff 
13444a2dd8d4SGleb Smirnoff 		carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0,
13454a2dd8d4SGleb Smirnoff 		    &sc->sc_carpdev->if_carp->cif_im6o, NULL, NULL));
1346a9771948SGleb Smirnoff 	}
1347a9771948SGleb Smirnoff #endif /* INET6 */
1348a9771948SGleb Smirnoff 
1349891122d1SGleb Smirnoff resched:
1350*5ee92cbdSGleb Smirnoff 	callout_reset(&sc->sc_ad_tmo, tvtohz(&tv), carp_callout, sc);
135108b68b0eSGleb Smirnoff }
1352a9771948SGleb Smirnoff 
135308b68b0eSGleb Smirnoff static void
135437115154SKristof Provost vrrp_send_ad_locked(struct carp_softc *sc)
135537115154SKristof Provost {
135637115154SKristof Provost 	struct vrrpv3_header *vh_ptr;
135737115154SKristof Provost 	struct ifaddr *ifa;
135837115154SKristof Provost 	struct mbuf *m;
135937115154SKristof Provost 	int len;
136037115154SKristof Provost 	struct vrrpv3_header vh = {
136137115154SKristof Provost 	    .vrrp_version = CARP_VERSION_VRRPv3,
136237115154SKristof Provost 	    .vrrp_type = VRRP_TYPE_ADVERTISEMENT,
136337115154SKristof Provost 	    .vrrp_vrtid = sc->sc_vhid,
136437115154SKristof Provost 	    .vrrp_priority = sc->sc_vrrp_prio,
136537115154SKristof Provost 	    .vrrp_count_addr = 0,
136637115154SKristof Provost 	    .vrrp_max_adver_int = htons(sc->sc_vrrp_adv_inter),
136737115154SKristof Provost 	    .vrrp_checksum = 0,
136837115154SKristof Provost 	};
136937115154SKristof Provost 
137037115154SKristof Provost 	NET_EPOCH_ASSERT();
137137115154SKristof Provost 	CARP_LOCK_ASSERT(sc);
137237115154SKristof Provost 	MPASS(sc->sc_version == CARP_VERSION_VRRPv3);
137337115154SKristof Provost 
137437115154SKristof Provost #ifdef INET
137537115154SKristof Provost 	if (sc->sc_naddrs) {
137637115154SKristof Provost 		struct ip *ip;
137737115154SKristof Provost 
137837115154SKristof Provost 		m = m_gethdr(M_NOWAIT, MT_DATA);
137937115154SKristof Provost 		if (m == NULL) {
138037115154SKristof Provost 			CARPSTATS_INC(carps_onomem);
138137115154SKristof Provost 			goto resched;
138237115154SKristof Provost 		}
138337115154SKristof Provost 		len = sizeof(*ip) + sizeof(vh);
138437115154SKristof Provost 		m->m_pkthdr.len = len;
138537115154SKristof Provost 		m->m_pkthdr.rcvif = NULL;
138637115154SKristof Provost 		m->m_len = len;
138737115154SKristof Provost 		M_ALIGN(m, m->m_len);
138837115154SKristof Provost 		m->m_flags |= M_MCAST;
138937115154SKristof Provost 		ip = mtod(m, struct ip *);
139037115154SKristof Provost 		ip->ip_v = IPVERSION;
139137115154SKristof Provost 		ip->ip_hl = sizeof(*ip) >> 2;
139237115154SKristof Provost 		ip->ip_tos = V_carp_dscp << IPTOS_DSCP_OFFSET;
139337115154SKristof Provost 		ip->ip_off = htons(IP_DF);
139437115154SKristof Provost 		ip->ip_ttl = CARP_DFLTTL;
139537115154SKristof Provost 		ip->ip_p = IPPROTO_CARP;
139637115154SKristof Provost 		ip->ip_sum = 0;
139737115154SKristof Provost 		ip_fillid(ip);
139837115154SKristof Provost 
139937115154SKristof Provost 		ifa = carp_best_ifa(AF_INET, sc->sc_carpdev);
140037115154SKristof Provost 		if (ifa != NULL) {
140137115154SKristof Provost 			ip->ip_src.s_addr =
140237115154SKristof Provost 			    ifatoia(ifa)->ia_addr.sin_addr.s_addr;
140337115154SKristof Provost 			ifa_free(ifa);
140437115154SKristof Provost 		} else
140537115154SKristof Provost 			ip->ip_src.s_addr = 0;
140637115154SKristof Provost 		ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP);
140737115154SKristof Provost 
140837115154SKristof Provost 		/* Include the IP addresses in the announcement. */
140937115154SKristof Provost 		for (int i = 0; i < (sc->sc_naddrs + sc->sc_naddrs6); i++) {
141037115154SKristof Provost 			struct sockaddr_in *in;
141137115154SKristof Provost 
141237115154SKristof Provost 			MPASS(sc->sc_ifas[i] != NULL);
141337115154SKristof Provost 			if (sc->sc_ifas[i]->ifa_addr->sa_family != AF_INET)
141437115154SKristof Provost 				continue;
141537115154SKristof Provost 
141637115154SKristof Provost 			in = (struct sockaddr_in *)sc->sc_ifas[i]->ifa_addr;
141737115154SKristof Provost 
141837115154SKristof Provost 			if (m_append(m, sizeof(in->sin_addr),
141937115154SKristof Provost 			    (caddr_t)&in->sin_addr) != 1) {
142037115154SKristof Provost 				m_freem(m);
142137115154SKristof Provost 				goto resched;
142237115154SKristof Provost 			}
142337115154SKristof Provost 
142437115154SKristof Provost 			vh.vrrp_count_addr++;
142537115154SKristof Provost 			len += sizeof(in->sin_addr);
142637115154SKristof Provost 		}
142737115154SKristof Provost 		ip->ip_len = htons(len);
142837115154SKristof Provost 
142937115154SKristof Provost 		vh_ptr = (struct vrrpv3_header *)mtodo(m, sizeof(*ip));
143037115154SKristof Provost 		bcopy(&vh, vh_ptr, sizeof(vh));
143137115154SKristof Provost 
143237115154SKristof Provost 		vh_ptr->vrrp_checksum = in_pseudo(ip->ip_src.s_addr,
143337115154SKristof Provost 		    ip->ip_dst.s_addr,
143437115154SKristof Provost 		    htonl((uint16_t)(len - sizeof(*ip)) + ip->ip_p));
143537115154SKristof Provost 		vh_ptr->vrrp_checksum = in_cksum_skip(m, len, sizeof(*ip));
143637115154SKristof Provost 
143737115154SKristof Provost 		if (carp_tag(sc, m))
143837115154SKristof Provost 			goto resched;
143937115154SKristof Provost 
144037115154SKristof Provost 		CARPSTATS_INC(carps_opackets);
144137115154SKristof Provost 
144237115154SKristof Provost 		carp_send_ad_error(sc, ip_output(m, NULL, NULL, IP_RAWOUTPUT,
144337115154SKristof Provost 		    &sc->sc_carpdev->if_carp->cif_imo, NULL));
144437115154SKristof Provost 	}
144537115154SKristof Provost #endif
144637115154SKristof Provost #ifdef INET6
144737115154SKristof Provost 	if (sc->sc_naddrs6) {
144837115154SKristof Provost 		struct ip6_hdr *ip6;
144937115154SKristof Provost 
145037115154SKristof Provost 		m = m_gethdr(M_NOWAIT, MT_DATA);
145137115154SKristof Provost 		if (m == NULL) {
145237115154SKristof Provost 			CARPSTATS_INC(carps_onomem);
145337115154SKristof Provost 			goto resched;
145437115154SKristof Provost 		}
145537115154SKristof Provost 		len = sizeof(*ip6) + sizeof(vh);
145637115154SKristof Provost 		m->m_pkthdr.len = len;
145737115154SKristof Provost 		m->m_pkthdr.rcvif = NULL;
145837115154SKristof Provost 		m->m_len = len;
145937115154SKristof Provost 		M_ALIGN(m, m->m_len);
146037115154SKristof Provost 		m->m_flags |= M_MCAST;
146137115154SKristof Provost 		ip6 = mtod(m, struct ip6_hdr *);
146237115154SKristof Provost 		bzero(ip6, sizeof(*ip6));
146337115154SKristof Provost 		ip6->ip6_vfc |= IPV6_VERSION;
146437115154SKristof Provost 		/* Traffic class isn't defined in ip6 struct instead
146537115154SKristof Provost 		 * it gets offset into flowid field */
146637115154SKristof Provost 		ip6->ip6_flow |= htonl(V_carp_dscp << (IPV6_FLOWLABEL_LEN +
146737115154SKristof Provost 		    IPTOS_DSCP_OFFSET));
146837115154SKristof Provost 		ip6->ip6_hlim = CARP_DFLTTL;
146937115154SKristof Provost 		ip6->ip6_nxt = IPPROTO_CARP;
147037115154SKristof Provost 
147137115154SKristof Provost 		/* set the source address */
147237115154SKristof Provost 		ifa = carp_best_ifa(AF_INET6, sc->sc_carpdev);
147337115154SKristof Provost 		if (ifa != NULL) {
147437115154SKristof Provost 			bcopy(IFA_IN6(ifa), &ip6->ip6_src,
147537115154SKristof Provost 			    sizeof(struct in6_addr));
147637115154SKristof Provost 			ifa_free(ifa);
147737115154SKristof Provost 		} else
147837115154SKristof Provost 			/* This should never happen with IPv6. */
147937115154SKristof Provost 			bzero(&ip6->ip6_src, sizeof(struct in6_addr));
148037115154SKristof Provost 
148137115154SKristof Provost 		/* Set the multicast destination. */
148237115154SKristof Provost 		bzero(&ip6->ip6_dst, sizeof(ip6->ip6_dst));
148337115154SKristof Provost 		ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
148437115154SKristof Provost 		ip6->ip6_dst.s6_addr8[15] = 0x12;
148537115154SKristof Provost 
148637115154SKristof Provost 		/* Include the IP addresses in the announcement. */
148737115154SKristof Provost 		len = sizeof(vh);
148837115154SKristof Provost 		for (int i = 0; i < (sc->sc_naddrs + sc->sc_naddrs6); i++) {
148937115154SKristof Provost 			struct sockaddr_in6 *in6;
149037115154SKristof Provost 
149137115154SKristof Provost 			MPASS(sc->sc_ifas[i] != NULL);
149237115154SKristof Provost 			if (sc->sc_ifas[i]->ifa_addr->sa_family != AF_INET6)
149337115154SKristof Provost 				continue;
149437115154SKristof Provost 
149537115154SKristof Provost 			in6 = (struct sockaddr_in6 *)sc->sc_ifas[i]->ifa_addr;
149637115154SKristof Provost 
149737115154SKristof Provost 			if (m_append(m, sizeof(in6->sin6_addr),
149837115154SKristof Provost 			    (char *)&in6->sin6_addr) != 1) {
149937115154SKristof Provost 				m_freem(m);
150037115154SKristof Provost 				goto resched;
150137115154SKristof Provost 			}
150237115154SKristof Provost 
150337115154SKristof Provost 			vh.vrrp_count_addr++;
150437115154SKristof Provost 			len += sizeof(in6->sin6_addr);
150537115154SKristof Provost 		}
150637115154SKristof Provost 		ip6->ip6_plen = htonl(len);
150737115154SKristof Provost 
150837115154SKristof Provost 		vh_ptr = (struct vrrpv3_header *)mtodo(m, sizeof(*ip6));
150937115154SKristof Provost 		bcopy(&vh, vh_ptr, sizeof(vh));
151037115154SKristof Provost 
151137115154SKristof Provost 		vh_ptr->vrrp_checksum = in6_cksum_pseudo(ip6, len, ip6->ip6_nxt, 0);
151237115154SKristof Provost 		vh_ptr->vrrp_checksum = in_cksum_skip(m, len + sizeof(*ip6), sizeof(*ip6));
151337115154SKristof Provost 
151437115154SKristof Provost 		if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
151537115154SKristof Provost 			m_freem(m);
151637115154SKristof Provost 			CARP_DEBUG("%s: in6_setscope failed\n", __func__);
151737115154SKristof Provost 			goto resched;
151837115154SKristof Provost 		}
151937115154SKristof Provost 
152037115154SKristof Provost 		if (carp_tag(sc, m))
152137115154SKristof Provost 			goto resched;
152237115154SKristof Provost 		CARPSTATS_INC(carps_opackets6);
152337115154SKristof Provost 
152437115154SKristof Provost 		carp_send_ad_error(sc, ip6_output(m, NULL, NULL, 0,
152537115154SKristof Provost 		    &sc->sc_carpdev->if_carp->cif_im6o, NULL, NULL));
152637115154SKristof Provost 	}
152737115154SKristof Provost #endif
152837115154SKristof Provost 
152937115154SKristof Provost resched:
153037115154SKristof Provost 	callout_reset(&sc->sc_ad_tmo, sc->sc_vrrp_adv_inter * hz / 100,
1531*5ee92cbdSGleb Smirnoff 	    carp_callout, sc);
153237115154SKristof Provost }
153337115154SKristof Provost 
153437115154SKristof Provost static void
153508b68b0eSGleb Smirnoff carp_addroute(struct carp_softc *sc)
153608b68b0eSGleb Smirnoff {
153708b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
153808b68b0eSGleb Smirnoff 
153908b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa)
15402512b096SGleb Smirnoff 		carp_ifa_addroute(ifa);
15412512b096SGleb Smirnoff }
15422512b096SGleb Smirnoff 
15432512b096SGleb Smirnoff static void
15442512b096SGleb Smirnoff carp_ifa_addroute(struct ifaddr *ifa)
15452512b096SGleb Smirnoff {
15462512b096SGleb Smirnoff 
154708b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
154808b68b0eSGleb Smirnoff #ifdef INET
154908b68b0eSGleb Smirnoff 	case AF_INET:
1550130aebbaSAlexander V. Chernikov 		in_addprefix(ifatoia(ifa));
155108b68b0eSGleb Smirnoff 		ifa_add_loopback_route(ifa,
155208b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia(ifa)->ia_addr);
155308b68b0eSGleb Smirnoff 		break;
155408b68b0eSGleb Smirnoff #endif
155508b68b0eSGleb Smirnoff #ifdef INET6
155608b68b0eSGleb Smirnoff 	case AF_INET6:
155708b68b0eSGleb Smirnoff 		ifa_add_loopback_route(ifa,
155808b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia6(ifa)->ia_addr);
1559f6b84910SAlexander V. Chernikov 		nd6_add_ifa_lle(ifatoia6(ifa));
156008b68b0eSGleb Smirnoff 		break;
156108b68b0eSGleb Smirnoff #endif
156208b68b0eSGleb Smirnoff 	}
156308b68b0eSGleb Smirnoff }
156408b68b0eSGleb Smirnoff 
156508b68b0eSGleb Smirnoff static void
156608b68b0eSGleb Smirnoff carp_delroute(struct carp_softc *sc)
156708b68b0eSGleb Smirnoff {
156808b68b0eSGleb Smirnoff 	struct ifaddr *ifa;
156908b68b0eSGleb Smirnoff 
157008b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa)
15712512b096SGleb Smirnoff 		carp_ifa_delroute(ifa);
15722512b096SGleb Smirnoff }
15732512b096SGleb Smirnoff 
15742512b096SGleb Smirnoff static void
15752512b096SGleb Smirnoff carp_ifa_delroute(struct ifaddr *ifa)
15762512b096SGleb Smirnoff {
15772512b096SGleb Smirnoff 
157808b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
157908b68b0eSGleb Smirnoff #ifdef INET
158008b68b0eSGleb Smirnoff 	case AF_INET:
158108b68b0eSGleb Smirnoff 		ifa_del_loopback_route(ifa,
158208b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia(ifa)->ia_addr);
158308b68b0eSGleb Smirnoff 		in_scrubprefix(ifatoia(ifa), LLE_STATIC);
158408b68b0eSGleb Smirnoff 		break;
158508b68b0eSGleb Smirnoff #endif
158608b68b0eSGleb Smirnoff #ifdef INET6
158708b68b0eSGleb Smirnoff 	case AF_INET6:
158808b68b0eSGleb Smirnoff 		ifa_del_loopback_route(ifa,
158908b68b0eSGleb Smirnoff 		    (struct sockaddr *)&ifatoia6(ifa)->ia_addr);
15903e7a2321SAlexander V. Chernikov 		nd6_rem_ifa_lle(ifatoia6(ifa), 1);
159108b68b0eSGleb Smirnoff 		break;
159208b68b0eSGleb Smirnoff #endif
159308b68b0eSGleb Smirnoff 	}
1594a9771948SGleb Smirnoff }
1595a9771948SGleb Smirnoff 
159624421c1cSGleb Smirnoff int
159724421c1cSGleb Smirnoff carp_master(struct ifaddr *ifa)
159824421c1cSGleb Smirnoff {
159924421c1cSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
160024421c1cSGleb Smirnoff 
160124421c1cSGleb Smirnoff 	return (sc->sc_state == MASTER);
160224421c1cSGleb Smirnoff }
160324421c1cSGleb Smirnoff 
1604a0ae8f04SBjoern A. Zeeb #ifdef INET
1605a9771948SGleb Smirnoff /*
1606a9771948SGleb Smirnoff  * Broadcast a gratuitous ARP request containing
1607a9771948SGleb Smirnoff  * the virtual router MAC address for each IP address
1608a9771948SGleb Smirnoff  * associated with the virtual router.
1609a9771948SGleb Smirnoff  */
16105c1f0f6dSGleb Smirnoff static void
1611a9771948SGleb Smirnoff carp_send_arp(struct carp_softc *sc)
1612a9771948SGleb Smirnoff {
1613a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1614d6e82913SSteven Hartland 	struct in_addr addr;
1615a9771948SGleb Smirnoff 
16161d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
16171d126e9bSKristof Provost 
16181c302b58SAlexander V. Chernikov 	CARP_FOREACH_IFA(sc, ifa) {
16191c302b58SAlexander V. Chernikov 		if (ifa->ifa_addr->sa_family != AF_INET)
16201c302b58SAlexander V. Chernikov 			continue;
1621d6e82913SSteven Hartland 		addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
1622d6e82913SSteven Hartland 		arp_announce_ifaddr(sc->sc_carpdev, addr, LLADDR(&sc->sc_addr));
16231c302b58SAlexander V. Chernikov 	}
1624a9771948SGleb Smirnoff }
162508b68b0eSGleb Smirnoff 
162608b68b0eSGleb Smirnoff int
162708b68b0eSGleb Smirnoff carp_iamatch(struct ifaddr *ifa, uint8_t **enaddr)
162808b68b0eSGleb Smirnoff {
162908b68b0eSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
163008b68b0eSGleb Smirnoff 
163108b68b0eSGleb Smirnoff 	if (sc->sc_state == MASTER) {
163208b68b0eSGleb Smirnoff 		*enaddr = LLADDR(&sc->sc_addr);
163308b68b0eSGleb Smirnoff 		return (1);
163408b68b0eSGleb Smirnoff 	}
163508b68b0eSGleb Smirnoff 
163608b68b0eSGleb Smirnoff 	return (0);
1637a9771948SGleb Smirnoff }
1638a0ae8f04SBjoern A. Zeeb #endif
1639a9771948SGleb Smirnoff 
1640a9771948SGleb Smirnoff #ifdef INET6
16415c1f0f6dSGleb Smirnoff static void
1642a9771948SGleb Smirnoff carp_send_na(struct carp_softc *sc)
1643a9771948SGleb Smirnoff {
1644d6e82913SSteven Hartland 	static struct in6_addr mcast = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
1645a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1646d6e82913SSteven Hartland 	struct in6_addr *in6;
1647a9771948SGleb Smirnoff 
164808b68b0eSGleb Smirnoff 	CARP_FOREACH_IFA(sc, ifa) {
1649d6e82913SSteven Hartland 		if (ifa->ifa_addr->sa_family != AF_INET6)
1650a9771948SGleb Smirnoff 			continue;
1651a9771948SGleb Smirnoff 
1652d6e82913SSteven Hartland 		in6 = IFA_IN6(ifa);
1653d6e82913SSteven Hartland 		nd6_na_output(sc->sc_carpdev, &mcast, in6,
1654d6e82913SSteven Hartland 		    ND_NA_FLAG_OVERRIDE, 1, NULL);
1655d6e82913SSteven Hartland 		DELAY(1000);	/* XXX */
1656a9771948SGleb Smirnoff 	}
1657a9771948SGleb Smirnoff }
1658a9771948SGleb Smirnoff 
16598253dcabSBjoern A. Zeeb /*
16608253dcabSBjoern A. Zeeb  * Returns ifa in case it's a carp address and it is MASTER, or if the address
16618253dcabSBjoern A. Zeeb  * matches and is not a carp address.  Returns NULL otherwise.
16628253dcabSBjoern A. Zeeb  */
1663a4e53905SMax Laier struct ifaddr *
166454bfbd51SWill Andrews carp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr)
1665a9771948SGleb Smirnoff {
1666a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1667a9771948SGleb Smirnoff 
1668b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1669b8a6e03fSGleb Smirnoff 
16708253dcabSBjoern A. Zeeb 	ifa = NULL;
1671d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
16728253dcabSBjoern A. Zeeb 		if (ifa->ifa_addr->sa_family != AF_INET6)
16738253dcabSBjoern A. Zeeb 			continue;
16748253dcabSBjoern A. Zeeb 		if (!IN6_ARE_ADDR_EQUAL(taddr, IFA_IN6(ifa)))
16758253dcabSBjoern A. Zeeb 			continue;
16768253dcabSBjoern A. Zeeb 		if (ifa->ifa_carp && ifa->ifa_carp->sc_state != MASTER)
16778253dcabSBjoern A. Zeeb 			ifa = NULL;
16788253dcabSBjoern A. Zeeb 		else
16798c0fec80SRobert Watson 			ifa_ref(ifa);
16808253dcabSBjoern A. Zeeb 		break;
1681a9771948SGleb Smirnoff 	}
1682a9771948SGleb Smirnoff 
16838253dcabSBjoern A. Zeeb 	return (ifa);
1684a9771948SGleb Smirnoff }
1685a9771948SGleb Smirnoff 
1686dad68fc3SBjoern A. Zeeb char *
168754bfbd51SWill Andrews carp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr)
1688a9771948SGleb Smirnoff {
1689a9771948SGleb Smirnoff 	struct ifaddr *ifa;
1690a9771948SGleb Smirnoff 
1691b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
1692b8a6e03fSGleb Smirnoff 
169308b68b0eSGleb Smirnoff 	IFNET_FOREACH_IFA(ifp, ifa)
169408b68b0eSGleb Smirnoff 		if (ifa->ifa_addr->sa_family == AF_INET6 &&
169508b68b0eSGleb Smirnoff 		    IN6_ARE_ADDR_EQUAL(taddr, IFA_IN6(ifa))) {
169608b68b0eSGleb Smirnoff 			struct carp_softc *sc = ifa->ifa_carp;
169708b68b0eSGleb Smirnoff 			struct m_tag *mtag;
169808b68b0eSGleb Smirnoff 
1699a9771948SGleb Smirnoff 			mtag = m_tag_get(PACKET_TAG_CARP,
1700a856ddc6SGleb Smirnoff 			    sizeof(struct carp_softc *), M_NOWAIT);
170108b68b0eSGleb Smirnoff 			if (mtag == NULL)
170208b68b0eSGleb Smirnoff 				/* Better a bit than nothing. */
170308b68b0eSGleb Smirnoff 				return (LLADDR(&sc->sc_addr));
170408b68b0eSGleb Smirnoff 
1705eaf151c4SGleb Smirnoff 			bcopy(&sc, mtag + 1, sizeof(sc));
1706a9771948SGleb Smirnoff 			m_tag_prepend(m, mtag);
1707a9771948SGleb Smirnoff 
170808b68b0eSGleb Smirnoff 			return (LLADDR(&sc->sc_addr));
1709a9771948SGleb Smirnoff 		}
1710a9771948SGleb Smirnoff 
1711a9771948SGleb Smirnoff 	return (NULL);
1712a9771948SGleb Smirnoff }
171308b68b0eSGleb Smirnoff #endif /* INET6 */
1714a9771948SGleb Smirnoff 
171508b68b0eSGleb Smirnoff int
171654bfbd51SWill Andrews carp_forus(struct ifnet *ifp, u_char *dhost)
1717a9771948SGleb Smirnoff {
171808b68b0eSGleb Smirnoff 	struct carp_softc *sc;
171908b68b0eSGleb Smirnoff 	uint8_t *ena = dhost;
1720a9771948SGleb Smirnoff 
1721a9771948SGleb Smirnoff 	if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1)
172208b68b0eSGleb Smirnoff 		return (0);
1723a9771948SGleb Smirnoff 
172408b68b0eSGleb Smirnoff 	CIF_LOCK(ifp->if_carp);
172508b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc) {
17268c3fbf3cSAlexander Motin 		/*
17278c3fbf3cSAlexander Motin 		 * CARP_LOCK() is not here, since would protect nothing, but
17288c3fbf3cSAlexander Motin 		 * cause deadlock with if_bridge, calling this under its lock.
17298c3fbf3cSAlexander Motin 		 */
173008b68b0eSGleb Smirnoff 		if (sc->sc_state == MASTER && !bcmp(dhost, LLADDR(&sc->sc_addr),
173108b68b0eSGleb Smirnoff 		    ETHER_ADDR_LEN)) {
173208b68b0eSGleb Smirnoff 			CIF_UNLOCK(ifp->if_carp);
173308b68b0eSGleb Smirnoff 			return (1);
1734a9771948SGleb Smirnoff 		}
173508b68b0eSGleb Smirnoff 	}
173608b68b0eSGleb Smirnoff 	CIF_UNLOCK(ifp->if_carp);
1737a9771948SGleb Smirnoff 
173808b68b0eSGleb Smirnoff 	return (0);
1739a9771948SGleb Smirnoff }
1740a9771948SGleb Smirnoff 
1741afdbac98SGleb Smirnoff /* Master down timeout event, executed in callout context. */
17425c1f0f6dSGleb Smirnoff static void
1743a9771948SGleb Smirnoff carp_master_down(void *v)
1744a9771948SGleb Smirnoff {
1745a9771948SGleb Smirnoff 	struct carp_softc *sc = v;
17461d126e9bSKristof Provost 	struct epoch_tracker et;
1747a9771948SGleb Smirnoff 
17481d126e9bSKristof Provost 	NET_EPOCH_ENTER(et);
174908b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
175008b68b0eSGleb Smirnoff 
1751afdbac98SGleb Smirnoff 	CURVNET_SET(sc->sc_carpdev->if_vnet);
175208b68b0eSGleb Smirnoff 	if (sc->sc_state == BACKUP) {
1753d01641e2SWill Andrews 		carp_master_down_locked(sc, "master timed out");
175408b68b0eSGleb Smirnoff 	}
1755afdbac98SGleb Smirnoff 	CURVNET_RESTORE();
175608b68b0eSGleb Smirnoff 
175708b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
17581d126e9bSKristof Provost 	NET_EPOCH_EXIT(et);
1759d220759bSGleb Smirnoff }
1760d220759bSGleb Smirnoff 
1761d220759bSGleb Smirnoff static void
1762d01641e2SWill Andrews carp_master_down_locked(struct carp_softc *sc, const char *reason)
1763d220759bSGleb Smirnoff {
176408b68b0eSGleb Smirnoff 
17651d126e9bSKristof Provost 	NET_EPOCH_ASSERT();
176608b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1767d220759bSGleb Smirnoff 
1768a9771948SGleb Smirnoff 	switch (sc->sc_state) {
1769a9771948SGleb Smirnoff 	case BACKUP:
1770d01641e2SWill Andrews 		carp_set_state(sc, MASTER, reason);
1771*5ee92cbdSGleb Smirnoff 		send_ad_locked(sc);
1772a0ae8f04SBjoern A. Zeeb #ifdef INET
1773a9771948SGleb Smirnoff 		carp_send_arp(sc);
1774a0ae8f04SBjoern A. Zeeb #endif
1775a9771948SGleb Smirnoff #ifdef INET6
1776a9771948SGleb Smirnoff 		carp_send_na(sc);
177708b68b0eSGleb Smirnoff #endif
1778a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
177908b68b0eSGleb Smirnoff 		carp_addroute(sc);
178008b68b0eSGleb Smirnoff 		break;
178108b68b0eSGleb Smirnoff 	case INIT:
178208b68b0eSGleb Smirnoff 	case MASTER:
178308b68b0eSGleb Smirnoff #ifdef INVARIANTS
178408b68b0eSGleb Smirnoff 		panic("carp: VHID %u@%s: master_down event in %s state\n",
178508b68b0eSGleb Smirnoff 		    sc->sc_vhid,
1786511a6d5eSKristof Provost 		    if_name(sc->sc_carpdev),
178708b68b0eSGleb Smirnoff 		    sc->sc_state ? "MASTER" : "INIT");
178808b68b0eSGleb Smirnoff #endif
1789a9771948SGleb Smirnoff 		break;
1790a9771948SGleb Smirnoff 	}
1791a9771948SGleb Smirnoff }
1792a9771948SGleb Smirnoff 
1793a9771948SGleb Smirnoff /*
1794a9771948SGleb Smirnoff  * When in backup state, af indicates whether to reset the master down timer
1795a9771948SGleb Smirnoff  * for v4 or v6. If it's set to zero, reset the ones which are already pending.
1796a9771948SGleb Smirnoff  */
17975c1f0f6dSGleb Smirnoff static void
1798a9771948SGleb Smirnoff carp_setrun(struct carp_softc *sc, sa_family_t af)
1799a9771948SGleb Smirnoff {
1800a9771948SGleb Smirnoff 	struct timeval tv;
180137115154SKristof Provost 	int timeout;
1802a9771948SGleb Smirnoff 
180308b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
1804d220759bSGleb Smirnoff 
180508b68b0eSGleb Smirnoff 	if ((sc->sc_carpdev->if_flags & IFF_UP) == 0 ||
180608b68b0eSGleb Smirnoff 	    sc->sc_carpdev->if_link_state != LINK_STATE_UP ||
1807167a3440SAlexander Motin 	    (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) ||
1808167a3440SAlexander Motin 	    !V_carp_allow)
1809a9771948SGleb Smirnoff 		return;
1810a9771948SGleb Smirnoff 
1811a9771948SGleb Smirnoff 	switch (sc->sc_state) {
1812a9771948SGleb Smirnoff 	case INIT:
1813d01641e2SWill Andrews 		carp_set_state(sc, BACKUP, "initialization complete");
1814a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
1815a9771948SGleb Smirnoff 		break;
1816a9771948SGleb Smirnoff 	case BACKUP:
1817a9771948SGleb Smirnoff 		callout_stop(&sc->sc_ad_tmo);
181837115154SKristof Provost 
181937115154SKristof Provost 		if (sc->sc_version == CARP_VERSION_CARP) {
1820a9771948SGleb Smirnoff 			tv.tv_sec = 3 * sc->sc_advbase;
1821a9771948SGleb Smirnoff 			tv.tv_usec = sc->sc_advskew * 1000000 / 256;
182237115154SKristof Provost 			timeout = tvtohz(&tv);
182337115154SKristof Provost 		} else {
182437115154SKristof Provost 			/* skew time */
182537115154SKristof Provost 			timeout = (256 - sc->sc_vrrp_prio) * sc->sc_vrrp_master_inter / 256;
182637115154SKristof Provost 			timeout += (3 * sc->sc_vrrp_master_inter);
182737115154SKristof Provost 			timeout *= hz;
182837115154SKristof Provost 			timeout /= 100; /* master interval is in centiseconds. */
182937115154SKristof Provost 		}
183037115154SKristof Provost 
1831a9771948SGleb Smirnoff 		switch (af) {
1832a9771948SGleb Smirnoff #ifdef INET
1833a9771948SGleb Smirnoff 		case AF_INET:
183437115154SKristof Provost 			callout_reset(&sc->sc_md_tmo, timeout,
1835a9771948SGleb Smirnoff 			    carp_master_down, sc);
1836a9771948SGleb Smirnoff 			break;
183708b68b0eSGleb Smirnoff #endif
1838a9771948SGleb Smirnoff #ifdef INET6
1839a9771948SGleb Smirnoff 		case AF_INET6:
184037115154SKristof Provost 			callout_reset(&sc->sc_md6_tmo, timeout,
1841a9771948SGleb Smirnoff 			    carp_master_down, sc);
1842a9771948SGleb Smirnoff 			break;
184308b68b0eSGleb Smirnoff #endif
1844a9771948SGleb Smirnoff 		default:
184508b68b0eSGleb Smirnoff #ifdef INET
1846a9771948SGleb Smirnoff 			if (sc->sc_naddrs)
184737115154SKristof Provost 				callout_reset(&sc->sc_md_tmo, timeout,
1848a9771948SGleb Smirnoff 				    carp_master_down, sc);
184908b68b0eSGleb Smirnoff #endif
185008b68b0eSGleb Smirnoff #ifdef INET6
1851a9771948SGleb Smirnoff 			if (sc->sc_naddrs6)
185237115154SKristof Provost 				callout_reset(&sc->sc_md6_tmo, timeout,
1853a9771948SGleb Smirnoff 				    carp_master_down, sc);
185408b68b0eSGleb Smirnoff #endif
1855a9771948SGleb Smirnoff 			break;
1856a9771948SGleb Smirnoff 		}
1857a9771948SGleb Smirnoff 		break;
1858a9771948SGleb Smirnoff 	case MASTER:
185937115154SKristof Provost 		if (sc->sc_version == CARP_VERSION_CARP) {
1860a9771948SGleb Smirnoff 			tv.tv_sec = sc->sc_advbase;
1861a9771948SGleb Smirnoff 			tv.tv_usec = sc->sc_advskew * 1000000 / 256;
1862a9771948SGleb Smirnoff 			callout_reset(&sc->sc_ad_tmo, tvtohz(&tv),
1863*5ee92cbdSGleb Smirnoff 			    carp_callout, sc);
186437115154SKristof Provost 		} else {
186537115154SKristof Provost 			callout_reset(&sc->sc_ad_tmo,
186637115154SKristof Provost 			    sc->sc_vrrp_adv_inter * hz / 100,
1867*5ee92cbdSGleb Smirnoff 			    carp_callout, sc);
186837115154SKristof Provost 		}
1869a9771948SGleb Smirnoff 		break;
1870a9771948SGleb Smirnoff 	}
1871a9771948SGleb Smirnoff }
1872a9771948SGleb Smirnoff 
187308b68b0eSGleb Smirnoff /*
187408b68b0eSGleb Smirnoff  * Setup multicast structures.
187508b68b0eSGleb Smirnoff  */
18765c1f0f6dSGleb Smirnoff static int
1877a9a2c40cSGleb Smirnoff carp_multicast_setup(struct carp_if *cif, sa_family_t sa)
1878a9771948SGleb Smirnoff {
1879a9a2c40cSGleb Smirnoff 	struct ifnet *ifp = cif->cif_ifp;
188008b68b0eSGleb Smirnoff 	int error = 0;
188108b68b0eSGleb Smirnoff 
188208b68b0eSGleb Smirnoff 	switch (sa) {
188308b68b0eSGleb Smirnoff #ifdef INET
188408b68b0eSGleb Smirnoff 	case AF_INET:
188508b68b0eSGleb Smirnoff 	    {
188608b68b0eSGleb Smirnoff 		struct ip_moptions *imo = &cif->cif_imo;
188759854ecfSHans Petter Selasky 		struct in_mfilter *imf;
1888a9771948SGleb Smirnoff 		struct in_addr addr;
1889a9771948SGleb Smirnoff 
189059854ecfSHans Petter Selasky 		if (ip_mfilter_first(&imo->imo_head) != NULL)
1891a9771948SGleb Smirnoff 			return (0);
1892a9771948SGleb Smirnoff 
189359854ecfSHans Petter Selasky 		imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
189459854ecfSHans Petter Selasky 		ip_mfilter_init(&imo->imo_head);
189508b68b0eSGleb Smirnoff 		imo->imo_multicast_vif = -1;
1896a9771948SGleb Smirnoff 
1897a9771948SGleb Smirnoff 		addr.s_addr = htonl(INADDR_CARP_GROUP);
189808b68b0eSGleb Smirnoff 		if ((error = in_joingroup(ifp, &addr, NULL,
189959854ecfSHans Petter Selasky 		    &imf->imf_inm)) != 0) {
190059854ecfSHans Petter Selasky 			ip_mfilter_free(imf);
190108b68b0eSGleb Smirnoff 			break;
19022d9cfabaSRobert Watson 		}
190359854ecfSHans Petter Selasky 
190459854ecfSHans Petter Selasky 		ip_mfilter_insert(&imo->imo_head, imf);
1905a9771948SGleb Smirnoff 		imo->imo_multicast_ifp = ifp;
1906a9771948SGleb Smirnoff 		imo->imo_multicast_ttl = CARP_DFLTTL;
1907a9771948SGleb Smirnoff 		imo->imo_multicast_loop = 0;
1908a9771948SGleb Smirnoff 		break;
1909a9771948SGleb Smirnoff 	   }
191008b68b0eSGleb Smirnoff #endif
191108b68b0eSGleb Smirnoff #ifdef INET6
191208b68b0eSGleb Smirnoff 	case AF_INET6:
191308b68b0eSGleb Smirnoff 	    {
191408b68b0eSGleb Smirnoff 		struct ip6_moptions *im6o = &cif->cif_im6o;
191559854ecfSHans Petter Selasky 		struct in6_mfilter *im6f[2];
191608b68b0eSGleb Smirnoff 		struct in6_addr in6;
191733cde130SBruce M Simpson 
191859854ecfSHans Petter Selasky 		if (ip6_mfilter_first(&im6o->im6o_head))
191908b68b0eSGleb Smirnoff 			return (0);
192008b68b0eSGleb Smirnoff 
192159854ecfSHans Petter Selasky 		im6f[0] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
192259854ecfSHans Petter Selasky 		im6f[1] = ip6_mfilter_alloc(M_WAITOK, 0, 0);
192359854ecfSHans Petter Selasky 
192459854ecfSHans Petter Selasky 		ip6_mfilter_init(&im6o->im6o_head);
192508b68b0eSGleb Smirnoff 		im6o->im6o_multicast_hlim = CARP_DFLTTL;
1926a9771948SGleb Smirnoff 		im6o->im6o_multicast_ifp = ifp;
1927a9771948SGleb Smirnoff 
192808b68b0eSGleb Smirnoff 		/* Join IPv6 CARP multicast group. */
1929a1f7e5f8SHajimu UMEMOTO 		bzero(&in6, sizeof(in6));
1930a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr16[0] = htons(0xff02);
1931a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr8[15] = 0x12;
193208b68b0eSGleb Smirnoff 		if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
193359854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
193459854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
193508b68b0eSGleb Smirnoff 			break;
193608b68b0eSGleb Smirnoff 		}
193759854ecfSHans Petter Selasky 		if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[0]->im6f_in6m, 0)) != 0) {
193859854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
193959854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
194008b68b0eSGleb Smirnoff 			break;
194108b68b0eSGleb Smirnoff 		}
1942a9771948SGleb Smirnoff 
194308b68b0eSGleb Smirnoff 		/* Join solicited multicast address. */
1944a1f7e5f8SHajimu UMEMOTO 		bzero(&in6, sizeof(in6));
1945a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr16[0] = htons(0xff02);
1946a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr32[1] = 0;
1947a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr32[2] = htonl(1);
194808b68b0eSGleb Smirnoff 		in6.s6_addr32[3] = 0;
1949a1f7e5f8SHajimu UMEMOTO 		in6.s6_addr8[12] = 0xff;
195059854ecfSHans Petter Selasky 
195108b68b0eSGleb Smirnoff 		if ((error = in6_setscope(&in6, ifp, NULL)) != 0) {
195259854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
195359854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
195408b68b0eSGleb Smirnoff 			break;
195508b68b0eSGleb Smirnoff 		}
195659854ecfSHans Petter Selasky 
195759854ecfSHans Petter Selasky 		if ((error = in6_joingroup(ifp, &in6, NULL, &im6f[1]->im6f_in6m, 0)) != 0) {
195859854ecfSHans Petter Selasky 			in6_leavegroup(im6f[0]->im6f_in6m, NULL);
195959854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[0]);
196059854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f[1]);
196108b68b0eSGleb Smirnoff 			break;
196208b68b0eSGleb Smirnoff 		}
196359854ecfSHans Petter Selasky 		ip6_mfilter_insert(&im6o->im6o_head, im6f[0]);
196459854ecfSHans Petter Selasky 		ip6_mfilter_insert(&im6o->im6o_head, im6f[1]);
1965a9771948SGleb Smirnoff 		break;
1966a9771948SGleb Smirnoff 	    }
1967a9771948SGleb Smirnoff #endif
196808b68b0eSGleb Smirnoff 	}
196908b68b0eSGleb Smirnoff 
197008b68b0eSGleb Smirnoff 	return (error);
1971a9771948SGleb Smirnoff }
1972a9771948SGleb Smirnoff 
1973a9771948SGleb Smirnoff /*
197408b68b0eSGleb Smirnoff  * Free multicast structures.
1975a9771948SGleb Smirnoff  */
19765c1f0f6dSGleb Smirnoff static void
1977a9a2c40cSGleb Smirnoff carp_multicast_cleanup(struct carp_if *cif, sa_family_t sa)
1978a9771948SGleb Smirnoff {
197959854ecfSHans Petter Selasky #ifdef INET
198059854ecfSHans Petter Selasky 	struct ip_moptions *imo = &cif->cif_imo;
198159854ecfSHans Petter Selasky 	struct in_mfilter *imf;
198259854ecfSHans Petter Selasky #endif
198359854ecfSHans Petter Selasky #ifdef INET6
198459854ecfSHans Petter Selasky 	struct ip6_moptions *im6o = &cif->cif_im6o;
198559854ecfSHans Petter Selasky 	struct in6_mfilter *im6f;
198659854ecfSHans Petter Selasky #endif
19879c2cd1aaSGleb Smirnoff 	sx_assert(&carp_sx, SA_XLOCKED);
19889c2cd1aaSGleb Smirnoff 
198908b68b0eSGleb Smirnoff 	switch (sa) {
199008b68b0eSGleb Smirnoff #ifdef INET
199108b68b0eSGleb Smirnoff 	case AF_INET:
199259854ecfSHans Petter Selasky 		if (cif->cif_naddrs != 0)
199359854ecfSHans Petter Selasky 			break;
199408b68b0eSGleb Smirnoff 
199559854ecfSHans Petter Selasky 		while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
199659854ecfSHans Petter Selasky 			ip_mfilter_remove(&imo->imo_head, imf);
199759854ecfSHans Petter Selasky 			in_leavegroup(imf->imf_inm, NULL);
199859854ecfSHans Petter Selasky 			ip_mfilter_free(imf);
199908b68b0eSGleb Smirnoff 		}
200008b68b0eSGleb Smirnoff 		break;
2001a9771948SGleb Smirnoff #endif
200208b68b0eSGleb Smirnoff #ifdef INET6
200308b68b0eSGleb Smirnoff 	case AF_INET6:
200459854ecfSHans Petter Selasky 		if (cif->cif_naddrs6 != 0)
200559854ecfSHans Petter Selasky 			break;
200608b68b0eSGleb Smirnoff 
200759854ecfSHans Petter Selasky 		while ((im6f = ip6_mfilter_first(&im6o->im6o_head)) != NULL) {
200859854ecfSHans Petter Selasky 			ip6_mfilter_remove(&im6o->im6o_head, im6f);
200959854ecfSHans Petter Selasky 			in6_leavegroup(im6f->im6f_in6m, NULL);
201059854ecfSHans Petter Selasky 			ip6_mfilter_free(im6f);
201108b68b0eSGleb Smirnoff 		}
201208b68b0eSGleb Smirnoff 		break;
201308b68b0eSGleb Smirnoff #endif
201408b68b0eSGleb Smirnoff 	}
2015a9771948SGleb Smirnoff }
2016a9771948SGleb Smirnoff 
2017a9771948SGleb Smirnoff int
201847e8d432SGleb Smirnoff carp_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa)
2019a9771948SGleb Smirnoff {
2020a9771948SGleb Smirnoff 	struct m_tag *mtag;
2021a9771948SGleb Smirnoff 	struct carp_softc *sc;
2022a9771948SGleb Smirnoff 
2023a9771948SGleb Smirnoff 	if (!sa)
2024a9771948SGleb Smirnoff 		return (0);
2025a9771948SGleb Smirnoff 
2026a9771948SGleb Smirnoff 	switch (sa->sa_family) {
2027a9771948SGleb Smirnoff #ifdef INET
2028a9771948SGleb Smirnoff 	case AF_INET:
2029a9771948SGleb Smirnoff 		break;
203008b68b0eSGleb Smirnoff #endif
2031a9771948SGleb Smirnoff #ifdef INET6
2032a9771948SGleb Smirnoff 	case AF_INET6:
2033a9771948SGleb Smirnoff 		break;
203408b68b0eSGleb Smirnoff #endif
2035a9771948SGleb Smirnoff 	default:
2036a9771948SGleb Smirnoff 		return (0);
2037a9771948SGleb Smirnoff 	}
2038a9771948SGleb Smirnoff 
2039a9771948SGleb Smirnoff 	mtag = m_tag_find(m, PACKET_TAG_CARP, NULL);
2040a9771948SGleb Smirnoff 	if (mtag == NULL)
2041a9771948SGleb Smirnoff 		return (0);
2042a9771948SGleb Smirnoff 
2043eaf151c4SGleb Smirnoff 	bcopy(mtag + 1, &sc, sizeof(sc));
2044a9771948SGleb Smirnoff 
204513781800SKristof Provost 	switch (sa->sa_family) {
204613781800SKristof Provost 	case AF_INET:
2047ccff2078SKristof Provost 		if (! IN_MULTICAST(ntohl(sc->sc_carpaddr.s_addr)))
204813781800SKristof Provost 			return (0);
204913781800SKristof Provost 		break;
205013781800SKristof Provost 	case AF_INET6:
205113781800SKristof Provost 		if (! IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6))
205213781800SKristof Provost 			return (0);
205313781800SKristof Provost 		break;
205413781800SKristof Provost 	default:
205513781800SKristof Provost 		panic("Unknown af");
205613781800SKristof Provost 	}
205713781800SKristof Provost 
205808b68b0eSGleb Smirnoff 	/* Set the source MAC address to the Virtual Router MAC Address. */
2059a9771948SGleb Smirnoff 	switch (ifp->if_type) {
2060630481bbSYaroslav Tykhiy 	case IFT_ETHER:
20619ca1fe0dSGleb Smirnoff 	case IFT_BRIDGE:
2062630481bbSYaroslav Tykhiy 	case IFT_L2VLAN: {
2063a9771948SGleb Smirnoff 			struct ether_header *eh;
2064a9771948SGleb Smirnoff 
2065a9771948SGleb Smirnoff 			eh = mtod(m, struct ether_header *);
2066a9771948SGleb Smirnoff 			eh->ether_shost[0] = 0;
2067a9771948SGleb Smirnoff 			eh->ether_shost[1] = 0;
2068a9771948SGleb Smirnoff 			eh->ether_shost[2] = 0x5e;
2069a9771948SGleb Smirnoff 			eh->ether_shost[3] = 0;
2070a9771948SGleb Smirnoff 			eh->ether_shost[4] = 1;
2071a9771948SGleb Smirnoff 			eh->ether_shost[5] = sc->sc_vhid;
2072a9771948SGleb Smirnoff 		}
2073a9771948SGleb Smirnoff 		break;
2074a9771948SGleb Smirnoff 	default:
207508b68b0eSGleb Smirnoff 		printf("%s: carp is not supported for the %d interface type\n",
2076511a6d5eSKristof Provost 		    if_name(ifp), ifp->if_type);
2077a9771948SGleb Smirnoff 		return (EOPNOTSUPP);
2078a9771948SGleb Smirnoff 	}
2079a9771948SGleb Smirnoff 
2080a9771948SGleb Smirnoff 	return (0);
2081a9771948SGleb Smirnoff }
2082a9771948SGleb Smirnoff 
208308b68b0eSGleb Smirnoff static struct carp_softc*
208408b68b0eSGleb Smirnoff carp_alloc(struct ifnet *ifp)
2085a9771948SGleb Smirnoff {
208608b68b0eSGleb Smirnoff 	struct carp_softc *sc;
208708b68b0eSGleb Smirnoff 	struct carp_if *cif;
2088d220759bSGleb Smirnoff 
208981098a01SAlexander Motin 	sx_assert(&carp_sx, SA_XLOCKED);
209081098a01SAlexander Motin 
20910cc726f2SGleb Smirnoff 	if ((cif = ifp->if_carp) == NULL)
209208b68b0eSGleb Smirnoff 		cif = carp_alloc_if(ifp);
2093a9771948SGleb Smirnoff 
209408b68b0eSGleb Smirnoff 	sc = malloc(sizeof(*sc), M_CARP, M_WAITOK|M_ZERO);
209508b68b0eSGleb Smirnoff 
209637115154SKristof Provost 	sc->sc_version = CARP_VERSION_CARP;
209708b68b0eSGleb Smirnoff 	sc->sc_advbase = CARP_DFLTINTV;
209808b68b0eSGleb Smirnoff 	sc->sc_vhid = -1;	/* required setting */
209908b68b0eSGleb Smirnoff 	sc->sc_init_counter = 1;
210008b68b0eSGleb Smirnoff 	sc->sc_state = INIT;
210108b68b0eSGleb Smirnoff 
210208b68b0eSGleb Smirnoff 	sc->sc_ifasiz = sizeof(struct ifaddr *);
210308b68b0eSGleb Smirnoff 	sc->sc_ifas = malloc(sc->sc_ifasiz, M_CARP, M_WAITOK|M_ZERO);
210408b68b0eSGleb Smirnoff 	sc->sc_carpdev = ifp;
210508b68b0eSGleb Smirnoff 
210613781800SKristof Provost 	sc->sc_carpaddr.s_addr = htonl(INADDR_CARP_GROUP);
210713781800SKristof Provost 	sc->sc_carpaddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
210813781800SKristof Provost 	sc->sc_carpaddr6.s6_addr8[15] = 0x12;
210913781800SKristof Provost 
211037115154SKristof Provost 	sc->sc_vrrp_adv_inter = 100;
211137115154SKristof Provost 	sc->sc_vrrp_master_inter = sc->sc_vrrp_adv_inter;
211237115154SKristof Provost 	sc->sc_vrrp_prio = 100;
211337115154SKristof Provost 
211408b68b0eSGleb Smirnoff 	CARP_LOCK_INIT(sc);
211508b68b0eSGleb Smirnoff #ifdef INET
211608b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_md_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
211708b68b0eSGleb Smirnoff #endif
211808b68b0eSGleb Smirnoff #ifdef INET6
211908b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_md6_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
212008b68b0eSGleb Smirnoff #endif
212108b68b0eSGleb Smirnoff 	callout_init_mtx(&sc->sc_ad_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
212208b68b0eSGleb Smirnoff 
212308b68b0eSGleb Smirnoff 	CIF_LOCK(cif);
212408b68b0eSGleb Smirnoff 	TAILQ_INSERT_TAIL(&cif->cif_vrs, sc, sc_list);
212508b68b0eSGleb Smirnoff 	CIF_UNLOCK(cif);
212608b68b0eSGleb Smirnoff 
212708b68b0eSGleb Smirnoff 	mtx_lock(&carp_mtx);
212808b68b0eSGleb Smirnoff 	LIST_INSERT_HEAD(&carp_list, sc, sc_next);
212908b68b0eSGleb Smirnoff 	mtx_unlock(&carp_mtx);
213008b68b0eSGleb Smirnoff 
213108b68b0eSGleb Smirnoff 	return (sc);
213208b68b0eSGleb Smirnoff }
213308b68b0eSGleb Smirnoff 
21349c2cd1aaSGleb Smirnoff static void
213508b68b0eSGleb Smirnoff carp_grow_ifas(struct carp_softc *sc)
213608b68b0eSGleb Smirnoff {
213708b68b0eSGleb Smirnoff 	struct ifaddr **new;
213808b68b0eSGleb Smirnoff 
21399c2cd1aaSGleb Smirnoff 	new = malloc(sc->sc_ifasiz * 2, M_CARP, M_WAITOK | M_ZERO);
21409c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
214108b68b0eSGleb Smirnoff 	bcopy(sc->sc_ifas, new, sc->sc_ifasiz);
214208b68b0eSGleb Smirnoff 	free(sc->sc_ifas, M_CARP);
214308b68b0eSGleb Smirnoff 	sc->sc_ifas = new;
214408b68b0eSGleb Smirnoff 	sc->sc_ifasiz *= 2;
21459c2cd1aaSGleb Smirnoff 	CARP_UNLOCK(sc);
214608b68b0eSGleb Smirnoff }
214708b68b0eSGleb Smirnoff 
214808b68b0eSGleb Smirnoff static void
214908b68b0eSGleb Smirnoff carp_destroy(struct carp_softc *sc)
215008b68b0eSGleb Smirnoff {
215108b68b0eSGleb Smirnoff 	struct ifnet *ifp = sc->sc_carpdev;
215208b68b0eSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
215308b68b0eSGleb Smirnoff 
21549c2cd1aaSGleb Smirnoff 	sx_assert(&carp_sx, SA_XLOCKED);
2155a9a2c40cSGleb Smirnoff 
21569c2cd1aaSGleb Smirnoff 	if (sc->sc_suppress)
21579c2cd1aaSGleb Smirnoff 		carp_demote_adj(-V_carp_ifdown_adj, "vhid removed");
21589c2cd1aaSGleb Smirnoff 	CARP_UNLOCK(sc);
21599c2cd1aaSGleb Smirnoff 
21609c2cd1aaSGleb Smirnoff 	CIF_LOCK(cif);
216108b68b0eSGleb Smirnoff 	TAILQ_REMOVE(&cif->cif_vrs, sc, sc_list);
21629c2cd1aaSGleb Smirnoff 	CIF_UNLOCK(cif);
216308b68b0eSGleb Smirnoff 
216408b68b0eSGleb Smirnoff 	mtx_lock(&carp_mtx);
216508b68b0eSGleb Smirnoff 	LIST_REMOVE(sc, sc_next);
216608b68b0eSGleb Smirnoff 	mtx_unlock(&carp_mtx);
216708b68b0eSGleb Smirnoff 
216808b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_ad_tmo);
216908b68b0eSGleb Smirnoff #ifdef INET
217008b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_md_tmo);
217108b68b0eSGleb Smirnoff #endif
217208b68b0eSGleb Smirnoff #ifdef INET6
217308b68b0eSGleb Smirnoff 	callout_drain(&sc->sc_md6_tmo);
217408b68b0eSGleb Smirnoff #endif
217508b68b0eSGleb Smirnoff 	CARP_LOCK_DESTROY(sc);
217608b68b0eSGleb Smirnoff 
217708b68b0eSGleb Smirnoff 	free(sc->sc_ifas, M_CARP);
217808b68b0eSGleb Smirnoff 	free(sc, M_CARP);
217908b68b0eSGleb Smirnoff }
218008b68b0eSGleb Smirnoff 
218108b68b0eSGleb Smirnoff static struct carp_if*
218208b68b0eSGleb Smirnoff carp_alloc_if(struct ifnet *ifp)
2183a9771948SGleb Smirnoff {
218454bfbd51SWill Andrews 	struct carp_if *cif;
21850cc726f2SGleb Smirnoff 	int error;
2186d220759bSGleb Smirnoff 
218708b68b0eSGleb Smirnoff 	cif = malloc(sizeof(*cif), M_CARP, M_WAITOK|M_ZERO);
218808b68b0eSGleb Smirnoff 
21890cc726f2SGleb Smirnoff 	if ((error = ifpromisc(ifp, 1)) != 0)
21900cc726f2SGleb Smirnoff 		printf("%s: ifpromisc(%s) failed: %d\n",
2191511a6d5eSKristof Provost 		    __func__, if_name(ifp), error);
21920cc726f2SGleb Smirnoff 	else
21930cc726f2SGleb Smirnoff 		cif->cif_flags |= CIF_PROMISC;
219408b68b0eSGleb Smirnoff 
219508b68b0eSGleb Smirnoff 	CIF_LOCK_INIT(cif);
219608b68b0eSGleb Smirnoff 	cif->cif_ifp = ifp;
219708b68b0eSGleb Smirnoff 	TAILQ_INIT(&cif->cif_vrs);
219808b68b0eSGleb Smirnoff 
2199137f91e8SJohn Baldwin 	IF_ADDR_WLOCK(ifp);
220008b68b0eSGleb Smirnoff 	ifp->if_carp = cif;
220108b68b0eSGleb Smirnoff 	if_ref(ifp);
2202137f91e8SJohn Baldwin 	IF_ADDR_WUNLOCK(ifp);
220308b68b0eSGleb Smirnoff 
220408b68b0eSGleb Smirnoff 	return (cif);
2205d220759bSGleb Smirnoff }
2206d220759bSGleb Smirnoff 
2207d220759bSGleb Smirnoff static void
220808b68b0eSGleb Smirnoff carp_free_if(struct carp_if *cif)
220908b68b0eSGleb Smirnoff {
221008b68b0eSGleb Smirnoff 	struct ifnet *ifp = cif->cif_ifp;
221108b68b0eSGleb Smirnoff 
221208b68b0eSGleb Smirnoff 	CIF_LOCK_ASSERT(cif);
221308b68b0eSGleb Smirnoff 	KASSERT(TAILQ_EMPTY(&cif->cif_vrs), ("%s: softc list not empty",
221408b68b0eSGleb Smirnoff 	    __func__));
221508b68b0eSGleb Smirnoff 
2216137f91e8SJohn Baldwin 	IF_ADDR_WLOCK(ifp);
221708b68b0eSGleb Smirnoff 	ifp->if_carp = NULL;
2218137f91e8SJohn Baldwin 	IF_ADDR_WUNLOCK(ifp);
221908b68b0eSGleb Smirnoff 
222008b68b0eSGleb Smirnoff 	CIF_LOCK_DESTROY(cif);
222108b68b0eSGleb Smirnoff 
22220cc726f2SGleb Smirnoff 	if (cif->cif_flags & CIF_PROMISC)
222308b68b0eSGleb Smirnoff 		ifpromisc(ifp, 0);
22241f6addd9SMikolaj Golub 	if_rele(ifp);
222508b68b0eSGleb Smirnoff 
222608b68b0eSGleb Smirnoff 	free(cif, M_CARP);
222708b68b0eSGleb Smirnoff }
222808b68b0eSGleb Smirnoff 
222940e04359SKristof Provost static bool
223040e04359SKristof Provost carp_carprcp(void *arg, struct carp_softc *sc, int priv)
223108b68b0eSGleb Smirnoff {
223240e04359SKristof Provost 	struct carpreq *carpr = arg;
223308b68b0eSGleb Smirnoff 
223408b68b0eSGleb Smirnoff 	CARP_LOCK(sc);
223508b68b0eSGleb Smirnoff 	carpr->carpr_state = sc->sc_state;
223608b68b0eSGleb Smirnoff 	carpr->carpr_vhid = sc->sc_vhid;
223708b68b0eSGleb Smirnoff 	carpr->carpr_advbase = sc->sc_advbase;
223808b68b0eSGleb Smirnoff 	carpr->carpr_advskew = sc->sc_advskew;
223908b68b0eSGleb Smirnoff 	if (priv)
224008b68b0eSGleb Smirnoff 		bcopy(sc->sc_key, carpr->carpr_key, sizeof(carpr->carpr_key));
224108b68b0eSGleb Smirnoff 	else
224208b68b0eSGleb Smirnoff 		bzero(carpr->carpr_key, sizeof(carpr->carpr_key));
224308b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
224440e04359SKristof Provost 
224540e04359SKristof Provost 	return (true);
224608b68b0eSGleb Smirnoff }
224708b68b0eSGleb Smirnoff 
224840e04359SKristof Provost static int
224913781800SKristof Provost carp_ioctl_set(if_t ifp, struct carpkreq *carpr)
225008b68b0eSGleb Smirnoff {
225149cad3daSZhenlei Huang 	struct epoch_tracker et;
225208b68b0eSGleb Smirnoff 	struct carp_softc *sc = NULL;
225340e04359SKristof Provost 	int error = 0;
225408b68b0eSGleb Smirnoff 
225540e04359SKristof Provost 	if (carpr->carpr_vhid <= 0 || carpr->carpr_vhid > CARP_MAXVHID ||
225637115154SKristof Provost 	    carpr->carpr_advbase < 0 || carpr->carpr_advskew < 0 ||
225737115154SKristof Provost 	    (carpr->carpr_version != CARP_VERSION_CARP &&
225837115154SKristof Provost 	    carpr->carpr_version != CARP_VERSION_VRRPv3)) {
225940e04359SKristof Provost 		return (EINVAL);
226008b68b0eSGleb Smirnoff 	}
226108b68b0eSGleb Smirnoff 
226208b68b0eSGleb Smirnoff 	if (ifp->if_carp) {
226308b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
226440e04359SKristof Provost 			if (sc->sc_vhid == carpr->carpr_vhid)
226508b68b0eSGleb Smirnoff 				break;
226608b68b0eSGleb Smirnoff 	}
226708b68b0eSGleb Smirnoff 	if (sc == NULL) {
226808b68b0eSGleb Smirnoff 		sc = carp_alloc(ifp);
226908b68b0eSGleb Smirnoff 		CARP_LOCK(sc);
227040e04359SKristof Provost 		sc->sc_vhid = carpr->carpr_vhid;
227108b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[0] = 0;
227208b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[1] = 0;
227308b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[2] = 0x5e;
227408b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[3] = 0;
227508b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[4] = 1;
227608b68b0eSGleb Smirnoff 		LLADDR(&sc->sc_addr)[5] = sc->sc_vhid;
227708b68b0eSGleb Smirnoff 	} else
227808b68b0eSGleb Smirnoff 		CARP_LOCK(sc);
227937115154SKristof Provost 	sc->sc_version = carpr->carpr_version;
228040e04359SKristof Provost 	if (carpr->carpr_advbase > 0) {
228140e04359SKristof Provost 		if (carpr->carpr_advbase > 255 ||
228240e04359SKristof Provost 		    carpr->carpr_advbase < CARP_DFLTINTV) {
228308b68b0eSGleb Smirnoff 			error = EINVAL;
228440e04359SKristof Provost 			goto out;
228508b68b0eSGleb Smirnoff 		}
228640e04359SKristof Provost 		sc->sc_advbase = carpr->carpr_advbase;
228708b68b0eSGleb Smirnoff 	}
228840e04359SKristof Provost 	if (carpr->carpr_advskew >= 255) {
228908b68b0eSGleb Smirnoff 		error = EINVAL;
229040e04359SKristof Provost 		goto out;
229108b68b0eSGleb Smirnoff 	}
229240e04359SKristof Provost 	sc->sc_advskew = carpr->carpr_advskew;
229313781800SKristof Provost 	if (carpr->carpr_addr.s_addr != INADDR_ANY)
229413781800SKristof Provost 		sc->sc_carpaddr = carpr->carpr_addr;
229513781800SKristof Provost 	if (! IN6_IS_ADDR_UNSPECIFIED(&carpr->carpr_addr6)) {
229613781800SKristof Provost 		memcpy(&sc->sc_carpaddr6, &carpr->carpr_addr6,
229713781800SKristof Provost 		    sizeof(sc->sc_carpaddr6));
229813781800SKristof Provost 	}
229940e04359SKristof Provost 	if (carpr->carpr_key[0] != '\0') {
230040e04359SKristof Provost 		bcopy(carpr->carpr_key, sc->sc_key, sizeof(sc->sc_key));
230108b68b0eSGleb Smirnoff 		carp_hmac_prepare(sc);
230208b68b0eSGleb Smirnoff 	}
230337115154SKristof Provost 	if (carpr->carpr_vrrp_priority != 0)
230437115154SKristof Provost 		sc->sc_vrrp_prio = carpr->carpr_vrrp_priority;
230537115154SKristof Provost 	if (carpr->carpr_vrrp_adv_inter)
230637115154SKristof Provost 		sc->sc_vrrp_adv_inter = carpr->carpr_vrrp_adv_inter;
230737115154SKristof Provost 
230808b68b0eSGleb Smirnoff 	if (sc->sc_state != INIT &&
230940e04359SKristof Provost 	    carpr->carpr_state != sc->sc_state) {
231040e04359SKristof Provost 		switch (carpr->carpr_state) {
231108b68b0eSGleb Smirnoff 		case BACKUP:
231208b68b0eSGleb Smirnoff 			callout_stop(&sc->sc_ad_tmo);
2313369a6708SWill Andrews 			carp_set_state(sc, BACKUP,
2314369a6708SWill Andrews 			    "user requested via ifconfig");
231508b68b0eSGleb Smirnoff 			carp_setrun(sc, 0);
231608b68b0eSGleb Smirnoff 			carp_delroute(sc);
231708b68b0eSGleb Smirnoff 			break;
231808b68b0eSGleb Smirnoff 		case MASTER:
231949cad3daSZhenlei Huang 			NET_EPOCH_ENTER(et);
2320369a6708SWill Andrews 			carp_master_down_locked(sc,
2321369a6708SWill Andrews 			    "user requested via ifconfig");
232249cad3daSZhenlei Huang 			NET_EPOCH_EXIT(et);
232308b68b0eSGleb Smirnoff 			break;
232408b68b0eSGleb Smirnoff 		default:
232508b68b0eSGleb Smirnoff 			break;
232608b68b0eSGleb Smirnoff 		}
232708b68b0eSGleb Smirnoff 	}
232808b68b0eSGleb Smirnoff 
232940e04359SKristof Provost out:
233040e04359SKristof Provost 	CARP_UNLOCK(sc);
233140e04359SKristof Provost 
233240e04359SKristof Provost 	return (error);
233340e04359SKristof Provost }
233440e04359SKristof Provost 
233540e04359SKristof Provost static int
233640e04359SKristof Provost carp_ioctl_get(if_t ifp, struct ucred *cred, struct carpreq *carpr,
233740e04359SKristof Provost     bool (*outfn)(void *, struct carp_softc *, int), void *arg)
233808b68b0eSGleb Smirnoff {
233908b68b0eSGleb Smirnoff 	int priveleged;
234040e04359SKristof Provost 	struct carp_softc *sc;
234108b68b0eSGleb Smirnoff 
234240e04359SKristof Provost 	if (carpr->carpr_vhid < 0 || carpr->carpr_vhid > CARP_MAXVHID)
234340e04359SKristof Provost 		return (EINVAL);
234440e04359SKristof Provost 	if (carpr->carpr_count < 1)
234540e04359SKristof Provost 		return (EMSGSIZE);
234640e04359SKristof Provost 	if (ifp->if_carp == NULL)
234740e04359SKristof Provost 		return (ENOENT);
234808b68b0eSGleb Smirnoff 
234940e04359SKristof Provost 	priveleged = (priv_check_cred(cred, PRIV_NETINET_CARP) == 0);
235040e04359SKristof Provost 	if (carpr->carpr_vhid != 0) {
235108b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
235240e04359SKristof Provost 			if (sc->sc_vhid == carpr->carpr_vhid)
235308b68b0eSGleb Smirnoff 				break;
235440e04359SKristof Provost 		if (sc == NULL)
235540e04359SKristof Provost 			return (ENOENT);
235640e04359SKristof Provost 
235740e04359SKristof Provost 		if (! outfn(arg, sc, priveleged))
235840e04359SKristof Provost 			return (ENOMEM);
235940e04359SKristof Provost 		carpr->carpr_count = 1;
236008b68b0eSGleb Smirnoff 	} else  {
236140e04359SKristof Provost 		int count;
236208b68b0eSGleb Smirnoff 
236308b68b0eSGleb Smirnoff 		count = 0;
236408b68b0eSGleb Smirnoff 		IFNET_FOREACH_CARP(ifp, sc)
236508b68b0eSGleb Smirnoff 			count++;
236608b68b0eSGleb Smirnoff 
236740e04359SKristof Provost 		if (count > carpr->carpr_count)
236840e04359SKristof Provost 			return (EMSGSIZE);
236940e04359SKristof Provost 
237040e04359SKristof Provost 		IFNET_FOREACH_CARP(ifp, sc) {
237140e04359SKristof Provost 			if (! outfn(arg, sc, priveleged))
237240e04359SKristof Provost 				return (ENOMEM);
237340e04359SKristof Provost 			carpr->carpr_count = count;
237440e04359SKristof Provost 		}
237508b68b0eSGleb Smirnoff 	}
237608b68b0eSGleb Smirnoff 
237740e04359SKristof Provost 	return (0);
237840e04359SKristof Provost }
237940e04359SKristof Provost 
238040e04359SKristof Provost int
238140e04359SKristof Provost carp_ioctl(struct ifreq *ifr, u_long cmd, struct thread *td)
238240e04359SKristof Provost {
238340e04359SKristof Provost 	struct carpreq carpr;
238413781800SKristof Provost 	struct carpkreq carprk = { };
238540e04359SKristof Provost 	struct ifnet *ifp;
238640e04359SKristof Provost 	int error = 0;
238740e04359SKristof Provost 
238840e04359SKristof Provost 	if ((error = copyin(ifr_data_get_ptr(ifr), &carpr, sizeof carpr)))
238940e04359SKristof Provost 		return (error);
239040e04359SKristof Provost 
239140e04359SKristof Provost 	ifp = ifunit_ref(ifr->ifr_name);
239240e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
239340e04359SKristof Provost 		goto out;
239440e04359SKristof Provost 
239540e04359SKristof Provost 	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
239640e04359SKristof Provost 		error = EADDRNOTAVAIL;
239740e04359SKristof Provost 		goto out;
239840e04359SKristof Provost 	}
239940e04359SKristof Provost 
240040e04359SKristof Provost 	sx_xlock(&carp_sx);
240140e04359SKristof Provost 	switch (cmd) {
240240e04359SKristof Provost 	case SIOCSVH:
240340e04359SKristof Provost 		if ((error = priv_check(td, PRIV_NETINET_CARP)))
240440e04359SKristof Provost 			break;
240540e04359SKristof Provost 
240613781800SKristof Provost 		memcpy(&carprk, &carpr, sizeof(carpr));
240713781800SKristof Provost 		error = carp_ioctl_set(ifp, &carprk);
240840e04359SKristof Provost 		break;
240940e04359SKristof Provost 
241040e04359SKristof Provost 	case SIOCGVH:
241140e04359SKristof Provost 		error = carp_ioctl_get(ifp, td->td_ucred, &carpr,
241240e04359SKristof Provost 		    carp_carprcp, &carpr);
241340e04359SKristof Provost 		if (error == 0) {
2414541d96aaSBrooks Davis 			error = copyout(&carpr,
241540e04359SKristof Provost 			    (char *)ifr_data_get_ptr(ifr),
241640e04359SKristof Provost 			    carpr.carpr_count * sizeof(carpr));
241708b68b0eSGleb Smirnoff 		}
241808b68b0eSGleb Smirnoff 		break;
241908b68b0eSGleb Smirnoff 	default:
242008b68b0eSGleb Smirnoff 		error = EINVAL;
242108b68b0eSGleb Smirnoff 	}
242293d4534cSGleb Smirnoff 	sx_xunlock(&carp_sx);
242308b68b0eSGleb Smirnoff 
242408b68b0eSGleb Smirnoff out:
242540e04359SKristof Provost 	if (ifp != NULL)
242608b68b0eSGleb Smirnoff 		if_rele(ifp);
242708b68b0eSGleb Smirnoff 
242808b68b0eSGleb Smirnoff 	return (error);
242908b68b0eSGleb Smirnoff }
243008b68b0eSGleb Smirnoff 
243108b68b0eSGleb Smirnoff static int
243208b68b0eSGleb Smirnoff carp_get_vhid(struct ifaddr *ifa)
243308b68b0eSGleb Smirnoff {
243408b68b0eSGleb Smirnoff 
243508b68b0eSGleb Smirnoff 	if (ifa == NULL || ifa->ifa_carp == NULL)
243608b68b0eSGleb Smirnoff 		return (0);
243708b68b0eSGleb Smirnoff 
243808b68b0eSGleb Smirnoff 	return (ifa->ifa_carp->sc_vhid);
243908b68b0eSGleb Smirnoff }
244008b68b0eSGleb Smirnoff 
244108b68b0eSGleb Smirnoff int
244208b68b0eSGleb Smirnoff carp_attach(struct ifaddr *ifa, int vhid)
244308b68b0eSGleb Smirnoff {
244408b68b0eSGleb Smirnoff 	struct ifnet *ifp = ifa->ifa_ifp;
2445a9a2c40cSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
244608b68b0eSGleb Smirnoff 	struct carp_softc *sc;
244708b68b0eSGleb Smirnoff 	int index, error;
244808b68b0eSGleb Smirnoff 
24499c2cd1aaSGleb Smirnoff 	KASSERT(ifa->ifa_carp == NULL, ("%s: ifa %p attached", __func__, ifa));
245008b68b0eSGleb Smirnoff 
245108b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
245208b68b0eSGleb Smirnoff #ifdef INET
245308b68b0eSGleb Smirnoff 	case AF_INET:
245408b68b0eSGleb Smirnoff #endif
245508b68b0eSGleb Smirnoff #ifdef INET6
245608b68b0eSGleb Smirnoff 	case AF_INET6:
245708b68b0eSGleb Smirnoff #endif
245808b68b0eSGleb Smirnoff 		break;
245908b68b0eSGleb Smirnoff 	default:
246008b68b0eSGleb Smirnoff 		return (EPROTOTYPE);
246108b68b0eSGleb Smirnoff 	}
246208b68b0eSGleb Smirnoff 
24639c2cd1aaSGleb Smirnoff 	sx_xlock(&carp_sx);
24649c2cd1aaSGleb Smirnoff 	if (ifp->if_carp == NULL) {
24659c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
24669c2cd1aaSGleb Smirnoff 		return (ENOPROTOOPT);
24679c2cd1aaSGleb Smirnoff 	}
24689c2cd1aaSGleb Smirnoff 
246908b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc)
247008b68b0eSGleb Smirnoff 		if (sc->sc_vhid == vhid)
247108b68b0eSGleb Smirnoff 			break;
2472a9a2c40cSGleb Smirnoff 	if (sc == NULL) {
24739c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
247408b68b0eSGleb Smirnoff 		return (ENOENT);
2475a9a2c40cSGleb Smirnoff 	}
247608b68b0eSGleb Smirnoff 
2477a9a2c40cSGleb Smirnoff 	error = carp_multicast_setup(cif, ifa->ifa_addr->sa_family);
2478a9a2c40cSGleb Smirnoff 	if (error) {
2479a9a2c40cSGleb Smirnoff 		CIF_FREE(cif);
24809c2cd1aaSGleb Smirnoff 		sx_xunlock(&carp_sx);
248108b68b0eSGleb Smirnoff 		return (error);
2482a9a2c40cSGleb Smirnoff 	}
248308b68b0eSGleb Smirnoff 
248408b68b0eSGleb Smirnoff 	index = sc->sc_naddrs + sc->sc_naddrs6 + 1;
248508b68b0eSGleb Smirnoff 	if (index > sc->sc_ifasiz / sizeof(struct ifaddr *))
24869c2cd1aaSGleb Smirnoff 		carp_grow_ifas(sc);
248708b68b0eSGleb Smirnoff 
248808b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
248908b68b0eSGleb Smirnoff #ifdef INET
249008b68b0eSGleb Smirnoff 	case AF_INET:
2491a9a2c40cSGleb Smirnoff 		cif->cif_naddrs++;
249208b68b0eSGleb Smirnoff 		sc->sc_naddrs++;
249308b68b0eSGleb Smirnoff 		break;
249408b68b0eSGleb Smirnoff #endif
249508b68b0eSGleb Smirnoff #ifdef INET6
249608b68b0eSGleb Smirnoff 	case AF_INET6:
2497a9a2c40cSGleb Smirnoff 		cif->cif_naddrs6++;
249808b68b0eSGleb Smirnoff 		sc->sc_naddrs6++;
249908b68b0eSGleb Smirnoff 		break;
250008b68b0eSGleb Smirnoff #endif
250108b68b0eSGleb Smirnoff 	}
250208b68b0eSGleb Smirnoff 
250308b68b0eSGleb Smirnoff 	ifa_ref(ifa);
25049c2cd1aaSGleb Smirnoff 
25059c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
250608b68b0eSGleb Smirnoff 	sc->sc_ifas[index - 1] = ifa;
250708b68b0eSGleb Smirnoff 	ifa->ifa_carp = sc;
250808b68b0eSGleb Smirnoff 	carp_hmac_prepare(sc);
250908b68b0eSGleb Smirnoff 	carp_sc_state(sc);
251008b68b0eSGleb Smirnoff 	CARP_UNLOCK(sc);
25119c2cd1aaSGleb Smirnoff 
25129c2cd1aaSGleb Smirnoff 	sx_xunlock(&carp_sx);
251308b68b0eSGleb Smirnoff 
251408b68b0eSGleb Smirnoff 	return (0);
251508b68b0eSGleb Smirnoff }
251608b68b0eSGleb Smirnoff 
251708b68b0eSGleb Smirnoff void
2518338e227aSLuiz Otavio O Souza carp_detach(struct ifaddr *ifa, bool keep_cif)
251908b68b0eSGleb Smirnoff {
2520a9a2c40cSGleb Smirnoff 	struct ifnet *ifp = ifa->ifa_ifp;
2521a9a2c40cSGleb Smirnoff 	struct carp_if *cif = ifp->if_carp;
252208b68b0eSGleb Smirnoff 	struct carp_softc *sc = ifa->ifa_carp;
252308b68b0eSGleb Smirnoff 	int i, index;
252408b68b0eSGleb Smirnoff 
252508b68b0eSGleb Smirnoff 	KASSERT(sc != NULL, ("%s: %p not attached", __func__, ifa));
252608b68b0eSGleb Smirnoff 
25279c2cd1aaSGleb Smirnoff 	sx_xlock(&carp_sx);
252808b68b0eSGleb Smirnoff 
25299c2cd1aaSGleb Smirnoff 	CARP_LOCK(sc);
253008b68b0eSGleb Smirnoff 	/* Shift array. */
253108b68b0eSGleb Smirnoff 	index = sc->sc_naddrs + sc->sc_naddrs6;
253208b68b0eSGleb Smirnoff 	for (i = 0; i < index; i++)
253308b68b0eSGleb Smirnoff 		if (sc->sc_ifas[i] == ifa)
253408b68b0eSGleb Smirnoff 			break;
253508b68b0eSGleb Smirnoff 	KASSERT(i < index, ("%s: %p no backref", __func__, ifa));
253608b68b0eSGleb Smirnoff 	for (; i < index - 1; i++)
253708b68b0eSGleb Smirnoff 		sc->sc_ifas[i] = sc->sc_ifas[i+1];
253808b68b0eSGleb Smirnoff 	sc->sc_ifas[index - 1] = NULL;
253908b68b0eSGleb Smirnoff 
254008b68b0eSGleb Smirnoff 	switch (ifa->ifa_addr->sa_family) {
254108b68b0eSGleb Smirnoff #ifdef INET
254208b68b0eSGleb Smirnoff 	case AF_INET:
2543a9a2c40cSGleb Smirnoff 		cif->cif_naddrs--;
254408b68b0eSGleb Smirnoff 		sc->sc_naddrs--;
254508b68b0eSGleb Smirnoff 		break;
254608b68b0eSGleb Smirnoff #endif
254708b68b0eSGleb Smirnoff #ifdef INET6
254808b68b0eSGleb Smirnoff 	case AF_INET6:
2549a9a2c40cSGleb Smirnoff 		cif->cif_naddrs6--;
255008b68b0eSGleb Smirnoff 		sc->sc_naddrs6--;
255108b68b0eSGleb Smirnoff 		break;
255208b68b0eSGleb Smirnoff #endif
255308b68b0eSGleb Smirnoff 	}
255408b68b0eSGleb Smirnoff 
25552512b096SGleb Smirnoff 	carp_ifa_delroute(ifa);
2556a9a2c40cSGleb Smirnoff 	carp_multicast_cleanup(cif, ifa->ifa_addr->sa_family);
255708b68b0eSGleb Smirnoff 
255808b68b0eSGleb Smirnoff 	ifa->ifa_carp = NULL;
255908b68b0eSGleb Smirnoff 	ifa_free(ifa);
256008b68b0eSGleb Smirnoff 
256108b68b0eSGleb Smirnoff 	carp_hmac_prepare(sc);
256208b68b0eSGleb Smirnoff 	carp_sc_state(sc);
256308b68b0eSGleb Smirnoff 
2564338e227aSLuiz Otavio O Souza 	if (!keep_cif && sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0)
256508b68b0eSGleb Smirnoff 		carp_destroy(sc);
25669c2cd1aaSGleb Smirnoff 	else
256708b68b0eSGleb Smirnoff 		CARP_UNLOCK(sc);
25689c2cd1aaSGleb Smirnoff 
2569338e227aSLuiz Otavio O Souza 	if (!keep_cif)
25709c2cd1aaSGleb Smirnoff 		CIF_FREE(cif);
25719c2cd1aaSGleb Smirnoff 
25729c2cd1aaSGleb Smirnoff 	sx_xunlock(&carp_sx);
257308b68b0eSGleb Smirnoff }
257408b68b0eSGleb Smirnoff 
257508b68b0eSGleb Smirnoff static void
2576d01641e2SWill Andrews carp_set_state(struct carp_softc *sc, int state, const char *reason)
257708b68b0eSGleb Smirnoff {
257808b68b0eSGleb Smirnoff 
257908b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
258008b68b0eSGleb Smirnoff 
258108b68b0eSGleb Smirnoff 	if (sc->sc_state != state) {
258208b68b0eSGleb Smirnoff 		const char *carp_states[] = { CARP_STATES };
258308b68b0eSGleb Smirnoff 		char subsys[IFNAMSIZ+5];
258408b68b0eSGleb Smirnoff 
258508b68b0eSGleb Smirnoff 		snprintf(subsys, IFNAMSIZ+5, "%u@%s", sc->sc_vhid,
2586511a6d5eSKristof Provost 		    if_name(sc->sc_carpdev));
2587d01641e2SWill Andrews 
2588d01641e2SWill Andrews 		CARP_LOG("%s: %s -> %s (%s)\n", subsys,
2589d01641e2SWill Andrews 		    carp_states[sc->sc_state], carp_states[state], reason);
2590d01641e2SWill Andrews 
2591d01641e2SWill Andrews 		sc->sc_state = state;
2592d01641e2SWill Andrews 
259308b68b0eSGleb Smirnoff 		devctl_notify("CARP", subsys, carp_states[state], NULL);
259408b68b0eSGleb Smirnoff 	}
259508b68b0eSGleb Smirnoff }
259608b68b0eSGleb Smirnoff 
259708b68b0eSGleb Smirnoff static void
259808b68b0eSGleb Smirnoff carp_linkstate(struct ifnet *ifp)
2599d220759bSGleb Smirnoff {
2600d220759bSGleb Smirnoff 	struct carp_softc *sc;
2601d220759bSGleb Smirnoff 
260208b68b0eSGleb Smirnoff 	CIF_LOCK(ifp->if_carp);
260308b68b0eSGleb Smirnoff 	IFNET_FOREACH_CARP(ifp, sc) {
260408b68b0eSGleb Smirnoff 		CARP_LOCK(sc);
260508b68b0eSGleb Smirnoff 		carp_sc_state(sc);
260608b68b0eSGleb Smirnoff 		CARP_UNLOCK(sc);
260708b68b0eSGleb Smirnoff 	}
260808b68b0eSGleb Smirnoff 	CIF_UNLOCK(ifp->if_carp);
26094cb39345SGleb Smirnoff }
26104cb39345SGleb Smirnoff 
26114cb39345SGleb Smirnoff static void
261208b68b0eSGleb Smirnoff carp_sc_state(struct carp_softc *sc)
26134cb39345SGleb Smirnoff {
261408b68b0eSGleb Smirnoff 
261508b68b0eSGleb Smirnoff 	CARP_LOCK_ASSERT(sc);
26164cb39345SGleb Smirnoff 
2617e8c34a71SGleb Smirnoff 	if (sc->sc_carpdev->if_link_state != LINK_STATE_UP ||
2618167a3440SAlexander Motin 	    !(sc->sc_carpdev->if_flags & IFF_UP) ||
2619167a3440SAlexander Motin 	    !V_carp_allow) {
2620a9771948SGleb Smirnoff 		callout_stop(&sc->sc_ad_tmo);
262108b68b0eSGleb Smirnoff #ifdef INET
2622a9771948SGleb Smirnoff 		callout_stop(&sc->sc_md_tmo);
262308b68b0eSGleb Smirnoff #endif
262408b68b0eSGleb Smirnoff #ifdef INET6
2625a9771948SGleb Smirnoff 		callout_stop(&sc->sc_md6_tmo);
262608b68b0eSGleb Smirnoff #endif
2627bb269f3aSWill Andrews 		carp_set_state(sc, INIT, "hardware interface down");
2628a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
2629600bf006SAndrey V. Elsukov 		carp_delroute(sc);
2630f08535f8SGleb Smirnoff 		if (!sc->sc_suppress)
2631c5c392e7SMikolaj Golub 			carp_demote_adj(V_carp_ifdown_adj, "interface down");
2632a9771948SGleb Smirnoff 		sc->sc_suppress = 1;
2633a9771948SGleb Smirnoff 	} else {
2634bb269f3aSWill Andrews 		carp_set_state(sc, INIT, "hardware interface up");
2635a9771948SGleb Smirnoff 		carp_setrun(sc, 0);
2636a9771948SGleb Smirnoff 		if (sc->sc_suppress)
2637c5c392e7SMikolaj Golub 			carp_demote_adj(-V_carp_ifdown_adj, "interface up");
2638a9771948SGleb Smirnoff 		sc->sc_suppress = 0;
2639a9771948SGleb Smirnoff 	}
2640a9771948SGleb Smirnoff }
2641a9771948SGleb Smirnoff 
2642f08535f8SGleb Smirnoff static void
2643f08535f8SGleb Smirnoff carp_demote_adj(int adj, char *reason)
2644f08535f8SGleb Smirnoff {
2645c5c392e7SMikolaj Golub 	atomic_add_int(&V_carp_demotion, adj);
2646c5c392e7SMikolaj Golub 	CARP_LOG("demoted by %d to %d (%s)\n", adj, V_carp_demotion, reason);
2647f08535f8SGleb Smirnoff 	taskqueue_enqueue(taskqueue_swi, &carp_sendall_task);
2648f08535f8SGleb Smirnoff }
264908b68b0eSGleb Smirnoff 
26507951008bSGleb Smirnoff static int
2651167a3440SAlexander Motin carp_allow_sysctl(SYSCTL_HANDLER_ARGS)
2652167a3440SAlexander Motin {
2653167a3440SAlexander Motin 	int new, error;
2654167a3440SAlexander Motin 	struct carp_softc *sc;
2655167a3440SAlexander Motin 
2656167a3440SAlexander Motin 	new = V_carp_allow;
2657167a3440SAlexander Motin 	error = sysctl_handle_int(oidp, &new, 0, req);
2658167a3440SAlexander Motin 	if (error || !req->newptr)
2659167a3440SAlexander Motin 		return (error);
2660167a3440SAlexander Motin 
2661167a3440SAlexander Motin 	if (V_carp_allow != new) {
2662167a3440SAlexander Motin 		V_carp_allow = new;
2663167a3440SAlexander Motin 
2664167a3440SAlexander Motin 		mtx_lock(&carp_mtx);
2665167a3440SAlexander Motin 		LIST_FOREACH(sc, &carp_list, sc_next) {
2666167a3440SAlexander Motin 			CARP_LOCK(sc);
2667167a3440SAlexander Motin 			if (curvnet == sc->sc_carpdev->if_vnet)
2668167a3440SAlexander Motin 				carp_sc_state(sc);
2669167a3440SAlexander Motin 			CARP_UNLOCK(sc);
2670167a3440SAlexander Motin 		}
2671167a3440SAlexander Motin 		mtx_unlock(&carp_mtx);
2672167a3440SAlexander Motin 	}
2673167a3440SAlexander Motin 
2674167a3440SAlexander Motin 	return (0);
2675167a3440SAlexander Motin }
2676167a3440SAlexander Motin 
2677167a3440SAlexander Motin static int
26780d3d234cSKristof Provost carp_dscp_sysctl(SYSCTL_HANDLER_ARGS)
26790d3d234cSKristof Provost {
26800d3d234cSKristof Provost 	int new, error;
26810d3d234cSKristof Provost 
26820d3d234cSKristof Provost 	new = V_carp_dscp;
26830d3d234cSKristof Provost 	error = sysctl_handle_int(oidp, &new, 0, req);
26840d3d234cSKristof Provost 	if (error || !req->newptr)
26850d3d234cSKristof Provost 		return (error);
26860d3d234cSKristof Provost 
26870d3d234cSKristof Provost 	if (new < 0 || new > 63)
26880d3d234cSKristof Provost 		return (EINVAL);
26890d3d234cSKristof Provost 
26900d3d234cSKristof Provost 	V_carp_dscp = new;
26910d3d234cSKristof Provost 
26920d3d234cSKristof Provost 	return (0);
26930d3d234cSKristof Provost }
26940d3d234cSKristof Provost 
26950d3d234cSKristof Provost static int
26967951008bSGleb Smirnoff carp_demote_adj_sysctl(SYSCTL_HANDLER_ARGS)
26977951008bSGleb Smirnoff {
26987951008bSGleb Smirnoff 	int new, error;
26997951008bSGleb Smirnoff 
2700c5c392e7SMikolaj Golub 	new = V_carp_demotion;
27017951008bSGleb Smirnoff 	error = sysctl_handle_int(oidp, &new, 0, req);
27027951008bSGleb Smirnoff 	if (error || !req->newptr)
27037951008bSGleb Smirnoff 		return (error);
27047951008bSGleb Smirnoff 
27057951008bSGleb Smirnoff 	carp_demote_adj(new, "sysctl");
27067951008bSGleb Smirnoff 
27077951008bSGleb Smirnoff 	return (0);
27087951008bSGleb Smirnoff }
27097951008bSGleb Smirnoff 
271040e04359SKristof Provost static int
271140e04359SKristof Provost nlattr_get_carp_key(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
271240e04359SKristof Provost {
271340e04359SKristof Provost 	if (__predict_false(NLA_DATA_LEN(nla) > CARP_KEY_LEN))
271440e04359SKristof Provost 		return (EINVAL);
271540e04359SKristof Provost 
271640e04359SKristof Provost 	memcpy(target, NLA_DATA_CONST(nla), NLA_DATA_LEN(nla));
271740e04359SKristof Provost 	return (0);
271840e04359SKristof Provost }
271940e04359SKristof Provost 
272040e04359SKristof Provost struct carp_nl_send_args {
272140e04359SKristof Provost 	struct nlmsghdr *hdr;
272240e04359SKristof Provost 	struct nl_pstate *npt;
272340e04359SKristof Provost };
272440e04359SKristof Provost 
272540e04359SKristof Provost static bool
272640e04359SKristof Provost carp_nl_send(void *arg, struct carp_softc *sc, int priv)
272740e04359SKristof Provost {
272840e04359SKristof Provost 	struct carp_nl_send_args *nlsa = arg;
272940e04359SKristof Provost 	struct nlmsghdr *hdr = nlsa->hdr;
273040e04359SKristof Provost 	struct nl_pstate *npt = nlsa->npt;
273140e04359SKristof Provost 	struct nl_writer *nw = npt->nw;
273240e04359SKristof Provost 	struct genlmsghdr *ghdr_new;
273340e04359SKristof Provost 
273440e04359SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
273540e04359SKristof Provost 		nlmsg_abort(nw);
273640e04359SKristof Provost 		return (false);
273740e04359SKristof Provost 	}
273840e04359SKristof Provost 
273940e04359SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
274040e04359SKristof Provost 	if (ghdr_new == NULL) {
274140e04359SKristof Provost 		nlmsg_abort(nw);
274240e04359SKristof Provost 		return (false);
274340e04359SKristof Provost 	}
274440e04359SKristof Provost 
274540e04359SKristof Provost 	ghdr_new->cmd = CARP_NL_CMD_GET;
274640e04359SKristof Provost 	ghdr_new->version = 0;
274740e04359SKristof Provost 	ghdr_new->reserved = 0;
274840e04359SKristof Provost 
274940e04359SKristof Provost 	CARP_LOCK(sc);
275040e04359SKristof Provost 
275140e04359SKristof Provost 	nlattr_add_u32(nw, CARP_NL_VHID, sc->sc_vhid);
275240e04359SKristof Provost 	nlattr_add_u32(nw, CARP_NL_STATE, sc->sc_state);
275340e04359SKristof Provost 	nlattr_add_s32(nw, CARP_NL_ADVBASE, sc->sc_advbase);
275440e04359SKristof Provost 	nlattr_add_s32(nw, CARP_NL_ADVSKEW, sc->sc_advskew);
275513781800SKristof Provost 	nlattr_add_in_addr(nw, CARP_NL_ADDR, &sc->sc_carpaddr);
275613781800SKristof Provost 	nlattr_add_in6_addr(nw, CARP_NL_ADDR6, &sc->sc_carpaddr6);
275737115154SKristof Provost 	nlattr_add_u8(nw, CARP_NL_VERSION, sc->sc_version);
275837115154SKristof Provost 	nlattr_add_u8(nw, CARP_NL_VRRP_PRIORITY, sc->sc_vrrp_prio);
275937115154SKristof Provost 	nlattr_add_u16(nw, CARP_NL_VRRP_ADV_INTER, sc->sc_vrrp_adv_inter);
276040e04359SKristof Provost 
276140e04359SKristof Provost 	if (priv)
276240e04359SKristof Provost 		nlattr_add(nw, CARP_NL_KEY, sizeof(sc->sc_key), sc->sc_key);
276340e04359SKristof Provost 
276440e04359SKristof Provost 	CARP_UNLOCK(sc);
276540e04359SKristof Provost 
276640e04359SKristof Provost 	if (! nlmsg_end(nw)) {
276740e04359SKristof Provost 		nlmsg_abort(nw);
276840e04359SKristof Provost 		return (false);
276940e04359SKristof Provost 	}
277040e04359SKristof Provost 
277140e04359SKristof Provost 	return (true);
277240e04359SKristof Provost }
277340e04359SKristof Provost 
277440e04359SKristof Provost struct nl_carp_parsed {
277540e04359SKristof Provost 	unsigned int	ifindex;
277628921c4fSKristof Provost 	char		*ifname;
277740e04359SKristof Provost 	uint32_t	state;
277840e04359SKristof Provost 	uint32_t	vhid;
277940e04359SKristof Provost 	int32_t		advbase;
278040e04359SKristof Provost 	int32_t		advskew;
278140e04359SKristof Provost 	char		key[CARP_KEY_LEN];
278213781800SKristof Provost 	struct in_addr	addr;
278313781800SKristof Provost 	struct in6_addr	addr6;
278437115154SKristof Provost 	carp_version_t	version;
278537115154SKristof Provost 	uint8_t		vrrp_prio;
278637115154SKristof Provost 	uint16_t	vrrp_adv_inter;
278740e04359SKristof Provost };
278840e04359SKristof Provost 
278940e04359SKristof Provost #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
279040e04359SKristof Provost #define	_OUT(_field)	offsetof(struct nl_carp_parsed, _field)
279140e04359SKristof Provost 
279240e04359SKristof Provost static const struct nlattr_parser nla_p_set[] = {
279340e04359SKristof Provost 	{ .type = CARP_NL_VHID, .off = _OUT(vhid), .cb = nlattr_get_uint32 },
279440e04359SKristof Provost 	{ .type = CARP_NL_STATE, .off = _OUT(state), .cb = nlattr_get_uint32 },
279540e04359SKristof Provost 	{ .type = CARP_NL_ADVBASE, .off = _OUT(advbase), .cb = nlattr_get_uint32 },
279640e04359SKristof Provost 	{ .type = CARP_NL_ADVSKEW, .off = _OUT(advskew), .cb = nlattr_get_uint32 },
279740e04359SKristof Provost 	{ .type = CARP_NL_KEY, .off = _OUT(key), .cb = nlattr_get_carp_key },
279840e04359SKristof Provost 	{ .type = CARP_NL_IFINDEX, .off = _OUT(ifindex), .cb = nlattr_get_uint32 },
279913781800SKristof Provost 	{ .type = CARP_NL_ADDR, .off = _OUT(addr), .cb = nlattr_get_in_addr },
280013781800SKristof Provost 	{ .type = CARP_NL_ADDR6, .off = _OUT(addr6), .cb = nlattr_get_in6_addr },
280128921c4fSKristof Provost 	{ .type = CARP_NL_IFNAME, .off = _OUT(ifname), .cb = nlattr_get_string },
280237115154SKristof Provost 	{ .type = CARP_NL_VERSION, .off = _OUT(version), .cb = nlattr_get_uint8 },
280337115154SKristof Provost 	{ .type = CARP_NL_VRRP_PRIORITY, .off = _OUT(vrrp_prio), .cb = nlattr_get_uint8 },
280437115154SKristof Provost 	{ .type = CARP_NL_VRRP_ADV_INTER, .off = _OUT(vrrp_adv_inter), .cb = nlattr_get_uint16 },
280540e04359SKristof Provost };
280640e04359SKristof Provost static const struct nlfield_parser nlf_p_set[] = {
280740e04359SKristof Provost };
280840e04359SKristof Provost NL_DECLARE_PARSER(carp_parser, struct genlmsghdr, nlf_p_set, nla_p_set);
280940e04359SKristof Provost #undef _IN
281040e04359SKristof Provost #undef _OUT
281140e04359SKristof Provost 
281240e04359SKristof Provost 
281340e04359SKristof Provost static int
281440e04359SKristof Provost carp_nl_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
281540e04359SKristof Provost {
281640e04359SKristof Provost 	struct nl_carp_parsed attrs = { };
281740e04359SKristof Provost 	struct carp_nl_send_args args;
281840e04359SKristof Provost 	struct carpreq carpr = { };
281940e04359SKristof Provost 	struct epoch_tracker et;
282028921c4fSKristof Provost 	if_t ifp = NULL;
282140e04359SKristof Provost 	int error;
282240e04359SKristof Provost 
282340e04359SKristof Provost 	error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
282440e04359SKristof Provost 	if (error != 0)
282540e04359SKristof Provost 		return (error);
282640e04359SKristof Provost 
282740e04359SKristof Provost 	NET_EPOCH_ENTER(et);
282828921c4fSKristof Provost 	if (attrs.ifname != NULL)
282928921c4fSKristof Provost 		ifp = ifunit_ref(attrs.ifname);
283028921c4fSKristof Provost 	else if (attrs.ifindex != 0)
283140e04359SKristof Provost 		ifp = ifnet_byindex_ref(attrs.ifindex);
283240e04359SKristof Provost 	NET_EPOCH_EXIT(et);
283340e04359SKristof Provost 
283440e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
283540e04359SKristof Provost 		goto out;
283640e04359SKristof Provost 
283740e04359SKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
283840e04359SKristof Provost 	args.hdr = hdr;
283940e04359SKristof Provost 	args.npt = npt;
284040e04359SKristof Provost 
284140e04359SKristof Provost 	carpr.carpr_vhid = attrs.vhid;
284240e04359SKristof Provost 	carpr.carpr_count = CARP_MAXVHID;
284340e04359SKristof Provost 
284440e04359SKristof Provost 	sx_xlock(&carp_sx);
284540e04359SKristof Provost 	error = carp_ioctl_get(ifp, nlp_get_cred(npt->nlp), &carpr,
284640e04359SKristof Provost 	    carp_nl_send, &args);
284740e04359SKristof Provost 	sx_xunlock(&carp_sx);
284840e04359SKristof Provost 
284940e04359SKristof Provost 	if (! nlmsg_end_dump(npt->nw, error, hdr))
285040e04359SKristof Provost 		error = ENOMEM;
285140e04359SKristof Provost 
285240e04359SKristof Provost out:
285340e04359SKristof Provost 	if (ifp != NULL)
285440e04359SKristof Provost 		if_rele(ifp);
285540e04359SKristof Provost 
285640e04359SKristof Provost 	return (error);
285740e04359SKristof Provost }
285840e04359SKristof Provost 
285940e04359SKristof Provost static int
286040e04359SKristof Provost carp_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
286140e04359SKristof Provost {
286240e04359SKristof Provost 	struct nl_carp_parsed attrs = { };
286313781800SKristof Provost 	struct carpkreq carpr;
286440e04359SKristof Provost 	struct epoch_tracker et;
286528921c4fSKristof Provost 	if_t ifp = NULL;
286640e04359SKristof Provost 	int error;
286740e04359SKristof Provost 
286840e04359SKristof Provost 	error = nl_parse_nlmsg(hdr, &carp_parser, npt, &attrs);
286940e04359SKristof Provost 	if (error != 0)
287040e04359SKristof Provost 		return (error);
287140e04359SKristof Provost 
287240e04359SKristof Provost 	if (attrs.vhid <= 0 || attrs.vhid > CARP_MAXVHID)
287340e04359SKristof Provost 		return (EINVAL);
287440e04359SKristof Provost 	if (attrs.state > CARP_MAXSTATE)
287540e04359SKristof Provost 		return (EINVAL);
287640e04359SKristof Provost 	if (attrs.advbase < 0 || attrs.advskew < 0)
287740e04359SKristof Provost 		return (EINVAL);
287840e04359SKristof Provost 	if (attrs.advbase > 255)
287940e04359SKristof Provost 		return (EINVAL);
288040e04359SKristof Provost 	if (attrs.advskew >= 255)
288140e04359SKristof Provost 		return (EINVAL);
288237115154SKristof Provost 	if (attrs.version == 0)
288337115154SKristof Provost 		attrs.version = CARP_VERSION_CARP;
288437115154SKristof Provost 	if (attrs.version != CARP_VERSION_CARP &&
288537115154SKristof Provost 	    attrs.version != CARP_VERSION_VRRPv3)
288637115154SKristof Provost 		return (EINVAL);
288737115154SKristof Provost 	if (attrs.vrrp_adv_inter > VRRP_MAX_INTERVAL)
288837115154SKristof Provost 		return (EINVAL);
288940e04359SKristof Provost 
289040e04359SKristof Provost 	NET_EPOCH_ENTER(et);
289128921c4fSKristof Provost 	if (attrs.ifname != NULL)
289228921c4fSKristof Provost 		ifp = ifunit_ref(attrs.ifname);
289328921c4fSKristof Provost 	else if (attrs.ifindex != 0)
289440e04359SKristof Provost 		ifp = ifnet_byindex_ref(attrs.ifindex);
289540e04359SKristof Provost 	NET_EPOCH_EXIT(et);
289640e04359SKristof Provost 
289740e04359SKristof Provost 	if ((error = carp_is_supported_if(ifp)) != 0)
289840e04359SKristof Provost 		goto out;
289940e04359SKristof Provost 
290040e04359SKristof Provost 	if ((ifp->if_flags & IFF_MULTICAST) == 0) {
290140e04359SKristof Provost 		error = EADDRNOTAVAIL;
290240e04359SKristof Provost 		goto out;
290340e04359SKristof Provost 	}
290440e04359SKristof Provost 
290540e04359SKristof Provost 	carpr.carpr_count = 1;
290640e04359SKristof Provost 	carpr.carpr_vhid = attrs.vhid;
290740e04359SKristof Provost 	carpr.carpr_state = attrs.state;
290840e04359SKristof Provost 	carpr.carpr_advbase = attrs.advbase;
290940e04359SKristof Provost 	carpr.carpr_advskew = attrs.advskew;
291013781800SKristof Provost 	carpr.carpr_addr = attrs.addr;
291113781800SKristof Provost 	carpr.carpr_addr6 = attrs.addr6;
291237115154SKristof Provost 	carpr.carpr_version = attrs.version;
291337115154SKristof Provost 	carpr.carpr_vrrp_priority = attrs.vrrp_prio;
291437115154SKristof Provost 	carpr.carpr_vrrp_adv_inter = attrs.vrrp_adv_inter;
291513781800SKristof Provost 
291640e04359SKristof Provost 	memcpy(&carpr.carpr_key, &attrs.key, sizeof(attrs.key));
291740e04359SKristof Provost 
291840e04359SKristof Provost 	sx_xlock(&carp_sx);
291940e04359SKristof Provost 	error = carp_ioctl_set(ifp, &carpr);
292040e04359SKristof Provost 	sx_xunlock(&carp_sx);
292140e04359SKristof Provost 
292240e04359SKristof Provost out:
292340e04359SKristof Provost 	if (ifp != NULL)
292440e04359SKristof Provost 		if_rele(ifp);
292540e04359SKristof Provost 
292640e04359SKristof Provost 	return (error);
292740e04359SKristof Provost }
292840e04359SKristof Provost 
292940e04359SKristof Provost static const struct nlhdr_parser *all_parsers[] = {
293040e04359SKristof Provost 	&carp_parser
293140e04359SKristof Provost };
293240e04359SKristof Provost 
293340e04359SKristof Provost static const struct genl_cmd carp_cmds[] = {
293440e04359SKristof Provost 	{
293540e04359SKristof Provost 		.cmd_num = CARP_NL_CMD_GET,
293640e04359SKristof Provost 		.cmd_name = "SIOCGVH",
293740e04359SKristof Provost 		.cmd_cb = carp_nl_get,
293840e04359SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP |
293940e04359SKristof Provost 		    GENL_CMD_CAP_HASPOL,
294040e04359SKristof Provost 	},
294140e04359SKristof Provost 	{
294240e04359SKristof Provost 		.cmd_num = CARP_NL_CMD_SET,
294340e04359SKristof Provost 		.cmd_name = "SIOCSVH",
294440e04359SKristof Provost 		.cmd_cb = carp_nl_set,
294540e04359SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
294640e04359SKristof Provost 		.cmd_priv = PRIV_NETINET_CARP,
294740e04359SKristof Provost 	},
294840e04359SKristof Provost };
294940e04359SKristof Provost 
295040e04359SKristof Provost static void
295140e04359SKristof Provost carp_nl_register(void)
295240e04359SKristof Provost {
295340e04359SKristof Provost 	bool ret __diagused;
295440e04359SKristof Provost 	int family_id __diagused;
295540e04359SKristof Provost 
295640e04359SKristof Provost 	NL_VERIFY_PARSERS(all_parsers);
295740e04359SKristof Provost 	family_id = genl_register_family(CARP_NL_FAMILY_NAME, 0, 2,
295840e04359SKristof Provost 	    CARP_NL_CMD_MAX);
295940e04359SKristof Provost 	MPASS(family_id != 0);
296040e04359SKristof Provost 
296140e04359SKristof Provost 	ret = genl_register_cmds(CARP_NL_FAMILY_NAME, carp_cmds,
296240e04359SKristof Provost 	    NL_ARRAY_LEN(carp_cmds));
296340e04359SKristof Provost 	MPASS(ret);
296440e04359SKristof Provost }
296540e04359SKristof Provost 
296640e04359SKristof Provost static void
296740e04359SKristof Provost carp_nl_unregister(void)
296840e04359SKristof Provost {
296940e04359SKristof Provost 	genl_unregister_family(CARP_NL_FAMILY_NAME);
297040e04359SKristof Provost }
297140e04359SKristof Provost 
297254bfbd51SWill Andrews static void
297354bfbd51SWill Andrews carp_mod_cleanup(void)
2974a9771948SGleb Smirnoff {
297554bfbd51SWill Andrews 
297640e04359SKristof Provost 	carp_nl_unregister();
297740e04359SKristof Provost 
297854bfbd51SWill Andrews #ifdef INET
297915249f73SWill Andrews 	(void)ipproto_unregister(IPPROTO_CARP);
298054bfbd51SWill Andrews 	carp_iamatch_p = NULL;
298154bfbd51SWill Andrews #endif
298254bfbd51SWill Andrews #ifdef INET6
298315249f73SWill Andrews 	(void)ip6proto_unregister(IPPROTO_CARP);
298454bfbd51SWill Andrews 	carp_iamatch6_p = NULL;
298554bfbd51SWill Andrews 	carp_macmatch6_p = NULL;
298654bfbd51SWill Andrews #endif
298708b68b0eSGleb Smirnoff 	carp_ioctl_p = NULL;
298808b68b0eSGleb Smirnoff 	carp_attach_p = NULL;
298908b68b0eSGleb Smirnoff 	carp_detach_p = NULL;
299008b68b0eSGleb Smirnoff 	carp_get_vhid_p = NULL;
299154bfbd51SWill Andrews 	carp_linkstate_p = NULL;
299254bfbd51SWill Andrews 	carp_forus_p = NULL;
299354bfbd51SWill Andrews 	carp_output_p = NULL;
2994f08535f8SGleb Smirnoff 	carp_demote_adj_p = NULL;
299524421c1cSGleb Smirnoff 	carp_master_p = NULL;
2996f08535f8SGleb Smirnoff 	mtx_unlock(&carp_mtx);
2997f08535f8SGleb Smirnoff 	taskqueue_drain(taskqueue_swi, &carp_sendall_task);
299854bfbd51SWill Andrews 	mtx_destroy(&carp_mtx);
299993d4534cSGleb Smirnoff 	sx_destroy(&carp_sx);
300054bfbd51SWill Andrews }
300154bfbd51SWill Andrews 
3002ee49c5d3SBoris Lytochkin static void
3003ee49c5d3SBoris Lytochkin ipcarp_sysinit(void)
3004ee49c5d3SBoris Lytochkin {
3005ee49c5d3SBoris Lytochkin 
3006ee49c5d3SBoris Lytochkin 	/* Load allow as tunable so to postpone carp start after module load */
3007ee49c5d3SBoris Lytochkin 	TUNABLE_INT_FETCH("net.inet.carp.allow", &V_carp_allow);
3008ee49c5d3SBoris Lytochkin }
3009ee49c5d3SBoris Lytochkin VNET_SYSINIT(ip_carp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipcarp_sysinit, NULL);
3010ee49c5d3SBoris Lytochkin 
301154bfbd51SWill Andrews static int
301254bfbd51SWill Andrews carp_mod_load(void)
301354bfbd51SWill Andrews {
301415249f73SWill Andrews 	int err;
301554bfbd51SWill Andrews 
3016d92d54d5SGleb Smirnoff 	mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF);
301793d4534cSGleb Smirnoff 	sx_init(&carp_sx, "carp_sx");
301808b68b0eSGleb Smirnoff 	LIST_INIT(&carp_list);
301908b68b0eSGleb Smirnoff 	carp_get_vhid_p = carp_get_vhid;
302054bfbd51SWill Andrews 	carp_forus_p = carp_forus;
302154bfbd51SWill Andrews 	carp_output_p = carp_output;
302208b68b0eSGleb Smirnoff 	carp_linkstate_p = carp_linkstate;
302308b68b0eSGleb Smirnoff 	carp_ioctl_p = carp_ioctl;
302408b68b0eSGleb Smirnoff 	carp_attach_p = carp_attach;
302508b68b0eSGleb Smirnoff 	carp_detach_p = carp_detach;
3026f08535f8SGleb Smirnoff 	carp_demote_adj_p = carp_demote_adj;
302724421c1cSGleb Smirnoff 	carp_master_p = carp_master;
302854bfbd51SWill Andrews #ifdef INET6
302954bfbd51SWill Andrews 	carp_iamatch6_p = carp_iamatch6;
303054bfbd51SWill Andrews 	carp_macmatch6_p = carp_macmatch6;
303178b1fc05SGleb Smirnoff 	err = ip6proto_register(IPPROTO_CARP, carp6_input, NULL);
303215249f73SWill Andrews 	if (err) {
303315249f73SWill Andrews 		printf("carp: error %d registering with INET6\n", err);
303415249f73SWill Andrews 		carp_mod_cleanup();
30356baf7a24SGleb Smirnoff 		return (err);
303615249f73SWill Andrews 	}
303754bfbd51SWill Andrews #endif
303854bfbd51SWill Andrews #ifdef INET
303954bfbd51SWill Andrews 	carp_iamatch_p = carp_iamatch;
304078b1fc05SGleb Smirnoff 	err = ipproto_register(IPPROTO_CARP, carp_input, NULL);
304115249f73SWill Andrews 	if (err) {
304215249f73SWill Andrews 		printf("carp: error %d registering with INET\n", err);
304315249f73SWill Andrews 		carp_mod_cleanup();
30446baf7a24SGleb Smirnoff 		return (err);
304515249f73SWill Andrews 	}
304654bfbd51SWill Andrews #endif
304740e04359SKristof Provost 
304840e04359SKristof Provost 	carp_nl_register();
304940e04359SKristof Provost 
305008b68b0eSGleb Smirnoff 	return (0);
305154bfbd51SWill Andrews }
3052a9771948SGleb Smirnoff 
305354bfbd51SWill Andrews static int
305454bfbd51SWill Andrews carp_modevent(module_t mod, int type, void *data)
305554bfbd51SWill Andrews {
305654bfbd51SWill Andrews 	switch (type) {
305754bfbd51SWill Andrews 	case MOD_LOAD:
305854bfbd51SWill Andrews 		return carp_mod_load();
305954bfbd51SWill Andrews 		/* NOTREACHED */
3060a9771948SGleb Smirnoff 	case MOD_UNLOAD:
306108b68b0eSGleb Smirnoff 		mtx_lock(&carp_mtx);
306208b68b0eSGleb Smirnoff 		if (LIST_EMPTY(&carp_list))
306354bfbd51SWill Andrews 			carp_mod_cleanup();
306408b68b0eSGleb Smirnoff 		else {
306508b68b0eSGleb Smirnoff 			mtx_unlock(&carp_mtx);
306654bfbd51SWill Andrews 			return (EBUSY);
306708b68b0eSGleb Smirnoff 		}
3068a9771948SGleb Smirnoff 		break;
3069a9771948SGleb Smirnoff 
3070a9771948SGleb Smirnoff 	default:
30710fa08018SGleb Smirnoff 		return (EINVAL);
3072a9771948SGleb Smirnoff 	}
3073a9771948SGleb Smirnoff 
30740fa08018SGleb Smirnoff 	return (0);
3075a9771948SGleb Smirnoff }
3076a9771948SGleb Smirnoff 
3077a9771948SGleb Smirnoff static moduledata_t carp_mod = {
3078a9771948SGleb Smirnoff 	"carp",
3079a9771948SGleb Smirnoff 	carp_modevent,
30809823d527SKevin Lo 	0
3081a9771948SGleb Smirnoff };
3082a9771948SGleb Smirnoff 
3083e24fa11dSWill Andrews DECLARE_MODULE(carp, carp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
3084