From 8856400bcbffb9213a76e1274fc1ce2f49a01d36 Mon Sep 17 00:00:00 2001 From: Mikolaj Golub Date: Sat, 24 Aug 2013 11:59:51 +0000 Subject: [PATCH] Make ipfw nat init/unint work correctly for VIMAGE: * Do per vnet instance cleanup (previously it was only for vnet0 on module unload, and led to libalias leaks and possible panics due to stale pointer dereferences). * Instead of protecting ipfw hooks registering/deregistering by only vnet0 lock (which does not prevent pointers access from another vnets), introduce per vnet ipfw_nat_loaded variable. The variable is set after hooks are registered and unset before they are deregistered. * Devirtualize ifaddr_event_tag as we run only one event handler for all vnets. * It is supposed that ifaddr_change event handler is called in the interface vnet context, so add an assertion. Reviewed by: zec MFC after: 2 weeks --- sys/netpfil/ipfw/ip_fw2.c | 2 + sys/netpfil/ipfw/ip_fw_nat.c | 76 ++++++++++++++++++++++---------- sys/netpfil/ipfw/ip_fw_private.h | 6 ++- 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 6317013307ae..128afad3ad6e 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -142,6 +142,8 @@ VNET_DEFINE(int, verbose_limit); /* layer3_chain contains the list of rules for layer 3 */ VNET_DEFINE(struct ip_fw_chain, layer3_chain); +VNET_DEFINE(int, ipfw_nat_ready) = 0; + ipfw_nat_t *ipfw_nat_ptr = NULL; struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 84852dbfc01a..155eddd4cf10 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -53,8 +53,7 @@ __FBSDID("$FreeBSD$"); #include /* XXX for in_cksum */ -static VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); -#define V_ifaddr_event_tag VNET(ifaddr_event_tag) +static eventhandler_tag ifaddr_event_tag; static void ifaddr_change(void *arg __unused, struct ifnet *ifp) @@ -63,6 +62,8 @@ ifaddr_change(void *arg __unused, struct ifnet *ifp) struct ifaddr *ifa; struct ip_fw_chain *chain; + KASSERT(curvnet == ifp->if_vnet, + ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); chain = &V_layer3_chain; IPFW_WLOCK(chain); /* Check every nat entry... */ @@ -589,26 +590,16 @@ ipfw_nat_get_log(struct sockopt *sopt) return(0); } -static void -ipfw_nat_init(void) +static int +vnet_ipfw_nat_init(const void *arg __unused) { - IPFW_WLOCK(&V_layer3_chain); - /* init ipfw hooks */ - ipfw_nat_ptr = ipfw_nat; - lookup_nat_ptr = lookup_nat; - ipfw_nat_cfg_ptr = ipfw_nat_cfg; - ipfw_nat_del_ptr = ipfw_nat_del; - ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; - ipfw_nat_get_log_ptr = ipfw_nat_get_log; - IPFW_WUNLOCK(&V_layer3_chain); - V_ifaddr_event_tag = EVENTHANDLER_REGISTER( - ifaddr_event, ifaddr_change, - NULL, EVENTHANDLER_PRI_ANY); + V_ipfw_nat_ready = 1; + return (0); } -static void -ipfw_nat_destroy(void) +static int +vnet_ipfw_nat_uninit(const void *arg __unused) { struct cfg_nat *ptr, *ptr_temp; struct ip_fw_chain *chain; @@ -621,8 +612,33 @@ ipfw_nat_destroy(void) LibAliasUninit(ptr->lib); free(ptr, M_IPFW); } - EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); flush_nat_ptrs(chain, -1 /* flush all */); + V_ipfw_nat_ready = 0; + IPFW_WUNLOCK(chain); + return (0); +} + +static void +ipfw_nat_init(void) +{ + + /* init ipfw hooks */ + ipfw_nat_ptr = ipfw_nat; + lookup_nat_ptr = lookup_nat; + ipfw_nat_cfg_ptr = ipfw_nat_cfg; + ipfw_nat_del_ptr = ipfw_nat_del; + ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; + ipfw_nat_get_log_ptr = ipfw_nat_get_log; + + ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, + NULL, EVENTHANDLER_PRI_ANY); +} + +static void +ipfw_nat_destroy(void) +{ + + EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); /* deregister ipfw_nat */ ipfw_nat_ptr = NULL; lookup_nat_ptr = NULL; @@ -630,7 +646,6 @@ ipfw_nat_destroy(void) ipfw_nat_del_ptr = NULL; ipfw_nat_get_cfg_ptr = NULL; ipfw_nat_get_log_ptr = NULL; - IPFW_WUNLOCK(chain); } static int @@ -640,11 +655,9 @@ ipfw_nat_modevent(module_t mod, int type, void *unused) switch (type) { case MOD_LOAD: - ipfw_nat_init(); break; case MOD_UNLOAD: - ipfw_nat_destroy(); break; default: @@ -660,8 +673,25 @@ static moduledata_t ipfw_nat_mod = { 0 }; -DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); +/* Define startup order. */ +#define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN +#define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 255) +#define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) +#define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) + +DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); MODULE_VERSION(ipfw_nat, 1); + +SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, + ipfw_nat_init, NULL); +VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, + vnet_ipfw_nat_init, NULL); + +SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, + ipfw_nat_destroy, NULL); +VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, + IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); + /* end of file */ diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index a41cdf55a361..a8d7eea4edfd 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -327,9 +327,11 @@ extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int); typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *); typedef int ipfw_nat_cfg_t(struct sockopt *); -extern ipfw_nat_t *ipfw_nat_ptr; -#define IPFW_NAT_LOADED (ipfw_nat_ptr != NULL) +VNET_DECLARE(int, ipfw_nat_ready); +#define V_ipfw_nat_ready VNET(ipfw_nat_ready) +#define IPFW_NAT_LOADED (V_ipfw_nat_ready) +extern ipfw_nat_t *ipfw_nat_ptr; extern ipfw_nat_cfg_t *ipfw_nat_cfg_ptr; extern ipfw_nat_cfg_t *ipfw_nat_del_ptr; extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;