mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-29 17:35:13 +08:00
🌴 Sync 2026-03-05 23:51:42
This commit is contained in:
1 parent
0d155c70be
commit
32600acc60
14258 files changed
+3954570
No files matched your search
@@ -0,0 +1,2 @@
|
||||
oaf-objs := app_filter.o af_utils.o af_config.o regexp.o cJSON.o af_log.o af_client.o af_client_fs.o af_conntrack.o af_rule_config.o af_user_config.o af_whitelist_config.o
|
||||
obj-m += oaf.o
|
||||
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
Author:Derry
|
||||
Date: 2019/11/12
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_acct.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include "af_client.h"
|
||||
#include "af_client_fs.h"
|
||||
#include "af_log.h"
|
||||
#include "af_utils.h"
|
||||
#include "app_filter.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
DEFINE_RWLOCK(af_client_lock);
|
||||
|
||||
u32 total_client = 0;
|
||||
struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
|
||||
|
||||
int af_send_msg_to_user(char *pbuf, uint16_t len);
|
||||
extern char *ipv6_to_str(const struct in6_addr *addr, char *str);
|
||||
|
||||
static void init_client_timer(af_client_info_t *client);
|
||||
static void stop_client_timer(af_client_info_t *client);
|
||||
|
||||
|
||||
static void
|
||||
nf_client_list_init(void)
|
||||
{
|
||||
int i;
|
||||
AF_CLIENT_LOCK_W();
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
INIT_LIST_HEAD(&af_client_list_table[i]);
|
||||
}
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
AF_INFO("client list init......ok\n");
|
||||
}
|
||||
|
||||
static void
|
||||
nf_client_list_clear(void)
|
||||
{
|
||||
int i;
|
||||
af_client_info_t *p = NULL;
|
||||
char mac_str[32] = {0};
|
||||
|
||||
AF_DEBUG("clean list\n");
|
||||
AF_CLIENT_LOCK_W();
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
while (!list_empty(&af_client_list_table[i]))
|
||||
{
|
||||
p = list_first_entry(&af_client_list_table[i], af_client_info_t, hlist);
|
||||
memset(mac_str, 0x0, sizeof(mac_str));
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
|
||||
AF_DEBUG("clean mac:%s\n", mac_str);
|
||||
stop_client_timer(p);
|
||||
list_del(&(p->hlist));
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
}
|
||||
|
||||
void af_client_list_reset_report_num(void)
|
||||
{
|
||||
int i;
|
||||
af_client_info_t *node = NULL;
|
||||
AF_CLIENT_LOCK_W();
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
list_for_each_entry(node, &af_client_list_table[i], hlist)
|
||||
{
|
||||
node->report_count = 0;
|
||||
}
|
||||
}
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
}
|
||||
|
||||
int get_mac_hash_code(unsigned char *mac)
|
||||
{
|
||||
if (!mac)
|
||||
return 0;
|
||||
else
|
||||
return mac[5] & (MAX_AF_CLIENT_HASH_SIZE - 1);
|
||||
}
|
||||
|
||||
af_client_info_t *find_af_client(unsigned char *mac)
|
||||
{
|
||||
af_client_info_t *node;
|
||||
unsigned int index;
|
||||
|
||||
index = get_mac_hash_code(mac);
|
||||
list_for_each_entry(node, &af_client_list_table[index], hlist)
|
||||
{
|
||||
if (0 == memcmp(node->mac, mac, 6))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
af_client_info_t *find_and_add_af_client(unsigned char *mac)
|
||||
{
|
||||
af_client_info_t *nfc;
|
||||
nfc = find_af_client(mac);
|
||||
if (!nfc){
|
||||
nfc = nf_client_add(mac);
|
||||
}
|
||||
return nfc;
|
||||
}
|
||||
|
||||
|
||||
af_client_info_t *find_af_client_by_ip(unsigned int ip)
|
||||
{
|
||||
af_client_info_t *node;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
list_for_each_entry(node, &af_client_list_table[i], hlist)
|
||||
{
|
||||
if (node->ip == ip)
|
||||
{
|
||||
AF_LMT_DEBUG("match node->ip=%pI4, ip=%pI4\n", &node->ip, &ip);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
af_client_info_t *find_af_client_by_ipv6(struct in6_addr *ipv6)
|
||||
{
|
||||
af_client_info_t *node;
|
||||
int i;
|
||||
char addr_str[64] = {0};
|
||||
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
list_for_each_entry(node, &af_client_list_table[i], hlist)
|
||||
{
|
||||
if (ipv6_addr_equal(&node->ipv6, ipv6))
|
||||
{
|
||||
AF_INFO("match node->ipv6=%s\n", ipv6_to_str(&node->ipv6, addr_str));
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
af_client_info_t *
|
||||
nf_client_add(unsigned char *mac)
|
||||
{
|
||||
af_client_info_t *node;
|
||||
int index = 0;
|
||||
|
||||
node = (af_client_info_t *)kmalloc(sizeof(af_client_info_t), GFP_ATOMIC);
|
||||
if (node == NULL)
|
||||
{
|
||||
AF_ERROR("kmalloc failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(node, 0, sizeof(af_client_info_t));
|
||||
memcpy(node->mac, mac, MAC_ADDR_LEN);
|
||||
|
||||
node->create_jiffies = jiffies;
|
||||
node->update_jiffies = jiffies;
|
||||
index = get_mac_hash_code(mac);
|
||||
|
||||
AF_LMT_INFO("new client mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
|
||||
total_client++;
|
||||
init_client_timer(node);
|
||||
list_add(&(node->hlist), &af_client_list_table[index]);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void check_client_expire(void)
|
||||
{
|
||||
af_client_info_t *node;
|
||||
int i;
|
||||
AF_CLIENT_LOCK_W();
|
||||
for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++)
|
||||
{
|
||||
list_for_each_entry(node, &af_client_list_table[i], hlist)
|
||||
{
|
||||
AF_DEBUG("mac:" MAC_FMT " update:%lu interval:%lu\n", MAC_ARRAY(node->mac),
|
||||
node->update_jiffies, (jiffies - node->update_jiffies) / HZ);
|
||||
if (jiffies > (node->update_jiffies + MAX_CLIENT_ACTIVE_TIME * HZ))
|
||||
{
|
||||
AF_INFO("del client:" MAC_FMT "\n", MAC_ARRAY(node->mac));
|
||||
stop_client_timer(node);
|
||||
list_del(&(node->hlist));
|
||||
kfree(node);
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
}
|
||||
|
||||
#define MAX_EXPIRED_VISIT_INFO_COUNT 10
|
||||
void flush_expired_visit_info(af_client_info_t *node)
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
u_int32_t cur_timep = 0;
|
||||
int timeout = 0;
|
||||
cur_timep = af_get_timestamp_sec();
|
||||
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
|
||||
{
|
||||
if (node->visit_info[i].app_id == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
|
||||
{
|
||||
if (count >= MAX_EXPIRED_VISIT_INFO_COUNT)
|
||||
break;
|
||||
|
||||
if (node->visit_info[i].total_num > 3)
|
||||
{
|
||||
timeout = 180;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout = 60;
|
||||
}
|
||||
|
||||
if (cur_timep - node->visit_info[i].latest_time > timeout)
|
||||
{
|
||||
// 3?��o?��??3y????
|
||||
memset(&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __af_visit_info_report(af_client_info_t *node)
|
||||
{
|
||||
unsigned char mac_str[32] = {0};
|
||||
unsigned char ip_str[32] = {0};
|
||||
int i;
|
||||
int count = 0;
|
||||
char *out = NULL;
|
||||
cJSON *visit_obj = NULL;
|
||||
cJSON *visit_info_array = NULL;
|
||||
cJSON *root_obj = NULL;
|
||||
|
||||
root_obj = cJSON_CreateObject();
|
||||
if (!root_obj)
|
||||
{
|
||||
AF_ERROR("create json obj failed");
|
||||
return 0;
|
||||
}
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
|
||||
sprintf(ip_str, "%pI4", &node->ip);
|
||||
cJSON_AddStringToObject(root_obj, "mac", mac_str);
|
||||
cJSON_AddStringToObject(root_obj, "ip", ip_str);
|
||||
cJSON_AddNumberToObject(root_obj, "app_num", node->visit_app_num);
|
||||
cJSON_AddNumberToObject(root_obj, "up_flow", (u32)(node->period_flow.up_bytes >> 10));
|
||||
cJSON_AddNumberToObject(root_obj, "down_flow", (u32)(node->period_flow.down_bytes >> 10));
|
||||
cJSON_AddNumberToObject(root_obj, "active", node->active);
|
||||
|
||||
visit_info_array = cJSON_CreateArray();
|
||||
for (i = 0; i < MAX_RECORD_APP_NUM; i++)
|
||||
{
|
||||
if (node->visit_info[i].app_id == 0)
|
||||
continue;
|
||||
count++;
|
||||
visit_obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(visit_obj, "appid", node->visit_info[i].app_id);
|
||||
cJSON_AddNumberToObject(visit_obj, "latest_action", node->visit_info[i].latest_action);
|
||||
memset((char *)&node->visit_info[i], 0x0, sizeof(app_visit_info_t));
|
||||
cJSON_AddItemToArray(visit_info_array, visit_obj);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array);
|
||||
out = cJSON_Print(root_obj);
|
||||
if (!out) {
|
||||
cJSON_Delete(root_obj);
|
||||
return 0;
|
||||
}
|
||||
cJSON_Minify(out);
|
||||
|
||||
AF_INFO("report:%s count=%d\n", out, node->report_count);
|
||||
node->report_count++;
|
||||
af_send_msg_to_user(out, strlen(out));
|
||||
cJSON_Delete(root_obj);
|
||||
|
||||
memset(&node->period_flow, 0x0, sizeof(node->period_flow));
|
||||
|
||||
kfree(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int get_packet_dir(struct net_device *in)
|
||||
{
|
||||
if (strstr(in->name, g_lan_ifname))
|
||||
{
|
||||
return PKT_DIR_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PKT_DIR_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void af_update_client_status(af_client_info_t *node)
|
||||
{
|
||||
if (node->last_flow.down_bytes > 0){
|
||||
node->period_flow.down_bytes += (node->flow.down_bytes - node->last_flow.down_bytes);
|
||||
}
|
||||
if (node->last_flow.up_bytes > 0){
|
||||
node->period_flow.up_bytes += (node->flow.up_bytes - node->last_flow.up_bytes);
|
||||
}
|
||||
AF_LMT_DEBUG("period flow down:%llu up: %llu pkg up %d\n", node->period_flow.down_bytes,
|
||||
node->period_flow.up_bytes, node->rate.pkt_up_rate);
|
||||
// 2s
|
||||
node->rate.up_rate = (node->flow.up_bytes - node->last_flow.up_bytes) >> 1;
|
||||
node->rate.down_rate = (node->flow.down_bytes - node->last_flow.down_bytes) >> 1;
|
||||
node->rate.pkt_up_rate = (node->flow.up_pkts - node->last_flow.up_pkts) >> 1;
|
||||
node->rate.pkt_down_rate = (node->flow.down_pkts - node->last_flow.down_pkts) >> 1;
|
||||
|
||||
node->last_flow.up_bytes = node->flow.up_bytes;
|
||||
node->last_flow.down_bytes = node->flow.down_bytes;
|
||||
node->last_flow.up_pkts = node->flow.up_pkts;
|
||||
node->last_flow.down_pkts = node->flow.down_pkts;
|
||||
if (node->rate.pkt_down_rate > 10 || node->rate.pkt_up_rate > 5){
|
||||
node->active_time++;
|
||||
node->inactive_time = 0;
|
||||
node->active = 1;
|
||||
}
|
||||
else{
|
||||
node->inactive_time++;
|
||||
node->active_time = 0;
|
||||
if (node->active && node->inactive_time > 30){
|
||||
node->active = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
static u_int32_t af_client_hook(void *priv,
|
||||
struct sk_buff *skb,
|
||||
const struct nf_hook_state *state)
|
||||
{
|
||||
#else
|
||||
static u_int32_t af_client_hook(unsigned int hook,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
#endif
|
||||
struct ethhdr *ethhdr = NULL;
|
||||
unsigned char smac[ETH_ALEN];
|
||||
af_client_info_t *nfc = NULL;
|
||||
int pkt_dir = 0;
|
||||
struct iphdr *iph = NULL;
|
||||
unsigned int ip = 0;
|
||||
struct ipv6hdr *ip6h = NULL;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
if (NULL == ct)
|
||||
return NF_ACCEPT;
|
||||
if (skb->protocol == htons(ETH_P_IPV6) && AF_MODE_GATEWAY != af_work_mode){
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
if (!skb->dev)
|
||||
return NF_ACCEPT;
|
||||
|
||||
pkt_dir = get_packet_dir(skb->dev);
|
||||
#else
|
||||
if (!in)
|
||||
{
|
||||
AF_ERROR("in is NULL\n");
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
pkt_dir = get_packet_dir(in);
|
||||
#endif
|
||||
|
||||
if (PKT_DIR_UP != pkt_dir)
|
||||
return NF_ACCEPT;
|
||||
|
||||
ethhdr = eth_hdr(skb);
|
||||
if (ethhdr)
|
||||
{
|
||||
memcpy(smac, ethhdr->h_source, ETH_ALEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(smac, &skb->cb[40], ETH_ALEN);
|
||||
}
|
||||
|
||||
|
||||
AF_CLIENT_LOCK_W();
|
||||
nfc = find_af_client(smac);
|
||||
if (!nfc)
|
||||
{
|
||||
if (skb->dev)
|
||||
AF_DEBUG("from dev:%s %pI4", skb->dev->name, &ip);
|
||||
nfc = nf_client_add(smac);
|
||||
}
|
||||
|
||||
if (nfc) {
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
iph = ip_hdr(skb);
|
||||
if (iph && nfc->ip != iph->saddr) {
|
||||
AF_DEBUG("update node " MAC_FMT " ipv4 %pI4--->%pI4\n",
|
||||
MAC_ARRAY(nfc->mac), &nfc->ip, &iph->saddr);
|
||||
nfc->ip = iph->saddr;
|
||||
}
|
||||
}
|
||||
else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
ip6h = ipv6_hdr(skb);
|
||||
if (ip6h && !ipv6_addr_equal(&nfc->ipv6, &ip6h->saddr)) {
|
||||
nfc->ipv6 = ip6h->saddr;
|
||||
}
|
||||
}
|
||||
nfc->flow.up_bytes += skb->len;
|
||||
nfc->flow.up_pkts++;
|
||||
}
|
||||
|
||||
AF_CLIENT_UNLOCK_W();
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
static u_int32_t af_client_hook2(void *priv,
|
||||
struct sk_buff *skb,
|
||||
const struct nf_hook_state *state)
|
||||
{
|
||||
#else
|
||||
static u_int32_t af_client_hook2(unsigned int hook,
|
||||
struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
int (*okfn)(struct sk_buff *))
|
||||
{
|
||||
#endif
|
||||
struct ethhdr *ethhdr = NULL;
|
||||
unsigned char smac[ETH_ALEN];
|
||||
af_client_info_t *nfc = NULL;
|
||||
int pkt_dir = 0;
|
||||
struct iphdr *iph = NULL;
|
||||
struct ipv6hdr *ip6h = NULL;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
|
||||
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||
if (ct == NULL)
|
||||
{
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
if (!skb->dev)
|
||||
return NF_ACCEPT;
|
||||
|
||||
pkt_dir = get_packet_dir(skb->dev);
|
||||
#else
|
||||
if (!in)
|
||||
{
|
||||
AF_ERROR("in is NULL\n");
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
pkt_dir = get_packet_dir(in);
|
||||
#endif
|
||||
if (!skb->dev)
|
||||
{
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
if (PKT_DIR_DOWN != pkt_dir)
|
||||
return NF_ACCEPT;
|
||||
|
||||
AF_CLIENT_LOCK_R();
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
iph = ip_hdr(skb);
|
||||
nfc = find_af_client_by_ip(iph->daddr);
|
||||
}
|
||||
else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
ip6h = ipv6_hdr(skb);
|
||||
nfc = find_af_client_by_ipv6(&ip6h->daddr);
|
||||
if (nfc){
|
||||
AF_LMT_DEBUG("found ipv6 %pI6 client\n", &ip6h->daddr);
|
||||
}
|
||||
else{
|
||||
AF_LMT_DEBUG("not found ipv6 %pI6 client\n", &ip6h->daddr);
|
||||
}
|
||||
}
|
||||
if (nfc){
|
||||
nfc->flow.down_bytes += skb->len;
|
||||
nfc->flow.down_pkts++;
|
||||
}
|
||||
|
||||
AF_CLIENT_UNLOCK_R();
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
static struct nf_hook_ops af_client_ops[] = {
|
||||
{
|
||||
.hook = af_client_hook,
|
||||
.pf = NFPROTO_INET,
|
||||
.hooknum = NF_INET_FORWARD,
|
||||
.priority = NF_IP_PRI_FIRST + 1,
|
||||
},
|
||||
{
|
||||
.hook = af_client_hook2,
|
||||
.pf = NFPROTO_INET,
|
||||
.hooknum = NF_INET_FORWARD,
|
||||
.priority = NF_IP_PRI_LAST - 1,
|
||||
},
|
||||
|
||||
};
|
||||
#else
|
||||
static struct nf_hook_ops af_client_ops[] = {
|
||||
{
|
||||
.hook = af_client_hook,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
|
||||
.owner = THIS_MODULE,
|
||||
#endif
|
||||
.pf = NFPROTO_IPV4,
|
||||
.hooknum = NF_INET_FORWARD,
|
||||
.priority = NF_IP_PRI_FIRST + 1,
|
||||
},
|
||||
{
|
||||
.hook = af_client_hook,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
|
||||
.owner = THIS_MODULE,
|
||||
#endif
|
||||
.pf = NFPROTO_IPV6,
|
||||
.hooknum = NF_INET_FORWARD,
|
||||
.priority = NF_IP_PRI_FIRST + 1,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||
static void client_timer_handler(struct timer_list *t)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0)
|
||||
af_client_info_t *client = from_timer(client, t, client_timer);
|
||||
#else
|
||||
af_client_info_t *client = timer_container_of(client, t, client_timer);
|
||||
#endif
|
||||
#else
|
||||
static void client_timer_handler(unsigned long data)
|
||||
{
|
||||
af_client_info_t *client = (af_client_info_t *)data;
|
||||
#endif
|
||||
if (!client) {
|
||||
AF_ERROR("client timer handler: invalid client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->timer_count >= 30) {
|
||||
__af_visit_info_report(client);
|
||||
client->timer_count = 0;
|
||||
}
|
||||
|
||||
af_update_client_status(client);
|
||||
client->timer_count++;
|
||||
mod_timer(&client->client_timer, jiffies + HZ * 2);
|
||||
}
|
||||
|
||||
void init_client_timer(af_client_info_t *client)
|
||||
{
|
||||
if (!client) {
|
||||
AF_ERROR("init_client_timer: invalid client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
|
||||
timer_setup(&client->client_timer, client_timer_handler, 0);
|
||||
#else
|
||||
setup_timer(&client->client_timer, client_timer_handler, (unsigned long)client);
|
||||
#endif
|
||||
|
||||
mod_timer(&client->client_timer, jiffies + HZ * 1);
|
||||
}
|
||||
|
||||
void stop_client_timer(af_client_info_t *client)
|
||||
{
|
||||
|
||||
if (!client) {
|
||||
AF_ERROR("stop_client_timer: invalid client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0)
|
||||
del_timer_sync(&client->client_timer);
|
||||
#else
|
||||
timer_delete_sync(&client->client_timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int af_client_init(void)
|
||||
{
|
||||
int err;
|
||||
nf_client_list_init();
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
|
||||
err = nf_register_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
|
||||
#else
|
||||
err = nf_register_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
|
||||
#endif
|
||||
if (err) {
|
||||
AF_ERROR("oaf register client hooks failed!\n");
|
||||
}
|
||||
AF_INFO("init app afclient ........ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void af_client_exit(void)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
|
||||
nf_unregister_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops));
|
||||
#else
|
||||
nf_unregister_hooks(af_client_ops, ARRAY_SIZE(af_client_ops));
|
||||
#endif
|
||||
nf_client_list_clear();
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifndef __AF_CLIENT_H__
|
||||
#define __AF_CLIENT_H__
|
||||
#include "app_filter.h"
|
||||
|
||||
extern rwlock_t af_client_lock;
|
||||
|
||||
extern u32 nfc_debug_level;
|
||||
|
||||
#define MAX_AF_CLIENT_HASH_SIZE 64
|
||||
#define NF_CLIENT_TIMER_EXPIRE 1
|
||||
#define MAX_CLIENT_ACTIVE_TIME 90
|
||||
|
||||
#define AF_CLIENT_LOCK_R() read_lock_bh(&af_client_lock);
|
||||
#define AF_CLIENT_UNLOCK_R() read_unlock_bh(&af_client_lock);
|
||||
#define AF_CLIENT_LOCK_W() write_lock_bh(&af_client_lock);
|
||||
#define AF_CLIENT_UNLOCK_W() write_unlock_bh(&af_client_lock);
|
||||
|
||||
#define NIPQUAD(addr) \
|
||||
((unsigned char *)&addr)[0], \
|
||||
((unsigned char *)&addr)[1], \
|
||||
((unsigned char *)&addr)[2], \
|
||||
((unsigned char *)&addr)[3]
|
||||
#define NIPQUAD_FMT "%u.%u.%u.%u"
|
||||
|
||||
enum NFC_PKT_DIR
|
||||
{
|
||||
PKT_DIR_DOWN,
|
||||
PKT_DIR_UP
|
||||
};
|
||||
|
||||
#define MAX_VISIT_HISTORY_TIME 24
|
||||
#define MAX_RECORD_APP_NUM 64
|
||||
#define MIN_REPORT_URL_LEN 4
|
||||
#define MAX_REPORT_URL_LEN 64
|
||||
|
||||
typedef struct flow_stat
|
||||
{
|
||||
long long up_bytes;
|
||||
long long down_bytes;
|
||||
long long up_pkts;
|
||||
long long down_pkts;
|
||||
} flow_stat_t;
|
||||
|
||||
typedef struct flow_rate
|
||||
{
|
||||
unsigned int up_rate;
|
||||
unsigned int down_rate;
|
||||
unsigned int pkt_up_rate;
|
||||
unsigned int pkt_down_rate;
|
||||
}flow_rate_t;
|
||||
typedef struct app_visit_info
|
||||
{
|
||||
unsigned int app_id;
|
||||
unsigned int total_num;
|
||||
unsigned int drop_num;
|
||||
unsigned long latest_time;
|
||||
unsigned int latest_action;
|
||||
} app_visit_info_t;
|
||||
|
||||
typedef struct visiting_info{
|
||||
int visiting_app;
|
||||
int app_time;
|
||||
char visiting_url[MAX_REPORT_URL_LEN];
|
||||
int url_time;
|
||||
}visiting_info_t;
|
||||
|
||||
typedef struct af_client_info
|
||||
{
|
||||
struct list_head hlist;
|
||||
unsigned char mac[MAC_ADDR_LEN];
|
||||
unsigned int ip;
|
||||
struct in6_addr ipv6;
|
||||
unsigned long create_jiffies;
|
||||
unsigned long update_jiffies;
|
||||
flow_stat_t flow;
|
||||
flow_stat_t last_flow;
|
||||
flow_stat_t period_flow;
|
||||
flow_rate_t rate;
|
||||
struct timer_list client_timer;
|
||||
unsigned int visit_app_num;
|
||||
int active_time;
|
||||
int inactive_time;
|
||||
int active;
|
||||
visiting_info_t visiting;
|
||||
int timer_count;
|
||||
int report_count;
|
||||
app_visit_info_t visit_info[MAX_RECORD_APP_NUM];
|
||||
} af_client_info_t;
|
||||
|
||||
int af_client_init(void);
|
||||
|
||||
void af_client_exit(void);
|
||||
af_client_info_t *find_af_client_by_ip(unsigned int ip);
|
||||
af_client_info_t *find_af_client_by_ipv6(struct in6_addr *addr);
|
||||
|
||||
af_client_info_t *find_af_client(unsigned char *mac);
|
||||
|
||||
void check_client_expire(void);
|
||||
|
||||
void af_visit_info_report(void);
|
||||
|
||||
void af_client_list_reset_report_num(void);
|
||||
af_client_info_t *nf_client_add(unsigned char *mac);
|
||||
af_client_info_t *find_and_add_af_client(unsigned char *mac);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,296 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/version.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include "af_utils.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "af_log.h"
|
||||
#include "af_client.h"
|
||||
extern char *ipv6_to_str(const struct in6_addr *addr, char *str);
|
||||
|
||||
extern struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE];
|
||||
struct af_client_iter_state
|
||||
{
|
||||
unsigned int bucket;
|
||||
void *head;
|
||||
};
|
||||
|
||||
static void *af_client_get_first(struct seq_file *seq)
|
||||
{
|
||||
struct af_client_iter_state *st = seq->private;
|
||||
for (st->bucket = 0; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
|
||||
{
|
||||
if (!list_empty(&(af_client_list_table[st->bucket])))
|
||||
{
|
||||
st->head = &(af_client_list_table[st->bucket]);
|
||||
return af_client_list_table[st->bucket].next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *af_client_get_next(struct seq_file *seq,
|
||||
void *head)
|
||||
{
|
||||
struct af_client_iter_state *st = seq->private;
|
||||
struct hlist_node *node = (struct hlist_node *)head;
|
||||
|
||||
node = node->next;
|
||||
if (node != st->head)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
else
|
||||
{
|
||||
st->bucket++;
|
||||
for (; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++)
|
||||
{
|
||||
if (!list_empty(&(af_client_list_table[st->bucket])))
|
||||
{
|
||||
st->head = &(af_client_list_table[st->bucket]);
|
||||
return af_client_list_table[st->bucket].next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void *af_client_get_idx(struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
void *head = af_client_get_first(seq);
|
||||
|
||||
if (head)
|
||||
while (pos && (head = af_client_get_next(seq, head)))
|
||||
pos--;
|
||||
|
||||
return pos ? NULL : head;
|
||||
}
|
||||
|
||||
static void *af_client_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
AF_CLIENT_LOCK_R();
|
||||
if (*pos == 0)
|
||||
{
|
||||
return SEQ_START_TOKEN;
|
||||
}
|
||||
|
||||
return af_client_get_idx(s, *pos - 1);
|
||||
}
|
||||
|
||||
static void *af_client_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
if (v == SEQ_START_TOKEN)
|
||||
return af_client_get_idx(s, 0);
|
||||
|
||||
return af_client_get_next(s, v);
|
||||
}
|
||||
|
||||
static void af_client_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
AF_CLIENT_UNLOCK_R();
|
||||
}
|
||||
|
||||
static int af_client_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
unsigned char mac_str[32] = {0};
|
||||
unsigned char ip_str[32] = {0};
|
||||
unsigned char ipv6_str[128];
|
||||
|
||||
static int index = 0;
|
||||
af_client_info_t *node = (af_client_info_t *)v;
|
||||
if (v == SEQ_START_TOKEN)
|
||||
{
|
||||
index = 0;
|
||||
seq_printf(s, "%-4s %-20s %-20s %-32s %-16s %-16s\n", "Id", "Mac", "IP", "IPv6", "UpRate", "DownRate");
|
||||
return 0;
|
||||
}
|
||||
index++;
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
|
||||
sprintf(ip_str, "%pI4", &node->ip);
|
||||
ipv6_to_str(&node->ipv6, ipv6_str);
|
||||
|
||||
|
||||
seq_printf(s, "%-4d %-20s %-20s %-32s %-16d %-16d\n", index, mac_str, ip_str, ipv6_str, node->rate.up_rate, node->rate.down_rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations nf_client_seq_ops = {
|
||||
.start = af_client_seq_start,
|
||||
.next = af_client_seq_next,
|
||||
.stop = af_client_seq_stop,
|
||||
.show = af_client_seq_show};
|
||||
|
||||
static int af_client_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
struct af_client_iter_state *iter;
|
||||
int err;
|
||||
|
||||
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (!iter)
|
||||
return -ENOMEM;
|
||||
|
||||
err = seq_open(file, &nf_client_seq_ops);
|
||||
if (err)
|
||||
{
|
||||
kfree(iter);
|
||||
return err;
|
||||
}
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = iter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
|
||||
static const struct file_operations af_client_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = af_client_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
#else
|
||||
static const struct proc_ops af_client_fops = {
|
||||
.proc_flags = PROC_ENTRY_PERMANENT,
|
||||
.proc_read = seq_read,
|
||||
.proc_open = af_client_open,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release_private,
|
||||
};
|
||||
#endif
|
||||
|
||||
#define AF_CLIENT_PROC_STR "af_client"
|
||||
|
||||
|
||||
|
||||
|
||||
static int af_visiting_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
unsigned char mac_str[32] = {0};
|
||||
unsigned char ip_str[32] = {0};
|
||||
static int index = 0;
|
||||
int i;
|
||||
af_client_info_t *node = (af_client_info_t *)v;
|
||||
if (v == SEQ_START_TOKEN)
|
||||
{
|
||||
index = 0;
|
||||
seq_printf(s, "%-20s %-12s %-32s\n", "Mac", "Appid", "Url");
|
||||
return 0;
|
||||
}
|
||||
index++;
|
||||
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac));
|
||||
int visiting_app = 0;
|
||||
char visiting_url[64] = {0};
|
||||
if (af_get_timestamp_sec() - node->visiting.app_time < 120){
|
||||
visiting_app = node->visiting.visiting_app;
|
||||
}
|
||||
if ( af_get_timestamp_sec() - node->visiting.url_time < 120 ){
|
||||
strncpy(visiting_url, node->visiting.visiting_url, sizeof(visiting_url));
|
||||
}
|
||||
else{
|
||||
strcpy(visiting_url, "none");
|
||||
}
|
||||
seq_printf(s, "%-20s %-12d %-32s\n", mac_str, visiting_app, visiting_url);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations nf_visiting_seq_ops = {
|
||||
.start = af_client_seq_start,
|
||||
.next = af_client_seq_next,
|
||||
.stop = af_client_seq_stop,
|
||||
.show = af_visiting_seq_show
|
||||
};
|
||||
|
||||
|
||||
static int af_visiting_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
struct af_client_iter_state *iter;
|
||||
int err;
|
||||
|
||||
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (!iter)
|
||||
return -ENOMEM;
|
||||
|
||||
err = seq_open(file, &nf_visiting_seq_ops);
|
||||
if (err)
|
||||
{
|
||||
kfree(iter);
|
||||
return err;
|
||||
}
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = iter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
|
||||
static const struct file_operations af_visiting_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = af_visiting_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
#else
|
||||
static const struct proc_ops af_visiting_fops = {
|
||||
.proc_flags = PROC_ENTRY_PERMANENT,
|
||||
.proc_read = seq_read,
|
||||
.proc_open = af_visiting_open,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release_private,
|
||||
};
|
||||
#endif
|
||||
#define AF_VISIT_INFO "af_visit"
|
||||
|
||||
int init_af_client_procfs(void)
|
||||
{
|
||||
struct proc_dir_entry *pde;
|
||||
struct net *net = &init_net;
|
||||
pde = proc_create(AF_CLIENT_PROC_STR, 0440, net->proc_net, &af_client_fops);
|
||||
|
||||
if (!pde)
|
||||
{
|
||||
AF_ERROR("nf_client proc file created error\n");
|
||||
return -1;
|
||||
}
|
||||
pde = proc_create(AF_VISIT_INFO, 0440, net->proc_net, &af_visiting_fops);
|
||||
|
||||
if (!pde)
|
||||
{
|
||||
AF_ERROR("client visiting proc file created error\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void finit_af_client_procfs(void)
|
||||
{
|
||||
struct net *net = &init_net;
|
||||
remove_proc_entry(AF_CLIENT_PROC_STR, net->proc_net);
|
||||
remove_proc_entry(AF_VISIT_INFO, net->proc_net);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef __AF_CLIENT_FS_H__
|
||||
#define __AF_CLIENT_FS_H__
|
||||
|
||||
int init_af_client_procfs(void);
|
||||
void finit_af_client_procfs(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/version.h>
|
||||
#include "cJSON.h"
|
||||
#include "app_filter.h"
|
||||
#include "af_config.h"
|
||||
#include "af_utils.h"
|
||||
#include "af_log.h"
|
||||
#include "af_rule_config.h"
|
||||
#include "af_user_config.h"
|
||||
#include "af_whitelist_config.h"
|
||||
|
||||
#define AF_DEV_NAME "appfilter"
|
||||
|
||||
extern u_int32_t g_update_jiffies;
|
||||
|
||||
static struct mutex af_cdev_mutex;
|
||||
struct af_config_dev
|
||||
{
|
||||
dev_t id;
|
||||
struct cdev char_dev;
|
||||
struct class *c;
|
||||
};
|
||||
struct af_config_dev g_af_dev;
|
||||
|
||||
struct af_cdev_file
|
||||
{
|
||||
size_t size;
|
||||
char buf[256 << 10];
|
||||
};
|
||||
|
||||
static struct af_config_interface af_config_interfaces[] = {
|
||||
{AF_CMD_ADD_APPID, af_config_add_appid, "Add App ID"},
|
||||
{AF_CMD_DEL_APPID, af_config_del_appid, "Delete App ID"},
|
||||
{AF_CMD_CLEAN_APPID, af_config_clean_appid, "Clean App ID"},
|
||||
{AF_CMD_SET_MAC_LIST, af_config_set_mac_list, "Set MAC List"},
|
||||
{AF_CMD_SET_WHITELIST_MAC_LIST, af_config_set_whitelist_mac_list, "Set Whitelist MAC List"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
static af_config_handler_t af_find_handler(enum AF_CONFIG_CMD cmd)
|
||||
{
|
||||
struct af_config_interface *interface = af_config_interfaces;
|
||||
|
||||
while (interface->handler != NULL) {
|
||||
if (interface->cmd == cmd) {
|
||||
return interface->handler;
|
||||
}
|
||||
interface++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
add:
|
||||
{
|
||||
"op":1,
|
||||
"data":{
|
||||
"apps":[]
|
||||
}
|
||||
}
|
||||
clean
|
||||
{
|
||||
"op":3,
|
||||
}
|
||||
*/
|
||||
int af_config_handle(char *config, unsigned int len)
|
||||
{
|
||||
cJSON *config_obj = NULL;
|
||||
cJSON *cmd_obj = NULL;
|
||||
cJSON *data_obj = NULL;
|
||||
int ret = 0;
|
||||
af_config_handler_t handler = NULL;
|
||||
|
||||
if (!config || len == 0)
|
||||
{
|
||||
AF_ERROR("config or len is invalid\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
AF_DEBUG("config = %s\n", config);
|
||||
config_obj = cJSON_Parse(config);
|
||||
if (!config_obj)
|
||||
{
|
||||
AF_ERROR("config_obj is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmd_obj = cJSON_GetObjectItem(config_obj, "op");
|
||||
if (!cmd_obj)
|
||||
{
|
||||
AF_ERROR("not find op object\n");
|
||||
cJSON_Delete(config_obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data_obj = cJSON_GetObjectItem(config_obj, "data");
|
||||
|
||||
handler = af_find_handler(cmd_obj->valueint);
|
||||
if (handler) {
|
||||
ret = handler(data_obj);
|
||||
g_update_jiffies = jiffies;
|
||||
cJSON_Delete(config_obj);
|
||||
return ret;
|
||||
} else {
|
||||
AF_ERROR("invalid cmd %d\n", cmd_obj->valueint);
|
||||
cJSON_Delete(config_obj);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int af_cdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct af_cdev_file *file;
|
||||
file = vzalloc(sizeof(*file));
|
||||
if (!file)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&af_cdev_mutex);
|
||||
filp->private_data = file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t af_cdev_read(struct file *filp, char *buf, size_t count, loff_t *off)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_cdev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct af_cdev_file *file = filp->private_data;
|
||||
AF_DEBUG("config size: %d,data = %s\n", (int)file->size, file->buf);
|
||||
af_config_handle(file->buf, file->size);
|
||||
filp->private_data = NULL;
|
||||
mutex_unlock(&af_cdev_mutex);
|
||||
vfree(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t af_cdev_write(struct file *filp, const char *buffer, size_t count, loff_t *off)
|
||||
{
|
||||
struct af_cdev_file *file = filp->private_data;
|
||||
int ret;
|
||||
if (file->size + count > sizeof(file->buf))
|
||||
{
|
||||
AF_ERROR("config overflow, cur_size: %d, block_size: %d, max_size: %d",
|
||||
(int)file->size, (int)count, (int)sizeof(file->buf));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = copy_from_user(file->buf + file->size, buffer, count);
|
||||
if (ret != 0)
|
||||
return -EINVAL;
|
||||
|
||||
file->size += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct file_operations af_cdev_ops = {
|
||||
owner : THIS_MODULE,
|
||||
release : af_cdev_release,
|
||||
open : af_cdev_open,
|
||||
write : af_cdev_write,
|
||||
read : af_cdev_read,
|
||||
};
|
||||
|
||||
int af_register_dev(void)
|
||||
{
|
||||
struct device *dev;
|
||||
int res;
|
||||
mutex_init(&af_cdev_mutex);
|
||||
|
||||
res = alloc_chrdev_region(&g_af_dev.id, 0, 1, AF_DEV_NAME);
|
||||
if (res != 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cdev_init(&g_af_dev.char_dev, &af_cdev_ops);
|
||||
res = cdev_add(&g_af_dev.char_dev, g_af_dev.id, 1);
|
||||
if (res < 0)
|
||||
{
|
||||
goto REGION_OUT;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||
g_af_dev.c = class_create(THIS_MODULE, AF_DEV_NAME);
|
||||
#else
|
||||
g_af_dev.c = class_create(AF_DEV_NAME);
|
||||
#endif
|
||||
if (IS_ERR_OR_NULL(g_af_dev.c))
|
||||
{
|
||||
goto CDEV_OUT;
|
||||
}
|
||||
|
||||
dev = device_create(g_af_dev.c, NULL, g_af_dev.id, NULL, AF_DEV_NAME);
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
{
|
||||
goto CLASS_OUT;
|
||||
}
|
||||
AF_INFO("register char dev....ok\n");
|
||||
return 0;
|
||||
|
||||
CLASS_OUT:
|
||||
class_destroy(g_af_dev.c);
|
||||
CDEV_OUT:
|
||||
cdev_del(&g_af_dev.char_dev);
|
||||
REGION_OUT:
|
||||
unregister_chrdev_region(g_af_dev.id, 1);
|
||||
|
||||
AF_ERROR("register char dev....fail\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void af_unregister_dev(void)
|
||||
{
|
||||
device_destroy(g_af_dev.c, g_af_dev.id);
|
||||
class_destroy(g_af_dev.c);
|
||||
cdev_del(&g_af_dev.char_dev);
|
||||
unregister_chrdev_region(g_af_dev.id, 1);
|
||||
AF_INFO("unregister char dev....ok\n");
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef __AF_CONFIG_H__
|
||||
#define __AF_CONFIG_H__
|
||||
#include "app_filter.h"
|
||||
|
||||
enum AF_CONFIG_CMD
|
||||
{
|
||||
AF_CMD_ADD_APPID = 1,
|
||||
AF_CMD_DEL_APPID,
|
||||
AF_CMD_CLEAN_APPID,
|
||||
AF_CMD_SET_MAC_LIST,
|
||||
AF_CMD_SET_WHITELIST_MAC_LIST,
|
||||
};
|
||||
|
||||
typedef int (*af_config_handler_t)(cJSON *data);
|
||||
|
||||
struct af_config_interface
|
||||
{
|
||||
enum AF_CONFIG_CMD cmd;
|
||||
af_config_handler_t handler;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
int af_register_dev(void);
|
||||
void af_unregister_dev(void);
|
||||
|
||||
int af_config_add_appid(cJSON *data);
|
||||
int af_config_del_appid(cJSON *data);
|
||||
int af_config_clean_appid(cJSON *data);
|
||||
int af_config_set_mac_list(cJSON *data);
|
||||
int af_config_set_whitelist_mac_list(cJSON *data);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,302 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/types.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/cdev.h>
|
||||
#include "af_conntrack.h"
|
||||
#include "af_log.h"
|
||||
|
||||
struct hlist_head af_conn_table[AF_CONN_HASH_SIZE];
|
||||
|
||||
DEFINE_SPINLOCK(af_conn_lock);
|
||||
|
||||
static u32 af_conn_hash(u32 src_ip, u32 dst_ip,
|
||||
u16 src_port, u16 dst_port,
|
||||
u8 protocol)
|
||||
{
|
||||
return jhash_3words(src_ip, dst_ip,
|
||||
((u32)protocol << 16) | src_port,
|
||||
dst_port) % AF_CONN_HASH_SIZE;
|
||||
}
|
||||
|
||||
|
||||
void af_conn_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
spin_lock(&af_conn_lock);
|
||||
af_conn_t *p = NULL;
|
||||
struct hlist_node *n;
|
||||
|
||||
for (i = 0; i < AF_CONN_HASH_SIZE; i++)
|
||||
{
|
||||
hlist_for_each_entry_safe(p, n, &af_conn_table[i], node)
|
||||
{
|
||||
hlist_del(&p->node);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
spin_unlock(&af_conn_lock);
|
||||
}
|
||||
|
||||
af_conn_t *af_conn_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
|
||||
{
|
||||
u32 hash;
|
||||
af_conn_t *conn;
|
||||
hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol);
|
||||
conn = kmalloc(sizeof(af_conn_t), GFP_ATOMIC);
|
||||
if (!conn) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn->src_ip = src_ip;
|
||||
conn->dst_ip = dst_ip;
|
||||
conn->src_port = src_port;
|
||||
conn->dst_port = dst_port;
|
||||
conn->protocol = protocol;
|
||||
conn->total_pkts = 0;
|
||||
conn->app_id = 0;
|
||||
conn->client_hello = 0;
|
||||
conn->drop = 0;
|
||||
conn->state = AF_CONN_NEW;
|
||||
conn->last_jiffies = jiffies;
|
||||
hlist_add_head(&conn->node, &af_conn_table[hash]);
|
||||
AF_LMT_INFO("add new conn ok...%pI4:%d->%pI4:%d %d\n",
|
||||
&conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol);
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
af_conn_t* af_conn_find(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
|
||||
{
|
||||
u32 hash;
|
||||
af_conn_t *conn;
|
||||
|
||||
hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol);
|
||||
hlist_for_each_entry(conn, &af_conn_table[hash], node)
|
||||
{
|
||||
if (conn->src_ip == src_ip && conn->dst_ip == dst_ip &&
|
||||
conn->src_port == src_port && conn->dst_port == dst_port &&
|
||||
conn->protocol == protocol) {
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
af_conn_t* af_conn_find_and_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol)
|
||||
{
|
||||
af_conn_t *conn;
|
||||
conn = af_conn_find(src_ip, dst_ip, src_port, dst_port, protocol);
|
||||
if (!conn)
|
||||
{
|
||||
conn = af_conn_add(src_ip, dst_ip, src_port, dst_port, protocol);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
void af_conn_update(af_conn_t *conn, u32 app_id, u8 drop)
|
||||
{
|
||||
spin_lock(&af_conn_lock);
|
||||
conn->app_id = app_id;
|
||||
conn->drop = drop;
|
||||
conn->last_jiffies = jiffies;
|
||||
spin_unlock(&af_conn_lock);
|
||||
}
|
||||
|
||||
#define MAX_AF_CONN_CHECK_COUNT 5
|
||||
void af_conn_clean_timeout(void)
|
||||
{
|
||||
int i;
|
||||
af_conn_t *conn;
|
||||
struct hlist_node *n;
|
||||
unsigned long timeout = AF_CONN_TIMEOUT * HZ;
|
||||
static int last_bucket = 0;
|
||||
int count = 0;
|
||||
spin_lock(&af_conn_lock);
|
||||
for (i = last_bucket; i < AF_CONN_HASH_SIZE; i++)
|
||||
{
|
||||
hlist_for_each_entry_safe(conn, n, &af_conn_table[i], node)
|
||||
{
|
||||
if (time_after(jiffies, conn->last_jiffies + timeout)) {
|
||||
AF_LMT_INFO("clean timeout conn ok...%pI4:%d->%pI4:%d %d\n",
|
||||
&conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol);
|
||||
hlist_del(&(conn->node));
|
||||
kfree(conn);
|
||||
}
|
||||
}
|
||||
last_bucket = i;
|
||||
count++;
|
||||
if (count > MAX_AF_CONN_CHECK_COUNT)
|
||||
break;
|
||||
}
|
||||
if (last_bucket == AF_CONN_HASH_SIZE - 1)
|
||||
{
|
||||
last_bucket = 0;
|
||||
}
|
||||
spin_unlock(&af_conn_lock);
|
||||
}
|
||||
|
||||
struct af_conn_iter_state
|
||||
{
|
||||
unsigned int bucket;
|
||||
};
|
||||
|
||||
|
||||
static void *af_conn_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
if (*pos == 0)
|
||||
{
|
||||
return SEQ_START_TOKEN;
|
||||
}
|
||||
struct af_conn_iter_state *st = s->private;
|
||||
while (st->bucket < AF_CONN_HASH_SIZE){
|
||||
if (!hlist_empty(&(af_conn_table[st->bucket])))
|
||||
{
|
||||
return &af_conn_table[st->bucket++].first->next;
|
||||
}
|
||||
st->bucket++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *af_conn_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
if (v == SEQ_START_TOKEN){
|
||||
return NULL;
|
||||
}
|
||||
struct hlist_node *node = (struct hlist_node *)v;
|
||||
node = node->next;
|
||||
if (node != NULL)
|
||||
return node;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void af_conn_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int af_conn_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
unsigned char src_ip_str[32] = {0};
|
||||
unsigned char dst_ip_str[32] = {0};
|
||||
static int index = 0;
|
||||
af_conn_t *node = (af_conn_t *)v;
|
||||
if (v == SEQ_START_TOKEN)
|
||||
{
|
||||
index = 0;
|
||||
seq_printf(s, "%-4s %-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n",
|
||||
"Id", "src_ip", "dst_ip", "src_port", "dst_port", "protocol", "app_id", "drop", "inactive", "total_pkts");
|
||||
return 0;
|
||||
}
|
||||
|
||||
index++;
|
||||
sprintf(src_ip_str, "%pI4", &node->src_ip);
|
||||
sprintf(dst_ip_str, "%pI4", &node->dst_ip);
|
||||
u_int32_t inactive_time = jiffies - node->last_jiffies;
|
||||
|
||||
seq_printf(s, "%-4d %-20s %-20s %-12d %-12d %-12d %-12d %-12d %-12d %-12d\n", index, src_ip_str, dst_ip_str,
|
||||
node->src_port, node->dst_port, node->protocol, node->app_id, node->drop, inactive_time, node->total_pkts);
|
||||
return 0;
|
||||
}
|
||||
static const struct seq_operations af_conn_seq_ops = {
|
||||
.start = af_conn_seq_start,
|
||||
.next = af_conn_seq_next,
|
||||
.stop = af_conn_seq_stop,
|
||||
.show = af_conn_seq_show
|
||||
};
|
||||
|
||||
|
||||
static int af_conn_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
struct af_conn_iter_state *iter;
|
||||
int err;
|
||||
|
||||
iter = kzalloc(sizeof(*iter), GFP_KERNEL);
|
||||
if (!iter)
|
||||
return -ENOMEM;
|
||||
|
||||
err = seq_open(file, &af_conn_seq_ops);
|
||||
if (err)
|
||||
{
|
||||
kfree(iter);
|
||||
return err;
|
||||
}
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = iter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0)
|
||||
static const struct file_operations af_conn_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = af_conn_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
#else
|
||||
static const struct proc_ops af_conn_fops = {
|
||||
.proc_flags = PROC_ENTRY_PERMANENT,
|
||||
.proc_read = seq_read,
|
||||
.proc_open = af_conn_open,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release_private,
|
||||
};
|
||||
#endif
|
||||
|
||||
#define AF_CONN_PROC_STR "af_conn"
|
||||
|
||||
int af_conn_init_procfs(void)
|
||||
{
|
||||
struct proc_dir_entry *pde;
|
||||
struct net *net = &init_net;
|
||||
pde = proc_create(AF_CONN_PROC_STR, 0644, net->proc_net, &af_conn_fops);
|
||||
if (!pde)
|
||||
{
|
||||
printk("af_conn seq file created error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void af_conn_remove_procfs(void)
|
||||
{
|
||||
struct net *net = &init_net;
|
||||
remove_proc_entry(AF_CONN_PROC_STR, net->proc_net);
|
||||
}
|
||||
|
||||
|
||||
int af_conn_init(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < AF_CONN_HASH_SIZE; i++)
|
||||
{
|
||||
INIT_HLIST_HEAD(&af_conn_table[i]);
|
||||
}
|
||||
af_conn_init_procfs();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void af_conn_exit(void){
|
||||
af_conn_remove_procfs();
|
||||
af_conn_cleanup();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef __AF_SIMPLE_CONNTRACK_H__
|
||||
#define __AF_SIMPLE_CONNTRACK_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#define AF_CONN_TIMEOUT 30
|
||||
#define AF_CONN_HASH_SIZE 256
|
||||
|
||||
extern spinlock_t af_conn_lock;
|
||||
typedef enum {
|
||||
AF_CONN_NEW = 0,
|
||||
AF_CONN_ESTABLISHED,
|
||||
AF_CONN_DPI_FINISHED,
|
||||
} af_conn_state_t;
|
||||
|
||||
typedef struct {
|
||||
struct hlist_node node;
|
||||
u32 src_ip;
|
||||
u32 dst_ip;
|
||||
u16 src_port;
|
||||
u16 dst_port;
|
||||
u8 protocol;
|
||||
u32 total_pkts;
|
||||
u32 app_id;
|
||||
u8 client_hello;
|
||||
u8 drop;
|
||||
u8 ignore;
|
||||
af_conn_state_t state;
|
||||
unsigned long last_jiffies;
|
||||
} af_conn_t;
|
||||
|
||||
int af_conn_init(void);
|
||||
|
||||
void af_conn_cleanup(void);
|
||||
af_conn_t* af_conn_add(u32 src_ip, u32 dst_ip,
|
||||
u16 src_port, u16 dst_port,
|
||||
u8 protocol);
|
||||
|
||||
af_conn_t* af_conn_find(u32 src_ip, u32 dst_ip,
|
||||
u16 src_port, u16 dst_port,
|
||||
u8 protocol);
|
||||
|
||||
af_conn_t* af_conn_find_and_add(u32 src_ip, u32 dst_ip,
|
||||
u16 src_port, u16 dst_port,
|
||||
u8 protocol);
|
||||
|
||||
void af_conn_update(af_conn_t *conn, u32 app_id, u8 drop);
|
||||
|
||||
void af_conn_clean_timeout(void);
|
||||
|
||||
|
||||
void af_conn_exit(void);
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include "app_filter.h"
|
||||
#include "af_log.h"
|
||||
int af_log_lvl = 1;
|
||||
int af_test_mode = 0;
|
||||
// todo: rename af_log.c
|
||||
int g_oaf_filter_enable __read_mostly = 0;
|
||||
int g_oaf_record_enable __read_mostly = 0;
|
||||
int g_by_pass_accl = 1;
|
||||
int g_user_mode = 0;
|
||||
int af_work_mode = AF_MODE_GATEWAY;
|
||||
unsigned int af_lan_ip = 0;
|
||||
unsigned int af_lan_mask = 0;
|
||||
char g_lan_ifname[64] = "br-lan";
|
||||
int g_tcp_rst = 1;
|
||||
int g_feature_init = 0;
|
||||
char g_oaf_version[64] = AF_VERSION;
|
||||
int g_disable_quic = 0;
|
||||
int g_app_filter_mode = 0; // 0 = specified apps, 1 = all apps
|
||||
/*
|
||||
cat /proc/sys/oaf/debug
|
||||
*/
|
||||
static struct ctl_table oaf_table[] = {
|
||||
{
|
||||
.procname = "debug",
|
||||
.data = &af_log_lvl,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "feature_init",
|
||||
.data = &g_feature_init,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "version",
|
||||
.data = g_oaf_version,
|
||||
.maxlen = 64,
|
||||
.mode = 0444,
|
||||
.proc_handler = proc_dostring,
|
||||
},
|
||||
{
|
||||
.procname = "test_mode",
|
||||
.data = &af_test_mode,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "enable",
|
||||
.data = &g_oaf_filter_enable,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "by_pass_accl",
|
||||
.data = &g_by_pass_accl,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "tcp_rst",
|
||||
.data = &g_tcp_rst,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "lan_ifname",
|
||||
.data = g_lan_ifname,
|
||||
.maxlen = 64,
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dostring,
|
||||
},
|
||||
{
|
||||
.procname = "record_enable",
|
||||
.data = &g_oaf_record_enable,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "user_mode",
|
||||
.data = &g_user_mode,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "work_mode",
|
||||
.data = &af_work_mode,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "lan_ip",
|
||||
.data = &af_lan_ip,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_douintvec,
|
||||
},
|
||||
{
|
||||
.procname = "lan_mask",
|
||||
.data = &af_lan_mask,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_douintvec,
|
||||
},
|
||||
{
|
||||
.procname = "disable_quic",
|
||||
.data = &g_disable_quic,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
{
|
||||
.procname = "app_filter_mode",
|
||||
.data = &g_app_filter_mode,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0666,
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
};
|
||||
#define OAF_SYS_PROC_DIR "oaf"
|
||||
|
||||
static struct ctl_table oaf_root_table[] = {
|
||||
{
|
||||
.procname = OAF_SYS_PROC_DIR,
|
||||
.mode = 0555,
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
|
||||
.child = oaf_table,
|
||||
#endif
|
||||
},
|
||||
{}
|
||||
};
|
||||
static struct ctl_table_header *oaf_table_header;
|
||||
|
||||
|
||||
static int af_init_log_sysctl(void)
|
||||
{
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
|
||||
oaf_table_header = register_sysctl_table(oaf_root_table);
|
||||
#else
|
||||
oaf_table_header = register_sysctl(OAF_SYS_PROC_DIR, oaf_table);
|
||||
#endif
|
||||
if (oaf_table_header == NULL){
|
||||
printk("init log sysctl...failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int af_fini_log_sysctl(void)
|
||||
{
|
||||
if (oaf_table_header)
|
||||
unregister_sysctl_table(oaf_table_header);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int af_log_init(void){
|
||||
af_init_log_sysctl();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int af_log_exit(void){
|
||||
af_fini_log_sysctl();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef __AF_DEBUG_H__
|
||||
#define __AF_DEBUG_H__
|
||||
extern int af_log_lvl;
|
||||
extern int af_test_mode;
|
||||
extern int af_work_mode;
|
||||
extern int g_oaf_filter_enable;
|
||||
extern int g_oaf_record_enable;
|
||||
extern int g_by_pass_accl;
|
||||
extern unsigned int af_lan_ip;
|
||||
extern unsigned int af_lan_mask;
|
||||
extern int g_feature_init;
|
||||
extern int g_user_mode;
|
||||
extern int g_disable_quic;
|
||||
extern int g_app_filter_mode;
|
||||
extern char g_lan_ifname[64];
|
||||
extern int g_tcp_rst;
|
||||
#define LOG(level, fmt, ...) do { \
|
||||
if ((level) <= af_log_lvl) { \
|
||||
printk(fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define LLOG(level, fmt, ...) do { \
|
||||
if ((level) <= af_log_lvl) { \
|
||||
pr_info_ratelimited(fmt, ##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define AF_ERROR(...) LOG(0, ##__VA_ARGS__)
|
||||
#define AF_WARN(...) LOG(1, ##__VA_ARGS__)
|
||||
#define AF_INFO(...) LOG(2, ##__VA_ARGS__)
|
||||
#define AF_DEBUG(...) LOG(3, ##__VA_ARGS__)
|
||||
|
||||
#define AF_LMT_ERROR(...) LLOG(0, ##__VA_ARGS__)
|
||||
#define AF_LMT_WARN(...) LLOG(1, ##__VA_ARGS__)
|
||||
#define AF_LMT_INFO(...) LLOG(2, ##__VA_ARGS__)
|
||||
#define AF_LMT_DEBUG(...) LLOG(3, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define TEST_MODE() (af_test_mode)
|
||||
int af_log_init(void);
|
||||
int af_log_exit(void);
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#include "cJSON.h"
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
#include "af_log.h"
|
||||
|
||||
#define AF_MAX_APP_TYPE_NUM 32
|
||||
#define AF_MAX_APP_NUM 512
|
||||
|
||||
DEFINE_RWLOCK(af_rule_lock);
|
||||
|
||||
#define af_rule_read_lock() read_lock_bh(&af_rule_lock);
|
||||
#define af_rule_read_unlock() read_unlock_bh(&af_rule_lock);
|
||||
#define af_rule_write_lock() write_lock_bh(&af_rule_lock);
|
||||
#define af_rule_write_unlock() write_unlock_bh(&af_rule_lock);
|
||||
|
||||
extern u_int32_t g_update_jiffies;
|
||||
|
||||
char g_app_id_array[AF_MAX_APP_TYPE_NUM][AF_MAX_APP_NUM] = {0};
|
||||
|
||||
void af_show_app_status(void)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++)
|
||||
{
|
||||
for (j = 0; j < AF_MAX_APP_NUM; j++)
|
||||
{
|
||||
af_rule_read_lock();
|
||||
if (g_app_id_array[i][j] == AF_TRUE)
|
||||
{
|
||||
AF_DEBUG("%d, %d\n", i, j);
|
||||
}
|
||||
af_rule_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
AF_DEBUG("\n\n\n");
|
||||
}
|
||||
|
||||
int af_change_app_status(cJSON *data_obj, int status)
|
||||
{
|
||||
int i;
|
||||
int id;
|
||||
int type;
|
||||
cJSON *appid_arr = NULL;
|
||||
if (!data_obj)
|
||||
{
|
||||
AF_ERROR("data obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
appid_arr = cJSON_GetObjectItem(data_obj, "apps");
|
||||
if (!appid_arr)
|
||||
{
|
||||
AF_ERROR("apps obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < cJSON_GetArraySize(appid_arr); i++)
|
||||
{
|
||||
cJSON *appid_obj = cJSON_GetArrayItem(appid_arr, i);
|
||||
if (!appid_obj)
|
||||
return -1;
|
||||
id = AF_APP_ID(appid_obj->valueint);
|
||||
type = AF_APP_TYPE(appid_obj->valueint);
|
||||
af_rule_write_lock();
|
||||
g_app_id_array[type][id] = status;
|
||||
af_rule_write_unlock();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void af_init_app_status(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++)
|
||||
{
|
||||
for (j = 0; j < AF_MAX_APP_NUM; j++)
|
||||
{
|
||||
af_rule_write_lock();
|
||||
g_app_id_array[i][j] = AF_FALSE;
|
||||
af_rule_write_unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
int af_get_app_status(int appid)
|
||||
{
|
||||
int status = 0;
|
||||
int id = AF_APP_ID(appid);
|
||||
int type = AF_APP_TYPE(appid);
|
||||
af_rule_read_lock();
|
||||
status = g_app_id_array[type][id];
|
||||
af_rule_read_unlock();
|
||||
return status;
|
||||
}
|
||||
|
||||
int af_config_add_appid(cJSON *data)
|
||||
{
|
||||
return af_change_app_status(data, 1);
|
||||
}
|
||||
|
||||
int af_config_del_appid(cJSON *data)
|
||||
{
|
||||
return af_change_app_status(data, 0);
|
||||
}
|
||||
|
||||
int af_config_clean_appid(cJSON *data)
|
||||
{
|
||||
af_init_app_status();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef __AF_RULE_CONFIG_H__
|
||||
#define __AF_RULE_CONFIG_H__
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
#include "af_log.h"
|
||||
void af_init_app_status(void);
|
||||
int af_get_app_status(int appid);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/version.h>
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
#include "af_log.h"
|
||||
#include "cJSON.h"
|
||||
#include "af_whitelist_config.h"
|
||||
#include "af_user_config.h"
|
||||
|
||||
DEFINE_RWLOCK(af_mac_lock);
|
||||
|
||||
u32 total_mac = 0;
|
||||
struct list_head af_mac_htable[MAX_AF_MAC_HASH_SIZE];
|
||||
void af_mac_list_init(void)
|
||||
{
|
||||
int i;
|
||||
write_lock_bh(&af_mac_lock);
|
||||
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
|
||||
{
|
||||
INIT_LIST_HEAD(&af_mac_htable[i]);
|
||||
}
|
||||
write_unlock_bh(&af_mac_lock);
|
||||
}
|
||||
|
||||
void af_mac_list_flush(void)
|
||||
{
|
||||
int i;
|
||||
af_mac_node_t *p = NULL;
|
||||
char mac_str[32] = {0};
|
||||
write_lock_bh(&af_mac_lock);
|
||||
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
|
||||
{
|
||||
while (!list_empty(&af_mac_htable[i]))
|
||||
{
|
||||
p = list_first_entry(&af_mac_htable[i], af_mac_node_t, list);
|
||||
memset(mac_str, 0x0, sizeof(mac_str));
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
|
||||
list_del(&(p->list));
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
total_mac = 0;
|
||||
write_unlock_bh(&af_mac_lock);
|
||||
}
|
||||
|
||||
af_mac_node_t *af_mac_find(unsigned char *mac)
|
||||
{
|
||||
af_mac_node_t *node;
|
||||
unsigned int index;
|
||||
|
||||
index = hash_mac(mac);
|
||||
read_lock_bh(&af_mac_lock);
|
||||
list_for_each_entry(node, &af_mac_htable[index], list)
|
||||
{
|
||||
if (0 == memcmp(node->mac, mac, 6))
|
||||
{
|
||||
read_unlock_bh(&af_mac_lock);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&af_mac_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
af_mac_node_t *af_mac_add(unsigned char *mac)
|
||||
{
|
||||
af_mac_node_t *node;
|
||||
int index = 0;
|
||||
|
||||
node = (af_mac_node_t *)kmalloc(sizeof(af_mac_node_t), GFP_ATOMIC);
|
||||
if (node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(node, 0, sizeof(af_mac_node_t));
|
||||
memcpy(node->mac, mac, MAC_ADDR_LEN);
|
||||
|
||||
index = hash_mac(mac);
|
||||
|
||||
printk("add user mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
|
||||
total_mac++;
|
||||
write_lock_bh(&af_mac_lock);
|
||||
list_add(&(node->list), &af_mac_htable[index]);
|
||||
write_unlock_bh(&af_mac_lock);
|
||||
return node;
|
||||
}
|
||||
|
||||
int is_user_match_enable(void)
|
||||
{
|
||||
return total_mac > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int af_config_set_mac_list(cJSON *data_obj)
|
||||
{
|
||||
int i;
|
||||
cJSON *mac_arr = NULL;
|
||||
u8 mac_hex[MAC_ADDR_LEN] = {0};
|
||||
if (!data_obj)
|
||||
{
|
||||
AF_ERROR("data obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
mac_arr = cJSON_GetObjectItem(data_obj, "mac_list");
|
||||
if (!mac_arr)
|
||||
{
|
||||
AF_ERROR("mac_list obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
af_mac_list_flush();
|
||||
for (i = 0; i < cJSON_GetArraySize(mac_arr); i++)
|
||||
{
|
||||
cJSON *mac_obj = cJSON_GetArrayItem(mac_arr, i);
|
||||
if (!mac_obj)
|
||||
{
|
||||
AF_ERROR("mac obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
if (-1 == mac_to_hex(mac_obj->valuestring, mac_hex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
af_mac_add(mac_hex);
|
||||
}
|
||||
AF_DEBUG("## mac num = %d\n", total_mac);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef __AF_USER_CONFIG_H__
|
||||
#define __AF_USER_CONFIG_H__
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
|
||||
typedef struct af_mac_node {
|
||||
struct list_head list;
|
||||
unsigned char mac[MAC_ADDR_LEN];
|
||||
}af_mac_node_t;
|
||||
|
||||
void af_mac_list_init(void);
|
||||
void af_mac_list_flush(void);
|
||||
af_mac_node_t *af_mac_find(unsigned char *mac);
|
||||
af_mac_node_t *af_mac_add(unsigned char *mac);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,402 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/version.h>
|
||||
#include "af_utils.h"
|
||||
#define MAX_DUMP_STR_LEN 256
|
||||
|
||||
u_int32_t af_get_timestamp_sec(void)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)
|
||||
struct timespec64 ts;
|
||||
ktime_get_real_ts64(&ts);
|
||||
return (u_int32_t)ts.tv_sec;
|
||||
#else
|
||||
struct timespec ts;
|
||||
ts = current_kernel_time();
|
||||
return ts.tv_sec;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
int k_atoi(const char *str) {
|
||||
int result = 0;
|
||||
|
||||
// Skip whitespace
|
||||
while (*str == ' ' || *str == '\t') {
|
||||
str++;
|
||||
}
|
||||
|
||||
// Convert characters to integer
|
||||
while (*str >= '0' && *str <= '9') {
|
||||
result = result * 10 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *k_trim(char *s)
|
||||
{
|
||||
char *start, *last, *bk;
|
||||
int len;
|
||||
|
||||
start = s;
|
||||
while (isspace(*start))
|
||||
start++;
|
||||
|
||||
bk = last = s + strlen(s) - 1;
|
||||
while (last > start && isspace(*last))
|
||||
last--;
|
||||
|
||||
if ((s != start) || (bk != last))
|
||||
{
|
||||
len = last - start + 1;
|
||||
strncpy(s, start, len);
|
||||
s[len] = '\0';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int check_local_network_ip(unsigned int ip)
|
||||
{
|
||||
if ((ip & 0xffff0000) == 0xc0a80000)
|
||||
return 1;
|
||||
else if ((ip & 0xfff00000) == 0xac100000)
|
||||
return 1;
|
||||
else if ((ip & 0xff000000) == 0x0a000000)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump_str(char *name, unsigned char *p, int len)
|
||||
{
|
||||
char buf[MAX_DUMP_STR_LEN] = {0};
|
||||
if (len > MAX_DUMP_STR_LEN) {
|
||||
len = MAX_DUMP_STR_LEN - 1;
|
||||
}
|
||||
printk("%s: ",name);
|
||||
strncpy(buf, p, len);
|
||||
printk("[%s]\n", buf);
|
||||
}
|
||||
int isprint_char(unsigned char c)
|
||||
{
|
||||
if (c >= 0x20 && c <= 0x7e)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_hex_ascii(const unsigned char *data, size_t size) {
|
||||
size_t i, j;
|
||||
|
||||
for (i = 0; i < size; i += 16) {
|
||||
printk(KERN_CONT"%08lx ", (unsigned long)i);
|
||||
for (j = 0; j < 16; ++j) {
|
||||
if (i + j < size) {
|
||||
printk(KERN_CONT"%02x ", data[i + j]);
|
||||
} else {
|
||||
printk(KERN_CONT" ");
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_CONT" ");
|
||||
|
||||
for (j = 0; j < 16; ++j) {
|
||||
if (i + j < size) {
|
||||
unsigned char c = data[i + j];
|
||||
printk(KERN_CONT"%c", isprint_char(c) ? c : '.');
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_CONT"\n");
|
||||
}
|
||||
printk(KERN_CONT"---------------------------------------\n");
|
||||
}
|
||||
|
||||
|
||||
void dump_hex(char *name, unsigned char *p, int len)
|
||||
{
|
||||
int i;
|
||||
if (len > MAX_DUMP_STR_LEN) {
|
||||
len = MAX_DUMP_STR_LEN - 1;
|
||||
}
|
||||
printk("%s: ",name);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i % 16 == 0)
|
||||
printk(KERN_CONT "\n");
|
||||
printk(KERN_CONT "%02X ",*(p + i));
|
||||
}
|
||||
printk(KERN_CONT "\n");
|
||||
}
|
||||
|
||||
#ifndef va_arg
|
||||
typedef signed int acpi_native_int;
|
||||
#ifndef _VALIST
|
||||
#define _VALIST
|
||||
typedef char *va_list;
|
||||
#endif
|
||||
|
||||
#define _AUPBND (sizeof (acpi_native_int) - 1)
|
||||
#define _ADNBND (sizeof (acpi_native_int) - 1)
|
||||
|
||||
|
||||
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
|
||||
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
|
||||
#define va_end(ap) (void) 0
|
||||
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef TOLOWER
|
||||
#define TOLOWER(x) ((x) | 0x20)
|
||||
#endif
|
||||
|
||||
|
||||
static long long k_simple_strtoll(const char *cp, char **endp, unsigned int base)
|
||||
{
|
||||
if (*cp == '-')
|
||||
return -simple_strtoull(cp + 1, endp, base);
|
||||
|
||||
return simple_strtoull(cp, endp, base);
|
||||
}
|
||||
|
||||
static int skip_atoi(const char **s)
|
||||
{
|
||||
int i=0;
|
||||
while (isdigit(**s))
|
||||
i = i*10 + *((*s)++) - '0';
|
||||
return i;
|
||||
}
|
||||
|
||||
char *skip_spaces(const char *str)
|
||||
{
|
||||
while (isspace(*str) && ((unsigned char )*str != 0xa0))
|
||||
++str;
|
||||
return (char *)str;
|
||||
}
|
||||
static int k_vsscanf(const char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
const char *str = buf;
|
||||
char *next;
|
||||
char digit;
|
||||
int num = 0;
|
||||
u8 qualifier;
|
||||
u8 base;
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
|
||||
int field_width;
|
||||
#else
|
||||
s16 field_width;
|
||||
#endif
|
||||
bool is_sign;
|
||||
while (*fmt && *str) {
|
||||
if (isspace(*fmt)) {
|
||||
fmt = skip_spaces(++fmt);
|
||||
str = skip_spaces(str);
|
||||
}
|
||||
|
||||
if (*fmt != '%' && *fmt) {
|
||||
if (*fmt++ != *str++)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (!*fmt)
|
||||
break;
|
||||
++fmt;
|
||||
|
||||
if (*fmt == '*') {
|
||||
while (!isspace(*fmt) && *fmt != '%' && *fmt)
|
||||
fmt++;
|
||||
while (!isspace(*str) && *str)
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
field_width = -1;
|
||||
if (isdigit(*fmt))
|
||||
field_width = skip_atoi(&fmt);
|
||||
qualifier = -1;
|
||||
if (*fmt == 'h' || TOLOWER(*fmt) == 'l' ||
|
||||
TOLOWER(*fmt) == 'z') {
|
||||
qualifier = *fmt++;
|
||||
if (unlikely(qualifier == *fmt)) {
|
||||
if (qualifier == 'h') {
|
||||
qualifier = 'H';
|
||||
fmt++;
|
||||
} else if (qualifier == 'l') {
|
||||
qualifier = 'L';
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!*fmt || !*str)
|
||||
break;
|
||||
base = 10;
|
||||
is_sign = 0;
|
||||
switch (*fmt++) {
|
||||
case 'c':
|
||||
{
|
||||
char *s = (char *)va_arg(args, char*);
|
||||
if (field_width == -1)
|
||||
field_width = 1;
|
||||
do {
|
||||
*s++ = *str++;
|
||||
} while (--field_width > 0 && *str);
|
||||
num++;
|
||||
}
|
||||
continue;
|
||||
case 's':
|
||||
{
|
||||
char *s = (char *)va_arg(args, char *);
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
|
||||
if(field_width == -1)
|
||||
field_width = INT_MAX;
|
||||
#else
|
||||
if (field_width == -1)
|
||||
field_width = SHRT_MAX;
|
||||
#endif
|
||||
str = skip_spaces(str);
|
||||
|
||||
while (*str && (!isspace(*str) || ((unsigned char )*str == 0xA0) )&& field_width--)
|
||||
*s++ = *str++;
|
||||
*s = '\0';
|
||||
num++;
|
||||
}
|
||||
continue;
|
||||
case 'n':
|
||||
{
|
||||
int *i = (int *)va_arg(args, int*);
|
||||
*i = str - buf;
|
||||
}
|
||||
continue;
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
break;
|
||||
case 'i':
|
||||
base = 0;
|
||||
case 'd':
|
||||
is_sign = 1;
|
||||
case 'u':
|
||||
break;
|
||||
case '%':
|
||||
if (*str++ != '%')
|
||||
return num;
|
||||
continue;
|
||||
default:
|
||||
return num;
|
||||
}
|
||||
str = skip_spaces(str);
|
||||
digit = *str;
|
||||
if (is_sign && digit == '-')
|
||||
digit = *(str + 1);
|
||||
if (!digit
|
||||
|| (base == 16 && !isxdigit(digit))
|
||||
|| (base == 10 && !isdigit(digit))
|
||||
|| (base == 8 && (!isdigit(digit) || digit > '7'))
|
||||
|| (base == 0 && !isdigit(digit)))
|
||||
break;
|
||||
|
||||
switch (qualifier) {
|
||||
case 'H':
|
||||
if (is_sign) {
|
||||
signed char *s = (signed char *)va_arg(args, signed char *);
|
||||
*s = (signed char)simple_strtol(str, &next, base);
|
||||
} else {
|
||||
unsigned char *s = (unsigned char *)va_arg(args, unsigned char *);
|
||||
*s = (unsigned char)simple_strtoul(str, &next, base);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (is_sign) {
|
||||
short *s = (short *)va_arg(args, short *);
|
||||
*s = (short)simple_strtol(str, &next, base);
|
||||
} else {
|
||||
unsigned short *s = (unsigned short *)va_arg(args, unsigned short *);
|
||||
*s = (unsigned short)simple_strtoul(str, &next, base);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (is_sign) {
|
||||
long *l = (long *)va_arg(args, long *);
|
||||
*l = simple_strtol(str, &next, base);
|
||||
} else {
|
||||
unsigned long *l = (unsigned long *)va_arg(args, unsigned long *);
|
||||
*l = simple_strtoul(str, &next, base);
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
if (is_sign) {
|
||||
long long *l = (long long *)va_arg(args, long long *);
|
||||
*l = k_simple_strtoll(str, &next, base);
|
||||
} else {
|
||||
unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *);
|
||||
*l = simple_strtoull(str, &next, base);
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
case 'z':
|
||||
{
|
||||
size_t *s = (size_t *)va_arg(args, size_t *);
|
||||
*s = (size_t)simple_strtoul(str, &next, base);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_sign) {
|
||||
int *i = (int *)va_arg(args, int *);
|
||||
*i = (int)simple_strtol(str, &next, base);
|
||||
} else {
|
||||
unsigned int *i = (unsigned int *)va_arg(args, unsigned int*);
|
||||
*i = (unsigned int)simple_strtoul(str, &next, base);
|
||||
}
|
||||
break;
|
||||
}
|
||||
num++;
|
||||
if (!next)
|
||||
break;
|
||||
str = next;
|
||||
}
|
||||
if (*fmt == '%' && *(fmt + 1) == 'n') {
|
||||
int *p = (int *)va_arg(args, int *);
|
||||
*p = str - buf;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
int k_sscanf(const char *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
va_start(args, fmt);
|
||||
i = k_vsscanf(buf, fmt, args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
int mac_to_hex(u8 *mac, u8 *mac_hex)
|
||||
{
|
||||
u32 mac_tmp[6];
|
||||
int ret = 0, i = 0;
|
||||
ret = sscanf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
(unsigned int *)&mac_tmp[0],
|
||||
(unsigned int *)&mac_tmp[1],
|
||||
(unsigned int *)&mac_tmp[2],
|
||||
(unsigned int *)&mac_tmp[3],
|
||||
(unsigned int *)&mac_tmp[4],
|
||||
(unsigned int *)&mac_tmp[5]);
|
||||
if (6 != ret)
|
||||
return -1;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
mac_hex[i] = mac_tmp[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef AF_UTILS_H
|
||||
#define AF_UTILS_H
|
||||
u_int32_t af_get_timestamp_sec(void);
|
||||
|
||||
char *k_trim(char *s);
|
||||
|
||||
int check_local_network_ip(unsigned int ip);
|
||||
|
||||
void dump_str(char *name, unsigned char *p, int len);
|
||||
|
||||
void dump_hex(char *name, unsigned char *p, int len);
|
||||
|
||||
int k_sscanf(const char *buf, const char *fmt, ...);
|
||||
int k_atoi(const char *str);
|
||||
void print_hex_ascii(const unsigned char *data, size_t size);
|
||||
int hash_mac(unsigned char *mac);
|
||||
int mac_to_hex(u8 *mac, u8 *mac_hex);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/version.h>
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
#include "af_log.h"
|
||||
#include "cJSON.h"
|
||||
#include "af_whitelist_config.h"
|
||||
|
||||
|
||||
DEFINE_RWLOCK(af_whitelist_mac_lock);
|
||||
|
||||
struct list_head af_whitelist_mac_htable[MAX_AF_MAC_HASH_SIZE];
|
||||
|
||||
void af_whitelist_mac_init(void)
|
||||
{
|
||||
int i;
|
||||
write_lock_bh(&af_whitelist_mac_lock);
|
||||
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
|
||||
{
|
||||
INIT_LIST_HEAD(&af_whitelist_mac_htable[i]);
|
||||
}
|
||||
write_unlock_bh(&af_whitelist_mac_lock);
|
||||
}
|
||||
|
||||
void af_whitelist_mac_flush(void)
|
||||
{
|
||||
int i;
|
||||
af_whitelist_mac_node_t *p = NULL;
|
||||
char mac_str[32] = {0};
|
||||
write_lock_bh(&af_whitelist_mac_lock);
|
||||
for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++)
|
||||
{
|
||||
while (!list_empty(&af_whitelist_mac_htable[i]))
|
||||
{
|
||||
p = list_first_entry(&af_whitelist_mac_htable[i], af_whitelist_mac_node_t, list);
|
||||
memset(mac_str, 0x0, sizeof(mac_str));
|
||||
sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac));
|
||||
list_del(&(p->list));
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
write_unlock_bh(&af_whitelist_mac_lock);
|
||||
}
|
||||
|
||||
af_whitelist_mac_node_t *af_whitelist_mac_find(unsigned char *mac)
|
||||
{
|
||||
af_whitelist_mac_node_t *node = NULL;
|
||||
unsigned int index = 0;
|
||||
|
||||
index = hash_mac(mac);
|
||||
read_lock_bh(&af_whitelist_mac_lock);
|
||||
list_for_each_entry(node, &af_whitelist_mac_htable[index], list)
|
||||
{
|
||||
if (0 == memcmp(node->mac, mac, 6))
|
||||
{
|
||||
read_unlock_bh(&af_whitelist_mac_lock);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&af_whitelist_mac_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
af_whitelist_mac_node_t *af_whitelist_mac_add(unsigned char *mac)
|
||||
{
|
||||
af_whitelist_mac_node_t *node = NULL;
|
||||
int index = 0;
|
||||
|
||||
node = (af_whitelist_mac_node_t *)kmalloc(sizeof(af_whitelist_mac_node_t), GFP_ATOMIC);
|
||||
if (node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(node, 0, sizeof(af_whitelist_mac_node_t));
|
||||
memcpy(node->mac, mac, MAC_ADDR_LEN);
|
||||
index = hash_mac(mac);
|
||||
|
||||
AF_DEBUG("add whitelist mac=" MAC_FMT "\n", MAC_ARRAY(node->mac));
|
||||
write_lock_bh(&af_whitelist_mac_lock);
|
||||
list_add(&(node->list), &af_whitelist_mac_htable[index]);
|
||||
write_unlock_bh(&af_whitelist_mac_lock);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
int af_config_set_whitelist_mac_list(cJSON *data_obj)
|
||||
{
|
||||
int i;
|
||||
cJSON *mac_arr = NULL;
|
||||
u8 mac_hex[MAC_ADDR_LEN] = {0};
|
||||
if (!data_obj)
|
||||
{
|
||||
AF_ERROR("data obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
mac_arr = cJSON_GetObjectItem(data_obj, "mac_list");
|
||||
if (!mac_arr)
|
||||
{
|
||||
AF_ERROR("mac_list obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
af_whitelist_mac_flush();
|
||||
for (i = 0; i < cJSON_GetArraySize(mac_arr); i++)
|
||||
{
|
||||
cJSON *mac_obj = cJSON_GetArrayItem(mac_arr, i);
|
||||
if (!mac_obj)
|
||||
{
|
||||
AF_ERROR("mac obj is null\n");
|
||||
return -1;
|
||||
}
|
||||
if (-1 == mac_to_hex(mac_obj->valuestring, mac_hex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
af_whitelist_mac_add(mac_hex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef __AF_WHITELIST_CONFIG_H__
|
||||
#define __AF_WHITELIST_CONFIG_H__
|
||||
#include "app_filter.h"
|
||||
#include "af_utils.h"
|
||||
|
||||
#define MAX_AF_WHITELIST_MAC_HASH_SIZE 64
|
||||
|
||||
typedef struct af_whitelist_mac_node{
|
||||
struct list_head list;
|
||||
unsigned char mac[MAC_ADDR_LEN];
|
||||
}af_whitelist_mac_node_t;
|
||||
|
||||
void af_whitelist_mac_init(void);
|
||||
void af_whitelist_mac_flush(void);
|
||||
af_whitelist_mac_node_t *af_whitelist_mac_find(unsigned char *mac);
|
||||
af_whitelist_mac_node_t *af_whitelist_mac_add(unsigned char *mac);
|
||||
int af_config_set_whitelist_mac_list(cJSON *data_obj);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,176 @@
|
||||
#ifndef APP_FILTER_H
|
||||
#define APP_FILTER_H
|
||||
|
||||
#define AF_VERSION "5.3.3"
|
||||
#define AF_FEATURE_CONFIG_FILE "/tmp/feature.cfg"
|
||||
|
||||
#define MAX_DPI_PKT_NUM 64
|
||||
#define MIN_HTTP_DATA_LEN 16
|
||||
#define MAX_APP_NAME_LEN 64
|
||||
#define MAX_FEATURE_NUM_PER_APP 16
|
||||
#define MIN_FEATURE_STR_LEN 8
|
||||
#define MAX_FEATURE_STR_LEN 128
|
||||
#define MAX_HOST_URL_LEN 128
|
||||
#define MAX_REQUEST_URL_LEN 128
|
||||
#define MAX_FEATURE_BITS 16
|
||||
#define MAX_POS_INFO_PER_FEATURE 16
|
||||
#define MAX_FEATURE_LINE_LEN 600
|
||||
#define MIN_FEATURE_LINE_LEN 16
|
||||
#define MAX_URL_MATCH_LEN 64
|
||||
#define MAX_BYPASS_DPI_PKT_LEN 600
|
||||
#define MAX_AF_MAC_HASH_SIZE 64
|
||||
|
||||
#define HTTP_GET_METHOD_STR "GET"
|
||||
#define HTTP_POST_METHOD_STR "POST"
|
||||
#define HTTP_HEADER "HTTP"
|
||||
#define NIPQUAD(addr) \
|
||||
((unsigned char *)&addr)[0], \
|
||||
((unsigned char *)&addr)[1], \
|
||||
((unsigned char *)&addr)[2], \
|
||||
((unsigned char *)&addr)[3]
|
||||
#define NIPQUAD_FMT "%u.%u.%u.%u"
|
||||
#define MAC_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
|
||||
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
|
||||
#define AF_TRUE 1
|
||||
#define AF_FALSE 0
|
||||
|
||||
#define AF_APP_TYPE(a) (a) / 1000
|
||||
#define AF_APP_ID(a) (a) % 1000
|
||||
#define MAC_ADDR_LEN 6
|
||||
|
||||
#define HTTPS_URL_OFFSET 9
|
||||
#define HTTPS_LEN_OFFSET 7
|
||||
|
||||
#define MAX_SEARCH_STR_LEN 32
|
||||
|
||||
enum AF_FEATURE_PARAM_INDEX{
|
||||
AF_PROTO_PARAM_INDEX,
|
||||
AF_SRC_PORT_PARAM_INDEX,
|
||||
AF_DST_PORT_PARAM_INDEX,
|
||||
AF_HOST_URL_PARAM_INDEX,
|
||||
AF_REQUEST_URL_PARAM_INDEX,
|
||||
AF_DICT_PARAM_INDEX,
|
||||
AF_STR_PARAM_INDEX,
|
||||
AF_IGNORE_PARAM_INDEX,
|
||||
};
|
||||
|
||||
|
||||
#define OAF_NETLINK_ID 29
|
||||
#define MAX_OAF_NL_MSG_LEN 1024
|
||||
|
||||
enum E_MSG_TYPE{
|
||||
AF_MSG_INIT,
|
||||
AF_MSG_ADD_FEATURE,
|
||||
AF_MSG_CLEAN_FEATURE,
|
||||
AF_MSG_MAX
|
||||
};
|
||||
enum AF_WORK_MODE {
|
||||
AF_MODE_GATEWAY,
|
||||
AF_MODE_BYPASS,
|
||||
AF_MODE_BRIDGE,
|
||||
};
|
||||
#define MAX_AF_MSG_DATA_LEN 800
|
||||
typedef struct af_msg{
|
||||
int action;
|
||||
}af_msg_t;
|
||||
|
||||
struct af_msg_hdr{
|
||||
int magic;
|
||||
int len;
|
||||
};
|
||||
|
||||
enum e_http_method{
|
||||
HTTP_METHOD_GET = 1,
|
||||
HTTP_METHOD_POST,
|
||||
};
|
||||
typedef struct http_proto{
|
||||
int match;
|
||||
int method;
|
||||
char *url_pos;
|
||||
int url_len;
|
||||
char *host_pos;
|
||||
int host_len;
|
||||
char *data_pos;
|
||||
int data_len;
|
||||
}http_proto_t;
|
||||
|
||||
typedef struct https_proto{
|
||||
int match;
|
||||
char *url_pos;
|
||||
int url_len;
|
||||
}https_proto_t;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct af_pos_info{
|
||||
int pos;
|
||||
unsigned char value;
|
||||
}af_pos_info_t;
|
||||
|
||||
#define MAX_PORT_RANGE_NUM 5
|
||||
|
||||
typedef struct range_value
|
||||
{
|
||||
int not ;
|
||||
int start;
|
||||
int end;
|
||||
} range_value_t;
|
||||
|
||||
typedef struct port_info
|
||||
{
|
||||
u_int8_t mode; // 0: match, 1: not match
|
||||
int num;
|
||||
range_value_t range_list[MAX_PORT_RANGE_NUM];
|
||||
} port_info_t;
|
||||
|
||||
typedef struct af_feature_node{
|
||||
struct list_head head;
|
||||
u_int32_t app_id;
|
||||
char app_name[MAX_APP_NAME_LEN];
|
||||
char feature[MAX_FEATURE_STR_LEN];
|
||||
u_int32_t proto;
|
||||
u_int32_t sport;
|
||||
u_int32_t dport;
|
||||
port_info_t dport_info;
|
||||
char host_url[MAX_HOST_URL_LEN];
|
||||
char request_url[MAX_REQUEST_URL_LEN];
|
||||
int pos_num;
|
||||
char search_str[MAX_SEARCH_STR_LEN];
|
||||
int ignore;
|
||||
af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE];
|
||||
}af_feature_node_t;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct flow_info{
|
||||
struct nf_conn *ct;
|
||||
u_int32_t src;
|
||||
u_int32_t dst;
|
||||
struct in6_addr *src6;
|
||||
struct in6_addr *dst6;
|
||||
int l4_protocol;
|
||||
u_int16_t sport;
|
||||
u_int16_t dport;
|
||||
unsigned char *l4_data;
|
||||
int l4_len;
|
||||
http_proto_t http;
|
||||
https_proto_t https;
|
||||
u_int32_t app_id;
|
||||
u_int8_t app_name[MAX_APP_NAME_LEN];
|
||||
u_int8_t drop;
|
||||
u_int8_t ignore;
|
||||
u_int8_t dir;
|
||||
u_int16_t total_len;
|
||||
u_int8_t client_hello;
|
||||
af_feature_node_t *feature;
|
||||
}flow_info_t;
|
||||
|
||||
|
||||
|
||||
int regexp_match(char *reg, char *text);
|
||||
int hash_mac(unsigned char *mac);
|
||||
|
||||
#endif
|
||||
+519
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// cJSON
|
||||
// JSON parser in C.
|
||||
|
||||
#if 0
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#if 0
|
||||
#if defined(WINDOWS) || defined(__WIN32__) || defined(WIN32) || defined(_WIN32)
|
||||
#define strcasecmp stricmp
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
static void *(*cJSON_malloc)(size_t sz) = malloc;
|
||||
static void *(*cJSON_realloc)(void *ptr, size_t sz) = realloc;
|
||||
static void (*cJSON_free)(void *ptr) = free;
|
||||
#endif
|
||||
|
||||
static void *cJSON_malloc(size_t sz) {
|
||||
return kmalloc(sz, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void *cJSON_realloc(void *ptr, size_t sz)
|
||||
{
|
||||
return krealloc(ptr, sz, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void cJSON_free(void *ptr)
|
||||
{
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static char* cJSON_strdup(const char* str)
|
||||
{
|
||||
size_t len;
|
||||
char* copy;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if (!(copy = (char*)cJSON_malloc(len))) return 0;
|
||||
memcpy(copy,str,len);
|
||||
return copy;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void cJSON_InitHooks(cJSON_Hooks* hooks)
|
||||
{
|
||||
if (!hooks) { /* Reset hooks */
|
||||
cJSON_malloc = malloc;
|
||||
cJSON_realloc = realloc;
|
||||
cJSON_free = free;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
|
||||
cJSON_realloc= (hooks->realloc_fn)?hooks->realloc_fn:realloc;
|
||||
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Internal constructor.
|
||||
static cJSON *cJSON_New_Item(void)
|
||||
{
|
||||
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
|
||||
if (node) memset(node,0,sizeof(cJSON));
|
||||
return node;
|
||||
}
|
||||
|
||||
// Delete a cJSON structure.
|
||||
void cJSON_Delete(cJSON *c)
|
||||
{
|
||||
cJSON *next;
|
||||
while (c)
|
||||
{
|
||||
next=c->next;
|
||||
if (c->child) cJSON_Delete(c->child);
|
||||
if (c->valuestring) cJSON_free(c->valuestring);
|
||||
if (c->string) cJSON_free(c->string);
|
||||
cJSON_free(c);
|
||||
c=next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Parse the input text to generate a number, and populate the result into item. */
|
||||
static const char *parse_number(cJSON *item,const char *num)
|
||||
{
|
||||
int n=0,sign=1;
|
||||
|
||||
if (*num=='-') sign=-1,num++; /* Has sign? */
|
||||
if (*num=='0') num++; /* is zero */
|
||||
if (*num>='1' && *num<='9') do n=(n*10)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
|
||||
item->valueint=(int)n;
|
||||
item->type=cJSON_Number;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Render the number nicely from the given item into a string. */
|
||||
static char *print_number(cJSON *item)
|
||||
{
|
||||
char *str;
|
||||
str=(char*)cJSON_malloc(21);
|
||||
if (str)
|
||||
sprintf(str,"%d",item->valueint);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
// Parse the input text into an unescaped cstring, and populate item.
|
||||
static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
static const char *parse_string(cJSON *item,const char *str)
|
||||
{
|
||||
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc;
|
||||
if (*str!='\"') return 0; // not a string!
|
||||
|
||||
while (*ptr!='\"' && *ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes.
|
||||
|
||||
out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly.
|
||||
if (!out) return 0;
|
||||
|
||||
ptr=str+1;ptr2=out;
|
||||
while (*ptr!='\"' && *ptr>31)
|
||||
{
|
||||
if (*ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
ptr++;
|
||||
switch (*ptr)
|
||||
{
|
||||
case 'b': *ptr2++='\b'; break;
|
||||
case 'f': *ptr2++='\f'; break;
|
||||
case 'n': *ptr2++='\n'; break;
|
||||
case 'r': *ptr2++='\r'; break;
|
||||
case 't': *ptr2++='\t'; break;
|
||||
case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
|
||||
sscanf(ptr+1,"%4x",&uc); // get the unicode char.
|
||||
len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
|
||||
|
||||
switch (len) {
|
||||
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
|
||||
case 1: *--ptr2 =(uc | firstByteMark[len]);
|
||||
}
|
||||
ptr2+=len;ptr+=4;
|
||||
break;
|
||||
default: *ptr2++=*ptr; break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
*ptr2=0;
|
||||
if (*ptr=='\"') ptr++;
|
||||
item->valuestring=out;
|
||||
item->type=cJSON_String;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Render the cstring provided to an escaped version that can be printed.
|
||||
static char *print_string_ptr(const char *str)
|
||||
{
|
||||
const char *ptr;char *ptr2,*out;int len=0;
|
||||
|
||||
ptr=str;while (*ptr && ++len) {if (*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;}
|
||||
|
||||
out=(char*)cJSON_malloc(len+3);
|
||||
ptr2=out;ptr=str;
|
||||
*ptr2++='\"';
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
|
||||
else
|
||||
{
|
||||
*ptr2++='\\';
|
||||
switch (*ptr++)
|
||||
{
|
||||
case '\\': *ptr2++='\\'; break;
|
||||
case '\"': *ptr2++='\"'; break;
|
||||
case '\b': *ptr2++='b'; break;
|
||||
case '\f': *ptr2++='f'; break;
|
||||
case '\n': *ptr2++='n'; break;
|
||||
case '\r': *ptr2++='r'; break;
|
||||
case '\t': *ptr2++='t'; break;
|
||||
default: ptr2--; break; // eviscerate with prejudice.
|
||||
}
|
||||
}
|
||||
}
|
||||
*ptr2++='\"';*ptr2++=0;
|
||||
return out;
|
||||
}
|
||||
// Invote print_string_ptr (which is useful) on an item.
|
||||
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
|
||||
|
||||
// Predeclare these prototypes.
|
||||
static const char *parse_value(cJSON *item,const char *value);
|
||||
static char *print_value(cJSON *item,int depth);
|
||||
static const char *parse_array(cJSON *item,const char *value);
|
||||
static char *print_array(cJSON *item,int depth);
|
||||
static const char *parse_object(cJSON *item,const char *value);
|
||||
static char *print_object(cJSON *item,int depth);
|
||||
|
||||
// Utility to jump whitespace and cr/lf
|
||||
static const char *skip(const char *in) {while (in && *in<=32) in++; return in;}
|
||||
|
||||
// Parse an object - create a new root, and populate.
|
||||
cJSON *cJSON_Parse(const char *value)
|
||||
{
|
||||
cJSON *c=cJSON_New_Item();
|
||||
if (!c) return 0; /* memory fail */
|
||||
|
||||
if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
|
||||
return c;
|
||||
}
|
||||
|
||||
// Render a cJSON item/entity/structure to text.
|
||||
char *cJSON_Print(cJSON *item) {return print_value(item,0);}
|
||||
|
||||
// Parser core - when encountering text, process appropriately.
|
||||
static const char *parse_value(cJSON *item,const char *value)
|
||||
{
|
||||
if (!value) return 0; // Fail on null.
|
||||
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
|
||||
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
|
||||
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
|
||||
if (*value=='\"') { return parse_string(item,value); }
|
||||
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
|
||||
if (*value=='[') { return parse_array(item,value); }
|
||||
if (*value=='{') { return parse_object(item,value); }
|
||||
|
||||
return 0; // failure.
|
||||
}
|
||||
|
||||
// Render a value to text.
|
||||
static char *print_value(cJSON *item,int depth)
|
||||
{
|
||||
char *out=0;
|
||||
switch (item->type)
|
||||
{
|
||||
case cJSON_NULL: out=cJSON_strdup("null"); break;
|
||||
case cJSON_False: out=cJSON_strdup("false");break;
|
||||
case cJSON_True: out=cJSON_strdup("true"); break;
|
||||
case cJSON_Number: out=print_number(item);break;
|
||||
case cJSON_String: out=print_string(item);break;
|
||||
case cJSON_Array: out=print_array(item,depth);break;
|
||||
case cJSON_Object: out=print_object(item,depth);break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Build an array from input text.
|
||||
static const char *parse_array(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='[') return 0; // not an array!
|
||||
|
||||
item->type=cJSON_Array;
|
||||
value=skip(value+1);
|
||||
if (*value==']') return value+1; // empty array.
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
if (!item->child) return 0; // memory fail
|
||||
value=skip(parse_value(child,skip(value))); // skip any spacing, get the value.
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; // memory fail
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_value(child,skip(value+1)));
|
||||
if (!value) return 0; // memory fail
|
||||
}
|
||||
|
||||
if (*value==']') return value+1; // end of array
|
||||
return 0; // malformed.
|
||||
}
|
||||
|
||||
// Render an array to text
|
||||
static char *print_array(cJSON *item,int depth)
|
||||
{
|
||||
char *out,*ptr,*ret;int len=5;
|
||||
cJSON *child=item->child;
|
||||
|
||||
out=(char*)cJSON_malloc(len);*out='[';
|
||||
ptr=out+1;*ptr=0;
|
||||
while (child)
|
||||
{
|
||||
ret=print_value(child,depth+1);
|
||||
if (!ret) {cJSON_free(out);return 0;} // Check for failure!
|
||||
len+=strlen(ret)+3;
|
||||
out=(char*)cJSON_realloc(out,len);
|
||||
ptr=out+strlen(out);
|
||||
ptr+=sprintf(ptr,ret);
|
||||
if (child->next) {*ptr++=',';*ptr++=' ';*ptr=0;}
|
||||
child=child->next;
|
||||
cJSON_free(ret);
|
||||
}
|
||||
*ptr++=']';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Build an object from the text.
|
||||
static const char *parse_object(cJSON *item,const char *value)
|
||||
{
|
||||
cJSON *child;
|
||||
if (*value!='{') return 0; // not an object!
|
||||
|
||||
item->type=cJSON_Object;
|
||||
value=skip(value+1);
|
||||
if (*value=='}') return value+1; // empty array.
|
||||
|
||||
item->child=child=cJSON_New_Item();
|
||||
value=skip(parse_string(child,skip(value)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') return 0; // fail!
|
||||
value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value.
|
||||
if (!value) return 0;
|
||||
|
||||
while (*value==',')
|
||||
{
|
||||
cJSON *new_item;
|
||||
if (!(new_item=cJSON_New_Item())) return 0; // memory fail
|
||||
child->next=new_item;new_item->prev=child;child=new_item;
|
||||
value=skip(parse_string(child,skip(value+1)));
|
||||
if (!value) return 0;
|
||||
child->string=child->valuestring;child->valuestring=0;
|
||||
if (*value!=':') return 0; // fail!
|
||||
value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value.
|
||||
if (!value) return 0;
|
||||
}
|
||||
|
||||
if (*value=='}') return value+1; // end of array
|
||||
return 0; // malformed.
|
||||
}
|
||||
|
||||
// Render an object to text.
|
||||
static char *print_object(cJSON *item,int depth)
|
||||
{
|
||||
char *out,*ptr,*ret,*str;int len=7,i;
|
||||
cJSON *child=item->child;
|
||||
|
||||
depth++;len+=depth;out=(char*)cJSON_malloc(len);*out='{';
|
||||
ptr=out+1;*ptr++='\n';*ptr=0;
|
||||
while (child)
|
||||
{
|
||||
str=print_string_ptr(child->string);
|
||||
if (!str) {cJSON_free(out);return 0;}
|
||||
ret=print_value(child,depth);
|
||||
if (!ret) {cJSON_free(str);cJSON_free(out);return 0;} // Check for failure!
|
||||
len+=strlen(ret)+strlen(str)+4+depth;
|
||||
out=(char*)cJSON_realloc(out,len);
|
||||
ptr=out+strlen(out);
|
||||
for (i=0;i<depth;i++) *ptr++='\t';
|
||||
ptr+=sprintf(ptr,str);
|
||||
*ptr++=':';*ptr++='\t';
|
||||
ptr+=sprintf(ptr,ret);
|
||||
if (child->next) *ptr++=',';
|
||||
*ptr++='\n';*ptr=0;
|
||||
child=child->next;
|
||||
cJSON_free(str);cJSON_free(ret);
|
||||
}
|
||||
for (i=0;i<depth-1;i++) *ptr++='\t';
|
||||
*ptr++='}';*ptr++=0;
|
||||
return out;
|
||||
}
|
||||
#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
|
||||
|
||||
static void skip_oneline_comment(char **input)
|
||||
{
|
||||
*input += static_strlen("//");
|
||||
|
||||
for (; (*input)[0] != '\0'; ++(*input))
|
||||
{
|
||||
if ((*input)[0] == '\n') {
|
||||
*input += static_strlen("\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void skip_multiline_comment(char **input)
|
||||
{
|
||||
*input += static_strlen("/*");
|
||||
|
||||
for (; (*input)[0] != '\0'; ++(*input))
|
||||
{
|
||||
if (((*input)[0] == '*') && ((*input)[1] == '/'))
|
||||
{
|
||||
*input += static_strlen("*/");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void minify_string(char **input, char **output) {
|
||||
(*output)[0] = (*input)[0];
|
||||
*input += static_strlen("\"");
|
||||
*output += static_strlen("\"");
|
||||
|
||||
|
||||
for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
|
||||
(*output)[0] = (*input)[0];
|
||||
|
||||
if ((*input)[0] == '\"') {
|
||||
(*output)[0] = '\"';
|
||||
*input += static_strlen("\"");
|
||||
*output += static_strlen("\"");
|
||||
return;
|
||||
} else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
|
||||
(*output)[1] = (*input)[1];
|
||||
*input += static_strlen("\"");
|
||||
*output += static_strlen("\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cJSON_Minify(char *json)
|
||||
{
|
||||
char *into = json;
|
||||
|
||||
if (json == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (json[0] != '\0')
|
||||
{
|
||||
switch (json[0])
|
||||
{
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
json++;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (json[1] == '/')
|
||||
{
|
||||
skip_oneline_comment(&json);
|
||||
}
|
||||
else if (json[1] == '*')
|
||||
{
|
||||
skip_multiline_comment(&json);
|
||||
} else {
|
||||
json++;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\"':
|
||||
minify_string(&json, (char**)&into);
|
||||
break;
|
||||
|
||||
default:
|
||||
into[0] = json[0];
|
||||
json++;
|
||||
into++;
|
||||
}
|
||||
}
|
||||
|
||||
/* and null-terminate. */
|
||||
*into = '\0';
|
||||
}
|
||||
|
||||
// Get Array size/item / object item.
|
||||
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
|
||||
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item) item--,c=c->next; return c;}
|
||||
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && strcasecmp(c->string,string)) c=c->next; return c;}
|
||||
|
||||
// Utility for array list handling.
|
||||
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
|
||||
|
||||
// Add item to array/object.
|
||||
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
|
||||
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
|
||||
|
||||
// Create basic types:
|
||||
cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;}
|
||||
cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;}
|
||||
cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;}
|
||||
cJSON *cJSON_CreateNumber(int num) {cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valueint=(int)num;return item;}
|
||||
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;}
|
||||
cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;}
|
||||
cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;}
|
||||
|
||||
// Create Arrays:
|
||||
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
#include <linux/slab.h>
|
||||
|
||||
// cJSON Types:
|
||||
#define cJSON_False 0
|
||||
#define cJSON_True 1
|
||||
#define cJSON_NULL 2
|
||||
#define cJSON_Number 3
|
||||
#define cJSON_String 4
|
||||
#define cJSON_Array 5
|
||||
#define cJSON_Object 6
|
||||
|
||||
// The cJSON structure:
|
||||
typedef struct cJSON {
|
||||
struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
|
||||
struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object.
|
||||
|
||||
int type; // The type of the item, as above.
|
||||
|
||||
char *valuestring; // The item's string, if type==cJSON_String
|
||||
int valueint; // The item's number, if type==cJSON_Number
|
||||
char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object.
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks {
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void *(*realloc_fn)(void *ptr, size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
// Supply malloc, realloc and free functions to cJSON
|
||||
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished.
|
||||
extern cJSON *cJSON_Parse(const char *value);
|
||||
// Render a cJSON entity to text for transfer/storage. Free the char* when finished.
|
||||
extern char *cJSON_Print(cJSON *item);
|
||||
// Delete a cJSON entity and all subentities.
|
||||
extern void cJSON_Delete(cJSON *c);
|
||||
|
||||
// Returns the number of items in an array (or object).
|
||||
extern int cJSON_GetArraySize(cJSON *array);
|
||||
// Retrieve item number "item" from array "array". Returns NULL if unsuccessful.
|
||||
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
|
||||
// Get item "string" from object. Case insensitive.
|
||||
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
|
||||
|
||||
// These calls create a cJSON item of the appropriate type.
|
||||
extern cJSON *cJSON_CreateNull(void);
|
||||
extern cJSON *cJSON_CreateTrue(void);
|
||||
extern cJSON *cJSON_CreateFalse(void);
|
||||
extern cJSON *cJSON_CreateNumber(int num);
|
||||
extern cJSON *cJSON_CreateString(const char *string);
|
||||
extern cJSON *cJSON_CreateArray(void);
|
||||
extern cJSON *cJSON_CreateObject(void);
|
||||
extern void cJSON_Minify(char *json);
|
||||
// These utilities create an Array of count items.
|
||||
extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
|
||||
|
||||
// Append item to the specified array/object.
|
||||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
|
||||
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
|
||||
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
|
||||
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
|
||||
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,302 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
//#include "regexp.h"
|
||||
|
||||
typedef enum{CHAR, DOT, BEGIN, END, STAR, PLUS, QUES, LIST, TYPENUM}TYPE;
|
||||
|
||||
typedef struct RE{
|
||||
TYPE type;
|
||||
int ch;
|
||||
char *ccl;
|
||||
int nccl;
|
||||
struct RE *next;
|
||||
}RE;
|
||||
|
||||
int match_longest = 0;
|
||||
char *match_first = NULL;
|
||||
|
||||
|
||||
static void * getmem(size_t size)
|
||||
{
|
||||
void *tmp;
|
||||
if((tmp = kmalloc(size, GFP_ATOMIC))==NULL)
|
||||
{
|
||||
printk("malloc failed");
|
||||
return NULL;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static size_t creat_list(char *str, int start, int end)
|
||||
{
|
||||
size_t cnt = end - start + 1;
|
||||
for(; start <= end ;start++)
|
||||
*str++ = start;
|
||||
return (cnt > 0)?cnt:0;
|
||||
}
|
||||
|
||||
static int in_list(char ch, RE *regexp)
|
||||
{
|
||||
char *str = regexp->ccl;
|
||||
if(regexp->type != LIST)
|
||||
return 0;
|
||||
for(; *str && ch != *str; str++)
|
||||
;
|
||||
return (*str != '\0') ^ regexp->nccl;
|
||||
}
|
||||
|
||||
static void regexp_free(RE *regexp)
|
||||
{
|
||||
RE *tmp;
|
||||
for(; regexp; regexp = tmp)
|
||||
{
|
||||
tmp = regexp->next;
|
||||
kfree(regexp);
|
||||
}
|
||||
}
|
||||
|
||||
static RE* compile(char *regexp)
|
||||
{
|
||||
RE head, *tail, *tmp;
|
||||
char *pstr;
|
||||
int err_flag = 0;
|
||||
|
||||
for(tail = &head; *regexp != '\0' && err_flag == 0; regexp++)
|
||||
{
|
||||
tmp = getmem(sizeof(RE));
|
||||
switch(*regexp){
|
||||
case '\\':
|
||||
regexp++;
|
||||
if(*regexp == 'd')
|
||||
{
|
||||
tmp->type = LIST;
|
||||
tmp->nccl = 0;
|
||||
tmp->ccl = getmem(11);
|
||||
creat_list(tmp->ccl, '0','9');
|
||||
tmp->ccl[11] = '\0';
|
||||
}else if(*regexp == 'D')
|
||||
{
|
||||
tmp->type = LIST;
|
||||
tmp->nccl = 1;
|
||||
tmp->ccl = getmem(11);
|
||||
creat_list(tmp->ccl, '0','9');
|
||||
tmp->ccl[11] = '\0';
|
||||
}else
|
||||
{
|
||||
tmp->type = CHAR;
|
||||
tmp->ch = *regexp;
|
||||
}
|
||||
break;
|
||||
case '.':
|
||||
tmp->type = DOT;
|
||||
break;
|
||||
case '^':
|
||||
tmp->type = BEGIN;
|
||||
tmp->ch = '^';
|
||||
break;
|
||||
case '$':
|
||||
tmp->type = END;
|
||||
tmp->ch = '$';
|
||||
break;
|
||||
case '*':
|
||||
tmp->type = STAR;
|
||||
break;
|
||||
case '+':
|
||||
tmp->type = PLUS;
|
||||
break;
|
||||
case '?':
|
||||
tmp->type = QUES;
|
||||
break;
|
||||
case '[':
|
||||
pstr = tmp->ccl = getmem(256);
|
||||
tmp->nccl = 0;
|
||||
if(*++regexp == '^')
|
||||
{
|
||||
tmp->nccl = 1;
|
||||
regexp++;
|
||||
}
|
||||
while(*regexp != '\0' && *regexp != ']')
|
||||
{
|
||||
if(*regexp != '-')
|
||||
{
|
||||
*pstr++ = *regexp++;
|
||||
continue;
|
||||
}
|
||||
if(pstr == tmp->ccl || *(regexp + 1) == ']')
|
||||
{
|
||||
err_flag = 1;
|
||||
break;
|
||||
}
|
||||
pstr += creat_list(pstr, *(regexp - 1) + 1, *(regexp + 1));
|
||||
regexp += 2;
|
||||
}
|
||||
*pstr = '\0';
|
||||
if(*regexp == '\0')
|
||||
err_flag = 1;
|
||||
tmp->type = LIST;
|
||||
break;
|
||||
default:
|
||||
tmp->type = CHAR;
|
||||
tmp->ch = *regexp;
|
||||
}
|
||||
|
||||
tail->next = tmp;
|
||||
tail = tmp;
|
||||
}
|
||||
|
||||
tail->next = NULL;
|
||||
if(err_flag)
|
||||
{
|
||||
regexp_free(head.next);
|
||||
return NULL;
|
||||
}
|
||||
return head.next;
|
||||
}
|
||||
|
||||
#define MATCH_ONE(reg, text) \
|
||||
(reg->type == DOT || in_list(*text, reg) || *text == reg->ch)
|
||||
#define MATCH_ONE_P(reg, text) \
|
||||
(in_list(*text++, reg) || *(text - 1) == reg->ch || reg->type == DOT)
|
||||
|
||||
static int matchhere(RE *regexp, char *text);
|
||||
|
||||
static int matchstar(RE *cur, RE *regexp, char *text)
|
||||
{
|
||||
do{
|
||||
if(matchhere(regexp, text))
|
||||
return 1;
|
||||
}while(*text != '\0' && MATCH_ONE_P(cur, text));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matchstar_l(RE *cur, RE *regexp, char *text)
|
||||
{
|
||||
char *t;
|
||||
for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++)
|
||||
;
|
||||
do{
|
||||
if(matchhere(regexp, t))
|
||||
return 1;
|
||||
}while(t-- > text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matchplus(RE *cur, RE *regexp, char *text)
|
||||
{
|
||||
while(*text != '\0' && MATCH_ONE_P(cur, text))
|
||||
{
|
||||
if(matchhere(regexp, text))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matchplus_l(RE *cur, RE *regexp, char *text)
|
||||
{
|
||||
char *t;
|
||||
for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++)
|
||||
;
|
||||
for(; t > text; t--)
|
||||
{
|
||||
if(matchhere(regexp, t))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int matchques(RE *cur, RE *regexp, char *text)
|
||||
{
|
||||
int cnt = 1;
|
||||
char *t = text;
|
||||
if(*t != '\0' && cnt-- && MATCH_ONE(cur, t))
|
||||
t++;
|
||||
do{
|
||||
if(matchhere(regexp, t))
|
||||
return 1;
|
||||
}while(t-- > text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*matchfun[TYPENUM][2])(RE *, RE *, char *) = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
matchstar, matchstar_l,
|
||||
matchplus, matchplus_l,
|
||||
matchques, matchques,
|
||||
};
|
||||
|
||||
static int matchhere(RE *regexp, char *text)
|
||||
{
|
||||
if(regexp == NULL)
|
||||
return 1;
|
||||
if(regexp->type == END && regexp->next == NULL)
|
||||
return *text == '\0';
|
||||
if(regexp->next && matchfun[regexp->next->type][match_longest])
|
||||
return matchfun[regexp->next->type][match_longest](regexp, regexp->next->next, text);
|
||||
|
||||
if(*text != '\0' && MATCH_ONE(regexp, text))
|
||||
return matchhere(regexp->next, text + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return value:
|
||||
* -1 error
|
||||
* 0 not match
|
||||
* 1 matched
|
||||
*/
|
||||
int regexp_match(char *reg, char *text)
|
||||
{
|
||||
int ret;
|
||||
RE *regexp = compile(reg);
|
||||
if(regexp == NULL)
|
||||
return -1;
|
||||
|
||||
if(regexp->type == BEGIN)
|
||||
{
|
||||
ret = matchhere(regexp->next, text);
|
||||
goto out;
|
||||
}
|
||||
|
||||
do{
|
||||
if(ret = matchhere(regexp, text))
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
}while(*text++ != '\0');
|
||||
|
||||
out:
|
||||
regexp_free(regexp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void TEST_reg_func(char *reg, char * str, int ret)
|
||||
{
|
||||
|
||||
if (ret != regexp_match(reg, str)) {
|
||||
if (reg)
|
||||
printk("reg = %s,", reg);
|
||||
else
|
||||
printk("reg = null");
|
||||
if (str)
|
||||
printk("str = %s ", str);
|
||||
else
|
||||
printk("str= null");
|
||||
printk("error, unit test.... failed, ret = %d\n",ret);
|
||||
}
|
||||
else {
|
||||
if (reg && str)
|
||||
printk("[unit test] %s %s......ok,ret = %d\n", reg, str, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void TEST_regexp(void)
|
||||
{
|
||||
TEST_reg_func(".*baidu.com$", "www.baidu.com", 1);
|
||||
TEST_reg_func("^sina.com", "www.sina.com.cn", 0);
|
||||
TEST_reg_func("^sina.com", "sina.com.cn", 1);
|
||||
TEST_reg_func(".*baidu.com$", "www.baidu.com223", 0);
|
||||
}
|
||||
Reference in new issue
Block a user