1/* 2 ************************************************************************** 3 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all copies. 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 13 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 ************************************************************************** 15 */ 16 17/* 18 * ecm_conntrack_notifier.c 19 * Conntrack notifier functionality. 20 */ 21 22#include <linux/version.h> 23#include <linux/types.h> 24#include <linux/ip.h> 25#include <linux/tcp.h> 26#include <linux/module.h> 27#include <linux/skbuff.h> 28#include <linux/icmp.h> 29#include <linux/kthread.h> 30#include <linux/debugfs.h> 31#include <linux/pkt_sched.h> 32#include <linux/string.h> 33#include <net/route.h> 34#include <net/ip.h> 35#include <net/tcp.h> 36#include <asm/unaligned.h> 37#include <asm/uaccess.h> /* for put_user */ 38#include <net/ipv6.h> 39#include <linux/inet.h> 40#include <linux/in.h> 41#include <linux/udp.h> 42#include <linux/tcp.h> 43 44#include <linux/inetdevice.h> 45#include <linux/if_arp.h> 46#include <linux/netfilter_ipv4.h> 47#include <linux/netfilter_bridge.h> 48#include <linux/if_bridge.h> 49#include <net/arp.h> 50#include <net/netfilter/nf_conntrack.h> 51#include <net/netfilter/nf_conntrack_acct.h> 52#include <net/netfilter/nf_conntrack_helper.h> 53#include <net/netfilter/nf_conntrack_l4proto.h> 54#include <net/netfilter/nf_conntrack_l3proto.h> 55#include <net/netfilter/nf_conntrack_zones.h> 56#include <net/netfilter/nf_conntrack_core.h> 57#include <net/netfilter/ipv4/nf_conntrack_ipv4.h> 58#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 59 60/* 61 * Debug output levels 62 * 0 = OFF 63 * 1 = ASSERTS / ERRORS 64 * 2 = 1 + WARN 65 * 3 = 2 + INFO 66 * 4 = 3 + TRACE 67 */ 68#define DEBUG_LEVEL ECM_CONNTRACK_NOTIFIER_DEBUG_LEVEL 69 70#include <nss_api_if.h> 71 72#include "ecm_types.h" 73#include "ecm_db_types.h" 74#include "ecm_tracker.h" 75#include "ecm_classifier.h" 76#include "ecm_front_end_types.h" 77#include "ecm_tracker_udp.h" 78#include "ecm_tracker_tcp.h" 79#include "ecm_tracker_datagram.h" 80#include "ecm_db.h" 81#include "ecm_classifier_default.h" 82#include "ecm_nss_ipv4.h" 83#include "ecm_front_end_ipv4.h" 84#ifdef ECM_IPV6_ENABLE 85#include "ecm_front_end_ipv6.h" 86#endif 87 88/* 89 * Locking of the classifier - concurrency control 90 */ 91static DEFINE_SPINLOCK(ecm_conntrack_notifier_lock); /* Protect against SMP access between netfilter, events and private threaded function. */ 92 93/* 94 * Debugfs dentry object. 95 */ 96static struct dentry *ecm_conntrack_notifier_dentry; 97 98/* 99 * General operational control 100 */ 101static int ecm_conntrack_notifier_stopped = 0; /* When non-zero further traffic will not be processed */ 102 103#ifdef CONFIG_NF_CONNTRACK_EVENTS 104/* 105 * ecm_conntrack_event() 106 * Callback event invoked when conntrack connection state changes, currently we handle destroy events to quickly release state 107 */ 108#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 109static int ecm_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr) 110#else 111static int ecm_conntrack_event(unsigned int events, struct nf_ct_event *item) 112#endif 113{ 114#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 115 struct nf_ct_event *item = (struct nf_ct_event *)ptr; 116#endif 117 struct nf_conn *ct = item->ct; 118 119 /* 120 * If operations have stopped then do not process event 121 */ 122 spin_lock_bh(&ecm_conntrack_notifier_lock); 123 if (unlikely(ecm_conntrack_notifier_stopped)) { 124 DEBUG_WARN("Ignoring event - stopped\n"); 125 spin_unlock_bh(&ecm_conntrack_notifier_lock); 126 return NOTIFY_DONE; 127 } 128 spin_unlock_bh(&ecm_conntrack_notifier_lock); 129 130 if (!ct) { 131 DEBUG_WARN("Error: no ct\n"); 132 return NOTIFY_DONE; 133 } 134 135 /* 136 * Special untracked connection is not monitored 137 */ 138 if (ct == &nf_conntrack_untracked) { 139 DEBUG_TRACE("Fake connection event - ignoring\n"); 140 return NOTIFY_DONE; 141 } 142 143 /* 144 * Only interested if this is IPv4 or IPv6. 145 */ 146 if (nf_ct_l3num(ct) == AF_INET) { 147 return ecm_nss_ipv4_conntrack_event(events, ct); 148 } 149#ifdef ECM_IPV6_ENABLE 150 if (nf_ct_l3num(ct) == AF_INET6) { 151 return ecm_front_end_ipv6_conntrack_event(events, ct); 152 } 153#endif 154 return NOTIFY_DONE; 155} 156 157#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 158/* 159 * struct notifier_block ecm_conntrack_notifier 160 * Netfilter conntrack event system to monitor connection tracking changes 161 */ 162static struct notifier_block ecm_conntrack_notifier = { 163 .notifier_call = ecm_conntrack_event, 164}; 165#else 166/* 167 * struct nf_ct_event_notifier ecm_conntrack_notifier 168 * Netfilter conntrack event system to monitor connection tracking changes 169 */ 170static struct nf_ct_event_notifier ecm_conntrack_notifier = { 171 .fcn = ecm_conntrack_event, 172}; 173#endif 174#endif 175 176/* 177 * ecm_conntrack_notifier_stop() 178 */ 179void ecm_conntrack_notifier_stop(int num) 180{ 181 ecm_conntrack_notifier_stopped = num; 182} 183EXPORT_SYMBOL(ecm_conntrack_notifier_stop); 184 185/* 186 * ecm_conntrack_notifier_init() 187 */ 188int ecm_conntrack_notifier_init(struct dentry *dentry) 189{ 190 int result; 191 DEBUG_INFO("ECM Conntrack Notifier init\n"); 192 193 ecm_conntrack_notifier_dentry = debugfs_create_dir("ecm_conntrack_notifier", dentry); 194 if (!ecm_conntrack_notifier_dentry) { 195 DEBUG_ERROR("Failed to create ecm conntrack notifier directory in debugfs\n"); 196 return -1; 197 } 198 199 if (!debugfs_create_u32("stop", S_IRUGO | S_IWUSR, ecm_conntrack_notifier_dentry, 200 (u32 *)&ecm_conntrack_notifier_stopped)) { 201 DEBUG_ERROR("Failed to create ecm conntrack notifier stopped file in debugfs\n"); 202 debugfs_remove_recursive(ecm_conntrack_notifier_dentry); 203 return -1; 204 } 205 206#ifdef CONFIG_NF_CONNTRACK_EVENTS 207 /* 208 * Eventing subsystem is available so we register a notifier hook to get fast notifications of expired connections 209 */ 210 result = nf_conntrack_register_notifier(&init_net, &ecm_conntrack_notifier); 211 if (result < 0) { 212 DEBUG_ERROR("Can't register nf notifier hook.\n"); 213 debugfs_remove_recursive(ecm_conntrack_notifier_dentry); 214 return result; 215 } 216#endif 217 218 return 0; 219} 220EXPORT_SYMBOL(ecm_conntrack_notifier_init); 221 222/* 223 * ecm_conntrack_notifier_exit() 224 */ 225void ecm_conntrack_notifier_exit(void) 226{ 227 DEBUG_INFO("ECM Conntrack Notifier exit\n"); 228#ifdef CONFIG_NF_CONNTRACK_EVENTS 229 nf_conntrack_unregister_notifier(&init_net, &ecm_conntrack_notifier); 230#endif 231 /* 232 * Remove the debugfs files recursively. 233 */ 234 if (ecm_conntrack_notifier_dentry) { 235 debugfs_remove_recursive(ecm_conntrack_notifier_dentry); 236 } 237} 238EXPORT_SYMBOL(ecm_conntrack_notifier_exit); 239