1/*
2	Author:Derry
3	Date: 2019/11/12
4*/
5#include <linux/init.h>
6#include <linux/module.h>
7#include <linux/version.h>
8#include <net/tcp.h>
9#include <linux/netfilter.h>
10#include <net/netfilter/nf_conntrack.h>
11#include <net/netfilter/nf_conntrack_acct.h>
12#include <linux/skbuff.h>
13#include <net/ip.h>
14#include <linux/types.h>
15#include <net/sock.h>
16#include <linux/etherdevice.h>
17#include <linux/cdev.h>
18#include <linux/device.h>
19#include <linux/list.h>
20
21#include "af_client.h"
22#include "af_client_fs.h"
23#include "af_log.h"
24#include "af_utils.h"
25#include "app_filter.h"
26#include "cJSON.h"
27
28DEFINE_RWLOCK(af_client_lock);
29
30u32 total_client = 0;
31struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
32
33int af_send_msg_to_user(char *pbuf, uint16_t len);
34
35static void
36nf_client_list_init(void)
37{
38	int i;
39	AF_CLIENT_LOCK_W();
40	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
41	{
42		INIT_LIST_HEAD(&af_client_list_table[i]);
43	}
44	AF_CLIENT_UNLOCK_W();
45	AF_INFO("client list init......ok\n");
46}
47
48static void
49nf_client_list_clear(void)
50{
51	int i;
52	af_client_info_t *p = NULL;
53	char mac_str[32] = {0};
54
55	AF_DEBUG("clean list\n");
56	AF_CLIENT_LOCK_W();
57	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
58	{
59		while (!list_empty(&af_client_list_table[i]))
60		{
61			p = list_first_entry(&af_client_list_table[i], af_client_info_t, hlist);
62			memset(mac_str, 0x0, sizeof(mac_str));
63			sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
64			AF_DEBUG("clean mac:%s\n", mac_str);
65			list_del(&(p->hlist));
66			kfree(p);
67		}
68	}
69	AF_CLIENT_UNLOCK_W();
70}
71
72void af_client_list_reset_report_num(void)
73{
74	int i;
75	af_client_info_t *node = NULL;
76	AF_CLIENT_LOCK_W();
77	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
78	{
79		list_for_each_entry(node, &af_client_list_table[i], hlist)
80		{
81			node->report_count = 0;
82		}
83	}
84	AF_CLIENT_UNLOCK_W();
85}
86
87int get_mac_hash_code(unsigned char *mac)
88{
89	if (!mac)
90		return 0;
91	else
92		return mac[5] & (MAX_AF_CLIENT_HASH_SIZE - 1);
93}
94
95af_client_info_t *find_af_client(unsigned char *mac)
96{
97	af_client_info_t *node;
98	unsigned int index;
99
100	index = get_mac_hash_code(mac);
101	list_for_each_entry(node, &af_client_list_table[index], hlist)
102	{
103		if (0 == memcmp(node->mac, mac, 6))
104		{
105			return node;
106		}
107	}
108	return NULL;
109}
110
111af_client_info_t *find_and_add_af_client(unsigned char *mac)
112{
113	af_client_info_t *nfc;
114	nfc = find_af_client(mac);
115	if (!nfc){
116		nfc = nf_client_add(mac);
117	}
118	return nfc;
119}
120
121
122af_client_info_t *find_af_client_by_ip(unsigned int ip)
123{
124	af_client_info_t *node;
125	int i;
126
127	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
128	{
129		list_for_each_entry(node, &af_client_list_table[i], hlist)
130		{
131			if (node->ip == ip)
132			{
133				AF_LMT_DEBUG("match node->ip=%pI4, ip=%pI4\n", &node->ip, &ip);
134				return node;
135			}
136		}
137	}
138	return NULL;
139}
140
141af_client_info_t *
142nf_client_add(unsigned char *mac)
143{
144	af_client_info_t *node;
145	int index = 0;
146
147	node = (af_client_info_t *)kmalloc(sizeof(af_client_info_t), GFP_ATOMIC);
148	if (node == NULL)
149	{
150		AF_ERROR("kmalloc failed\n");
151		return NULL;
152	}
153
154	memset(node, 0, sizeof(af_client_info_t));
155	memcpy(node->mac, mac, MAC_ADDR_LEN);
156
157	node->create_jiffies = jiffies;
158	node->update_jiffies = jiffies;
159	index = get_mac_hash_code(mac);
160
161	AF_LMT_INFO("new client mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
162	total_client++;
163	list_add(&(node->hlist), &af_client_list_table[index]);
164	return node;
165}
166
167
168
169
170void check_client_expire(void)
171{
172	af_client_info_t *node;
173	int i;
174	AF_CLIENT_LOCK_W();
175	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
176	{
177		list_for_each_entry(node, &af_client_list_table[i], hlist)
178		{
179			AF_DEBUG("mac:" MAC_FMT " update:%lu interval:%lu\n", MAC_ARRAY(node->mac),
180					 node->update_jiffies, (jiffies - node->update_jiffies) / HZ);
181			if (jiffies > (node->update_jiffies + MAX_CLIENT_ACTIVE_TIME * HZ))
182			{
183				AF_INFO("del client:" MAC_FMT "\n", MAC_ARRAY(node->mac));
184				list_del(&(node->hlist));
185				kfree(node);
186				AF_CLIENT_UNLOCK_W();
187				return;
188			}
189		}
190	}
191	AF_CLIENT_UNLOCK_W();
192}
193
194#define MAX_EXPIRED_VISIT_INFO_COUNT 10
195void flush_expired_visit_info(af_client_info_t *node)
196{
197	int i;
198	int count = 0;
199	u_int32_t cur_timep = 0;
200	int timeout = 0;
201	cur_timep = af_get_timestamp_sec();
202	for (i = 0; i < MAX_RECORD_APP_NUM; i++)
203	{
204		if (node->visit_info[i].app_id == 0)
205		{
206			return;
207		}
208	}
209	for (i = 0; i < MAX_RECORD_APP_NUM; i++)
210	{
211		if (count >= MAX_EXPIRED_VISIT_INFO_COUNT)
212			break;
213
214		if (node->visit_info[i].total_num > 3)
215		{
216			timeout = 180;
217		}
218		else
219		{
220			timeout = 60;
221		}
222
223		if (cur_timep - node->visit_info[i].latest_time > timeout)
224		{
225			// 3?������o?������??3y????
226			memset(&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
227			count++;
228		}
229	}
230}
231
232int __af_visit_info_report(af_client_info_t *node)
233{
234	unsigned char mac_str[32] = {0};
235	unsigned char ip_str[32] = {0};
236	int i;
237	int count = 0;
238	char *out = NULL;
239	cJSON *visit_obj = NULL;
240	cJSON *visit_info_array = NULL;
241	cJSON *root_obj = NULL;
242
243	root_obj = cJSON_CreateObject();
244	if (!root_obj)
245	{
246		AF_ERROR("create json obj failed");
247		return 0;
248	}
249	sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
250	sprintf(ip_str, "%pI4", &node->ip);
251	cJSON_AddStringToObject(root_obj, "mac", mac_str);
252	cJSON_AddStringToObject(root_obj, "ip", ip_str);
253	cJSON_AddNumberToObject(root_obj, "app_num", node->visit_app_num);
254	visit_info_array = cJSON_CreateArray();
255	for (i = 0; i < MAX_RECORD_APP_NUM; i++)
256	{
257		if (node->visit_info[i].app_id == 0)
258			continue;
259		count++;
260		visit_obj = cJSON_CreateObject();
261		cJSON_AddNumberToObject(visit_obj, "appid", node->visit_info[i].app_id);
262		cJSON_AddNumberToObject(visit_obj, "latest_action", node->visit_info[i].latest_action);
263		cJSON_AddNumberToObject(visit_obj, "up_bytes", node->visit_info[i].total_up_bytes);
264		cJSON_AddNumberToObject(visit_obj, "down_bytes", node->visit_info[i].total_down_bytes);
265		memset((char *)&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
266		cJSON_AddItemToArray(visit_info_array, visit_obj);
267	}
268
269	cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array);
270	out = cJSON_Print(root_obj);
271	if (!out)
272		return 0;
273	cJSON_Minify(out);
274	if (count > 0 || node->report_count == 0)
275	{
276		AF_INFO("report:%s count=%d\n", out, node->report_count);
277		node->report_count++;
278		af_send_msg_to_user(out, strlen(out));
279	}
280	cJSON_Delete(root_obj);
281	kfree(out);
282	return 0;
283}
284void af_visit_info_report(void)
285{
286	af_client_info_t *node;
287	int i;
288	AF_CLIENT_LOCK_W();
289	for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
290	{
291		list_for_each_entry(node, &af_client_list_table[i], hlist)
292		{
293			// flush_expired_visit_info(node);
294			AF_INFO("report %s\n", node->mac);
295			__af_visit_info_report(node);
296		}
297	}
298	AF_CLIENT_UNLOCK_W();
299}
300static inline int get_packet_dir(struct net_device *in)
301{
302	if (0 == strncmp(in->name, "br", 2))
303	{
304		return PKT_DIR_UP;
305	}
306	else
307	{
308		return PKT_DIR_DOWN;
309	}
310}
311
312#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
313static u_int32_t af_client_hook(void *priv,
314								struct sk_buff *skb,
315								const struct nf_hook_state *state)
316{
317#else
318static u_int32_t af_client_hook(unsigned int hook,
319								struct sk_buff *skb,
320								const struct net_device *in,
321								const struct net_device *out,
322								int (*okfn)(struct sk_buff *))
323{
324#endif
325	struct ethhdr *ethhdr = NULL;
326	unsigned char smac[ETH_ALEN];
327	af_client_info_t *nfc = NULL;
328	int pkt_dir = 0;
329	struct iphdr *iph = NULL;
330
331// 4.10-->4.11 nfct-->_nfct
332#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
333	struct nf_conn *ct = (struct nf_conn *)skb->_nfct;
334#else
335	struct nf_conn *ct = (struct nf_conn *)skb->nfct;
336#endif
337	if (ct == NULL)
338	{
339		return NF_ACCEPT;
340	}
341
342#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
343	if (!skb->dev)
344		return NF_ACCEPT;
345
346	pkt_dir = get_packet_dir(skb->dev);
347#else
348	if (!in)
349	{
350		AF_ERROR("in is NULL\n");
351		return NF_ACCEPT;
352	}
353	pkt_dir = get_packet_dir(in);
354#endif
355
356	if (PKT_DIR_UP != pkt_dir)
357		return NF_ACCEPT;
358
359	ethhdr = eth_hdr(skb);
360	if (ethhdr)
361	{
362		memcpy(smac, ethhdr->h_source, ETH_ALEN);
363	}
364	else
365	{
366		memcpy(smac, &skb->cb[40], ETH_ALEN);
367	}
368
369	iph = ip_hdr(skb);
370	if (!iph)
371	{
372		return NF_ACCEPT;
373	}
374
375	AF_CLIENT_LOCK_W();
376	nfc = find_af_client(smac);
377	if (!nfc)
378	{
379		if (skb->dev)
380			AF_DEBUG("from dev:%s [%s] %pI4--->%pI4", skb->dev->name, (iph->protocol == IPPROTO_TCP ? "TCP" : "UDP"),
381					 &iph->saddr, &iph->daddr);
382		nfc = nf_client_add(smac);
383	}
384	if (nfc && nfc->ip != iph->saddr)
385	{
386		AF_DEBUG("update node " MAC_FMT " ip %pI4--->%pI4\n", MAC_ARRAY(nfc->mac), &nfc->ip, &iph->saddr);
387		nfc->update_jiffies = jiffies;
388		nfc->ip = iph->saddr;
389	}
390	AF_CLIENT_UNLOCK_W();
391
392	return NF_ACCEPT;
393}
394
395#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
396static struct nf_hook_ops af_client_ops[] = {
397	{
398		.hook = af_client_hook,
399		.pf = PF_INET,
400		.hooknum = NF_INET_FORWARD,
401		.priority = NF_IP_PRI_FIRST + 1,
402	},
403};
404#else
405static struct nf_hook_ops af_client_ops[] = {
406	{
407		.hook = af_client_hook,
408		.owner = THIS_MODULE,
409		.pf = PF_INET,
410		.hooknum = NF_INET_FORWARD,
411		.priority = NF_IP_PRI_FIRST + 1,
412	},
413};
414#endif
415
416int af_client_init(void)
417{
418	nf_client_list_init();
419#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
420	nf_register_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
421#else
422	nf_register_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
423#endif
424	AF_INFO("init app afclient ........ok\n");
425
426	return 0;
427}
428
429void af_client_exit(void)
430{
431#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
432	nf_unregister_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
433#else
434	nf_unregister_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
435#endif
436	nf_client_list_clear();
437	return;
438}
439