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