OpenAppFilter/kernel/app_filter.c

318 lines
7.6 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 "app_filter.h"
#include "af_utils.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("destan19@126.com");
MODULE_DESCRIPTION("app filter module");
#define MIN_HTTP_DATA_LEN 16
int parse_flow_base(struct sk_buff *skb, flow_info_t *flow)
{
struct tcphdr * tcph = NULL;
struct udphdr * udph = NULL;
struct nf_conn *ct = NULL;
struct iphdr *iph = NULL;
if (!skb) {
return -1;
}
ct = (struct nf_conn *)skb->nfct;
if (!ct) {
return -1;
}
iph = ip_hdr(skb);
if ( !iph ) {
return -1;
}
flow->ct = ct;
flow->src = iph->saddr;
flow->dst = iph->daddr;
flow->l4_protocol = iph->protocol;
switch (iph->protocol) {
case IPPROTO_TCP:
tcph = (struct tcphdr *)(iph + 1);
flow->l4_data = skb->data + iph->ihl * 4 + tcph->doff * 4;
flow->l4_len = ntohs(iph->tot_len) - iph->ihl * 4 - tcph->doff * 4;
flow->dport = htons(tcph->dest);
flow->sport = htons(tcph->source);
break;
case IPPROTO_UDP:
udph = (struct udphdr *)(iph + 1);
flow->l4_data = skb->data + iph->ihl * 4 + 8;
flow->l4_len = ntohs(udph->len) - 8;
flow->dport = htons(udph->dest);
flow->sport = htons(udph->source);
break;
case IPPROTO_ICMP:
break;
default:
return -1;
}
return -1;
}
/*
desc: ½âÎöhttps urlÐÅÏ¢£¬±£´æµ½flowÖÐ
return:
-1: error
0: match
author: Derry
Date:2018/12/19
*/
int parse_https_proto(flow_info_t *flow) {
int i ;
short url_len = 0 ;
char * p = flow->l4_data;
int data_len = flow->l4_len;
if (NULL == flow) {
AF_ERROR("flow is NULL\n");
return -1;
}
if (NULL == p || data_len == 0) {
return -1;
}
if (!(p[0] == 0x16 && p[1] == 0x03 && p[2] == 0x01))
return -1;
for(i = 0; i < data_len; i++) {
if(i + HTTPS_URL_OFFSET >= data_len) {
return -1;
}
if(p[i] == 0x0 && p[i + 1] == 0x0 && p[i + 2] == 0x0 && p[i + 3] != 0x0) {
// 2 bytes
memcpy(&url_len , p + i + HTTPS_LEN_OFFSET, 2);
if(ntohs(url_len) <= 0 || ntohs(url_len) > data_len) {
continue ;
}
if(i + HTTPS_URL_OFFSET + ntohs(url_len) < data_len) {
//dump_hex("https hex", p, data_len);
flow->https.match = AF_TRUE;
flow->https.url_pos = p + i + HTTPS_URL_OFFSET;
//dump_str("https url", flow->https.url_pos, 5);
flow->https.url_len = ntohs(url_len);
return 0;
}
}
}
return -1;
}
void parse_http_proto(flow_info_t *flow)
{
if (!flow) {
AF_ERROR("flow is null\n");
return;
}
if (flow->l4_protocol != IPPROTO_TCP) {
return;
}
int i = 0;
int start = 0;
char *data = flow->l4_data;
int data_len = flow->l4_len;
if (data_len < MIN_HTTP_DATA_LEN) {
return;
}
if (flow->sport != 80 && flow->dport != 80)
return;
for (i = 0; i < data_len - 4; i++) {
if (data[i] == 0x0d && data[i + 1] == 0x0a){
if (0 == memcmp(&data[start], "POST ", 5)) {
flow->http.match = AF_TRUE;
flow->http.method = HTTP_METHOD_POST;
flow->http.url_pos = data + start + 5;
flow->http.url_len = i - start - 5;
//dump_str("get request", flow->http.url_pos, flow->http.url_len);
}
else if(0 == memcmp(&data[start], "GET ", 4)) {
flow->http.match = AF_TRUE;
flow->http.method = HTTP_METHOD_GET;
flow->http.url_pos = data + start + 4;
flow->http.url_len = i - start - 4;
//dump_str("post request", flow->http.url_pos, flow->http.url_len);
}
else if (0 == memcmp(&data[start], "Host: ", 6) ){
flow->http.host_pos = data + start + 6;
flow->http.host_len = i - start - 6;
//dump_str("host ", flow->http.host_pos, flow->http.host_len);
}
// ÅжÏhttpÍ·²¿½áÊø
if (data[i + 2] == 0x0d && data[i + 3] == 0x0a){
flow->http.data_pos = data + i + 4;
flow->http.data_len = data_len - i - 4;
break;
}
// 0x0d 0x0a
start = i + 2;
}
}
}
static void dump_http_flow_info(http_proto_t *http) {
if (!http) {
AF_ERROR("http ptr is NULL\n");
return ;
}
if (!http->match)
return;
if (http->method == HTTP_METHOD_GET){
printk("Http method: "HTTP_GET_METHOD_STR"\n");
}
else if (http->method == HTTP_METHOD_POST) {
printk("Http method: "HTTP_POST_METHOD_STR"\n");
}
if (http->url_len > 0 && http->url_pos){
dump_str("Request url", http->url_pos, http->url_len);
}
if (http->host_len > 0 && http->host_pos){
dump_str("Host", http->host_pos, http->host_len);
}
printk("--------------------------------------------------------\n\n\n");
}
static void dump_https_flow_info(https_proto_t *https) {
if (!https) {
AF_ERROR("https ptr is NULL\n");
return ;
}
if (!https->match)
return;
if (https->url_len > 0 && https->url_pos){
printk("url len = %d\n",https->url_len);
dump_str("https server name", https->url_pos, https->url_len);
}
printk("--------------------------------------------------------\n\n\n");
}
static void dump_flow_info(flow_info_t *flow)
{
if (!flow) {
AF_ERROR("flow is null\n");
return;
}
#if 0
if (check_local_network_ip(ntohl(flow->src))) {
printk("src ip(inner net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
}
else {
printk("src ip(outer net):"NIPQUAD_FMT", dst ip = "NIPQUAD_FMT"\n", NIPQUAD(flow->src), NIPQUAD(flow->dst));
}
#endif
if (flow->l4_protocol == IPPROTO_TCP) {
if (AF_TRUE == flow->http.match) {
printk("-------------------http protocol-------------------------\n");
printk("protocol:TCP , sport: %-8d, dport: %-8d, data_len: %-8d\n",
flow->sport, flow->dport, flow->l4_len);
dump_http_flow_info(&flow->http);
}
if (AF_TRUE == flow->https.match) {
dump_https_flow_info(&flow->https);
}
}
else if (flow->l4_protocol == IPPROTO_UDP) {
// printk("protocol:UDP ,sport: %-8d, dport: %-8d, data_len: %-8d\n",
// flow->sport, flow->dport, flow->l4_len);
}
else {
return;
}
}
/* ÔÚnetfilter¿ò¼Ü×¢²áµÄ¹³×Ó */
static u_int32_t app_filter_hook(unsigned int hook,
struct sk_buff *pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nf_conn *ct = (struct nf_conn *)pskb->nfct;
if (ct == NULL) {
AF_ERROR("ct is null\n");
return NF_ACCEPT;
}
flow_info_t flow;
memset((char *)&flow, 0x0, sizeof(flow_info_t));
parse_flow_base(pskb, &flow);
parse_http_proto(&flow);
parse_https_proto(&flow);
dump_flow_info(&flow);
// todo: match url rules
// this is example
if (flow.http.match == AF_TRUE) {
if (flow.http.host_pos &&
strnstr(flow.http.host_pos, "sohu", flow.http.host_len)){
dump_str("Drop http url ",flow.http.host_pos, flow.http.host_len);
return NF_DROP;
}
}
if (flow.https.match == AF_TRUE) {
if (flow.https.url_pos &&
strnstr(flow.https.url_pos, "hao123", flow.https.url_len)){
dump_str("Drop https url ",flow.https.url_pos, flow.https.url_len);
return NF_DROP;
}
}
return NF_ACCEPT;
}
static struct nf_hook_ops app_filter_ops[] __read_mostly = {
{
.hook = app_filter_hook,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_MANGLE + 10,
},
};
/*
Ä£¿éÍ˳ö
*/
static void app_filter_fini(void)
{
AF_DEBUG("app filter module exit\n");
nf_unregister_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
return ;
}
/*
Ä£¿é³õʼ»¯
*/
static int __init app_filter_init(void)
{
AF_INFO("appfilter version:"AF_VERSION"\n");
AF_DEBUG("app filter module init\n");
nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops));
printk("init app filter ........ok\n");
return 0;
}
module_init(app_filter_init);
module_exit(app_filter_fini);