diff options
author | will <will@FreeBSD.org> | 2010-08-11 00:51:50 +0000 |
---|---|---|
committer | will <will@FreeBSD.org> | 2010-08-11 00:51:50 +0000 |
commit | aa4e762c4a720fc3e1b4d3f08d09c5b65bb07735 (patch) | |
tree | 5194d90f80d5d4fac28f6f736d5c02cf80511112 /sys/netinet/ip_carp.c | |
parent | 8afd703de9fc0419708d83e8c31c1f0facca78dd (diff) | |
download | FreeBSD-src-aa4e762c4a720fc3e1b4d3f08d09c5b65bb07735.zip FreeBSD-src-aa4e762c4a720fc3e1b4d3f08d09c5b65bb07735.tar.gz |
Allow carp(4) to be loaded as a kernel module. Follow precedent set by
bridge(4), lagg(4) etc. and make use of function pointers and
pf_proto_register() to hook carp into the network stack.
Currently, because of the uncertainty about whether the unload path is free
of race condition panics, unloads are disallowed by default. Compiling with
CARPMOD_CAN_UNLOAD in CFLAGS removes this anti foot shooting measure.
This commit requires IP6PROTOSPACER, introduced in r211115.
Reviewed by: bz, simon
Approved by: ken (mentor)
MFC after: 2 weeks
Diffstat (limited to 'sys/netinet/ip_carp.c')
-rw-r--r-- | sys/netinet/ip_carp.c | 176 |
1 files changed, 152 insertions, 24 deletions
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 743a901..1820ae0 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -27,7 +27,6 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include "opt_carp.h" #include "opt_bpf.h" #include "opt_inet.h" #include "opt_inet6.h" @@ -44,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/priv.h> #include <sys/proc.h> +#include <sys/protosw.h> #include <sys/sysctl.h> #include <sys/syslog.h> #include <sys/signalvar.h> @@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$"); #ifdef INET6 #include <netinet/icmp6.h> #include <netinet/ip6.h> +#include <netinet6/ip6protosw.h> #include <netinet6/ip6_var.h> #include <netinet6/scope6_var.h> #include <netinet6/nd6.h> @@ -134,8 +135,29 @@ struct carp_softc { }; #define SC2IFP(sc) ((sc)->sc_ifp) +/* These are external networking stack hooks for CARP */ +/* net/if.c */ +extern void (*carp_linkstate_p)(struct ifnet *); +/* net/if_bridge.c net/if_ethersubr.c */ +extern struct ifnet *(*carp_forus_p)(struct ifnet *, u_char *); +/* net/if_ethersubr.c */ +extern int (*carp_output_p)(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); +#ifdef INET +/* netinet/if_ether.c */ +extern int (*carp_iamatch_p)(struct ifnet *, struct in_ifaddr *, + struct in_addr *, u_int8_t **); +#endif +#ifdef INET6 +/* netinet6/nd6_nbr.c */ +extern struct ifaddr *(*carp_iamatch6_p)(struct ifnet *, struct in6_addr *); +extern caddr_t (*carp_macmatch6_p)(struct ifnet *, struct mbuf *, + const struct in6_addr *); +#endif + int carp_suppress_preempt = 0; int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */ +SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP"); SYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW, &carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets"); SYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW, @@ -160,6 +182,10 @@ struct carp_if { struct mtx vhif_mtx; }; +#define CARP_INET 0 +#define CARP_INET6 1 +static int proto_reg[] = {-1, -1}; + /* Get carp_if from softc. Valid after carp_set_addr{,6}. */ #define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp) @@ -1146,14 +1172,15 @@ carp_addrcount(struct carp_if *cif, struct in_ifaddr *ia, int type) } int -carp_iamatch(void *v, struct in_ifaddr *ia, +carp_iamatch(struct ifnet *ifp, struct in_ifaddr *ia, struct in_addr *isaddr, u_int8_t **enaddr) { - struct carp_if *cif = v; + struct carp_if *cif; struct carp_softc *vh; int index, count = 0; struct ifaddr *ifa; + cif = ifp->if_carp; CARP_LOCK(cif); if (carp_opts[CARPCTL_ARPBALANCE]) { @@ -1222,12 +1249,13 @@ carp_iamatch(void *v, struct in_ifaddr *ia, #ifdef INET6 struct ifaddr * -carp_iamatch6(void *v, struct in6_addr *taddr) +carp_iamatch6(struct ifnet *ifp, struct in6_addr *taddr) { - struct carp_if *cif = v; + struct carp_if *cif; struct carp_softc *vh; struct ifaddr *ifa; + cif = ifp->if_carp; CARP_LOCK(cif); TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { IF_ADDR_LOCK(SC2IFP(vh)); @@ -1250,14 +1278,15 @@ carp_iamatch6(void *v, struct in6_addr *taddr) return (NULL); } -void * -carp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr) +caddr_t +carp_macmatch6(struct ifnet *ifp, struct mbuf *m, const struct in6_addr *taddr) { struct m_tag *mtag; - struct carp_if *cif = v; + struct carp_if *cif; struct carp_softc *sc; struct ifaddr *ifa; + cif = ifp->if_carp; CARP_LOCK(cif); TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { IF_ADDR_LOCK(SC2IFP(sc)); @@ -1293,15 +1322,16 @@ carp_macmatch6(void *v, struct mbuf *m, const struct in6_addr *taddr) #endif struct ifnet * -carp_forus(void *v, void *dhost) +carp_forus(struct ifnet *ifp, u_char *dhost) { - struct carp_if *cif = v; + struct carp_if *cif; struct carp_softc *vh; u_int8_t *ena = dhost; if (ena[0] || ena[1] || ena[2] != 0x5e || ena[3] || ena[4] != 1) return (NULL); + cif = ifp->if_carp; CARP_LOCK(cif); TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) if ((SC2IFP(vh)->if_flags & IFF_UP) && @@ -2210,10 +2240,11 @@ carp_set_state(struct carp_softc *sc, int state) } void -carp_carpdev_state(void *v) +carp_carpdev_state(struct ifnet *ifp) { - struct carp_if *cif = v; + struct carp_if *cif; + cif = ifp->if_carp; CARP_LOCK(cif); carp_carpdev_state_locked(cif); CARP_UNLOCK(cif); @@ -2264,24 +2295,121 @@ carp_sc_state_locked(struct carp_softc *sc) return; } +#ifdef INET +extern struct domain inetdomain; +static struct protosw in_carp_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_CARP, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = carp_input, + .pr_output = (pr_output_t *)rip_output, + .pr_ctloutput = rip_ctloutput, + .pr_usrreqs = &rip_usrreqs +}; +#endif + +#ifdef INET6 +extern struct domain inet6domain; +static struct ip6protosw in6_carp_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inet6domain, + .pr_protocol = IPPROTO_CARP, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = carp6_input, + .pr_output = rip6_output, + .pr_ctloutput = rip6_ctloutput, + .pr_usrreqs = &rip6_usrreqs +}; +#endif + +static void +carp_mod_cleanup(void) +{ + + if (if_detach_event_tag == NULL) + return; + EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); + if_clone_detach(&carp_cloner); +#ifdef INET + if (proto_reg[CARP_INET] == 0) { + pf_proto_unregister(PF_INET, IPPROTO_CARP, SOCK_RAW); + proto_reg[CARP_INET] = -1; + } + carp_iamatch_p = NULL; +#endif +#ifdef INET6 + if (proto_reg[CARP_INET6] == 0) { + pf_proto_unregister(PF_INET6, IPPROTO_CARP, SOCK_RAW); + proto_reg[CARP_INET6] = -1; + } + carp_iamatch6_p = NULL; + carp_macmatch6_p = NULL; +#endif + carp_linkstate_p = NULL; + carp_forus_p = NULL; + carp_output_p = NULL; + mtx_destroy(&carp_mtx); +} + +static int +carp_mod_load(void) +{ + + if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, + carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); + if (if_detach_event_tag == NULL) + return (ENOMEM); + mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); + LIST_INIT(&carpif_list); + if_clone_attach(&carp_cloner); + carp_linkstate_p = carp_carpdev_state; + carp_forus_p = carp_forus; + carp_output_p = carp_output; +#ifdef INET6 + carp_iamatch6_p = carp_iamatch6; + carp_macmatch6_p = carp_macmatch6; + proto_reg[CARP_INET6] = pf_proto_register(PF_INET6, + (struct protosw *)&in6_carp_protosw); + if (proto_reg[CARP_INET6] != 0) { + printf("carp: error %d attaching to PF_INET6\n", + proto_reg[CARP_INET6]); + carp_mod_cleanup(); + return (EINVAL); + } +#endif +#ifdef INET + carp_iamatch_p = carp_iamatch; + proto_reg[CARP_INET] = pf_proto_register(PF_INET, &in_carp_protosw); + if (proto_reg[CARP_INET] != 0) { + printf("carp: error %d attaching to PF_INET\n", + proto_reg[CARP_INET]); + carp_mod_cleanup(); + return (EINVAL); + } +#endif + return 0; +} + static int carp_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: - if_detach_event_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, - carp_ifdetach, NULL, EVENTHANDLER_PRI_ANY); - if (if_detach_event_tag == NULL) - return (ENOMEM); - mtx_init(&carp_mtx, "carp_mtx", NULL, MTX_DEF); - LIST_INIT(&carpif_list); - if_clone_attach(&carp_cloner); - break; - + return carp_mod_load(); + /* NOTREACHED */ case MOD_UNLOAD: - EVENTHANDLER_DEREGISTER(ifnet_departure_event, if_detach_event_tag); - if_clone_detach(&carp_cloner); - mtx_destroy(&carp_mtx); + /* + * XXX: For now, disallow module unloading by default due to + * a race condition where a thread may dereference one of the + * function pointer hooks after the module has been + * unloaded, during processing of a packet, causing a panic. + */ +#ifdef CARPMOD_CAN_UNLOAD + carp_mod_cleanup(); +#else + return (EBUSY); +#endif break; default: |