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