1/*
2 **************************************************************************
3 * Copyright (c) 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_sfe_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 <sfe_drv.h>
71
72#include "ecm_types.h"
73#include "ecm_db_types.h"
74#include "ecm_state.h"
75#include "ecm_tracker.h"
76#include "ecm_classifier.h"
77#include "ecm_front_end_types.h"
78#include "ecm_tracker_udp.h"
79#include "ecm_tracker_tcp.h"
80#include "ecm_tracker_datagram.h"
81#include "ecm_db.h"
82#include "ecm_sfe_ipv4.h"
83#ifdef ECM_IPV6_ENABLE
84#include "ecm_sfe_ipv6.h"
85#endif
86
87/*
88 * Locking of the classifier - concurrency control
89 */
90static DEFINE_SPINLOCK(ecm_sfe_conntrack_notifier_lock);				/* Protect against SMP access between netfilter, events and private threaded function. */
91
92/*
93 * Debugfs dentry object.
94 */
95static struct dentry *ecm_sfe_conntrack_notifier_dentry;
96
97/*
98 * General operational control
99 */
100static int ecm_sfe_conntrack_notifier_stopped = 0;				/* When non-zero further traffic will not be processed */
101
102#ifdef CONFIG_NF_CONNTRACK_EVENTS
103/*
104 * ecm_sfe_conntrack_event()
105 *	Callback event invoked when conntrack connection state changes, currently we handle destroy events to quickly release state
106 */
107#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
108static int ecm_sfe_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr)
109#else
110static int ecm_sfe_conntrack_event(unsigned int events, struct nf_ct_event *item)
111#endif
112{
113#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
114	struct nf_ct_event *item = (struct nf_ct_event *)ptr;
115#endif
116	struct nf_conn *ct = item->ct;
117
118	/*
119	 * If operations have stopped then do not process event
120	 */
121	spin_lock_bh(&ecm_sfe_conntrack_notifier_lock);
122	if (unlikely(ecm_sfe_conntrack_notifier_stopped)) {
123		DEBUG_WARN("Ignoring event - stopped\n");
124		spin_unlock_bh(&ecm_sfe_conntrack_notifier_lock);
125		return NOTIFY_DONE;
126	}
127	spin_unlock_bh(&ecm_sfe_conntrack_notifier_lock);
128
129	if (!ct) {
130		DEBUG_WARN("Error: no ct\n");
131		return NOTIFY_DONE;
132	}
133
134	/*
135	 * Special untracked connection is not monitored
136	 */
137	if (ct == &nf_conntrack_untracked) {
138		DEBUG_TRACE("Fake connection event - ignoring\n");
139		return NOTIFY_DONE;
140	}
141
142	/*
143	 * Only interested if this is IPv4 or IPv6.
144	 */
145	if (nf_ct_l3num(ct) == AF_INET) {
146		return ecm_sfe_ipv4_conntrack_event(events, ct);
147	}
148#ifdef ECM_IPV6_ENABLE
149	if (nf_ct_l3num(ct) == AF_INET6) {
150		return ecm_sfe_ipv6_conntrack_event(events, ct);
151	}
152#endif
153	return NOTIFY_DONE;
154}
155
156#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
157/*
158 * struct notifier_block ecm_sfe_conntrack_notifier
159 *	Netfilter conntrack event system to monitor connection tracking changes
160 */
161static struct notifier_block ecm_sfe_conntrack_notifier = {
162	.notifier_call	= ecm_sfe_conntrack_event,
163};
164#else
165/*
166 * struct nf_ct_event_notifier ecm_sfe_conntrack_notifier
167 *	Netfilter conntrack event system to monitor connection tracking changes
168 */
169static struct nf_ct_event_notifier ecm_sfe_conntrack_notifier = {
170	.fcn	= ecm_sfe_conntrack_event,
171};
172#endif
173#endif
174
175/*
176 * ecm_sfe_conntrack_notifier_stop()
177 */
178void ecm_sfe_conntrack_notifier_stop(int num)
179{
180	ecm_sfe_conntrack_notifier_stopped = num;
181}
182EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_stop);
183
184/*
185 * ecm_sfe_conntrack_notifier_init()
186 */
187int ecm_sfe_conntrack_notifier_init(struct dentry *dentry)
188{
189	int result;
190	DEBUG_INFO("ECM Conntrack Notifier init\n");
191
192	ecm_sfe_conntrack_notifier_dentry = debugfs_create_dir("ecm_sfe_conntrack_notifier", dentry);
193	if (!ecm_sfe_conntrack_notifier_dentry) {
194		DEBUG_ERROR("Failed to create ecm conntrack notifier directory in debugfs\n");
195		return -1;
196	}
197
198	if (!debugfs_create_u32("stop", S_IRUGO | S_IWUSR, ecm_sfe_conntrack_notifier_dentry,
199					(u32 *)&ecm_sfe_conntrack_notifier_stopped)) {
200		DEBUG_ERROR("Failed to create ecm conntrack notifier stopped file in debugfs\n");
201		debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry);
202		return -1;
203	}
204
205#ifdef CONFIG_NF_CONNTRACK_EVENTS
206	/*
207	 * Eventing subsystem is available so we register a notifier hook to get fast notifications of expired connections
208	 */
209	result = nf_conntrack_register_notifier(&init_net, &ecm_sfe_conntrack_notifier);
210	if (result < 0) {
211		DEBUG_ERROR("Can't register nf notifier hook.\n");
212		debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry);
213		return result;
214	}
215#endif
216
217	return 0;
218}
219EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_init);
220
221/*
222 * ecm_sfe_conntrack_notifier_exit()
223 */
224void ecm_sfe_conntrack_notifier_exit(void)
225{
226	DEBUG_INFO("ECM Conntrack Notifier exit\n");
227#ifdef CONFIG_NF_CONNTRACK_EVENTS
228	nf_conntrack_unregister_notifier(&init_net, &ecm_sfe_conntrack_notifier);
229#endif
230	/*
231	 * Remove the debugfs files recursively.
232	 */
233	if (ecm_sfe_conntrack_notifier_dentry) {
234		debugfs_remove_recursive(ecm_sfe_conntrack_notifier_dentry);
235	}
236}
237EXPORT_SYMBOL(ecm_sfe_conntrack_notifier_exit);
238