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/ipv4/nf_conntrack_ipv4.h>
47#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
48
49#include <hyfi_ecm.h>
50#include <hyfi_hash.h>
51
52/*
53 * Debug output levels
54 * 0 = OFF
55 * 1 = ASSERTS / ERRORS
56 * 2 = 1 + WARN
57 * 3 = 2 + INFO
58 * 4 = 3 + TRACE
59 */
60#define DEBUG_LEVEL ECM_CLASSIFIER_HYFI_DEBUG_LEVEL
61
62#include "ecm_types.h"
63#include "ecm_db_types.h"
64#include "ecm_state.h"
65#include "ecm_tracker.h"
66#include "ecm_classifier.h"
67#include "ecm_front_end_types.h"
68#include "ecm_tracker_udp.h"
69#include "ecm_tracker_tcp.h"
70#include "ecm_db.h"
71#include "ecm_classifier_hyfi.h"
72
73/*
74 * Magic numbers
75 */
76#define ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC 0xFE25
77
78/*
79 * Definitions
80 */
81#define ECM_CLASSIFIER_HYFI_STATE_INIT              ( 1 << 0 )
82#define ECM_CLASSIFIER_HYFI_STATE_REGISTERED	    ( 1 << 1 )
83#define ECM_CLASSIFIER_HYFI_STATE_IGNORE            ( 1 << 2 )
84
85/*
86 * struct ecm_classifier_hyfi_instance
87 * 	State to allow tracking of dynamic qos for a connection
88 */
89struct ecm_classifier_hyfi_instance {
90	struct ecm_classifier_instance base;			/* Base type */
91
92	struct ecm_classifier_hyfi_instance *next;		/* Next classifier state instance (for accouting and reporting purposes) */
93	struct ecm_classifier_hyfi_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
98	uint32_t hyfi_state;
99	struct hyfi_ecm_flow_data_t flow;
100
101	int refs;						/* Integer to trap we never go negative */
102#if (DEBUG_LEVEL > 0)
103	uint16_t magic;
104#endif
105};
106
107/*
108 * Listener for db events
109 */
110struct ecm_db_listener_instance *ecm_classifier_hyfi_li = NULL;
111
112/*
113 * Operational control
114 */
115static bool ecm_classifier_hyfi_enabled = true;		/* Operational behaviour */
116
117/*
118 * Management thread control
119 */
120static bool ecm_classifier_hyfi_terminate_pending = false;		/* True when the user wants us to terminate */
121
122/*
123 * Debugfs dentry object.
124 */
125static struct dentry *ecm_classifier_hyfi_dentry;
126
127/*
128 * Locking of the classifier structures
129 */
130static DEFINE_SPINLOCK(ecm_classifier_hyfi_lock);			/* Protect SMP access. */
131
132/*
133 * List of our classifier instances
134 */
135static struct ecm_classifier_hyfi_instance *ecm_classifier_hyfi_instances = NULL;
136									/* list of all active instances */
137static int ecm_classifier_hyfi_count = 0;					/* Tracks number of instances allocated */
138
139/*
140 * ecm_classifier_hyfi_ref()
141 *	Ref
142 */
143static void ecm_classifier_hyfi_ref(struct ecm_classifier_instance *ci)
144{
145	struct ecm_classifier_hyfi_instance *chfi;
146	chfi = (struct ecm_classifier_hyfi_instance *)ci;
147
148	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
149	spin_lock_bh(&ecm_classifier_hyfi_lock);
150	chfi->refs++;
151	DEBUG_TRACE("%p: chfi ref %d\n", chfi, chfi->refs);
152	DEBUG_ASSERT(chfi->refs > 0, "%p: ref wrap\n", chfi);
153	spin_unlock_bh(&ecm_classifier_hyfi_lock);
154}
155
156/*
157 * ecm_classifier_hyfi_deref()
158 *	Deref
159 */
160static int ecm_classifier_hyfi_deref(struct ecm_classifier_instance *ci)
161{
162	struct ecm_classifier_hyfi_instance *chfi;
163	chfi = (struct ecm_classifier_hyfi_instance *)ci;
164
165	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
166	spin_lock_bh(&ecm_classifier_hyfi_lock);
167	chfi->refs--;
168	DEBUG_ASSERT(chfi->refs >= 0, "%p: refs wrapped\n", chfi);
169	DEBUG_TRACE("%p: HyFi classifier deref %d\n", chfi, chfi->refs);
170	if (chfi->refs) {
171		int refs = chfi->refs;
172		spin_unlock_bh(&ecm_classifier_hyfi_lock);
173		return refs;
174	}
175
176	/*
177	 * Object to be destroyed
178	 */
179	ecm_classifier_hyfi_count--;
180	DEBUG_ASSERT(ecm_classifier_hyfi_count >= 0, "%p: ecm_classifier_hyfi_count wrap\n", chfi);
181
182	/*
183	 * UnLink the instance from our list
184	 */
185	if (chfi->next) {
186		chfi->next->prev = chfi->prev;
187	}
188	if (chfi->prev) {
189		chfi->prev->next = chfi->next;
190	} else {
191		DEBUG_ASSERT(ecm_classifier_hyfi_instances == chfi, "%p: list bad %p\n", chfi, ecm_classifier_hyfi_instances);
192		ecm_classifier_hyfi_instances = chfi->next;
193	}
194	chfi->next = NULL;
195	chfi->prev = NULL;
196
197	spin_unlock_bh(&ecm_classifier_hyfi_lock);
198
199	/*
200	 * Final
201	 */
202	DEBUG_INFO("%p: Final HyFi classifier instance\n", chfi);
203	kfree(chfi);
204
205	return 0;
206}
207
208/*
209 * ecm_classifier_hyfi_process()
210 *	Process new data for connection
211 */
212static void ecm_classifier_hyfi_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender,
213						struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb,
214						struct ecm_classifier_process_response *process_response)
215{
216	struct ecm_classifier_hyfi_instance *chfi;
217	ecm_classifier_relevence_t relevance;
218	bool enabled;
219	struct ecm_db_connection_instance *ci;
220	struct ecm_front_end_connection_instance *feci;
221	ecm_front_end_acceleration_mode_t accel_mode;
222	uint32_t became_relevant = 0;
223
224	chfi = (struct ecm_classifier_hyfi_instance *)aci;
225	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
226
227	/*
228	 * Are we yet to decide if this instance is relevant to the connection?
229	 */
230	spin_lock_bh(&ecm_classifier_hyfi_lock);
231	relevance = chfi->process_response.relevance;
232	if (relevance != ECM_CLASSIFIER_RELEVANCE_MAYBE) {
233		/*
234		 * We already know
235		 * NOTE: Lock still held
236		 */
237		goto hyfi_classifier_out;
238	}
239
240	enabled = ecm_classifier_hyfi_enabled;
241	spin_unlock_bh(&ecm_classifier_hyfi_lock);
242
243	/*
244	 * Need to decide our relevance to this connection.
245	 * If classifier is enabled and the front end says it can accel then we are "relevant".
246	 * Any other condition and we are not and will stop analysing this connection.
247	 */
248	relevance = ECM_CLASSIFIER_RELEVANCE_NO;
249	ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial);
250	if (!ci) {
251		DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial);
252		accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DENIED;
253	} else {
254		feci = ecm_db_connection_front_end_get_and_ref(ci);
255		accel_mode = feci->accel_state_get(feci);
256		feci->deref(feci);
257		ecm_db_connection_deref(ci);
258	}
259
260	if (enabled && ECM_FRONT_END_ACCELERATION_POSSIBLE(accel_mode)) {
261		relevance = ECM_CLASSIFIER_RELEVANCE_YES;
262		became_relevant = ecm_db_time_get();
263	}
264
265	spin_lock_bh(&ecm_classifier_hyfi_lock);
266	chfi->process_response.relevance = relevance;
267	chfi->process_response.became_relevant = became_relevant;
268
269hyfi_classifier_out:
270	;
271
272	/*
273	 * Return our process response
274	 */
275	*process_response = chfi->process_response;
276	if (relevance == ECM_CLASSIFIER_RELEVANCE_NO) {
277		goto hyfi_classifier_done;
278	}
279
280	/*
281	 * Fast path, already accelerated or ignored
282	 */
283	if (chfi->hyfi_state & (ECM_CLASSIFIER_HYFI_STATE_REGISTERED | ECM_CLASSIFIER_HYFI_STATE_IGNORE)) {
284		goto hyfi_classifier_done;
285	}
286
287	/*
288	 * Compute the hash
289	 */
290	if (unlikely(hyfi_hash_skbuf(skb, &chfi->flow.hash, &chfi->flow.flag, &chfi->flow.priority, &chfi->flow.seq))) {
291		goto hyfi_classifier_done;
292	}
293
294	chfi->flow.ecm_serial = chfi->ci_serial;
295	memcpy(&chfi->flow.sa, eth_hdr(skb)->h_source, ETH_ALEN);
296	memcpy(&chfi->flow.da, eth_hdr(skb)->h_dest, ETH_ALEN);
297
298	DEBUG_INFO("Flow serial: %d\nFlow hash: 0x%02x, priority 0x%08x, flag: %d\nSA: %pM\nDA: %pM\n\n",
299			chfi->flow.ecm_serial, chfi->flow.hash, chfi->flow.priority, chfi->flow.flag,
300			chfi->flow.sa, chfi->flow.da);
301
302	chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED;
303
304hyfi_classifier_done:
305	;
306
307	spin_unlock_bh(&ecm_classifier_hyfi_lock);
308}
309
310/*
311 * ecm_classifier_hyfi_sync_to_v4()
312 *	Front end is pushing accel engine state to us
313 */
314static void ecm_classifier_hyfi_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
315{
316	struct ecm_classifier_hyfi_instance *chfi;
317	struct ecm_db_connection_instance *ci;
318	uint64_t num_packets = 0;
319	uint64_t num_bytes = 0;
320	int32_t ret;
321
322	chfi = (struct ecm_classifier_hyfi_instance *)aci;
323	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi);
324
325	if (chfi->hyfi_state &
326			(ECM_CLASSIFIER_HYFI_STATE_IGNORE)) {
327		return;
328	}
329
330	ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial);
331	if (!ci) {
332		DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial);
333		return;
334	}
335
336	/*
337	 * Update the stats on Hy-Fi side
338	 */
339	ecm_db_connection_data_stats_get(ci, NULL, &num_bytes,
340			NULL, &num_packets,
341			NULL, NULL,
342			NULL, NULL );
343	ecm_db_connection_deref(ci);
344
345	DEBUG_INFO("UPDATE STATS: Flow serial: %d\nFlow hash: 0x%02x, priority 0x%08x, flag: %d\nSA: %pM\nDA: %pM\n\n",
346			chfi->flow.ecm_serial, chfi->flow.hash, chfi->flow.priority, chfi->flow.flag,
347			chfi->flow.sa, chfi->flow.da);
348
349	DEBUG_INFO("num_bytes = %lld, num_packets = %lld\n", num_bytes, num_packets);
350
351	ret = hyfi_ecm_update_stats(&chfi->flow, num_bytes, num_packets);
352
353	if (ret < 0) {
354		printk("%s: Fatal error\n", __func__);
355		return;
356	}
357
358	switch(ret)
359	{
360	case 0: /* OK */
361		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED;
362		break;
363
364	case 1: /* Not interested */
365		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_IGNORE;
366		break;
367
368	case 2: /* Not attached, may be interested in the future */
369	default:
370		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT;
371		break;
372	}
373}
374
375/*
376 * ecm_classifier_hyfi_sync_from_v4()
377 *	Front end is retrieving accel engine state from us
378 */
379static void ecm_classifier_hyfi_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
380{
381	struct ecm_classifier_hyfi_instance *chfi;
382
383	chfi = (struct ecm_classifier_hyfi_instance *)aci;
384	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi);
385}
386
387/*
388 * ecm_classifier_hyfi_sync_to_v6()
389 *	Front end is pushing accel engine state to us
390 */
391static void ecm_classifier_hyfi_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
392{
393	struct ecm_classifier_hyfi_instance *chfi;
394	struct ecm_db_connection_instance *ci;
395	uint64_t num_packets = 0;
396	uint64_t num_bytes = 0;
397	int32_t ret;
398
399	chfi = (struct ecm_classifier_hyfi_instance *)aci;
400	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi);
401
402	if (chfi->hyfi_state &
403			(ECM_CLASSIFIER_HYFI_STATE_IGNORE)) {
404		return;
405	}
406
407	ci = ecm_db_connection_serial_find_and_ref(chfi->ci_serial);
408	if (!ci) {
409		DEBUG_TRACE("%p: No ci found for %u\n", chfi, chfi->ci_serial);
410		return;
411	}
412
413	/*
414	 * Update the stats on Hy-Fi side
415	 */
416	ecm_db_connection_data_stats_get(ci, NULL, &num_bytes,
417			NULL, &num_packets,
418			NULL, NULL,
419			NULL, NULL );
420	ecm_db_connection_deref(ci);
421
422	ret = hyfi_ecm_update_stats(&chfi->flow, num_bytes, num_packets);
423
424	if (ret < 0) {
425		DEBUG_ERROR("%s: Fatal error\n", __func__);
426		return;
427	}
428
429	switch(ret)
430	{
431	case 0: /* OK */
432		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_REGISTERED;
433		break;
434
435	case 1: /* Not interested */
436		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_IGNORE;
437		break;
438
439	case 2: /* Not attached, may be interested in the future */
440	default:
441		chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT;
442		break;
443	}
444}
445
446/*
447 * ecm_classifier_hyfi_sync_from_v6()
448 *	Front end is retrieving accel engine state from us
449 */
450static void ecm_classifier_hyfi_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
451{
452	struct ecm_classifier_hyfi_instance *chfi;
453
454	chfi = (struct ecm_classifier_hyfi_instance *)aci;
455	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi);
456
457}
458
459/*
460 * ecm_classifier_hyfi_type_get()
461 *	Get type of classifier this is
462 */
463static ecm_classifier_type_t ecm_classifier_hyfi_type_get(struct ecm_classifier_instance *ci)
464{
465	struct ecm_classifier_hyfi_instance *chfi;
466	chfi = (struct ecm_classifier_hyfi_instance *)ci;
467
468	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
469	return ECM_CLASSIFIER_TYPE_HYFI;
470}
471
472/*
473 * ecm_classifier_hyfi_last_process_response_get()
474 *	Get result code returned by the last process call
475 */
476static void ecm_classifier_hyfi_last_process_response_get(struct ecm_classifier_instance *ci,
477							struct ecm_classifier_process_response *process_response)
478{
479	struct ecm_classifier_hyfi_instance *chfi;
480
481	chfi = (struct ecm_classifier_hyfi_instance *)ci;
482	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
483
484	spin_lock_bh(&ecm_classifier_hyfi_lock);
485	*process_response = chfi->process_response;
486	spin_unlock_bh(&ecm_classifier_hyfi_lock);
487}
488
489/*
490 * ecm_classifier_hyfi_reclassify_allowed()
491 *	Indicate if reclassify is allowed
492 */
493static bool ecm_classifier_hyfi_reclassify_allowed(struct ecm_classifier_instance *ci)
494{
495	struct ecm_classifier_hyfi_instance *chfi;
496	chfi = (struct ecm_classifier_hyfi_instance *)ci;
497	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
498
499	return true;
500}
501
502/*
503 * ecm_classifier_hyfi_reclassify()
504 *	Reclassify
505 */
506static void ecm_classifier_hyfi_reclassify(struct ecm_classifier_instance *ci)
507{
508	struct ecm_classifier_hyfi_instance *chfi;
509	chfi = (struct ecm_classifier_hyfi_instance *)ci;
510	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed\n", chfi);
511}
512
513#ifdef ECM_STATE_OUTPUT_ENABLE
514/*
515 * ecm_classifier_hyfi_state_get()
516 *	Return state
517 */
518static int ecm_classifier_hyfi_state_get(struct ecm_classifier_instance *ci, char *buf, int buf_sz)
519{
520	struct ecm_classifier_hyfi_instance *chfi;
521	struct ecm_classifier_process_response process_response;
522
523	chfi = (struct ecm_classifier_hyfi_instance *)ci;
524	DEBUG_CHECK_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC, "%p: magic failed", chfi);
525
526	if ((result = ecm_state_prefix_add(sfi, "hyfi"))) {
527		return result;
528	}
529
530	spin_lock_bh(&ecm_classifier_hyfi_lock);
531	process_response = chfi->process_response;
532	spin_unlock_bh(&ecm_classifier_hyfi_lock);
533
534	/*
535	 * Output our last process response
536	 */
537	if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) {
538		return result;
539	}
540
541	return ecm_state_prefix_remove(sfi);
542}
543#endif
544
545/*
546 * ecm_classifier_hyfi_instance_alloc()
547 *	Allocate an instance of the HyFi classifier
548 */
549struct ecm_classifier_hyfi_instance *ecm_classifier_hyfi_instance_alloc(struct ecm_db_connection_instance *ci)
550{
551	struct ecm_classifier_hyfi_instance *chfi;
552
553	/*
554	 * Allocate the instance
555	 */
556	chfi = (struct ecm_classifier_hyfi_instance *)kzalloc(sizeof(struct ecm_classifier_hyfi_instance), GFP_ATOMIC | __GFP_NOWARN);
557	if (!chfi) {
558		DEBUG_WARN("Failed to allocate HyFi instance\n");
559		return NULL;
560	}
561
562	DEBUG_SET_MAGIC(chfi, ECM_CLASSIFIER_HYFI_INSTANCE_MAGIC);
563	chfi->refs = 1;
564	chfi->base.process = ecm_classifier_hyfi_process;
565	chfi->base.sync_from_v4 = ecm_classifier_hyfi_sync_from_v4;
566	chfi->base.sync_to_v4 = ecm_classifier_hyfi_sync_to_v4;
567	chfi->base.sync_from_v6 = ecm_classifier_hyfi_sync_from_v6;
568	chfi->base.sync_to_v6 = ecm_classifier_hyfi_sync_to_v6;
569	chfi->base.type_get = ecm_classifier_hyfi_type_get;
570	chfi->base.last_process_response_get = ecm_classifier_hyfi_last_process_response_get;
571	chfi->base.reclassify_allowed = ecm_classifier_hyfi_reclassify_allowed;
572	chfi->base.reclassify = ecm_classifier_hyfi_reclassify;
573#ifdef ECM_STATE_OUTPUT_ENABLE
574	chfi->base.state_get = ecm_classifier_hyfi_state_get;
575#endif
576	chfi->base.ref = ecm_classifier_hyfi_ref;
577	chfi->base.deref = ecm_classifier_hyfi_deref;
578	chfi->ci_serial = ecm_db_connection_serial_get(ci);
579	chfi->process_response.process_actions = 0;
580	chfi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
581
582	/*
583	 * Init Hy-Fi state
584	 */
585	chfi->hyfi_state = ECM_CLASSIFIER_HYFI_STATE_INIT;
586
587	spin_lock_bh(&ecm_classifier_hyfi_lock);
588
589	/*
590	 * Final check if we are pending termination
591	 */
592	if (ecm_classifier_hyfi_terminate_pending) {
593		spin_unlock_bh(&ecm_classifier_hyfi_lock);
594		DEBUG_INFO("%p: Terminating\n", ci);
595		kfree(chfi);
596		return NULL;
597	}
598
599	/*
600	 * Link the new instance into our list at the head
601	 */
602	chfi->next = ecm_classifier_hyfi_instances;
603	if (ecm_classifier_hyfi_instances) {
604		ecm_classifier_hyfi_instances->prev = chfi;
605	}
606	ecm_classifier_hyfi_instances = chfi;
607
608	/*
609	 * Increment stats
610	 */
611	ecm_classifier_hyfi_count++;
612	DEBUG_ASSERT(ecm_classifier_hyfi_count > 0, "%p: ecm_classifier_hyfi_count wrap\n", chfi);
613	spin_unlock_bh(&ecm_classifier_hyfi_lock);
614
615	DEBUG_INFO("HyFi instance alloc: %p\n", chfi);
616	return chfi;
617}
618EXPORT_SYMBOL(ecm_classifier_hyfi_instance_alloc);
619
620/*
621 * ecm_classifier_hyfi_connection_added()
622 *	Invoked when a connection is added to the DB
623 */
624static void ecm_classifier_hyfi_connection_added(void *arg, struct ecm_db_connection_instance *ci)
625{
626#if (DEBUG_LEVEL > 2)
627	uint32_t serial = ecm_db_connection_serial_get(ci);
628	DEBUG_INFO("%p: HYFI LISTENER: added conn with serial: %u\n", ci, serial);
629#endif
630//	Add your own code here
631}
632
633/*
634 * ecm_classifier_hyfi_connection_removed()
635 *	Invoked when a connection is removed from the DB
636 */
637static void ecm_classifier_hyfi_connection_removed(void *arg, struct ecm_db_connection_instance *ci)
638{
639#if (DEBUG_LEVEL > 2)
640	uint32_t serial = ecm_db_connection_serial_get(ci);
641	DEBUG_INFO("%p: HYFI LISTENER: removed conn with serial: %u\n", ci, serial);
642#endif
643//	Add your own code here
644}
645
646/*
647 * ecm_classifier_hyfi_set_set_command()
648 *	Set hyfi command to accel/decel connection.
649 */
650static ssize_t ecm_classifier_hyfi_set_command(struct file *file,
651						const char __user *user_buf,
652						size_t sz, loff_t *ppos)
653{
654#define ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS 2
655	char *cmd_buf;
656	int field_count;
657	char *field_ptr;
658	char *fields[ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS];
659	char cmd;
660	uint32_t serial;
661	struct ecm_db_connection_instance *ci;
662	struct ecm_front_end_connection_instance *feci;
663
664	/*
665	 * Check if we are enabled
666	 */
667	spin_lock_bh(&ecm_classifier_hyfi_lock);
668	if (!ecm_classifier_hyfi_enabled) {
669		spin_unlock_bh(&ecm_classifier_hyfi_lock);
670		return -EINVAL;
671	}
672	spin_unlock_bh(&ecm_classifier_hyfi_lock);
673
674	/*
675	 * buf is formed as:
676	 * [0]   [1]
677	 * <CMD>/<SERIAL>
678	 * CMD:
679	 *	s = Decelerate based on <SERIAL> number given.
680	 */
681	cmd_buf = (char *)kzalloc(sz + 1, GFP_ATOMIC);
682	if (!cmd_buf) {
683		return -ENOMEM;
684	}
685
686	sz = simple_write_to_buffer(cmd_buf, sz, ppos, user_buf, sz);
687
688	/*
689	 * Split the buffer into its fields
690	 */
691	field_count = 0;
692	field_ptr = cmd_buf;
693	fields[field_count] = strsep(&field_ptr, "/");
694	while (fields[field_count] != NULL) {
695		DEBUG_TRACE("FIELD %d: %s\n", field_count, fields[field_count]);
696		field_count++;
697		if (field_count == ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS) {
698			break;
699		}
700		fields[field_count] = strsep(&field_ptr, "/");
701	}
702
703	if (field_count != ECM_CLASSIFIER_HYFI_SET_IP_COMMAND_FIELDS) {
704		DEBUG_WARN("invalid field count %d\n", field_count);
705		kfree(cmd_buf);
706		return -EINVAL;
707	}
708
709	if (!sscanf(fields[0], "%c", &cmd)) {
710		DEBUG_WARN("invalid cmd\n");
711		kfree(cmd_buf);
712		return -EINVAL;
713	}
714	if (!sscanf(fields[1], "%u", &serial)) {
715		DEBUG_WARN("invalid serial\n");
716		kfree(cmd_buf);
717		return -EINVAL;
718	}
719
720	kfree(cmd_buf);
721
722	/*
723	 * Locate the connection using the serial or tuple given
724	 */
725	switch (cmd) {
726	case 's':
727		DEBUG_TRACE("Lookup connection using serial: %u\n", serial);
728		ci = ecm_db_connection_serial_find_and_ref(serial);
729		break;
730	default:
731		DEBUG_WARN("invalid cmd %c\n", cmd);
732		return -EINVAL;
733	}
734
735	if (!ci) {
736		DEBUG_WARN("database connection not found\n");
737		return -ENOMEM;
738	}
739	DEBUG_TRACE("Connection found: %p\n", ci);
740
741	/*
742	 * Now action the command
743	 */
744	switch (cmd) {
745	case 's':
746	case 'f':
747		/*
748		 * Decelerate the connection
749		 */
750		DEBUG_TRACE("Force decel: %p\n", ci);
751		feci = ecm_db_connection_front_end_get_and_ref(ci);
752		feci->decelerate(feci);
753		feci->deref(feci);
754		break;
755	}
756	ecm_db_connection_deref(ci);
757
758	return sz;
759}
760
761/*
762 * File operations for hyfi classifier command.
763 */
764static struct file_operations ecm_classifier_hyfi_cmd_fops = {
765	.write = ecm_classifier_hyfi_set_command,
766};
767
768/*
769 * ecm_classifier_hyfi_rules_init()
770 */
771int ecm_classifier_hyfi_rules_init(struct dentry *dentry)
772{
773	DEBUG_INFO("HyFi classifier Module init\n");
774
775	ecm_classifier_hyfi_dentry = debugfs_create_dir("ecm_classifier_hyfi", dentry);
776	if (!ecm_classifier_hyfi_dentry) {
777		DEBUG_ERROR("Failed to create ecm hyfi classifier directory in debugfs\n");
778		goto classifier_task_cleanup;
779	}
780
781	if (!debugfs_create_bool("enabled", S_IRUGO | S_IWUSR, ecm_classifier_hyfi_dentry,
782					(u32 *)&ecm_classifier_hyfi_enabled)) {
783		DEBUG_ERROR("Failed to create ecm hyfi classifier enabled file in debugfs\n");
784		goto classifier_task_cleanup;
785	}
786
787	if (!debugfs_create_file("cmd", S_IWUSR, ecm_classifier_hyfi_dentry,
788					NULL, &ecm_classifier_hyfi_cmd_fops)) {
789		DEBUG_ERROR("Failed to create ecm hyfi classifier cmd file in debugfs\n");
790		goto classifier_task_cleanup;
791	}
792
793	/*
794	 * Allocate listener instance to listen for db events
795	 */
796	ecm_classifier_hyfi_li = ecm_db_listener_alloc();
797	if (!ecm_classifier_hyfi_li) {
798		DEBUG_ERROR("Failed to allocate listener\n");
799		goto classifier_task_cleanup;
800	}
801
802	/*
803	 * Add the listener into the database
804	 * NOTE: Ref the thread count for the listener
805	 */
806	ecm_db_listener_add(ecm_classifier_hyfi_li,
807			NULL /* ecm_classifier_hyfi_iface_added */,
808			NULL /* ecm_classifier_hyfi_iface_removed */,
809			NULL /* ecm_classifier_hyfi_node_added */,
810			NULL /* ecm_classifier_hyfi_node_removed */,
811			NULL /* ecm_classifier_hyfi_host_added */,
812			NULL /* ecm_classifier_hyfi_host_removed */,
813			NULL /* ecm_classifier_hyfi_mapping_added */,
814			NULL /* ecm_classifier_hyfi_mapping_removed */,
815			ecm_classifier_hyfi_connection_added,
816			ecm_classifier_hyfi_connection_removed,
817			NULL /* ecm_classifier_hyfi_listener_final */,
818			ecm_classifier_hyfi_li);
819
820	return 0;
821
822classifier_task_cleanup:
823
824	debugfs_remove_recursive(ecm_classifier_hyfi_dentry);
825	return -1;
826}
827EXPORT_SYMBOL(ecm_classifier_hyfi_rules_init);
828
829/*
830 * ecm_classifier_hyfi_rules_exit()
831 */
832void ecm_classifier_hyfi_rules_exit(void)
833{
834	DEBUG_INFO("HyFi classifier Module exit\n");
835
836	spin_lock_bh(&ecm_classifier_hyfi_lock);
837	ecm_classifier_hyfi_terminate_pending = true;
838	spin_unlock_bh(&ecm_classifier_hyfi_lock);
839
840	/*
841	 * Release our ref to the listener.
842	 * This will cause it to be unattached to the db listener list.
843	 * NOTE: Our thread refs will be released on final callback when
844	 * we know there will be no more callbacks to it.
845	 */
846	if (ecm_classifier_hyfi_li) {
847		ecm_db_listener_deref(ecm_classifier_hyfi_li);
848		ecm_classifier_hyfi_li = NULL;
849	}
850
851	/*
852	 * Remove the debugfs files recursively.
853	 */
854	if (ecm_classifier_hyfi_dentry) {
855		debugfs_remove_recursive(ecm_classifier_hyfi_dentry);
856	}
857}
858EXPORT_SYMBOL(ecm_classifier_hyfi_rules_exit);
859