From d5d2419399ff7da7b044e4f8512baef3ed86c0c6 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Tue, 4 Apr 2006 15:13:12 +0200 Subject: [PATCH] initial version --- ct_flush-kernel.patch | 169 ++++++++++++++++++++++++ ct_flush.c | 292 ++++++++++++++++++++++++++++++++++++++++++ ct_flush.h | 18 +++ 3 files changed, 479 insertions(+) create mode 100644 ct_flush-kernel.patch create mode 100644 ct_flush.c create mode 100644 ct_flush.h diff --git a/ct_flush-kernel.patch b/ct_flush-kernel.patch new file mode 100644 index 0000000..ffebd1a --- /dev/null +++ b/ct_flush-kernel.patch @@ -0,0 +1,169 @@ +diff -uNr linux-2.6.11/net/ipv4/netfilter/Kconfig linux-2.6.11-work/net/ipv4/netfilter/Kconfig +--- linux-2.6.11/net/ipv4/netfilter/Kconfig 2005-03-12 16:48:12.000000000 +0100 ++++ linux-2.6.11-work/net/ipv4/netfilter/Kconfig 2005-03-09 21:11:14.000000000 +0100 +@@ -99,6 +99,12 @@ + + To compile it as a module, choose M here. If unsure, say Y. + ++config IP_NF_CT_FLUSH ++ tristate "Conntrack userspace flush" ++ depends on IP_NF_CONNTRACK ++ help ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP_NF_QUEUE + tristate "Userspace queueing via NETLINK" + help +diff -uNr linux-2.6.11/net/ipv4/netfilter/Makefile linux-2.6.11-work/net/ipv4/netfilter/Makefile +--- linux-2.6.11/net/ipv4/netfilter/Makefile 2005-03-12 16:48:21.000000000 +0100 ++++ linux-2.6.11-work/net/ipv4/netfilter/Makefile 2005-03-09 21:18:03.000000000 +0100 +@@ -88,3 +88,5 @@ + obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o + + obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o ++ ++obj-$(CONFIG_IP_NF_CT_FLUSH) += ct_flush.o +diff -uNr linux-2.6.11/net/ipv4/netfilter/ct_flush.c linux-2.6.11-work/net/ipv4/netfilter/ct_flush.c +--- linux-2.6.11/net/ipv4/netfilter/ct_flush.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.11-work/net/ipv4/netfilter/ct_flush.c 2005-03-12 13:56:25.000000000 +0100 +@@ -0,0 +1,118 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ct_flush.h" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Olaf Rempel "); ++MODULE_DESCRIPTION("connection tracking flush module"); ++ ++//#define DEBUG ++ ++static int ct_flush_match_real(struct ip_conntrack_tuple *tp, struct search_pattern *pat) { ++ /* compare proto only if one is given */ ++ if (pat->proto != 0 && tp->dst.protonum != pat->proto) ++ return 0; ++ ++ /* compare src-ip */ ++ if ((tp->src.ip ^ pat->src.ip) & pat->src.mask) ++ return 0; ++ ++ /* compare dst-ip */ ++ if ((tp->dst.ip ^ pat->dst.ip) & pat->dst.mask) ++ return 0; ++ ++ /* check ports only if tcp/udp */ ++ if (pat->proto != IPPROTO_TCP && pat->proto != IPPROTO_UDP) ++ return 1; ++ ++ /* compare src-portrange */ ++ if (tp->src.u.all < pat->src.portlo || tp->src.u.all > pat->src.porthi) ++ return 0; ++ ++ /* compare dst-portrange */ ++ if (tp->dst.u.all < pat->dst.portlo || tp->dst.u.all > pat->dst.porthi) ++ return 0; ++ ++ return 1; ++} ++ ++static int ct_flush_match(struct ip_conntrack *ct, void *data) { ++ if (ct_flush_match_real(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, data) || ++ ct_flush_match_real(&ct->tuplehash[IP_CT_DIR_REPLY].tuple, data)) { ++ ((struct search_pattern *)data)->count++; ++ return 1; ++ } ++ return 0; ++} ++ ++static int ct_flush_open(struct inode *inode, struct file *file) { ++ return 0; ++} ++ ++static int ct_flush_close(struct inode *inode, struct file *file) { ++ return 0; ++} ++ ++static int ct_flush_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { ++ struct search_pattern pat; ++ int ret = 0; ++ ++ switch (cmd) { ++ case IOCTL_CT_FLUSH: ++ if (copy_from_user(&pat, (struct search_pattern *)arg, sizeof(pat))) ++ return -EFAULT; ++ ++ ip_ct_iterate_cleanup(ct_flush_match, &pat); ++#ifdef DEBUG ++ printk(KERN_DEBUG "ct_flush: proto: %u src: %u.%u.%u.%u/%u.%u.%u.%u %u:%u dst: %u.%u.%u.%u/%u.%u.%u.%u %u:%u found: %u\n", ++ pat.proto, ++ NIPQUAD(pat.src.ip), NIPQUAD(pat.src.mask), ntohs(pat.src.portlo), ntohs(pat.src.porthi), ++ NIPQUAD(pat.dst.ip), NIPQUAD(pat.dst.mask), ntohs(pat.dst.portlo), ntohs(pat.dst.porthi), ++ pat.count); ++#endif ++ if (copy_to_user((void *)arg, &pat, sizeof(pat))) ++ return -EFAULT; ++ break; ++ } ++ return ret; ++} ++ ++static struct file_operations ct_flush_fops = ++{ ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .ioctl = ct_flush_ioctl, ++ .open = ct_flush_open, ++ .release = ct_flush_close, ++}; ++ ++static struct miscdevice ct_flush_miscdev = ++{ ++ .minor = CT_FLUSH_MINOR, ++ .name = "ct_flush", ++ .fops = &ct_flush_fops, ++}; ++ ++static int __init init(void) { ++ misc_register(&ct_flush_miscdev); ++ return 0; ++} ++ ++static void __exit fini(void) { ++ misc_deregister(&ct_flush_miscdev); ++} ++ ++module_init(init); ++module_exit(fini); +diff -uNr linux-2.6.11/net/ipv4/netfilter/ct_flush.h linux-2.6.11-work/net/ipv4/netfilter/ct_flush.h +--- linux-2.6.11/net/ipv4/netfilter/ct_flush.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.11-work/net/ipv4/netfilter/ct_flush.h 2005-03-12 14:29:47.000000000 +0100 +@@ -0,0 +1,18 @@ ++#ifndef _IP_CT_FLUSH_H ++#define _IP_CT_FLUSH_H ++ ++struct search_pattern { ++ struct { ++ u_int32_t ip; ++ u_int32_t mask; ++ u_int16_t portlo; ++ u_int16_t porthi; ++ } src, dst; ++ u_int8_t proto; ++ u_int32_t count; ++}; ++ ++#define CT_FLUSH_MINOR 243 ++#define IOCTL_CT_FLUSH _IOWR('W', 0, struct search_pattern) ++ ++#endif diff --git a/ct_flush.c b/ct_flush.c new file mode 100644 index 0000000..c3c7174 --- /dev/null +++ b/ct_flush.c @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ct_flush.h" + +/* name of device (major 10, minor 243) */ +#define CT_FLUSH_DEVICE "/dev/ct_flush" + +/* default pattern: match no IP, all ports */ +#define PATTERN_INIT { {0, 0, 0, 0xffff}, {0, 0, 0, 0xffff}, 0, 0 } + +int exec_flush(struct search_pattern *hostpat) { + struct search_pattern pat; + int dev, ret = 0; + + if ((dev = open(CT_FLUSH_DEVICE, O_RDWR)) == -1 ) { + perror("open"); + return -1; + } + + pat.count = 0; + pat.proto = hostpat->proto; + pat.src.ip = htonl(hostpat->src.ip); + pat.src.mask = htonl(hostpat->src.mask); + pat.src.portlo = htons(hostpat->src.portlo); + pat.src.porthi = htons(hostpat->src.porthi); + pat.dst.ip = htonl(hostpat->dst.ip); + pat.dst.mask = htonl(hostpat->dst.mask); + pat.dst.portlo = htons(hostpat->dst.portlo); + pat.dst.porthi = htons(hostpat->dst.porthi); + + if ((ret = ioctl(dev, IOCTL_CT_FLUSH, &pat)) == -1) + perror ("ioctl"); + + hostpat->count = pat.count; + + close(dev); + + return ret; +} + +int parse_proto(char *arg, u_int8_t *val) { + struct protoent *p; + + p = getprotobyname(arg); + if (p != NULL) { + *val = (u_int8_t)(p->p_proto); + + } else if (!strcmp(arg, "all")) { + *val = 0; + + } else { + long num = strtol(arg, NULL, 0); + if (num <= 0 || num > 255) + return 1; + + *val = (u_int8_t)num; + } + return 0; +} + +void print_proto(char *pre, u_int8_t val) { + struct protoent *p; + + if (val) { + if ((p = getprotobynumber(val))) { + printf("%s%s ", pre, p->p_name); + } else { + printf("%s%d ", pre, p->p_proto); + } + } +} + +int parse_ipmask(char *arg, u_int32_t *ip, u_int32_t *mask) { + struct in_addr tmp; + char *p; + + /* netmask */ + if ((p = strchr(arg, '/')) != NULL) { + if (inet_pton(AF_INET, p+1, &tmp) > 0) { + *mask = (u_int32_t)(tmp.s_addr); + + } else { + long num = strtol(p+1, NULL, 0); + if (num <= 0 || num > 32) + return 1; + + *mask = (u_int32_t) htonl(0xFFFFFFFF<<(32-num)); + } + *p = 0; + + } else { + *mask = 0xFFFFFFFF; + } + + /* ip address */ + if (inet_pton(AF_INET, arg, &tmp) <= 0) + return 1; + + *ip = (u_int32_t)(tmp.s_addr); + + return 0; +} + +void print_ipmask(char *pre, u_int32_t ip, u_int32_t mask) { + if (mask != 0 && ip != 0) { + int i = 32; + char ip_str[INET_ADDRSTRLEN]; + u_int32_t bits = 0xFFFFFFFEL; + u_int32_t maskaddr = ntohl(mask); + + inet_ntop(AF_INET, &ip, ip_str, sizeof(ip_str)); + + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + + if (bits == 0) { + printf("%s%s ", pre, ip_str); + + } else if (i >= 0) { + printf("%s%s/%d ", pre, ip_str, i); + + } else { + char mask_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &mask, mask_str, sizeof(ip_str)); + printf("%s%s/%s ", pre, ip_str, mask_str); + } + } +} + +int parse_portrange(char *arg, u_int16_t *portlo, u_int16_t *porthi) { + long num; + char *p; + + /* check for portrange */ + if ((p = strchr(arg, ':')) != NULL || (p = strchr(arg, '-')) != NULL) { + /* try parse port */ + num = strtol(p+1, NULL, 0); + if (num < 0 || num > 65535) + return 1; + + *porthi = (u_int16_t)num; + *p = 0; + } + + /* try parse port */ + num = strtol(arg, NULL, 0); + if (num < 0 || num > 65535) + return 1; + + *portlo = (u_int16_t)num; + + if (p == NULL) + *porthi = *portlo; + + if (portlo > porthi) + return 1; + + return 0; +} + +void print_portrange(char *pre, u_int16_t portlo, u_int16_t porthi) { + if (portlo == porthi) + printf("%s%d ", pre, portlo); + + else if (portlo != 0 || porthi != 65535) + printf("%s%d:%d ", pre, portlo, porthi); +} + +static struct option opts[] = { + {"protocol", 1, 0, 'p'}, + {"source", 1, 0, 's'}, + {"src", 1, 0, 's'}, + {"sport", 1, 0, 'a'}, + {"destination", 1, 0, 'd'}, + {"dst", 1, 0, 'd'}, + {"dport", 1, 0, 'b'}, + {"help", 0, 0, 'h'}, + {"verbose", 0, 0, 'v'}, + {0, 0, 0, 0} +}; + +int main(int argc, char *argv[]) { + struct search_pattern pat = PATTERN_INIT; + int arg = 0, code = 0, verbose = 0; + + if (argc == 1) { + fprintf(stderr, "No argument given. try --help\n"); + exit(-1); + } + + while (code != -1) { + code = getopt_long(argc, argv, "p:s:d:hv", opts, &arg); + + switch (code) { + case 'p': /* proto */ + if (parse_proto(optarg, &pat.proto)) { + fprintf(stderr, "Error parsing value: %s\n", optarg); + exit(-1); + } + break; + + case 's': /* src */ + if (parse_ipmask(optarg, &pat.src.ip, &pat.src.mask)) { + fprintf(stderr, "Error parsing value: %s\n", optarg); + exit(-1); + } + break; + + case 'a': /* sport */ + if (pat.proto != IPPROTO_TCP && pat.proto != IPPROTO_UDP) { + fprintf(stderr, "--sport only allowed with tcp/udp protocol\n"); + exit(-1); + } + if (parse_portrange(optarg, &pat.src.portlo, &pat.src.porthi)) { + fprintf(stderr, "Error parsing value: %s\n", optarg); + exit(-1); + } + break; + + case 'd': /* dst */ + if (parse_ipmask(optarg, &pat.dst.ip, &pat.dst.mask)) { + fprintf(stderr, "Error parsing value: %s\n", optarg); + exit(-1); + } + break; + + case 'b': /* dport */ + if (pat.proto != IPPROTO_TCP && pat.proto != IPPROTO_UDP) { + fprintf(stderr, "--dport only allowed with tcp/udp protocol\n"); + exit(-1); + } + if (parse_portrange(optarg, &pat.dst.portlo, &pat.dst.porthi)) { + fprintf(stderr, "Error parsing value: %s\n", optarg); + exit(-1); + } + break; + + case 'h': /* help */ + printf("Usage: ct_flush [options]\n" + "Options: \n" + " --protocol -p protocol ('tcp', '6')\n" + " --source -s address[/mask]\n" + " --destination -d address[/mask]\n" + " --sport port[-port] (only with proto tcp/udp)\n" + " --dport port[-port] (only with proto tcp/udp)\n" + " --verbose -v be verbose\n" + " --help -h this help\n" + "\n"); + exit(0); + break; + + case 'v': /* verbose */ + verbose = 1; + break; + + case '?': /* error */ + exit(-1); + break; + + default: /* unknown / all options parsed */ + break; + } + } + + if (verbose) { + printf("flushing: "); + print_proto("proto=", pat.proto); + print_ipmask("src=", pat.src.ip, pat.src.mask); + print_portrange("sport=", pat.src.portlo, pat.src.porthi); + print_ipmask("dst=", pat.dst.ip, pat.dst.mask); + print_portrange("dport=", pat.dst.portlo, pat.dst.porthi); + printf("\n"); + } + + exec_flush(&pat); + + if (verbose) + printf("%d flushed\n", pat.count); + + return 0; +} diff --git a/ct_flush.h b/ct_flush.h new file mode 100644 index 0000000..6bd8ddb --- /dev/null +++ b/ct_flush.h @@ -0,0 +1,18 @@ +#ifndef _IP_CT_FLUSH_H +#define _IP_CT_FLUSH_H + +struct search_pattern { + struct { + u_int32_t ip; + u_int32_t mask; + u_int16_t portlo; + u_int16_t porthi; + } src, dst; + u_int8_t proto; + u_int32_t count; +}; + +#define CT_FLUSH_MINOR 243 +#define IOCTL_CT_FLUSH _IOWR('W', 0, struct search_pattern) + +#endif