/* ************************************************************************** * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all copies. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ************************************************************************** */ /* * ecm_sfe_conntrack_notifier.c * Conntrack notifier functionality. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for put_user */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Debug output levels * 0 = OFF * 1 = ASSERTS / ERRORS * 2 = 1 + WARN * 3 = 2 + INFO * 4 = 3 + TRACE */ #define DEBUG_LEVEL ECM_CONNTRACK_NOTIFIER_DEBUG_LEVEL #include #include "ecm_types.h" #include "ecm_db_types.h" #include "ecm_state.h" #include "ecm_tracker.h" #include "ecm_classifier.h" #include "ecm_front_end_types.h" #include "ecm_tracker_udp.h" #include "ecm_tracker_tcp.h" #include "ecm_tracker_datagram.h" #include "ecm_db.h" #include "ecm_sfe_ipv4.h" #ifdef ECM_IPV6_ENABLE #include "ecm_sfe_ipv6.h" #endif /* * Locking of the classifier - concurrency control */ static DEFINE_SPINLOCK(ecm_sfe_conntrack_notifier_lock); /* Protect against SMP access between netfilter, events and private threaded function. */ /* * Debugfs dentry object. */ static struct dentry *ecm_sfe_conntrack_notifier_dentry; /* * General operational control */ static int ecm_sfe_conntrack_notifier_stopped = 0; /* When non-zero further traffic will not be processed */ #ifdef CONFIG_NF_CONNTRACK_EVENTS /* * ecm_sfe_conntrack_event() * Callback event invoked when conntrack connection state changes, currently we handle destroy events to quickly release state */ #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS static int ecm_sfe_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr) #else static int ecm_sfe_conntrack_event(unsigned int events, struct nf_ct_event *item) #endif { #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS struct nf_ct_event *item = (struct nf_ct_event *)ptr; #endif struct nf_conn *ct = item->ct; /* * If operations have stopped then do not process event */ spin_lock_bh(&ecm_sfe_conntrack_notifier_lock); if (unlikely(ecm_sfe_conntrack_notifier_stopped)) { DEBUG_WARN("Ignoring event - stopped\n"); spin_unlock_bh(&ecm_sfe_conntrack_notifier_lock); return NOTIFY_DONE; } spin_unlock_bh(&ecm_sfe_conntrack_notifier_lock); if (!ct) { DEBUG_WARN("Error: no ct\n"); return NOTIFY_DONE; } /* * Special untracked connection is not monitored */ if (ct == &nf_conntrack_untracked) { DEBUG_TRACE("Fake connection event - ignoring\n"); return NOTIFY_DONE; } /* * Only interested if this is IPv4 or IPv6. */ if (nf_ct_l3num(ct) == AF_INET) { return ecm_sfe_ipv4_conntrack_event(events, ct); } #ifdef ECM_IPV6_ENABLE if (nf_ct_l3num(ct) == AF_INET6) { return ecm_sfe_ipv6_conntrack_event(events, ct); } #endif return NOTIFY_DONE; } #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS /* * struct notifier_block ecm_sfe_conntrack_notifier * Netfilter conntrack event system to monitor connection tracking changes */ static struct notifier_block ecm_sfe_conntrack_notifier = { .notifier_call = ecm_sfe_conntrack_event, }; #else /* * struct nf_ct_event_notifier ecm_sfe_conntrack_notifier * Netfilter conntrack event system to monitor connection tracking changes */ static struct nf_ct_event_notifier ecm_sfe_conntrack_notifier = { .fcn = ecm_sfe_conntrack_event, }; #endif #endif /* * ecm_sfe_conntrack_notifier_stop() */ void ecm_sfe_conntrack_notifier_stop(int num) { ecm_sfe_conntrack_notifier_stopped = num; } EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_stop); /* * ecm_sfe_conntrack_notifier_init() */ int ecm_sfe_conntrack_notifier_init(struct dentry *dentry) { int result; DEBUG_INFO("ECM Conntrack Notifier init\n"); ecm_sfe_conntrack_notifier_dentry = debugfs_create_dir("ecm_sfe_conntrack_notifier", dentry); if (!ecm_sfe_conntrack_notifier_dentry) { DEBUG_ERROR("Failed to create ecm conntrack notifier directory in debugfs\n"); return -1; } if (!debugfs_create_u32("stop", S_IRUGO | S_IWUSR, ecm_sfe_conntrack_notifier_dentry, (u32 *)&ecm_sfe_conntrack_notifier_stopped)) { DEBUG_ERROR("Failed to create ecm conntrack notifier stopped file in debugfs\n"); debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry); return -1; } #ifdef CONFIG_NF_CONNTRACK_EVENTS /* * Eventing subsystem is available so we register a notifier hook to get fast notifications of expired connections */ result = nf_conntrack_register_notifier(&init_net, &ecm_sfe_conntrack_notifier); if (result < 0) { DEBUG_ERROR("Can't register nf notifier hook.\n"); debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry); return result; } #endif return 0; } EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_init); /* * ecm_sfe_conntrack_notifier_exit() */ void ecm_sfe_conntrack_notifier_exit(void) { DEBUG_INFO("ECM Conntrack Notifier exit\n"); #ifdef CONFIG_NF_CONNTRACK_EVENTS nf_conntrack_unregister_notifier(&init_net, &ecm_sfe_conntrack_notifier); #endif /* * Remove the debugfs files recursively. */ if (ecm_sfe_conntrack_notifier_dentry) { debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry); } } EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_exit);