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_nss_bond_notifier.c
19 * 	Bonding 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/debugfs.h>
30#include <linux/kthread.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 <linux/if_bonding.h>
50#include <net/arp.h>
51#include <net/netfilter/nf_conntrack.h>
52#include <net/netfilter/nf_conntrack_acct.h>
53#include <net/netfilter/nf_conntrack_helper.h>
54#include <net/netfilter/nf_conntrack_l4proto.h>
55#include <net/netfilter/nf_conntrack_l3proto.h>
56#include <net/netfilter/nf_conntrack_zones.h>
57#include <net/netfilter/nf_conntrack_core.h>
58#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
59#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
60
61/*
62 * Debug output levels
63 * 0 = OFF
64 * 1 = ASSERTS / ERRORS
65 * 2 = 1 + WARN
66 * 3 = 2 + INFO
67 * 4 = 3 + TRACE
68 */
69#define DEBUG_LEVEL ECM_BOND_NOTIFIER_DEBUG_LEVEL
70
71#include <nss_api_if.h>
72
73#include "ecm_types.h"
74#include "ecm_db_types.h"
75#include "ecm_state.h"
76#include "ecm_tracker.h"
77#include "ecm_classifier.h"
78#include "ecm_front_end_types.h"
79#include "ecm_tracker_udp.h"
80#include "ecm_tracker_tcp.h"
81#include "ecm_tracker_datagram.h"
82#include "ecm_db.h"
83#include "ecm_classifier_default.h"
84#include "ecm_nss_ipv4.h"
85#include "ecm_interface.h"
86// GGG #include "ecm_front_end_ipv6.h"
87
88#define ECM_BOND_ID_INVALID			0xFFFFFFFF
89
90/*
91 * Locking of the classifier - concurrency control
92 */
93static DEFINE_SPINLOCK(ecm_nss_bond_notifier_lock);			/* Protect against SMP access between netfilter, events and private threaded function. */
94
95/*
96 * Debugfs dentry object.
97 */
98static struct dentry *ecm_nss_bond_notifier_dentry;
99
100/*
101 * General operational control
102 */
103static int ecm_nss_bond_notifier_stopped = 0;			/* When non-zero further traffic will not be processed */
104
105/*
106 * ecm_nss_bond_notifier_bond_cb
107 *	Bond driver notifier
108 */
109static struct bond_cb ecm_nss_bond_notifier_bond_cb;
110
111/*
112 * NSS Context for LAG function
113 */
114static void *ecm_nss_bond_notifier_nss_context = NULL;		/* Registration for LAG */
115
116/*
117 * ecm_nss_bond_notifier_send_lag_state()
118 *	Send the currnet LAG state of a physical interface that has changed state in the bonding driver.
119 */
120static nss_tx_status_t ecm_nss_bond_notifier_send_lag_state(struct nss_ctx_instance *nss_ctx, struct net_device *slave, enum nss_lag_state_change_ev slave_state)
121{
122	int32_t lagid = 0;
123	int32_t bondid = 0;
124	int32_t slave_ifnum;
125	nss_tx_status_t nss_tx_status;
126	struct nss_lag_msg nm;
127	struct nss_lag_state_change *nlsc = NULL;
128
129	DEBUG_INFO("Send LAG update for: %p (%s)\n", slave, slave->name);
130
131	/*
132	 * Can only handle interfaces known to the nss
133	 */
134	slave_ifnum = nss_cmn_get_interface_number(nss_ctx, slave);
135	if (slave_ifnum < 0) {
136		DEBUG_WARN("Not an NSS interface: %p\n", slave);
137		return NSS_TX_FAILURE_BAD_PARAM;
138	}
139
140	/*
141	 * Figure out the aggregation id of this slave
142	 */
143	memset(&nm, 0, sizeof(nm));
144	bondid = bond_get_id(slave->master);
145	if (bondid == ECM_BOND_ID_INVALID) {
146		DEBUG_WARN("Invalid LAG group id 0x%x\n", bondid);
147		return NSS_TX_FAILURE;
148	}
149
150	lagid = bondid + NSS_LAG0_INTERFACE_NUM;
151
152	/*
153	 * Construct a message to the NSS to update it
154	 */
155	nss_lag_msg_init(&nm, lagid,
156			 NSS_TX_METADATA_LAG_STATE_CHANGE,
157			 sizeof(struct nss_lag_state_change),
158			 NULL, NULL);
159
160	nlsc = &nm.msg.state;
161
162	nlsc->event = slave_state;
163
164	nlsc->interface = slave_ifnum;
165
166	nss_tx_status = nss_lag_tx(nss_ctx, &nm);
167	if (nss_tx_status != NSS_TX_SUCCESS) {
168		DEBUG_WARN("%p: Send LAG update failed, status: %d\n", slave, nss_tx_status);
169		return NSS_TX_FAILURE;
170	}
171	DEBUG_TRACE("%p: Send LAG update sent\n", slave);
172	return NSS_TX_SUCCESS;
173}
174
175/*
176 * ecm_nss_bond_notifier_bond_release()
177 *	Callback when a slave device is released from slavedom and no longer a part of a bonded interface.
178 */
179static void ecm_nss_bond_notifier_bond_release(struct net_device *slave_dev)
180{
181	/*
182	 * If operations have stopped then do not process event
183	 */
184	DEBUG_INFO("Bond slave release: %p (%s)\n", slave_dev, slave_dev->name);
185	spin_lock_bh(&ecm_nss_bond_notifier_lock);
186	if (unlikely(ecm_nss_bond_notifier_stopped)) {
187		DEBUG_WARN("Ignoring bond release event - stopped\n");
188		spin_unlock_bh(&ecm_nss_bond_notifier_lock);
189		return;
190	}
191	spin_unlock_bh(&ecm_nss_bond_notifier_lock);
192	ecm_nss_bond_notifier_send_lag_state(ecm_nss_bond_notifier_nss_context, slave_dev, NSS_LAG_RELEASE);
193}
194
195/*
196 * ecm_nss_bond_notifier_bond_enslave()
197 *	Callback when a device is enslaved by a LAG master device
198 */
199static void ecm_nss_bond_notifier_bond_enslave(struct net_device *slave_dev)
200{
201	/*
202	 * If operations have stopped then do not process event
203	 */
204	DEBUG_INFO("Bond slave enslave: %p (%s)\n", slave_dev, slave_dev->name);
205	spin_lock_bh(&ecm_nss_bond_notifier_lock);
206	if (unlikely(ecm_nss_bond_notifier_stopped)) {
207		DEBUG_WARN("Ignoring bond enslave event - stopped\n");
208		spin_unlock_bh(&ecm_nss_bond_notifier_lock);
209		return;
210	}
211	spin_unlock_bh(&ecm_nss_bond_notifier_lock);
212	ecm_nss_bond_notifier_send_lag_state(ecm_nss_bond_notifier_nss_context, slave_dev, NSS_LAG_ENSLAVE);
213}
214
215/*
216 * ecm_nss_bond_notifier_bond_link_down()
217 *	Callback when a link goes down on a LAG slave
218 */
219static void ecm_nss_bond_notifier_bond_link_down(struct net_device *slave_dev)
220{
221	struct net_device *master;
222
223	/*
224	 * If operations have stopped then do not process event
225	 */
226	spin_lock_bh(&ecm_nss_bond_notifier_lock);
227	if (unlikely(ecm_nss_bond_notifier_stopped)) {
228		DEBUG_WARN("Ignoring bond link down event - stopped\n");
229		spin_unlock_bh(&ecm_nss_bond_notifier_lock);
230		return;
231	}
232	spin_unlock_bh(&ecm_nss_bond_notifier_lock);
233
234	/*
235	 * A net device that is a LAG slave has lost link.
236	 * Due to the heiarchical nature of network topologies, this can
237	 * change the packet transmit path for any connection that is using
238	 * a device that it sitting "higher" in the heirarchy. Regenerate all
239	 * connections using the LAG master.
240	 */
241
242	master = ecm_interface_get_and_hold_dev_master(slave_dev);
243	ecm_interface_dev_regenerate_connections(master);
244	dev_put(master);
245}
246
247/*
248 * ecm_bond_notifier_bond_link_up()
249 *	Callback when a device is enslaved by a LAG master device
250 */
251static void ecm_nss_bond_notifier_bond_link_up(struct net_device *slave_dev)
252{
253	struct net_device *master;
254
255	/*
256	 * If operations have stopped then do not process event
257	 */
258	spin_lock_bh(&ecm_nss_bond_notifier_lock);
259	if (unlikely(ecm_nss_bond_notifier_stopped)) {
260		DEBUG_WARN("Ignoring bond enslave event - stopped\n");
261		spin_unlock_bh(&ecm_nss_bond_notifier_lock);
262		return;
263	}
264	spin_unlock_bh(&ecm_nss_bond_notifier_lock);
265
266	/*
267	 * Tricky to handle, this one.
268	 * A net device that is a LAG slave has become active.
269	 * Due to the heiarchical nature of network topologies, this can change the packet transmit path
270	 * for any connection that is using a device that it sitting "higher" in the heirarchy.
271	 * Regenerate all connections using the LAG master.
272	 */
273
274	master = ecm_interface_get_and_hold_dev_master(slave_dev);
275	ecm_interface_dev_regenerate_connections(master);
276	dev_put(master);
277}
278
279/*
280 * ecm_nss_bond_notifier_lag_event_cb()
281 *	Handle LAG event from the NSS driver
282 */
283static void ecm_nss_bond_notifier_lag_event_cb(void *if_ctx, struct nss_lag_msg *msg)
284{
285	/*
286	 * If operations have stopped then do not process event
287	 */
288	spin_lock_bh(&ecm_nss_bond_notifier_lock);
289	if (unlikely(ecm_nss_bond_notifier_stopped)) {
290		DEBUG_WARN("Ignoring LAG event event - stopped\n");
291		spin_unlock_bh(&ecm_nss_bond_notifier_lock);
292		return;
293	}
294	spin_unlock_bh(&ecm_nss_bond_notifier_lock);
295
296	/*
297	 * GGG TODO Figure out if there is anything we need to do here, the old CM did nothing..
298	 */
299	switch (msg->cm.type)
300	{
301		default:
302			DEBUG_INFO("Unknown LAG event from NSS: %d", msg->cm.type);
303			break;
304	}
305}
306
307void ecm_nss_bond_notifier_stop(int num)
308{
309	ecm_nss_bond_notifier_stopped = num;
310}
311EXPORT_SYMBOL(ecm_nss_bond_notifier_stop);
312
313/*
314 * ecm_nss_bond_notifier_init()
315 */
316int ecm_nss_bond_notifier_init(struct dentry *dentry)
317{
318	DEBUG_INFO("ECM Bonding Notifier init\n");
319
320	ecm_nss_bond_notifier_dentry = debugfs_create_dir("ecm_nss_bond_notifier", dentry);
321	if (!ecm_nss_bond_notifier_dentry) {
322		DEBUG_ERROR("Failed to create ecm bond notifier directory in debugfs\n");
323		return -1;
324	}
325
326	if (!debugfs_create_u32("stop", S_IRUGO | S_IWUSR, ecm_nss_bond_notifier_dentry,
327					(u32 *)&ecm_nss_bond_notifier_stopped)) {
328		DEBUG_ERROR("Failed to create ecm bond notifier stopped file in debugfs\n");
329		debugfs_remove_recursive(ecm_nss_bond_notifier_dentry);
330		return -1;
331	}
332
333	/*
334	 * Register Link Aggregation interfaces with NSS driver
335	 */
336	ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG0_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL);
337	ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG1_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL);
338	ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG2_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL);
339	ecm_nss_bond_notifier_nss_context = nss_register_lag_if(NSS_LAG3_INTERFACE_NUM, NULL, ecm_nss_bond_notifier_lag_event_cb, NULL);
340
341	/*
342	 * Register Link Aggregation callbacks with the bonding driver
343	 */
344	ecm_nss_bond_notifier_bond_cb.bond_cb_link_up = ecm_nss_bond_notifier_bond_link_up;
345	ecm_nss_bond_notifier_bond_cb.bond_cb_link_down = ecm_nss_bond_notifier_bond_link_down;
346	ecm_nss_bond_notifier_bond_cb.bond_cb_release = ecm_nss_bond_notifier_bond_release;
347	ecm_nss_bond_notifier_bond_cb.bond_cb_enslave = ecm_nss_bond_notifier_bond_enslave;
348	bond_register_cb(&ecm_nss_bond_notifier_bond_cb);
349
350	return 0;
351}
352EXPORT_SYMBOL(ecm_nss_bond_notifier_init);
353
354/*
355 * ecm_nss_bond_notifier_exit()
356 */
357void ecm_nss_bond_notifier_exit(void)
358{
359	DEBUG_INFO("ECM Bonding Notifier exit\n");
360
361	/*
362	 * Unregister from the bond driver
363	 */
364	bond_unregister_cb();
365
366	/*
367	 * Unregister Link Aggregation interfaces with NSS driver
368	 */
369	nss_unregister_lag_if(NSS_LAG0_INTERFACE_NUM);
370	nss_unregister_lag_if(NSS_LAG1_INTERFACE_NUM);
371	nss_unregister_lag_if(NSS_LAG2_INTERFACE_NUM);
372	nss_unregister_lag_if(NSS_LAG3_INTERFACE_NUM);
373
374	/*
375	 * Remove the debugfs files recursively.
376	 */
377	if (ecm_nss_bond_notifier_dentry) {
378		debugfs_remove_recursive(ecm_nss_bond_notifier_dentry);
379	}
380}
381EXPORT_SYMBOL(ecm_nss_bond_notifier_exit);
382