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#include <linux/version.h>
18#include <linux/types.h>
19#include <linux/ip.h>
20#include <linux/tcp.h>
21#include <linux/module.h>
22#include <linux/skbuff.h>
23#include <linux/icmp.h>
24#include <linux/kthread.h>
25#include <linux/debugfs.h>
26#include <linux/pkt_sched.h>
27#include <linux/string.h>
28#include <net/route.h>
29#include <net/ip.h>
30#include <net/tcp.h>
31#include <asm/unaligned.h>
32#include <asm/uaccess.h>	/* for put_user */
33#include <net/ipv6.h>
34#include <linux/inet.h>
35#include <linux/in.h>
36#include <linux/udp.h>
37#include <linux/tcp.h>
38
39#include <linux/netfilter_ipv4.h>
40#include <linux/netfilter_bridge.h>
41#include <net/netfilter/nf_conntrack.h>
42#include <net/netfilter/nf_conntrack_helper.h>
43#include <net/netfilter/nf_conntrack_l4proto.h>
44#include <net/netfilter/nf_conntrack_l3proto.h>
45#include <net/netfilter/nf_conntrack_core.h>
46#include <net/netfilter/nf_conntrack_zones.h>
47#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
48#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
49#include <net/genetlink.h>
50
51/*
52 * Debug output levels
53 * 0 = OFF
54 * 1 = ASSERTS / ERRORS
55 * 2 = 1 + WARN
56 * 3 = 2 + INFO
57 * 4 = 3 + TRACE
58 */
59#define DEBUG_LEVEL ECM_CLASSIFIER_NL_DEBUG_LEVEL
60
61#include "ecm_types.h"
62#include "ecm_db_types.h"
63#include "ecm_state.h"
64#include "ecm_tracker.h"
65#include "ecm_classifier.h"
66#include "ecm_front_end_types.h"
67#include "ecm_tracker_udp.h"
68#include "ecm_tracker_tcp.h"
69#include "ecm_classifier_nl.h"
70#include "ecm_db.h"
71#include "ecm_front_end_ipv4.h"
72#ifdef ECM_IPV6_ENABLE
73#include "ecm_front_end_ipv6.h"
74#endif
75
76/*
77 * Magic numbers
78 */
79#define ECM_CLASSIFIER_NL_INSTANCE_MAGIC 0xFE12
80
81#define ECM_CLASSIFIER_NL_F_ACCEL	(1 << 0) /* acceleration requested */
82#define ECM_CLASSIFIER_NL_F_ACCEL_OK	(1 << 1) /* acceleration confirmed */
83#define ECM_CLASSIFIER_NL_F_CLOSED	(1 << 2) /* close event issued */
84
85/*
86 * struct ecm_classifier_nl_instance
87 * 	State to allow tracking of dynamic qos for a connection
88 */
89struct ecm_classifier_nl_instance {
90	struct ecm_classifier_instance base;			/* Base type */
91
92	struct ecm_classifier_nl_instance *next;		/* Next classifier state instance (for accouting and reporting purposes) */
93	struct ecm_classifier_nl_instance *prev;		/* Next classifier state instance (for accouting and reporting purposes) */
94
95	uint32_t ci_serial;					/* RO: Serial of the connection */
96	struct ecm_classifier_process_response process_response;/* Last process response computed */
97	int refs;						/* Integer to trap we never go negative */
98	unsigned int flags;					/* See ECM_CLASSIFIER_NL_F_* */
99#if (DEBUG_LEVEL > 0)
100	uint16_t magic;
101#endif
102};
103
104/*
105 * Operational control
106 */
107static bool ecm_classifier_nl_enabled = false;		/* Operational behaviour */
108
109/*
110 * Management thread control
111 */
112static bool ecm_classifier_nl_terminate_pending = false;		/* True when the user wants us to terminate */
113
114/*
115 * Debugfs dentry object.
116 */
117static struct dentry *ecm_classifier_nl_dentry;
118
119/*
120 * Locking of the classifier structures
121 */
122static DEFINE_SPINLOCK(ecm_classifier_nl_lock);			/* Protect SMP access. */
123
124/*
125 * List of our classifier instances
126 */
127static struct ecm_classifier_nl_instance *ecm_classifier_nl_instances = NULL;
128									/* list of all active instances */
129static int ecm_classifier_nl_count = 0;					/* Tracks number of instances allocated */
130
131/*
132 * Listener for db events
133 */
134struct ecm_db_listener_instance *ecm_classifier_nl_li = NULL;
135
136/*
137 * Generic Netlink family and multicast group names
138 */
139static struct genl_multicast_group ecm_cl_nl_genl_mcgrp = {
140	.name = ECM_CL_NL_GENL_MCGRP,
141};
142
143static struct genl_family ecm_cl_nl_genl_family = {
144	.id = GENL_ID_GENERATE,
145	.hdrsize = 0,
146	.name = ECM_CL_NL_GENL_NAME,
147	.version = ECM_CL_NL_GENL_VERSION,
148	.maxattr = ECM_CL_NL_GENL_ATTR_MAX,
149};
150
151/*
152 * helper for sending basic genl commands requiring only a tuple attribute
153 *
154 * TODO: implement a message queue serviced by a thread to allow automatic
155 *	 retries for accel_ok and closed messages.
156 */
157static int
158ecm_classifier_nl_send_genl_msg(enum ECM_CL_NL_GENL_CMD cmd,
159				struct ecm_cl_nl_genl_attr_tuple *tuple)
160{
161	int ret;
162	void *msg_head;
163	struct sk_buff *skb;
164
165	skb = genlmsg_new(sizeof(*tuple) + ecm_cl_nl_genl_family.hdrsize,
166			  GFP_ATOMIC);
167	if (skb == NULL) {
168		DEBUG_WARN("failed to alloc nlmsg\n");
169		return -ENOMEM;
170	}
171
172	msg_head = genlmsg_put(skb,
173			       0, /* netlink PID */
174			       0, /* sequence number */
175			       &ecm_cl_nl_genl_family,
176			       0, /* flags */
177			       cmd);
178	if (msg_head == NULL) {
179		DEBUG_WARN("failed to add genl headers\n");
180		nlmsg_free(skb);
181		return -ENOMEM;
182	}
183
184	ret = nla_put(skb, ECM_CL_NL_GENL_ATTR_TUPLE, sizeof(*tuple), tuple);
185	if (ret != 0) {
186		DEBUG_WARN("failed to put tuple into genl msg: %d\n", ret);
187		nlmsg_free(skb);
188		return ret;
189	}
190
191	ret = genlmsg_end(skb, msg_head);
192	if (ret < 0) {
193		DEBUG_WARN("failed to finalize genl msg: %d\n", ret);
194		nlmsg_free(skb);
195		return ret;
196	}
197
198	/* genlmsg_multicast frees the skb in both success and error cases */
199	ret = genlmsg_multicast(skb, 0, ecm_cl_nl_genl_mcgrp.id, GFP_ATOMIC);
200	if (ret != 0) {
201		DEBUG_WARN("genl multicast failed: %d\n", ret);
202		return ret;
203	}
204
205	return 0;
206}
207
208/*
209 * ecm_cl_nl_genl_attr_tuple_encode()
210 *	Helper function to convert connection IP info into a genl_attr_tuple
211 */
212static int ecm_cl_nl_genl_attr_tuple_encode(struct ecm_cl_nl_genl_attr_tuple *tuple,
213				 int ip_version,
214				 int proto,
215				 ip_addr_t src_ip,
216				 int src_port,
217				 ip_addr_t dst_ip,
218				 int dst_port)
219{
220	memset(tuple, 0, sizeof(*tuple));
221	tuple->proto = (uint8_t)proto;
222	tuple->src_port = htons((uint16_t)src_port);
223	tuple->dst_port = htons((uint16_t)dst_port);
224	if (ip_version == 4) {
225		tuple->af = AF_INET;
226		ECM_IP_ADDR_TO_NIN4_ADDR(tuple->src_ip.in.s_addr, src_ip);
227		ECM_IP_ADDR_TO_NIN4_ADDR(tuple->dst_ip.in.s_addr, dst_ip);
228		return 0;
229	}
230#ifdef ECM_IPV6_ENABLE
231	if (ip_version == 6) {
232		tuple->af = AF_INET6;
233		ECM_IP_ADDR_TO_NIN6_ADDR(tuple->src_ip.in6, src_ip);
234		ECM_IP_ADDR_TO_NIN6_ADDR(tuple->dst_ip.in6, dst_ip);
235		return 0;
236	}
237#endif
238	return -EAFNOSUPPORT;
239}
240
241/*
242 * ecm_cl_nl_genl_attr_tuple_decode()
243 *	Helper function to convert a genl_attr_tuple into connection IP info
244 */
245static int ecm_cl_nl_genl_attr_tuple_decode(struct ecm_cl_nl_genl_attr_tuple *tuple,
246				 int *proto,
247				 ip_addr_t src_ip,
248				 int *src_port,
249				 ip_addr_t dst_ip,
250				 int *dst_port)
251{
252	*proto = tuple->proto;
253	*src_port = ntohs(tuple->src_port);
254	*dst_port = ntohs(tuple->dst_port);
255	if (AF_INET == tuple->af) {
256		ECM_NIN4_ADDR_TO_IP_ADDR(src_ip, tuple->src_ip.in.s_addr);
257		ECM_NIN4_ADDR_TO_IP_ADDR(dst_ip, tuple->dst_ip.in.s_addr);
258		return 0;
259	}
260#ifdef ECM_IPV6_ENABLE
261	if (AF_INET6 == tuple->af) {
262		ECM_NIN6_ADDR_TO_IP_ADDR(src_ip, tuple->src_ip.in6);
263		ECM_NIN6_ADDR_TO_IP_ADDR(dst_ip, tuple->dst_ip.in6);
264		return 0;
265	}
266#endif
267	return -EAFNOSUPPORT;
268}
269
270/*
271 * ecm_classifier_nl_genl_msg_ACCEL_OK()
272 *	Indicates that Accelleration is okay to the netlink channel
273 */
274static void ecm_classifier_nl_genl_msg_ACCEL_OK(struct ecm_classifier_nl_instance *cnli)
275{
276	struct ecm_db_connection_instance *ci;
277	int ret;
278	int ip_version;
279	int proto;
280	int src_port;
281	int dst_port;
282	ip_addr_t src_ip;
283	ip_addr_t dst_ip;
284	struct ecm_cl_nl_genl_attr_tuple tuple;
285
286	/*
287	 * Lookup the associated connection
288	 */
289	ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial);
290	if (!ci) {
291		DEBUG_TRACE("%p: No ci found for %u\n", cnli, cnli->ci_serial);
292		return;
293	}
294
295	spin_lock_bh(&ecm_classifier_nl_lock);
296
297	/* if we've already issued an ACCEL_OK on this connection,
298	   do not send it again */
299	if (cnli->flags & ECM_CLASSIFIER_NL_F_ACCEL_OK) {
300		spin_unlock_bh(&ecm_classifier_nl_lock);
301		ecm_db_connection_deref(ci);
302		return;
303	}
304
305	spin_unlock_bh(&ecm_classifier_nl_lock);
306
307	proto = ecm_db_connection_protocol_get(ci);
308	ecm_db_connection_from_address_get(ci, src_ip);
309	src_port = (uint16_t)ecm_db_connection_from_port_get(ci);
310	ecm_db_connection_to_address_get(ci, dst_ip);
311	dst_port = ecm_db_connection_to_port_get(ci);
312
313	ip_version = ecm_db_connection_ip_version_get(ci);
314	ecm_db_connection_deref(ci);
315
316	ret = ecm_cl_nl_genl_attr_tuple_encode(&tuple,
317						ip_version,
318						proto,
319						src_ip,
320						src_port,
321						dst_ip,
322						dst_port);
323	if (ret != 0) {
324		DEBUG_WARN("failed to encode genl_attr_tuple: %d\n", ret);
325		return;
326	}
327
328	ret = ecm_classifier_nl_send_genl_msg(ECM_CL_NL_GENL_CMD_ACCEL_OK,
329					      &tuple);
330	if (ret != 0) {
331		DEBUG_WARN("failed to send ACCEL_OK: %p, serial %u\n",
332			   cnli, cnli->ci_serial);
333		return;
334	}
335
336	spin_lock_bh(&ecm_classifier_nl_lock);
337	cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL_OK;
338	spin_unlock_bh(&ecm_classifier_nl_lock);
339}
340
341/*
342 * ecm_classifier_nl_genl_msg_closed()
343 *	Invoke this when the connection has been closed and it has been accelerated previously.
344 *
345 * GGG TODO The purpose of this is not clear, esp. wrt. "accel ok" message.
346 * DO NOT CALL THIS UNLESS ECM_CLASSIFIER_NL_F_ACCEL_OK has been set.
347 */
348static void ecm_classifier_nl_genl_msg_closed(struct ecm_db_connection_instance *ci, struct ecm_classifier_nl_instance *cnli,
349					int proto, ip_addr_t src_ip, ip_addr_t dst_ip, int src_port, int dst_port)
350{
351	int ip_version;
352	int ret;
353	struct ecm_cl_nl_genl_attr_tuple tuple;
354
355	spin_lock_bh(&ecm_classifier_nl_lock);
356	cnli->flags |= ECM_CLASSIFIER_NL_F_CLOSED;
357	spin_unlock_bh(&ecm_classifier_nl_lock);
358
359	ip_version = ecm_db_connection_ip_version_get(ci);
360	ret = ecm_cl_nl_genl_attr_tuple_encode(&tuple,
361						ip_version,
362						proto,
363						src_ip,
364						src_port,
365						dst_ip,
366						dst_port);
367	if (ret != 0) {
368		DEBUG_WARN("failed to encode genl_attr_tuple: %d\n", ret);
369		return;
370	}
371
372	ecm_classifier_nl_send_genl_msg(ECM_CL_NL_GENL_CMD_CONNECTION_CLOSED, &tuple);
373}
374
375/*
376 * ecm_classifier_nl_genl_msg_ACCEL()
377 *	handles a ECM_CL_NL_ACCEL message
378 */
379static int ecm_classifier_nl_genl_msg_ACCEL(struct sk_buff *skb,
380					    struct genl_info *info)
381{
382	int ret;
383	struct nlattr *na;
384	struct ecm_cl_nl_genl_attr_tuple *tuple;
385	struct ecm_db_connection_instance *ci;
386	struct ecm_classifier_nl_instance *cnli;
387
388	/* the netlink message comes to us in network order, but ECM
389	   stores addresses in host order */
390	int proto;
391	int src_port;
392	int dst_port;
393	ip_addr_t src_ip;
394	ip_addr_t dst_ip;
395
396	/*
397	 * Check if we are enabled
398	 */
399	spin_lock_bh(&ecm_classifier_nl_lock);
400	if (!ecm_classifier_nl_enabled) {
401		spin_unlock_bh(&ecm_classifier_nl_lock);
402		return -ECONNREFUSED;
403	}
404	spin_unlock_bh(&ecm_classifier_nl_lock);
405
406	na = info->attrs[ECM_CL_NL_GENL_ATTR_TUPLE];
407	tuple = nla_data(na);
408
409	ret = ecm_cl_nl_genl_attr_tuple_decode(tuple,
410					       &proto,
411					       src_ip,
412					       &src_port,
413					       dst_ip,
414					       &dst_port);
415	if (ret != 0) {
416		DEBUG_WARN("failed to decode genl_attr_tuple: %d\n", ret);
417		return ret;
418	}
419
420	/*
421	 * Locate the connection using the tuple given
422	 */
423	DEBUG_TRACE("ACCEL: Lookup connection "
424		    ECM_IP_ADDR_OCTAL_FMT ":%d <> "
425		    ECM_IP_ADDR_OCTAL_FMT ":%d "
426		    "protocol %d\n",
427		    ECM_IP_ADDR_TO_OCTAL(src_ip),
428		    src_port,
429		    ECM_IP_ADDR_TO_OCTAL(dst_ip),
430		    dst_port,
431		    tuple->proto);
432	ci = ecm_db_connection_find_and_ref(src_ip,
433					    dst_ip,
434					    proto,
435					    src_port,
436					    dst_port);
437	if (!ci) {
438		DEBUG_WARN("database connection not found\n");
439		return -ENOENT;
440	}
441	DEBUG_TRACE("Connection found: %p\n", ci);
442
443	/*
444	 * Get the NL classifier for this connection
445	 */
446	cnli = (struct ecm_classifier_nl_instance *)
447		ecm_db_connection_assigned_classifier_find_and_ref(ci,
448			ECM_CLASSIFIER_TYPE_NL);
449	if (!cnli) {
450		ecm_db_connection_deref(ci);
451		return -EUNATCH;
452	}
453
454	/*
455	 * Allow acceleration of the connection.  This will be done as
456	 * packets are processed in the usual way.
457	 */
458	DEBUG_TRACE("Permit accel: %p\n", ci);
459	spin_lock_bh(&ecm_classifier_nl_lock);
460	cnli->process_response.accel_mode =
461		ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
462	cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL;
463	spin_unlock_bh(&ecm_classifier_nl_lock);
464
465	cnli->base.deref((struct ecm_classifier_instance *)cnli);
466	ecm_db_connection_deref(ci);
467
468	return 0;
469}
470
471
472/*
473 * ecm_classifier_nl_ref()
474 *	Ref
475 */
476static void ecm_classifier_nl_ref(struct ecm_classifier_instance *ci)
477{
478	struct ecm_classifier_nl_instance *cnli;
479	cnli = (struct ecm_classifier_nl_instance *)ci;
480
481	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
482	spin_lock_bh(&ecm_classifier_nl_lock);
483	cnli->refs++;
484	DEBUG_TRACE("%p: cnli ref %d\n", cnli, cnli->refs);
485	DEBUG_ASSERT(cnli->refs > 0, "%p: ref wrap\n", cnli);
486	spin_unlock_bh(&ecm_classifier_nl_lock);
487}
488
489/*
490 * ecm_classifier_nl_deref()
491 *	Deref
492 */
493static int ecm_classifier_nl_deref(struct ecm_classifier_instance *ci)
494{
495	struct ecm_classifier_nl_instance *cnli;
496	cnli = (struct ecm_classifier_nl_instance *)ci;
497
498	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
499
500	spin_lock_bh(&ecm_classifier_nl_lock);
501	cnli->refs--;
502	DEBUG_ASSERT(cnli->refs >= 0, "%p: refs wrapped\n", cnli);
503	DEBUG_TRACE("%p: Netlink classifier deref %d\n", cnli, cnli->refs);
504	if (cnli->refs) {
505		int refs = cnli->refs;
506		spin_unlock_bh(&ecm_classifier_nl_lock);
507		return refs;
508	}
509
510	/*
511	 * Object to be destroyed
512	 */
513	ecm_classifier_nl_count--;
514	DEBUG_ASSERT(ecm_classifier_nl_count >= 0, "%p: ecm_classifier_nl_count wrap\n", cnli);
515
516	/*
517	 * UnLink the instance from our list
518	 */
519	if (cnli->next) {
520		cnli->next->prev = cnli->prev;
521	}
522	if (cnli->prev) {
523		cnli->prev->next = cnli->next;
524	} else {
525		DEBUG_ASSERT(ecm_classifier_nl_instances == cnli, "%p: list bad %p\n", cnli, ecm_classifier_nl_instances);
526		ecm_classifier_nl_instances = cnli->next;
527	}
528	cnli->next = NULL;
529	cnli->prev = NULL;
530	spin_unlock_bh(&ecm_classifier_nl_lock);
531
532	/*
533	 * Final
534	 */
535	DEBUG_INFO("%p: Final Netlink classifier instance\n", cnli);
536	kfree(cnli);
537
538	return 0;
539}
540
541void
542ecm_classifier_nl_process_mark(struct ecm_classifier_nl_instance *cnli,
543			       uint32_t mark)
544{
545	bool updated;
546	ecm_front_end_acceleration_mode_t accel_mode;
547	struct ecm_db_connection_instance *ci;
548	struct ecm_front_end_connection_instance *feci;
549
550	updated = false;
551
552	spin_lock_bh(&ecm_classifier_nl_lock);
553
554	/*
555	 * If the mark is different to either of the current flow or return qos tags then we override them.
556	 * NOTE: This will force a change of the skb priority and also drive through these qos tags in any acceleration rule.
557	 */
558	if ((mark != cnli->process_response.flow_qos_tag) || (mark != cnli->process_response.return_qos_tag)) {
559		cnli->process_response.flow_qos_tag = mark;
560		cnli->process_response.return_qos_tag = mark;
561		cnli->process_response.process_actions |=
562			ECM_CLASSIFIER_PROCESS_ACTION_QOS_TAG;
563		updated = true;
564	}
565	spin_unlock_bh(&ecm_classifier_nl_lock);
566
567	if (!updated) {
568		return;
569	}
570
571	/*
572	 * we need to make sure to propagate the new mark to the
573	 * accel engine if the connection has been accelerated.  to do that,
574	 * since there's no way to directly update an offload rule,
575	 * we simply decelerate the connection which should result
576	 * in a re-acceleration when the next packet is processed
577	 * by the front end, thereby applying the new mark.
578	 */
579
580	/*
581	 * Lookup the associated connection
582	 */
583	ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial);
584	if (!ci) {
585		DEBUG_TRACE("%p: No ci found for %u\n", cnli, cnli->ci_serial);
586		return;
587	}
588	feci = ecm_db_connection_front_end_get_and_ref(ci);
589	accel_mode = feci->accel_state_get(feci);
590	if ((accel_mode == ECM_FRONT_END_ACCELERATION_MODE_ACCEL)
591			|| (accel_mode == ECM_FRONT_END_ACCELERATION_MODE_ACCEL_PENDING)) {
592		DEBUG_TRACE("%p: mark changed on offloaded connection, decelerate. new mark: 0x%08x\n",
593			    cnli, mark);
594		feci->decelerate(feci);
595	} else {
596		DEBUG_TRACE("%p: mark changed on non-offloaded connection. new mark: 0x%08x\n",
597			    cnli, mark);
598	}
599	feci->deref(feci);
600	ecm_db_connection_deref(ci);
601}
602EXPORT_SYMBOL(ecm_classifier_nl_process_mark);
603
604/*
605 * ecm_classifier_nl_process()
606 *	Process new data for connection
607 */
608static void ecm_classifier_nl_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender,
609						struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb,
610						struct ecm_classifier_process_response *process_response)
611{
612	struct ecm_classifier_nl_instance *cnli;
613	ecm_classifier_relevence_t relevance;
614	bool enabled;
615	struct ecm_db_connection_instance *ci;
616	ecm_front_end_acceleration_mode_t accel_mode;
617	uint32_t became_relevant = 0;
618
619	cnli = (struct ecm_classifier_nl_instance *)aci;
620	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
621
622	/*
623	 * Have we decided our relevance?  If so return our state.
624	 */
625	spin_lock_bh(&ecm_classifier_nl_lock);
626	relevance = cnli->process_response.relevance;
627	if (relevance != ECM_CLASSIFIER_RELEVANCE_MAYBE) {
628		*process_response = cnli->process_response;
629		spin_unlock_bh(&ecm_classifier_nl_lock);
630		return;
631	}
632
633	/*
634	 * Decide upon relevance
635	 */
636	enabled = ecm_classifier_nl_enabled;
637	spin_unlock_bh(&ecm_classifier_nl_lock);
638
639	/*
640	 * If classifier is enabled, the connection is routed and the front end says it can accel then we are "relevant".
641	 * Any other condition and we are not and will stop analysing this connection.
642	 */
643	relevance = ECM_CLASSIFIER_RELEVANCE_NO;
644	ci = ecm_db_connection_serial_find_and_ref(cnli->ci_serial);
645	if (ci) {
646		struct ecm_front_end_connection_instance *feci;
647		feci = ecm_db_connection_front_end_get_and_ref(ci);
648		accel_mode = feci->accel_state_get(feci);
649		feci->deref(feci);
650		if (enabled && ECM_FRONT_END_ACCELERATION_POSSIBLE(accel_mode) && ecm_db_connection_is_routed_get(ci)) {
651			relevance = ECM_CLASSIFIER_RELEVANCE_YES;
652			became_relevant = ecm_db_time_get();
653		}
654		ecm_db_connection_deref(ci);
655	}
656
657	/*
658	 * Return process response
659	 */
660	spin_lock_bh(&ecm_classifier_nl_lock);
661	cnli->process_response.relevance = relevance;
662	cnli->process_response.became_relevant = became_relevant;
663	*process_response = cnli->process_response;
664	spin_unlock_bh(&ecm_classifier_nl_lock);
665}
666
667/*
668 * ecm_classifier_nl_sync_to_v4()
669 *	Front end is pushing accel engine state to us
670 */
671static void ecm_classifier_nl_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
672{
673	struct ecm_classifier_nl_instance *cnli;
674
675	if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) {
676		/*
677		 * Nothing to update.
678		 * We only care about flows that are actively being accelerated.
679		 */
680		return;
681	}
682
683	cnli = (struct ecm_classifier_nl_instance *)aci;
684	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli);
685
686	switch(sync->reason) {
687	case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_FLUSH:
688		/* do nothing */
689		DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_FLUSH\n", cnli);
690		break;
691	case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_EVICT:
692		/* do nothing */
693		DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_EVICT\n", cnli);
694		break;
695	case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_DESTROY:
696		DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_DESTROY\n", cnli);
697		break;
698	case ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS:
699		DEBUG_TRACE("%p: nl_sync_to_v4: SYNC_STATS\n", cnli);
700		ecm_classifier_nl_genl_msg_ACCEL_OK(cnli);
701		break;
702	default:
703		DEBUG_TRACE("%p: nl_sync_to_v4: unsupported reason\n", cnli);
704		break;
705	}
706}
707
708/*
709 * ecm_classifier_nl_sync_from_v4()
710 *	Front end is retrieving accel engine state from us
711 */
712static void ecm_classifier_nl_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
713{
714	struct ecm_classifier_nl_instance *cnli;
715
716	cnli = (struct ecm_classifier_nl_instance *)aci;
717	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli);
718}
719
720/*
721 * ecm_classifier_nl_sync_to_v6()
722 *	Front end is pushing accel engine state to us
723 */
724static void ecm_classifier_nl_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
725{
726	struct ecm_classifier_nl_instance *cnli;
727
728	cnli = (struct ecm_classifier_nl_instance *)aci;
729	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli);
730
731	if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) {
732		/*
733		 * No traffic has been accelerated.
734		 * Nothing to update. We only care about flows that are actively being accelerated.
735		 */
736		DEBUG_TRACE("%p: No traffic\n", cnli);
737		return;
738	}
739
740	/*
741	 * If this sync does NOT contain a final sync then we have seen traffic
742	 * and that acceleration is continuing - acceleration is OK
743	 */
744	switch(sync->reason) {
745	case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_FLUSH:
746		/* do nothing */
747		DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_FLUSH\n", cnli);
748		break;
749	case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_EVICT:
750		/* do nothing */
751		DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_EVICT\n", cnli);
752		break;
753	case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_DESTROY:
754		/* do nothing */
755		DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_DESTROY\n", cnli);
756		break;
757	case ECM_FRONT_END_IPV6_RULE_SYNC_REASON_STATS:
758		DEBUG_TRACE("%p: nl_sync_to_v6: SYNC_STATS\n", cnli);
759		ecm_classifier_nl_genl_msg_ACCEL_OK(cnli);
760		break;
761	default:
762		DEBUG_TRACE("%p: nl_sync_to_v6: unsupported reason\n", cnli);
763		break;
764	}
765}
766
767/*
768 * ecm_classifier_nl_sync_from_v6()
769 *	Front end is retrieving accel engine state from us
770 */
771static void ecm_classifier_nl_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
772{
773	struct ecm_classifier_nl_instance *cnli;
774
775	cnli = (struct ecm_classifier_nl_instance *)aci;
776	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli);
777
778}
779
780/*
781 * ecm_classifier_nl_type_get()
782 *	Get type of classifier this is
783 */
784static ecm_classifier_type_t ecm_classifier_nl_type_get(struct ecm_classifier_instance *ci)
785{
786	struct ecm_classifier_nl_instance *cnli;
787	cnli = (struct ecm_classifier_nl_instance *)ci;
788
789	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
790	return ECM_CLASSIFIER_TYPE_NL;
791}
792
793/*
794 * ecm_classifier_nl_last_process_response_get()
795 *	Get result code returned by the last process call
796 */
797static void ecm_classifier_nl_last_process_response_get(struct ecm_classifier_instance *ci,
798							struct ecm_classifier_process_response *process_response)
799{
800	struct ecm_classifier_nl_instance *cnli;
801
802	cnli = (struct ecm_classifier_nl_instance *)ci;
803	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
804
805	spin_lock_bh(&ecm_classifier_nl_lock);
806	*process_response = cnli->process_response;
807	spin_unlock_bh(&ecm_classifier_nl_lock);
808}
809
810/*
811 * ecm_classifier_nl_reclassify_allowed()
812 *	Indicate if reclassify is allowed
813 */
814static bool ecm_classifier_nl_reclassify_allowed(struct ecm_classifier_instance *ci)
815{
816	struct ecm_classifier_nl_instance *cnli;
817	cnli = (struct ecm_classifier_nl_instance *)ci;
818	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
819
820	return true;
821}
822
823/*
824 * ecm_classifier_nl_reclassify()
825 *	Reclassify
826 */
827static void ecm_classifier_nl_reclassify(struct ecm_classifier_instance *ci)
828{
829	struct ecm_classifier_nl_instance *cnli;
830	cnli = (struct ecm_classifier_nl_instance *)ci;
831	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed\n", cnli);
832
833	/*
834	 * Revert back to MAYBE relevant - we will evaluate when we get the next process() call.
835	 */
836	spin_lock_bh(&ecm_classifier_nl_lock);
837	cnli->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
838	spin_unlock_bh(&ecm_classifier_nl_lock);
839}
840
841#ifdef ECM_STATE_OUTPUT_ENABLE
842/*
843 * ecm_classifier_nl_state_get()
844 *	Return state
845 */
846static int ecm_classifier_nl_state_get(struct ecm_classifier_instance *ci, struct ecm_state_file_instance *sfi)
847{
848	int result;
849	struct ecm_classifier_nl_instance *cnli;
850	struct ecm_classifier_process_response process_response;
851
852	cnli = (struct ecm_classifier_nl_instance *)ci;
853	DEBUG_CHECK_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC, "%p: magic failed", cnli);
854
855	if ((result = ecm_state_prefix_add(sfi, "nl"))) {
856		return result;
857	}
858
859	spin_lock_bh(&ecm_classifier_nl_lock);
860	process_response = cnli->process_response;
861	spin_unlock_bh(&ecm_classifier_nl_lock);
862
863	/*
864	 * Output our last process response
865	 */
866	if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) {
867		return result;
868	}
869
870	return ecm_state_prefix_remove(sfi);
871}
872#endif
873
874/*
875 * ecm_classifier_nl_ct_get_and_ref()
876 * 	Returns the conntrack entry for an ecm_db_connection_instance.
877 * @param ci The connection instance to be used for the conntrack search.
878 * @return struct nf_conn * A pointer and a reference to a matching conntrack
879 *                          entry, or NULL if no such entry is found.
880 * @pre The ci instance is not NULL.
881 * @note This function takes a reference to the associated conntrack entry if
882 *       it is found, and the caller must nf_ct_put() this entry when done.
883 * @note FIXME: The param ci should be const, but none of the called functions
884 *       are declared const.  This would be a larger change.
885 */
886static struct nf_conn *ecm_classifier_nl_ct_get_and_ref(struct ecm_db_connection_instance *ci)
887{
888	int ip_version;
889	int proto;
890	int src_port;
891	int dst_port;
892	ip_addr_t src_ip;
893	ip_addr_t dst_ip;
894	struct nf_conntrack_tuple_hash *h;
895	struct nf_conntrack_tuple tuple = {};
896
897	DEBUG_ASSERT(ci != NULL, "ci was NULL for ct lookup");
898	ip_version = ecm_db_connection_ip_version_get(ci);
899	proto = ecm_db_connection_protocol_get(ci);
900	ecm_db_connection_from_address_get(ci, src_ip);
901	src_port = (uint16_t)ecm_db_connection_from_port_get(ci);
902	ecm_db_connection_to_address_nat_get(ci, dst_ip);
903	dst_port = (uint16_t)ecm_db_connection_to_port_nat_get(ci);
904
905	if (ip_version == 4) {
906		tuple.src.l3num = AF_INET;
907		ECM_IP_ADDR_TO_NIN4_ADDR(tuple.src.u3.ip, src_ip);
908		ECM_IP_ADDR_TO_NIN4_ADDR(tuple.dst.u3.ip, dst_ip);
909		goto ip_check_done;
910	}
911#ifdef ECM_IPV6_ENABLE
912	if (ip_version == 6) {
913		tuple.src.l3num = AF_INET6;
914		ECM_IP_ADDR_TO_NIN6_ADDR(tuple.src.u3.in6, src_ip);
915		ECM_IP_ADDR_TO_NIN6_ADDR(tuple.dst.u3.in6, dst_ip);
916		goto ip_check_done;
917	}
918#endif
919	return NULL;
920
921ip_check_done:
922	tuple.dst.protonum = proto;
923	tuple.src.u.all = htons(src_port);
924	tuple.dst.u.all = htons(dst_port);
925
926	h = nf_conntrack_find_get(&init_net, NF_CT_DEFAULT_ZONE, &tuple);
927	if (NULL == h) {
928		return NULL;
929	}
930
931	return nf_ct_tuplehash_to_ctrack(h);
932}
933
934/*
935 * ecm_classifier_nl_connection_added()
936 *	Invoked when a connection is added to the DB
937 */
938static void ecm_classifier_nl_connection_added(void *arg, struct ecm_db_connection_instance *ci)
939{
940	struct nf_conn *ct;
941	struct ecm_classifier_instance *classi;
942	struct ecm_classifier_nl_instance *cnli;
943	uint32_t serial __attribute__((unused)) = ecm_db_connection_serial_get(ci);
944	DEBUG_INFO("%p: NL Listener: conn added with serial: %u\n", ci, serial);
945
946	/*
947	 * Only handle events if there is an NL classifier attached
948	 */
949	classi = ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL);
950	if (NULL == classi) {
951		DEBUG_TRACE("%p: Connection added ignored - no NL classifier\n", ci);
952		return;
953	}
954	cnli = (struct ecm_classifier_nl_instance *)classi;
955	DEBUG_TRACE("%p: added conn, serial: %u, NL classifier: %p\n", ci,
956		    serial, classi);
957
958	ct = ecm_classifier_nl_ct_get_and_ref(ci);
959	if (NULL == ct) {
960		DEBUG_TRACE("%p: Connection add skipped - no associated CT entry.\n", ci);
961		goto classi;
962	}
963	DEBUG_TRACE("%p: added conn, serial: %u, NL classifier: %p, CT: %p\n",
964		    ci, serial, classi, ct);
965
966	spin_lock_bh(&ecm_classifier_nl_lock);
967	cnli->process_response.flow_qos_tag = ct->mark;
968	cnli->process_response.return_qos_tag = ct->mark;
969	spin_unlock_bh(&ecm_classifier_nl_lock);
970	nf_ct_put(ct);
971
972classi:
973	classi->deref(classi);
974
975	return;
976}
977
978/*
979 * ecm_classifier_nl_connection_removed()
980 *	Invoked when a connection is removed from the DB
981 */
982static void ecm_classifier_nl_connection_removed(void *arg, struct ecm_db_connection_instance *ci)
983{
984	uint32_t serial __attribute__((unused)) = ecm_db_connection_serial_get(ci);
985	struct ecm_classifier_instance *classi;
986	struct ecm_classifier_nl_instance *cnli;
987	bool accel_ok;
988	int proto;
989	int src_port;
990	int dst_port;
991	ip_addr_t src_ip;
992	ip_addr_t dst_ip;
993
994	DEBUG_INFO("%p: NL Listener: conn removed with serial: %u\n", ci, serial);
995
996	/*
997	 * Only handle events if there is an NL classifier attached
998	 */
999	classi = ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL);
1000	if (!classi) {
1001		DEBUG_TRACE("%p: Connection removed ignored - no NL classifier\n", ci);
1002		return;
1003	}
1004
1005	cnli = (struct ecm_classifier_nl_instance *)classi;
1006	DEBUG_INFO("%p: removed conn with serial: %u, NL classifier: %p\n", ci, serial, cnli);
1007
1008	/*
1009	 * If the connection was accelerated OK then issue a close
1010	 */
1011	spin_lock_bh(&ecm_classifier_nl_lock);
1012	accel_ok = (cnli->flags & ECM_CLASSIFIER_NL_F_ACCEL_OK)? true : false;
1013	spin_unlock_bh(&ecm_classifier_nl_lock);
1014	if (!accel_ok) {
1015		DEBUG_INFO("%p: cnli: %p, accel not ok\n", ci, cnli);
1016		classi->deref(classi);
1017		return;
1018	}
1019
1020	proto = ecm_db_connection_protocol_get(ci);
1021	ecm_db_connection_from_address_get(ci, src_ip);
1022	src_port = (uint16_t)ecm_db_connection_from_port_get(ci);
1023	ecm_db_connection_to_address_get(ci, dst_ip);
1024	dst_port = ecm_db_connection_to_port_get(ci);
1025
1026	DEBUG_INFO("%p: NL classifier: %p, issue Close\n", ci, cnli);
1027	ecm_classifier_nl_genl_msg_closed(ci, cnli, proto, src_ip, dst_ip, src_port, dst_port);
1028
1029	classi->deref(classi);
1030}
1031
1032/*
1033 * ecm_classifier_nl_instance_alloc()
1034 *	Allocate an instance of the Netlink classifier
1035 */
1036struct ecm_classifier_nl_instance *ecm_classifier_nl_instance_alloc(struct ecm_db_connection_instance *ci)
1037{
1038	struct ecm_classifier_nl_instance *cnli;
1039
1040	/*
1041	 * Allocate the instance
1042	 */
1043	cnli = (struct ecm_classifier_nl_instance *)kzalloc(sizeof(struct ecm_classifier_nl_instance), GFP_ATOMIC | __GFP_NOWARN);
1044	if (!cnli) {
1045		DEBUG_WARN("Failed to allocate Netlink instance\n");
1046		return NULL;
1047	}
1048
1049	DEBUG_SET_MAGIC(cnli, ECM_CLASSIFIER_NL_INSTANCE_MAGIC);
1050	cnli->refs = 1;
1051	cnli->base.process = ecm_classifier_nl_process;
1052	cnli->base.sync_from_v4 = ecm_classifier_nl_sync_from_v4;
1053	cnli->base.sync_to_v4 = ecm_classifier_nl_sync_to_v4;
1054	cnli->base.sync_from_v6 = ecm_classifier_nl_sync_from_v6;
1055	cnli->base.sync_to_v6 = ecm_classifier_nl_sync_to_v6;
1056	cnli->base.type_get = ecm_classifier_nl_type_get;
1057	cnli->base.last_process_response_get = ecm_classifier_nl_last_process_response_get;
1058	cnli->base.reclassify_allowed = ecm_classifier_nl_reclassify_allowed;
1059	cnli->base.reclassify = ecm_classifier_nl_reclassify;
1060#ifdef ECM_STATE_OUTPUT_ENABLE
1061	cnli->base.state_get = ecm_classifier_nl_state_get;
1062#endif
1063	cnli->base.ref = ecm_classifier_nl_ref;
1064	cnli->base.deref = ecm_classifier_nl_deref;
1065	cnli->ci_serial = ecm_db_connection_serial_get(ci);
1066
1067	/*
1068	 * Classifier initially denies acceleration.
1069	 */
1070	cnli->process_response.flow_qos_tag = 0;
1071	cnli->process_response.return_qos_tag = 0;
1072	cnli->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
1073	cnli->process_response.process_actions =
1074		ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
1075	cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
1076
1077	spin_lock_bh(&ecm_classifier_nl_lock);
1078
1079	/*
1080	 * Final check if we are pending termination
1081	 */
1082	if (ecm_classifier_nl_terminate_pending) {
1083		spin_unlock_bh(&ecm_classifier_nl_lock);
1084		DEBUG_INFO("%p: Terminating\n", ci);
1085		kfree(cnli);
1086		return NULL;
1087	}
1088
1089	/*
1090	 * Link the new instance into our list at the head
1091	 */
1092	cnli->next = ecm_classifier_nl_instances;
1093	if (ecm_classifier_nl_instances) {
1094		ecm_classifier_nl_instances->prev = cnli;
1095	}
1096	ecm_classifier_nl_instances = cnli;
1097
1098	/*
1099	 * Increment stats
1100	 */
1101	ecm_classifier_nl_count++;
1102	DEBUG_ASSERT(ecm_classifier_nl_count > 0, "%p: ecm_classifier_nl_count wrap\n", cnli);
1103	spin_unlock_bh(&ecm_classifier_nl_lock);
1104
1105	DEBUG_INFO("Netlink instance alloc: %p\n", cnli);
1106	return cnli;
1107}
1108EXPORT_SYMBOL(ecm_classifier_nl_instance_alloc);
1109
1110/*
1111 * ecm_classifier_nl_set_command()
1112 *	Set Netlink command to accel/decel connection.
1113 */
1114static ssize_t ecm_classifier_nl_set_command(struct file *file,
1115						const char __user *user_buf,
1116						size_t sz, loff_t *ppos)
1117
1118{
1119#define ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS 7
1120	char *cmd_buf;
1121	int field_count;
1122	char *field_ptr;
1123	char *fields[ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS];
1124	char cmd;
1125	uint32_t serial;
1126	ip_addr_t src_ip;
1127	uint32_t src_port;
1128	int proto;
1129	ip_addr_t dest_ip;
1130	uint32_t dest_port;
1131	struct ecm_db_connection_instance *ci;
1132	struct ecm_classifier_nl_instance *cnli;
1133	struct ecm_front_end_connection_instance *feci;
1134
1135	/*
1136	 * Check if we are enabled
1137	 */
1138	spin_lock_bh(&ecm_classifier_nl_lock);
1139	if (!ecm_classifier_nl_enabled) {
1140		spin_unlock_bh(&ecm_classifier_nl_lock);
1141		return -EINVAL;
1142	}
1143	spin_unlock_bh(&ecm_classifier_nl_lock);
1144
1145	/*
1146	 * buf is formed as:
1147	 * [0]   [1]      [2]      [3]        [4]     [5]       [6]
1148	 * <CMD>/<SERIAL>/<src_ip>/<src_port>/<proto>/<dest_ip>/<dest_port>
1149	 * CMD:
1150	 *	F = Accelerate based on IP address, <SERIAL> unused
1151	 *	f = Decelerate based on IP address, <SERIAL> unused
1152	 *	S = Accelerate based on serial, <SERIAL> only becomes relevant
1153	 *	s = Decelerate based on serial, <SERIAL> only becomes relevant
1154	 */
1155	cmd_buf = (char *)kzalloc(sz + 1, GFP_ATOMIC);
1156	if (!cmd_buf) {
1157		return -ENOMEM;
1158	}
1159
1160	sz = simple_write_to_buffer(cmd_buf, sz, ppos, user_buf, sz);
1161
1162	/*
1163	 * Split the buffer into its fields
1164	 */
1165	field_count = 0;
1166	field_ptr = cmd_buf;
1167	fields[field_count] = strsep(&field_ptr, "/");
1168	while (fields[field_count] != NULL) {
1169		DEBUG_TRACE("FIELD %d: %s\n", field_count, fields[field_count]);
1170		field_count++;
1171		if (field_count == ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS) {
1172			break;
1173		}
1174		fields[field_count] = strsep(&field_ptr, "/");
1175	}
1176
1177	if (field_count != ECM_CLASSIFIER_NL_SET_IP_COMMAND_FIELDS) {
1178		DEBUG_WARN("invalid field count %d\n", field_count);
1179		kfree(cmd_buf);
1180		return -EINVAL;
1181	}
1182
1183	sscanf(fields[0], "%c", &cmd);
1184	sscanf(fields[1], "%u", &serial);
1185	ecm_string_to_ip_addr(src_ip, fields[2]);
1186	sscanf(fields[3], "%u", &src_port);
1187	sscanf(fields[4], "%d", &proto);
1188	ecm_string_to_ip_addr(dest_ip, fields[5]);
1189	sscanf(fields[6], "%u", &dest_port);
1190
1191	kfree(cmd_buf);
1192
1193	/*
1194	 * Locate the connection using the serial or tuple given
1195	 */
1196	switch (cmd) {
1197	case 'F':
1198	case 'f':
1199		DEBUG_TRACE("Lookup connection " ECM_IP_ADDR_OCTAL_FMT ":%d <> " ECM_IP_ADDR_OCTAL_FMT ":%d protocol %d\n",
1200				ECM_IP_ADDR_TO_OCTAL(src_ip), src_port, ECM_IP_ADDR_TO_OCTAL(dest_ip), dest_port, proto);
1201		ci = ecm_db_connection_find_and_ref(src_ip, dest_ip, proto, src_port, dest_port);
1202		break;
1203	case 'S':
1204	case 's':
1205		DEBUG_TRACE("Lookup connection using serial: %u\n", serial);
1206		ci = ecm_db_connection_serial_find_and_ref(serial);
1207		break;
1208	default:
1209		DEBUG_WARN("invalid cmd %c\n", cmd);
1210		return -EINVAL;
1211	}
1212
1213	if (!ci) {
1214		DEBUG_WARN("database connection not found\n");
1215		return -ENOMEM;
1216	}
1217	DEBUG_TRACE("Connection found: %p\n", ci);
1218
1219	/*
1220	 * Get the NL classifier
1221	 */
1222	cnli = (struct ecm_classifier_nl_instance *)ecm_db_connection_assigned_classifier_find_and_ref(ci, ECM_CLASSIFIER_TYPE_NL);
1223	if (!cnli) {
1224		ecm_db_connection_deref(ci);
1225		return -ENOMEM;
1226	}
1227
1228	/*
1229	 * Now action the command
1230	 */
1231	switch (cmd) {
1232	case 's':
1233	case 'f':
1234		/*
1235		 * Decelerate the connection, NL is denying further accel until it says so.
1236		 */
1237		DEBUG_TRACE("Force decel: %p\n", ci);
1238		spin_lock_bh(&ecm_classifier_nl_lock);
1239		cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
1240		spin_unlock_bh(&ecm_classifier_nl_lock);
1241		feci = ecm_db_connection_front_end_get_and_ref(ci);
1242		feci->decelerate(feci);
1243		feci->deref(feci);
1244		break;
1245	case 'S':
1246	case 'F':
1247		/*
1248		 * Allow acceleration of the connection.  This will be done as packets are processed in the usual way.
1249		 */
1250		DEBUG_TRACE("Permit accel: %p\n", ci);
1251		spin_lock_bh(&ecm_classifier_nl_lock);
1252		cnli->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
1253		cnli->flags |= ECM_CLASSIFIER_NL_F_ACCEL;
1254		spin_unlock_bh(&ecm_classifier_nl_lock);
1255		break;
1256	}
1257
1258	cnli->base.deref((struct ecm_classifier_instance *)cnli);
1259	ecm_db_connection_deref(ci);
1260
1261	return sz;
1262}
1263
1264/*
1265 * ecm_classifier_nl_rule_get_enabled()
1266 */
1267static int ecm_classifier_nl_rule_get_enabled(void *data, u64 *val)
1268{
1269	DEBUG_TRACE("get enabled\n");
1270
1271	*val = ecm_classifier_nl_enabled;
1272
1273	return 0;
1274}
1275
1276/*
1277 * ecm_classifier_nl_rule_set_enabled()
1278 */
1279static int ecm_classifier_nl_rule_set_enabled(void *data, u64 val)
1280{
1281	bool prev_state;
1282
1283	/*
1284	 * Boolean-ise
1285	 */
1286	if (val) {
1287		val = true;
1288	}
1289	DEBUG_TRACE("ecm_classifier_nl_enabled = %d\n", (int)val);
1290
1291	/*
1292	 * Operate under our locks
1293	 */
1294	spin_lock_bh(&ecm_classifier_nl_lock);
1295	prev_state = ecm_classifier_nl_enabled;
1296	ecm_classifier_nl_enabled = (bool)val;
1297	spin_unlock_bh(&ecm_classifier_nl_lock);
1298
1299	/*
1300	 * If there is a change in operating state, and that change is that we are now disabled
1301	 * then we need to re-generate all connections relevant to this classifier type
1302	 */
1303	if (!val && (prev_state ^ (bool)val)) {
1304		/*
1305		 * Change in state to become disabled.
1306		 */
1307#ifdef ECM_DB_CTA_TRACK_ENABLE
1308		ecm_db_connection_regenerate_by_assignment_type(ECM_CLASSIFIER_TYPE_NL);
1309#else
1310		ecm_db_regeneration_needed();
1311#endif
1312	}
1313	return 0;
1314}
1315
1316/*
1317 * Debugfs attribute for the nl classifier enabled flag.
1318 */
1319DEFINE_SIMPLE_ATTRIBUTE(ecm_classifier_nl_enabled_fops, ecm_classifier_nl_rule_get_enabled, ecm_classifier_nl_rule_set_enabled, "%llu\n");
1320
1321/*
1322 * File operations for nl classifier command.
1323 */
1324static struct file_operations ecm_classifier_nl_cmd_fops = {
1325	.write = ecm_classifier_nl_set_command,
1326};
1327
1328/*
1329 * Generic Netlink attr checking policies
1330 */
1331static struct nla_policy
1332ecm_cl_nl_genl_policy[ECM_CL_NL_GENL_ATTR_COUNT] = {
1333	[ECM_CL_NL_GENL_ATTR_TUPLE] = {
1334		.type = NLA_UNSPEC,
1335		.len = sizeof(struct ecm_cl_nl_genl_attr_tuple), },
1336};
1337
1338/*
1339 * Generic Netlink message-to-handler mapping
1340 */
1341static struct genl_ops ecm_cl_nl_genl_ops[] = {
1342	{
1343		.cmd = ECM_CL_NL_GENL_CMD_ACCEL,
1344		.flags = 0,
1345		.policy = ecm_cl_nl_genl_policy,
1346		.doit = ecm_classifier_nl_genl_msg_ACCEL,
1347		.dumpit = NULL,
1348	},
1349	{
1350		.cmd = ECM_CL_NL_GENL_CMD_ACCEL_OK,
1351		.flags = 0,
1352		.policy = ecm_cl_nl_genl_policy,
1353		.doit = NULL,
1354		.dumpit = NULL,
1355	},
1356	{
1357		.cmd = ECM_CL_NL_GENL_CMD_CONNECTION_CLOSED,
1358		.flags = 0,
1359		.policy = ecm_cl_nl_genl_policy,
1360		.doit = NULL,
1361		.dumpit = NULL,
1362	},
1363};
1364
1365static int ecm_classifier_nl_register_genl(void)
1366{
1367	int result;
1368
1369	result = genl_register_family(&ecm_cl_nl_genl_family);
1370	if (result != 0) {
1371		DEBUG_ERROR("failed to register genl family: %d\n", result);
1372		goto err1;
1373	}
1374
1375	result = genl_register_ops(&ecm_cl_nl_genl_family,
1376				   ecm_cl_nl_genl_ops);
1377	if (result != 0) {
1378		DEBUG_ERROR("failed to register genl ops: %d\n", result);
1379		goto err2;
1380	}
1381
1382	result = genl_register_mc_group(&ecm_cl_nl_genl_family,
1383					&ecm_cl_nl_genl_mcgrp);
1384	if (result != 0) {
1385		DEBUG_ERROR("failed to register genl multicast group: %d\n",
1386			    result);
1387		goto err3;
1388	}
1389
1390	return 0;
1391
1392err3:
1393	genl_unregister_ops(&ecm_cl_nl_genl_family, ecm_cl_nl_genl_ops);
1394err2:
1395	genl_unregister_family(&ecm_cl_nl_genl_family);
1396err1:
1397	return result;
1398}
1399
1400static void ecm_classifier_nl_unregister_genl(void)
1401{
1402	genl_unregister_ops(&ecm_cl_nl_genl_family, ecm_cl_nl_genl_ops);
1403	genl_unregister_family(&ecm_cl_nl_genl_family);
1404}
1405
1406/*
1407 * ecm_classifier_nl_rules_init()
1408 */
1409int ecm_classifier_nl_rules_init(struct dentry *dentry)
1410{
1411	int result;
1412	DEBUG_INFO("Netlink classifier Module init\n");
1413
1414	ecm_classifier_nl_dentry = debugfs_create_dir("ecm_classifier_nl", dentry);
1415	if (!ecm_classifier_nl_dentry) {
1416		DEBUG_ERROR("Failed to create ecm nl classifier directory in debugfs\n");
1417		return -1;
1418	}
1419
1420	if (!debugfs_create_file("enabled", S_IRUGO | S_IWUSR, ecm_classifier_nl_dentry,
1421					NULL, &ecm_classifier_nl_enabled_fops)) {
1422		DEBUG_ERROR("Failed to create ecm nl classifier enabled file in debugfs\n");
1423		debugfs_remove_recursive(ecm_classifier_nl_dentry);
1424		return -1;
1425	}
1426
1427	if (!debugfs_create_file("cmd", S_IRUGO | S_IWUSR, ecm_classifier_nl_dentry,
1428					NULL, &ecm_classifier_nl_cmd_fops)) {
1429		DEBUG_ERROR("Failed to create ecm nl classifier cmd file in debugfs\n");
1430		debugfs_remove_recursive(ecm_classifier_nl_dentry);
1431		return -1;
1432	}
1433
1434	result = ecm_classifier_nl_register_genl();
1435	if (result) {
1436		DEBUG_ERROR("Failed to register genl sockets\n");
1437		return result;
1438	}
1439
1440	/*
1441	 * Allocate listener instance to listen for db events
1442	 */
1443	ecm_classifier_nl_li = ecm_db_listener_alloc();
1444	if (!ecm_classifier_nl_li) {
1445		DEBUG_ERROR("Failed to allocate listener\n");
1446		return -1;
1447	}
1448
1449	/*
1450	 * Add the listener into the database
1451	 * NOTE: Ref the thread count for the listener
1452	 */
1453	ecm_db_listener_add(ecm_classifier_nl_li,
1454			NULL /* ecm_classifier_nl_iface_added */,
1455			NULL /* ecm_classifier_nl_iface_removed */,
1456			NULL /* ecm_classifier_nl_node_added */,
1457			NULL /* ecm_classifier_nl_node_removed */,
1458			NULL /* ecm_classifier_nl_host_added */,
1459			NULL /* ecm_classifier_nl_host_removed */,
1460			NULL /* ecm_classifier_nl_mapping_added */,
1461			NULL /* ecm_classifier_nl_mapping_removed */,
1462			ecm_classifier_nl_connection_added,
1463			ecm_classifier_nl_connection_removed,
1464			NULL /* ecm_classifier_nl_listener_final */,
1465			ecm_classifier_nl_li);
1466
1467	return 0;
1468}
1469EXPORT_SYMBOL(ecm_classifier_nl_rules_init);
1470
1471/*
1472 * ecm_classifier_nl_rules_exit()
1473 */
1474void ecm_classifier_nl_rules_exit(void)
1475{
1476	DEBUG_INFO("Netlink classifier Module exit\n");
1477
1478	/*
1479	 * Release our ref to the listener.
1480	 * This will cause it to be unattached to the db listener list.
1481	 * GGG TODO THIS IS TOTALLY BROKEN (DUE TO REF COUNT HANDLING NOT BEING HONOURED)
1482	 */
1483	if (ecm_classifier_nl_li) {
1484		ecm_db_listener_deref(ecm_classifier_nl_li);
1485		ecm_classifier_nl_li = NULL;
1486	}
1487
1488	spin_lock_bh(&ecm_classifier_nl_lock);
1489	ecm_classifier_nl_terminate_pending = true;
1490	spin_unlock_bh(&ecm_classifier_nl_lock);
1491
1492	ecm_classifier_nl_unregister_genl();
1493
1494	/*
1495	 * Remove the debugfs files recursively.
1496	 */
1497	if (ecm_classifier_nl_dentry) {
1498		debugfs_remove_recursive(ecm_classifier_nl_dentry);
1499	}
1500}
1501EXPORT_SYMBOL(ecm_classifier_nl_rules_exit);
1502