From 9ab85762233756f1e828f7c4c6007d25ac26f494 Mon Sep 17 00:00:00 2001 From: "/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=pablo/emailAddress=pablo@netfilter.org" Date: Tue, 12 Jul 2005 23:24:06 +0000 Subject: o Use conntrack netlink attributes: Major change o Kill action setting: Mask based dumping --- ChangeLog | 31 ++- extensions/libct_proto_icmp.c | 31 +-- extensions/libct_proto_sctp.c | 108 ++++++--- extensions/libct_proto_tcp.c | 118 +++++++--- extensions/libct_proto_udp.c | 94 ++++++-- include/libct_proto.h | 25 +- src/conntrack.c | 535 ++++++++++++++++++++++++++++-------------- src/libct.c | 526 ++++++++++++++++++++++++++--------------- test.sh | 77 ++++-- 9 files changed, 1027 insertions(+), 518 deletions(-) diff --git a/ChangeLog b/ChangeLog index 688b0cc..0ae9fc5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2005-07-12 + + o Use conntrack netlink attributes: Major change + o Kill action setting: Mask based dumping + o Fix ChangeLog + 2005-05-23 o Fixed syntax error (tab/space issue) in help message @@ -9,11 +15,16 @@ o Add SCTP extension o Add support for expect creation o Bump version number to 0.63 -2005-04-25 + +2005-05-17 - o Added support for mask based event dumping - o Added support for mask based event notification - o On-demand autoload of ip_conntrack_netlink + o Added descriptive error messages. + o Fix wrong flags check in [tcp|udp] proto helpers. + +2005-05-16 + + o Implemented ICMP proto helper + o Added help() and final_check() functions for proto helpers. 2005-05-01 @@ -33,12 +44,8 @@ o Autoconf stuff for conntrack + some pablo's modifications. o Fixed packet counters formatting (use %llu instead of %lu) -2005-05-16 - - o Implemented ICMP proto helper - o Added help() and final_check() functions for proto helpers. - -2005-05-17 +2005-04-25 - o Added descriptive error messages. - o Fix wrong flags check in [tcp|udp] proto helpers. + o Added support for mask based event dumping + o Added support for mask based event notification + o On-demand autoload of ip_conntrack_netlink diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c index 6a2db92..24d3d3f 100644 --- a/extensions/libct_proto_icmp.c +++ b/extensions/libct_proto_icmp.c @@ -11,8 +11,6 @@ #include #include #include /* For htons */ -#include -#include #include "libct_proto.h" static struct option opts[] = { @@ -41,27 +39,28 @@ void help() } int parse(char c, char *argv[], - struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, - union ip_conntrack_proto *proto, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + struct ctnl_tuple *mask, + union ctnl_protoinfo *proto, unsigned int *flags) { switch(c) { case '1': if (optarg) { - orig->dst.u.icmp.type = atoi(optarg); + orig->l4dst.icmp.type = atoi(optarg); *flags |= ICMP_TYPE; } break; case '2': if (optarg) { - orig->dst.u.icmp.code = atoi(optarg); + orig->l4dst.icmp.code = atoi(optarg); *flags |= ICMP_CODE; } break; case '3': if (optarg) { - reply->src.u.icmp.id = atoi(optarg); + orig->l4src.icmp.id = atoi(optarg); *flags |= ICMP_ID; } break; @@ -69,7 +68,9 @@ int parse(char c, char *argv[], return 1; } -int final_check(unsigned int flags) +int final_check(unsigned int flags, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply) { if (!(flags & ICMP_TYPE)) return 0; @@ -79,18 +80,18 @@ int final_check(unsigned int flags) return 1; } -void print_tuple(struct ip_conntrack_tuple *t) +void print_proto(struct ctnl_tuple *t) { - fprintf(stdout, "type=%d code=%d id=%d ", t->dst.u.icmp.type, - t->dst.u.icmp.code, - t->src.u.icmp.id); + fprintf(stdout, "type=%d code=%d id=%d", t->l4dst.icmp.type, + t->l4dst.icmp.code, + t->l4src.icmp.id); } static struct ctproto_handler icmp = { .name = "icmp", .protonum = 1, - .parse = parse, - .print_tuple = print_tuple, + .parse_opts = parse, + .print_proto = print_proto, .final_check = final_check, .help = help, .opts = opts diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c index b84c2ba..5b50fbc 100644 --- a/extensions/libct_proto_sctp.c +++ b/extensions/libct_proto_sctp.c @@ -1,26 +1,26 @@ /* - * (C) 2005 by Harald Welte + * (C) 2005 by Harald Welte * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License Version 2 as - * published by the Free Software Foundation + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * */ #include #include #include -#include #include /* For htons */ -#include -#include +#include #include "libct_proto.h" +#include "libctnetlink.h" static struct option opts[] = { {"orig-port-src", 1, 0, '1'}, {"orig-port-dst", 1, 0, '2'}, {"reply-port-src", 1, 0, '3'}, {"reply-port-dst", 1, 0, '4'}, - {"state", 1, 0, '5'}, + {"state", 1, 0, '7'}, {0, 0, 0, 0} }; @@ -52,43 +52,44 @@ static const char *states[] = { "SHUTDOWN_ACK_SENT", }; -static void help() +void help() { fprintf(stdout, "--orig-port-src original source port\n"); fprintf(stdout, "--orig-port-dst original destination port\n"); fprintf(stdout, "--reply-port-src reply source port\n"); fprintf(stdout, "--reply-port-dst reply destination port\n"); - fprintf(stdout, "--state SCTP state, eg. ESTABLISHED\n"); + fprintf(stdout, "--state SCTP state, fe. ESTABLISHED\n"); } -static int parse(char c, char *argv[], - struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, - union ip_conntrack_proto *proto, - unsigned int *flags) +int parse_options(char c, char *argv[], + struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + struct ctnl_tuple *mask, + union ctnl_protoinfo *proto, + unsigned int *flags) { switch(c) { case '1': if (optarg) { - orig->src.u.sctp.port = htons(atoi(optarg)); + orig->l4src.sctp.port = htons(atoi(optarg)); *flags |= ORIG_SPORT; } break; case '2': if (optarg) { - orig->dst.u.sctp.port = htons(atoi(optarg)); + orig->l4dst.sctp.port = htons(atoi(optarg)); *flags |= ORIG_DPORT; } break; case '3': if (optarg) { - reply->src.u.sctp.port = htons(atoi(optarg)); + reply->l4src.sctp.port = htons(atoi(optarg)); *flags |= REPL_SPORT; } break; case '4': if (optarg) { - reply->dst.u.sctp.port = htons(atoi(optarg)); + reply->l4dst.sctp.port = htons(atoi(optarg)); *flags |= REPL_DPORT; } break; @@ -97,7 +98,9 @@ static int parse(char c, char *argv[], int i; for (i=0; i<10; i++) { if (strcmp(optarg, states[i]) == 0) { - proto->sctp.state = i; + /* FIXME: Add state to + * ctnl_protoinfo + proto->sctp.state = i; */ break; } } @@ -111,39 +114,68 @@ static int parse(char c, char *argv[], return 1; } -static int final_check(unsigned int flags) +int final_check(unsigned int flags, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply) { - if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT)) + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && !(flags & (REPL_SPORT|REPL_DPORT))) { + reply->l4src.sctp.port = orig->l4dst.sctp.port; + reply->l4dst.sctp.port = orig->l4src.sctp.port; return 1; - else if ((flags & REPL_SPORT) && (flags & REPL_DPORT)) + } else if (!(flags & (ORIG_SPORT|ORIG_DPORT)) + && (flags & (REPL_SPORT|REPL_DPORT))) { + orig->l4src.sctp.port = reply->l4dst.sctp.port; + orig->l4dst.sctp.port = reply->l4src.sctp.port; + return 1; + } + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && ((flags & (REPL_SPORT|REPL_DPORT)))) return 1; return 0; } -static void print_tuple(struct ip_conntrack_tuple *t) +void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple) +{ + if (cda[CTA_PROTO_SCTP_SRC-1]) + tuple->l4src.sctp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SCTP_SRC-1]); + if (cda[CTA_PROTO_SCTP_DST-1]) + tuple->l4dst.sctp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_SCTP_DST-1]); +} + +void parse_protoinfo(struct nfattr *cda[], struct ctnl_conntrack *ct) +{ +/* if (cda[CTA_PROTOINFO_SCTP_STATE-1]) + ct->protoinfo.sctp.state = + *(u_int8_t *)NFA_DATA(cda[CTA_PROTOINFO_SCTP_STATE-1]); +*/ +} + +void print_protoinfo(union ctnl_protoinfo *protoinfo) { - fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.sctp.port), - ntohs(t->dst.u.sctp.port)); +/* fprintf(stdout, "%s ", states[protoinfo->sctp.state]); */ } -static void print_proto(union ip_conntrack_proto *proto) +void print_proto(struct ctnl_tuple *tuple) { - if (proto->sctp.state > sizeof(states)/sizeof(char *)) - fprintf(stdout, "[%u] ", proto->sctp.state); - else - fprintf(stdout, "[%s] ", states[proto->sctp.state]); + fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.sctp.port), + htons(tuple->l4dst.sctp.port)); } static struct ctproto_handler sctp = { - .name = "sctp", - .protonum = 132, - .parse = parse, - .print_tuple = print_tuple, - .print_proto = print_proto, - .final_check = final_check, - .help = help, - .opts = opts, + .name = "sctp", + .protonum = 132, + .parse_opts = parse_options, + .parse_protoinfo = parse_protoinfo, + .parse_proto = parse_proto, + .print_proto = print_proto, + .print_protoinfo = print_protoinfo, + .final_check = final_check, + .help = help, + .opts = opts }; void __attribute__ ((constructor)) init(void); diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c index 45ee29b..5fa3652 100644 --- a/extensions/libct_proto_tcp.c +++ b/extensions/libct_proto_tcp.c @@ -10,18 +10,19 @@ #include #include #include -#include #include /* For htons */ -#include -#include +#include #include "libct_proto.h" +#include "libctnetlink.h" static struct option opts[] = { {"orig-port-src", 1, 0, '1'}, {"orig-port-dst", 1, 0, '2'}, {"reply-port-src", 1, 0, '3'}, {"reply-port-dst", 1, 0, '4'}, - {"state", 1, 0, '5'}, + {"mask-port-src", 1, 0, '5'}, + {"mask-port-dst", 1, 0, '6'}, + {"state", 1, 0, '7'}, {0, 0, 0, 0} }; @@ -38,7 +39,13 @@ enum tcp_param_flags { REPL_DPORT_BIT = 3, REPL_DPORT = (1 << REPL_DPORT_BIT), - STATE_BIT = 4, + MASK_SPORT_BIT = 4, + MASK_SPORT = (1 << MASK_SPORT_BIT), + + MASK_DPORT_BIT = 5, + MASK_DPORT = (1 << MASK_DPORT_BIT), + + STATE_BIT = 6, STATE = (1 << STATE_BIT) }; @@ -55,47 +62,62 @@ static const char *states[] = { "LISTEN" }; -static void help() +void help() { fprintf(stdout, "--orig-port-src original source port\n"); fprintf(stdout, "--orig-port-dst original destination port\n"); fprintf(stdout, "--reply-port-src reply source port\n"); fprintf(stdout, "--reply-port-dst reply destination port\n"); + fprintf(stdout, "--mask-port-src mask source port\n"); + fprintf(stdout, "--mask-port-dst mask destination port\n"); fprintf(stdout, "--state TCP state, fe. ESTABLISHED\n"); } -static int parse(char c, char *argv[], - struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, - union ip_conntrack_proto *proto, - unsigned int *flags) +int parse_options(char c, char *argv[], + struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + struct ctnl_tuple *mask, + union ctnl_protoinfo *proto, + unsigned int *flags) { switch(c) { case '1': if (optarg) { - orig->src.u.tcp.port = htons(atoi(optarg)); + orig->l4src.tcp.port = htons(atoi(optarg)); *flags |= ORIG_SPORT; } break; case '2': if (optarg) { - orig->dst.u.tcp.port = htons(atoi(optarg)); + orig->l4dst.tcp.port = htons(atoi(optarg)); *flags |= ORIG_DPORT; } break; case '3': if (optarg) { - reply->src.u.tcp.port = htons(atoi(optarg)); + reply->l4src.tcp.port = htons(atoi(optarg)); *flags |= REPL_SPORT; } break; case '4': if (optarg) { - reply->dst.u.tcp.port = htons(atoi(optarg)); + reply->l4dst.tcp.port = htons(atoi(optarg)); *flags |= REPL_DPORT; } break; case '5': + if (optarg) { + mask->l4src.tcp.port = htons(atoi(optarg)); + *flags |= MASK_SPORT; + } + break; + case '6': + if (optarg) { + mask->l4src.tcp.port = htons(atoi(optarg)); + *flags |= MASK_DPORT; + } + break; + case '7': if (optarg) { int i; for (i=0; i<10; i++) { @@ -114,39 +136,67 @@ static int parse(char c, char *argv[], return 1; } -static int final_check(unsigned int flags) +int final_check(unsigned int flags, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply) { - if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT)) + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && !(flags & (REPL_SPORT|REPL_DPORT))) { + reply->l4src.tcp.port = orig->l4dst.tcp.port; + reply->l4dst.tcp.port = orig->l4src.tcp.port; return 1; - else if ((flags & REPL_SPORT) && (flags & REPL_DPORT)) + } else if (!(flags & (ORIG_SPORT|ORIG_DPORT)) + && (flags & (REPL_SPORT|REPL_DPORT))) { + orig->l4src.tcp.port = reply->l4dst.tcp.port; + orig->l4dst.tcp.port = reply->l4src.tcp.port; + return 1; + } + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && ((flags & (REPL_SPORT|REPL_DPORT)))) return 1; return 0; } -static void print_tuple(struct ip_conntrack_tuple *t) +void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple) +{ + if (cda[CTA_PROTO_TCP_SRC-1]) + tuple->l4src.tcp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_TCP_SRC-1]); + if (cda[CTA_PROTO_TCP_DST-1]) + tuple->l4dst.tcp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_TCP_DST-1]); +} + +void parse_protoinfo(struct nfattr *cda[], struct ctnl_conntrack *ct) +{ + if (cda[CTA_PROTOINFO_TCP_STATE-1]) + ct->protoinfo.tcp.state = + *(u_int8_t *)NFA_DATA(cda[CTA_PROTOINFO_TCP_STATE-1]); +} + +void print_protoinfo(union ctnl_protoinfo *protoinfo) { - fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.tcp.port), - ntohs(t->dst.u.tcp.port)); + fprintf(stdout, "%s ", states[protoinfo->tcp.state]); } -static void print_proto(union ip_conntrack_proto *proto) +void print_proto(struct ctnl_tuple *tuple) { - if (proto->tcp.state > sizeof(states)/sizeof(char *)) - fprintf(stdout, "[%u] ", states[proto->tcp.state]); - else - fprintf(stdout, "[%s] ", states[proto->tcp.state]); + fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.tcp.port), + htons(tuple->l4dst.tcp.port)); } static struct ctproto_handler tcp = { - .name = "tcp", - .protonum = 6, - .parse = parse, - .print_tuple = print_tuple, - .print_proto = print_proto, - .final_check = final_check, - .help = help, - .opts = opts, + .name = "tcp", + .protonum = 6, + .parse_opts = parse_options, + .parse_protoinfo = parse_protoinfo, + .parse_proto = parse_proto, + .print_proto = print_proto, + .print_protoinfo = print_protoinfo, + .final_check = final_check, + .help = help, + .opts = opts }; void __attribute__ ((constructor)) init(void); diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c index 0088cc5..aa733f0 100644 --- a/extensions/libct_proto_udp.c +++ b/extensions/libct_proto_udp.c @@ -11,15 +11,17 @@ #include #include #include /* For htons */ -#include -#include +#include #include "libct_proto.h" +#include "libctnetlink.h" static struct option opts[] = { {"orig-port-src", 1, 0, '1'}, {"orig-port-dst", 1, 0, '2'}, {"reply-port-src", 1, 0, '3'}, {"reply-port-dst", 1, 0, '4'}, + {"mask-port-src", 1, 0, '5'}, + {"mask-port-dst", 1, 0, '6'}, {0, 0, 0, 0} }; @@ -35,6 +37,12 @@ enum udp_param_flags { REPL_DPORT_BIT = 3, REPL_DPORT = (1 << REPL_DPORT_BIT), + + MASK_SPORT_BIT = 4, + MASK_SPORT = (1 << MASK_SPORT_BIT), + + MASK_DPORT_BIT = 5, + MASK_DPORT = (1 << MASK_DPORT_BIT), }; void help() @@ -43,67 +51,105 @@ void help() fprintf(stdout, "--orig-port-dst original destination port\n"); fprintf(stdout, "--reply-port-src reply source port\n"); fprintf(stdout, "--reply-port-dst reply destination port\n"); + fprintf(stdout, "--mask-port-src mask source port\n"); + fprintf(stdout, "--mask-port-dst mask destination port\n"); } -int parse(char c, char *argv[], - struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, - union ip_conntrack_proto *proto, - unsigned int *flags) +int parse_options(char c, char *argv[], + struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + struct ctnl_tuple *mask, + union ctnl_protoinfo *proto, + unsigned int *flags) { switch(c) { case '1': if (optarg) { - orig->src.u.udp.port = htons(atoi(optarg)); + orig->l4src.udp.port = htons(atoi(optarg)); *flags |= ORIG_SPORT; } break; case '2': if (optarg) { - orig->dst.u.udp.port = htons(atoi(optarg)); + orig->l4dst.udp.port = htons(atoi(optarg)); *flags |= ORIG_DPORT; } break; case '3': if (optarg) { - reply->src.u.udp.port = htons(atoi(optarg)); + reply->l4src.udp.port = htons(atoi(optarg)); *flags |= REPL_SPORT; } break; case '4': if (optarg) { - reply->dst.u.udp.port = htons(atoi(optarg)); + reply->l4dst.udp.port = htons(atoi(optarg)); *flags |= REPL_DPORT; } break; + case '5': + if (optarg) { + mask->l4src.udp.port = htons(atoi(optarg)); + *flags |= MASK_SPORT; + } + break; + case '6': + if (optarg) { + mask->l4src.udp.port = htons(atoi(optarg)); + *flags |= MASK_DPORT; + } + break; } return 1; } -int final_check(unsigned int flags) +int final_check(unsigned int flags, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply) { - if ((flags & ORIG_SPORT) && (flags & ORIG_DPORT)) + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && !(flags & (REPL_SPORT|REPL_DPORT))) { + reply->l4src.udp.port = orig->l4dst.udp.port; + reply->l4dst.udp.port = orig->l4src.udp.port; + return 1; + } else if (!(flags & (ORIG_SPORT|ORIG_DPORT)) + && (flags & (REPL_SPORT|REPL_DPORT))) { + orig->l4src.udp.port = reply->l4dst.udp.port; + orig->l4dst.udp.port = reply->l4src.udp.port; return 1; - else if ((flags & REPL_SPORT) && (flags & REPL_DPORT)) + } + if ((flags & (ORIG_SPORT|ORIG_DPORT)) + && ((flags & (REPL_SPORT|REPL_DPORT)))) return 1; return 0; } -void print_tuple(struct ip_conntrack_tuple *t) +void parse_proto(struct nfattr *cda[], struct ctnl_tuple *tuple) +{ + if (cda[CTA_PROTO_UDP_SRC-1]) + tuple->l4src.udp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_UDP_SRC-1]); + if (cda[CTA_PROTO_UDP_DST-1]) + tuple->l4dst.udp.port = + *(u_int16_t *)NFA_DATA(cda[CTA_PROTO_UDP_DST-1]); +} + +void print_proto(struct ctnl_tuple *tuple) { - fprintf(stdout, "sport=%d dport=%d ", ntohs(t->src.u.udp.port), - ntohs(t->dst.u.udp.port)); + fprintf(stdout, "sport=%u dport=%u ", htons(tuple->l4src.udp.port), + htons(tuple->l4dst.udp.port)); } static struct ctproto_handler udp = { - .name = "udp", - .protonum = 17, - .parse = parse, - .print_tuple = print_tuple, - .final_check = final_check, - .help = help, - .opts = opts + .name = "udp", + .protonum = 17, + .parse_opts = parse_options, + .parse_proto = parse_proto, + .print_proto = print_proto, + .final_check = final_check, + .help = help, + .opts = opts }; void __attribute__ ((constructor)) init(void); diff --git a/include/libct_proto.h b/include/libct_proto.h index 6df03e7..51e23fc 100644 --- a/include/libct_proto.h +++ b/include/libct_proto.h @@ -5,6 +5,7 @@ #include "linux_list.h" #include +#include "libctnetlink.h" struct cta_proto; @@ -13,16 +14,24 @@ struct ctproto_handler { char *name; u_int16_t protonum; + + enum ctattr_protoinfo protoinfo_attr; - int (*parse)(char c, char *argv[], - struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, - union ip_conntrack_proto *proto, + int (*parse_opts)(char c, char *argv[], + struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + struct ctnl_tuple *mask, + union ctnl_protoinfo *proto, unsigned int *flags); - void (*print_tuple)(struct ip_conntrack_tuple *t); - void (*print_proto)(union ip_conntrack_proto *proto); - - int (*final_check)(unsigned int flags); + void (*parse_proto)(struct nfattr *cda[], struct ctnl_tuple *tuple); + void (*parse_protoinfo)(struct nfattr *cda[], + struct ctnl_conntrack *ct); + void (*print_proto)(struct ctnl_tuple *t); + void (*print_protoinfo)(union ctnl_protoinfo *protoinfo); + + int (*final_check)(unsigned int flags, + struct ctnl_tuple *orig, + struct ctnl_tuple *reply); void (*help)(); diff --git a/src/conntrack.c b/src/conntrack.c index dfedf68..a6efd8b 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include "libctnetlink.h" #include "libnfnetlink.h" @@ -62,34 +61,60 @@ #endif enum action { + CT_NONE = 0, + CT_LIST_BIT = 0, CT_LIST = (1 << CT_LIST_BIT), CT_CREATE_BIT = 1, CT_CREATE = (1 << CT_CREATE_BIT), + + CT_UPDATE_BIT = 2, + CT_UPDATE = (1 << CT_UPDATE_BIT), - CT_DELETE_BIT = 2, + CT_DELETE_BIT = 3, CT_DELETE = (1 << CT_DELETE_BIT), - CT_GET_BIT = 3, + CT_GET_BIT = 4, CT_GET = (1 << CT_GET_BIT), - CT_FLUSH_BIT = 4, + CT_FLUSH_BIT = 5, CT_FLUSH = (1 << CT_FLUSH_BIT), - CT_EVENT_BIT = 5, + CT_EVENT_BIT = 6, CT_EVENT = (1 << CT_EVENT_BIT), - CT_ACTION_BIT = 6, - CT_ACTION = (1 << CT_ACTION_BIT), - CT_VERSION_BIT = 7, CT_VERSION = (1 << CT_VERSION_BIT), CT_HELP_BIT = 8, CT_HELP = (1 << CT_HELP_BIT), + + EXP_LIST_BIT = 9, + EXP_LIST = (1 << EXP_LIST_BIT), + + EXP_CREATE_BIT = 10, + EXP_CREATE = (1 << EXP_CREATE_BIT), + + EXP_DELETE_BIT = 11, + EXP_DELETE = (1 << EXP_DELETE_BIT), + + EXP_GET_BIT = 12, + EXP_GET = (1 << EXP_GET_BIT), + + EXP_FLUSH_BIT = 13, + EXP_FLUSH = (1 << EXP_FLUSH_BIT), + + EXP_EVENT_BIT = 14, + EXP_EVENT = (1 << EXP_EVENT_BIT), }; -#define NUMBER_OF_CMD 9 +#define NUMBER_OF_CMD 15 + +static const char cmdflags[NUMBER_OF_CMD] += {'L','I','U','D','G','F','E','V','h','L','I','D','G','F','E'}; + +static const char cmd_need_param[NUMBER_OF_CMD] += {' ','x','x','x','x',' ',' ',' ',' ',' ','x','x','x',' ',' '}; enum options { CT_OPT_ORIG_SRC_BIT = 0, @@ -120,36 +145,34 @@ enum options { CT_OPT_ZERO_BIT = 7, CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT), - CT_OPT_DUMP_MASK_BIT = 8, - CT_OPT_DUMP_MASK = (1 << CT_OPT_DUMP_MASK_BIT), - - CT_OPT_GROUP_MASK_BIT = 9, - CT_OPT_GROUP_MASK = (1 << CT_OPT_GROUP_MASK_BIT), - - CT_OPT_EVENT_MASK_BIT = 10, + CT_OPT_EVENT_MASK_BIT = 8, CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT), - CT_OPT_TUPLE_SRC_BIT = 11, - CT_OPT_TUPLE_SRC = (1 << CT_OPT_TUPLE_SRC_BIT), + CT_OPT_EXP_SRC_BIT = 9, + CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT), - CT_OPT_TUPLE_DST_BIT = 12, - CT_OPT_TUPLE_DST = (1 << CT_OPT_TUPLE_DST_BIT), + CT_OPT_EXP_DST_BIT = 10, + CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT), - CT_OPT_MASK_SRC_BIT = 13, + CT_OPT_MASK_SRC_BIT = 11, CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT), - CT_OPT_MASK_DST_BIT = 14, + CT_OPT_MASK_DST_BIT = 12, CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT), + + CT_OPT_NATRANGE_BIT = 13, + CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT), }; -#define NUMBER_OF_OPT 15 +#define NUMBER_OF_OPT 14 static const char optflags[NUMBER_OF_OPT] -= { 's', 'd', 'r', 'q', 'p', 't', 'u', 'z','m','g','e', '[',']','{','}'}; += {'s','d','r','q','p','t','u','z','e','[',']','{','}','a'}; static struct option original_opts[] = { {"dump", 2, 0, 'L'}, {"create", 1, 0, 'I'}, {"delete", 1, 0, 'D'}, + {"update", 1, 0, 'U'}, {"get", 1, 0, 'G'}, {"flush", 1, 0, 'F'}, {"event", 1, 0, 'E'}, @@ -164,13 +187,12 @@ static struct option original_opts[] = { {"timeout", 1, 0, 't'}, {"status", 1, 0, 'u'}, {"zero", 0, 0, 'z'}, - {"dump-mask", 1, 0, 'm'}, - {"groups", 1, 0, 'g'}, {"event-mask", 1, 0, 'e'}, {"tuple-src", 1, 0, '['}, {"tuple-dst", 1, 0, ']'}, {"mask-src", 1, 0, '{'}, {"mask-dst", 1, 0, '}'}, + {"nat-range", 1, 0, 'a'}, {0, 0, 0, 0} }; @@ -188,22 +210,29 @@ static unsigned int global_option_offset = 0; * optional */ +/* FIXME: I'd need something different than this table to catch up some + * particular cases. Better later Pablo */ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /* Well, it's better than "Re: Linux vs FreeBSD" */ { - /* -s -d -r -q -p -t -u -z -m -g -e ts td ms md */ -/*LIST*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x','x'}, -/*CREATE*/ {'+','+','+','+','+','+','+','x','x','x','x','+','+','+','+'}, -/*DELETE*/ {' ',' ',' ',' ',' ','x','x','x','x','x','x',' ',' ',' ',' '}, -/*GET*/ {' ',' ',' ',' ','+','x','x','x','x','x','x',' ',' ',' ',' '}, -/*FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, -/*EVENT*/ {'x','x','x','x','x','x','x','x','x',' ','x','x','x','x','x'}, -/*ACTION*/ {'x','x','x','x','x','x','x','x',' ','x',' ','x','x','x','x'}, -/*VERSION*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, -/*HELP*/ {'x','x','x','x',' ','x','x','x','x','x','x','x','x','x','x'}, + /* -s -d -r -q -p -t -u -z -e -x -y -k -l -a */ +/*CT_LIST*/ {'x','x','x','x','x','x','x',' ','x','x','x','x','x','x'}, +/*CT_CREATE*/ {' ',' ',' ',' ','+','+','+','x','x','x','x','x','x',' '}, +/*CT_UPDATE*/ {' ',' ',' ',' ','+','+','+','x','x','x','x','x','x','x'}, +/*CT_DELETE*/ {' ',' ',' ',' ',' ','x','x','x','x','x','x','x','x','x'}, +/*CT_GET*/ {' ',' ',' ',' ','+','x','x','x','x','x','x','x','x','x'}, +/*CT_FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, +/*CT_EVENT*/ {'x','x','x','x','x','x','x','x',' ','x','x','x','x','x'}, +/*VERSION*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, +/*HELP*/ {'x','x','x','x',' ','x','x','x','x','x','x','x','x','x'}, +/*EXP_LIST*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, +/*EXP_CREATE*/{'+','+',' ',' ','+','+',' ','x','x','+','+','+','+','x'}, +/*EXP_DELETE*/{'+','+',' ',' ','+','x','x','x','x','x','x','x','x','x'}, +/*EXP_GET*/ {'+','+',' ',' ','+','x','x','x','x','x','x','x','x','x'}, +/*EXP_FLUSH*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, +/*EXP_EVENT*/ {'x','x','x','x','x','x','x','x','x','x','x','x','x','x'}, }; -/* FIXME: hardcoded!, this must be defined during compilation time */ char *lib_dir = CONNTRACK_LIB_DIR; LIST_HEAD(proto_list); @@ -258,6 +287,22 @@ exit_error(enum exittype status, char *msg, ...) exit(status); } +static void +generic_cmd_check(int command, int options) +{ + int i; + + for (i = 0; i < NUMBER_OF_CMD; i++) { + if (!(command & (1< %u.%u.%u.%u:%hu\n", - tp, tp->dst.protonum, - NIPQUAD(tp->src.ip), ntohs(tp->src.u.all), - NIPQUAD(tp->dst.ip), ntohs(tp->dst.u.all)); + tp, tp->protonum, + NIPQUAD(tp->src.v4), ntohs(tp->l4src.all), + NIPQUAD(tp->dst.v4), ntohs(tp->l4dst.all)); } -void not_implemented_yet() -{ - exit_error(OTHER_PROBLEM, "Sorry, not implemented yet :(\n"); -} - - #define PARSE_STATUS 0 -#define PARSE_GROUP 1 -#define PARSE_EVENT 2 -#define PARSE_DUMP 3 -#define PARSE_MAX PARSE_DUMP+1 +#define PARSE_EVENT 1 +#define PARSE_MAX 2 static struct parse_parameter { - char *parameter[10]; + char *parameter[5]; size_t size; - unsigned int value[10]; + unsigned int value[5]; } parse_array[PARSE_MAX] = { - { {"EXPECTED", "ASSURED", "SEEN_REPLY", "CONFIRMED", "SNAT", "DNAT", - "SEQ_ADJUST", "UNSET"}, - 8, - { IPS_EXPECTED, IPS_ASSURED, IPS_SEEN_REPLY, IPS_CONFIRMED, - IPS_SRC_NAT, IPS_DST_NAT, IPS_SEQ_ADJUST, 0} }, - { {"ALL", "TCP", "UDP", "ICMP"}, - 4, - {~0U, NFGRP_IPV4_CT_TCP, NFGRP_IPV4_CT_UDP, NFGRP_IPV4_CT_ICMP} }, - { {"ALL", "NEW", "RELATED", "DESTROY", "REFRESH", "STATUS", - "PROTOINFO", "HELPER", "HELPINFO", "NATINFO"}, - 10, - {~0U, IPCT_NEW, IPCT_RELATED, IPCT_DESTROY, IPCT_REFRESH, IPCT_STATUS, - IPCT_PROTOINFO, IPCT_HELPER, IPCT_HELPINFO, IPCT_NATINFO} }, - { {"ALL", "TUPLE", "STATUS", "TIMEOUT", "PROTOINFO", "HELPINFO", - "COUNTERS", "MARK"}, 8, - {~0U, DUMP_TUPLE, DUMP_STATUS, DUMP_TIMEOUT, DUMP_PROTOINFO, - DUMP_HELPINFO, DUMP_COUNTERS, DUMP_MARK} } + { {"ASSURED", "SEEN_REPLY", "UNSET", "SRC_NAT", "DST_NAT"}, 5, + { IPS_ASSURED, IPS_SEEN_REPLY, 0, + IPS_SRC_NAT_DONE, IPS_DST_NAT_DONE} }, + { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4, + {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE, + NF_NETLINK_CONNTRACK_DESTROY} }, }; static int @@ -425,6 +454,14 @@ parse_parameter(const char *arg, unsigned int *status, int parse_type) exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg); } +static void +add_command(int *cmd, const int newcmd, const int othercmds) +{ + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Invalid commands combination\n"); + *cmd |= newcmd; +} + unsigned int check_type(int argc, char *argv[]) { char *table = NULL; @@ -515,15 +552,90 @@ int iptables_insmod(const char *modname, const char *modprobe) return -1; } +/* Shamelessly stolen from libipt_DNAT ;). Ranges expected in network order. */ +static void +nat_parse(char *arg, int portok, struct ctnl_nat *range) +{ + char *colon, *dash, *error; + unsigned long ip; + + memset(range, 0, sizeof(range)); + colon = strchr(arg, ':'); + + if (colon) { + int port; + + if (!portok) + exit_error(PARAMETER_PROBLEM, + "Need TCP or UDP with port specification"); + + port = atoi(colon+1); + if (port == 0 || port > 65535) + exit_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", colon+1); + + error = strchr(colon+1, ':'); + if (error) + exit_error(PARAMETER_PROBLEM, + "Invalid port:port syntax - use dash\n"); + + dash = strchr(colon, '-'); + if (!dash) { + range->l4min.tcp.port + = range->l4max.tcp.port + = htons(port); + } else { + int maxport; + + maxport = atoi(dash + 1); + if (maxport == 0 || maxport > 65535) + exit_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", dash+1); + if (maxport < port) + // People are stupid. + exit_error(PARAMETER_PROBLEM, + "Port range `%s' funky\n", colon+1); + range->l4min.tcp.port = htons(port); + range->l4max.tcp.port = htons(maxport); + } + // Starts with a colon? No IP info... + if (colon == arg) + return; + *colon = '\0'; + } + + dash = strchr(arg, '-'); + if (colon && dash && dash > colon) + dash = NULL; + + if (dash) + *dash = '\0'; + + ip = inet_addr(arg); + if (!ip) + exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n", + arg); + range->min_ip = ip; + if (dash) { + ip = inet_addr(dash+1); + if (!ip) + exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n", + dash+1); + range->max_ip = ip; + } else + range->max_ip = range->min_ip; +} + void usage(char *prog) { fprintf(stdout, "Tool to manipulate conntrack and expectations. Version %s\n", VERSION); fprintf(stdout, "Usage: %s [commands] [options]\n", prog); fprintf(stdout, "\n"); fprintf(stdout, "Commands:\n"); fprintf(stdout, "-L [table] [-z] List conntrack or expectation table\n"); -fprintf(stdout, "-G [table] parameters Get conntrack or expectation\n"); +fprintf(stdout, "-G [table] parameters Get conntrack or expectation\n"); fprintf(stdout, "-D [table] parameters Delete conntrack or expectation\n"); fprintf(stdout, "-I [table] parameters Create a conntrack or expectation\n"); +fprintf(stdout, "-U [table] parameters Update a conntrack\n"); fprintf(stdout, "-E [table] [options] Show events\n"); fprintf(stdout, "-F [table] Flush table\n"); fprintf(stdout, "-A [table] [options] Set action\n"); @@ -535,108 +647,127 @@ fprintf(stdout, "--reply-src ip Source addres from reply direction\n"); fprintf(stdout, "--reply-dst ip Destination address from reply direction\n"); fprintf(stdout, "--tuple-src ip Source address in expect tuple\n"); fprintf(stdout, "--tuple-dst ip Destination address in expect tuple\n"); -fprintf(stdout, "--mask-src ip Source mask in expect\n"); -fprintf(stdout, "--mask-dst ip Destination mask in expect\n"); +fprintf(stdout, "--mask-src ip Source mask address for expectation\n"); +fprintf(stdout, "--mask-dst ip Destination mask address for expectations\n"); fprintf(stdout, "-p proto Layer 4 Protocol\n"); fprintf(stdout, "-t timeout Set timeout\n"); fprintf(stdout, "-u status Set status\n"); fprintf(stdout, "-m dumpmask Set dump mask\n"); fprintf(stdout, "-g groupmask Set group mask\n"); fprintf(stdout, "-e eventmask Set event mask\n"); +fprintf(stdout, "-a min_ip[-max_ip] NAT ip range\n"); fprintf(stdout, "-z Zero Counters\n"); } int main(int argc, char *argv[]) { - int c; + char c; unsigned int command = 0, options = 0; - struct ip_conntrack_tuple orig, reply, tuple, mask, *o = NULL, *r = NULL; + struct ctnl_tuple orig, reply, mask, *o = NULL, *r = NULL; + struct ctnl_tuple exptuple; struct ctproto_handler *h = NULL; - union ip_conntrack_proto proto; + union ctnl_protoinfo proto; + struct ctnl_nat range; unsigned long timeout = 0; - unsigned int status = 0, group_mask = 0; + unsigned int status = IPS_CONFIRMED; unsigned long id = 0; unsigned int type = 0, dump_mask = 0, extra_flags = 0, event_mask = 0; + int manip = -1; int res = 0, retry = 2; - memset(&proto, 0, sizeof(union ip_conntrack_proto)); - memset(&orig, 0, sizeof(struct ip_conntrack_tuple)); - memset(&reply, 0, sizeof(struct ip_conntrack_tuple)); - orig.dst.dir = IP_CT_DIR_ORIGINAL; - reply.dst.dir = IP_CT_DIR_REPLY; + memset(&proto, 0, sizeof(union ctnl_protoinfo)); + memset(&orig, 0, sizeof(struct ctnl_tuple)); + memset(&reply, 0, sizeof(struct ctnl_tuple)); + memset(&mask, 0, sizeof(struct ctnl_tuple)); + memset(&range, 0, sizeof(struct ctnl_nat)); while ((c = getopt_long(argc, argv, - "L::I::D::G::E::A::F::hVs:d:r:q:p:t:u:m:g:e:z[:]:{:}:", + "L::I::U::D::G::E::A::F::hVs:d:r:q:p:t:u:e:a:z[:]:{:}:", opts, NULL)) != -1) { switch(c) { case 'L': - command |= CT_LIST; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_LIST, CT_NONE); + else if (type == 1) + add_command(&command, EXP_LIST, CT_NONE); break; case 'I': - command |= CT_CREATE; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_CREATE, CT_NONE); + else if (type == 1) + add_command(&command, EXP_CREATE, CT_NONE); + break; + case 'U': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_UPDATE, CT_NONE); + else + exit_error(PARAMETER_PROBLEM, "Can't update " + "expectations"); break; case 'D': - command |= CT_DELETE; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_DELETE, CT_NONE); + else if (type == 1) + add_command(&command, EXP_DELETE, CT_NONE); break; case 'G': - command |= CT_GET; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_GET, CT_NONE); + else if (type == 1) + add_command(&command, EXP_GET, CT_NONE); break; case 'F': - command |= CT_FLUSH; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_FLUSH, CT_NONE); + else if (type == 1) + add_command(&command, EXP_FLUSH, CT_NONE); break; case 'E': - command |= CT_EVENT; - type = check_type(argc, argv); - break; - case 'A': - command |= CT_ACTION; type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_EVENT, CT_NONE); + else if (type == 1) + add_command(&command, EXP_EVENT, CT_NONE); break; case 'V': - command |= CT_VERSION; + add_command(&command, CT_VERSION, CT_NONE); break; case 'h': - command |= CT_HELP; - break; - case 'm': - if (!optarg) - continue; - - options |= CT_OPT_DUMP_MASK; - parse_parameter(optarg, &dump_mask, PARSE_DUMP); + add_command(&command, CT_HELP, CT_NONE); break; case 's': options |= CT_OPT_ORIG_SRC; if (optarg) - orig.src.ip = inet_addr(optarg); + orig.src.v4 = inet_addr(optarg); break; case 'd': options |= CT_OPT_ORIG_DST; if (optarg) - orig.dst.ip = inet_addr(optarg); + orig.dst.v4 = inet_addr(optarg); break; case 'r': options |= CT_OPT_REPL_SRC; if (optarg) - reply.src.ip = inet_addr(optarg); + reply.src.v4 = inet_addr(optarg); break; case 'q': options |= CT_OPT_REPL_DST; if (optarg) - reply.dst.ip = inet_addr(optarg); + reply.dst.v4 = inet_addr(optarg); break; case 'p': options |= CT_OPT_PROTO; h = findproto(optarg); if (!h) exit_error(PARAMETER_PROBLEM, "proto needed\n"); - orig.dst.protonum = h->protonum; - reply.dst.protonum = h->protonum; + orig.protonum = h->protonum; + reply.protonum = h->protonum; opts = merge_options(opts, h->opts, &h->option_offset); break; @@ -646,20 +777,13 @@ int main(int argc, char *argv[]) timeout = atol(optarg); break; case 'u': { - /* FIXME: NAT stuff, later... */ if (!optarg) continue; options |= CT_OPT_STATUS; parse_parameter(optarg, &status, PARSE_STATUS); - /* Just insert confirmed conntracks */ - status |= IPS_CONFIRMED; break; } - case 'g': - options |= CT_OPT_GROUP_MASK; - parse_parameter(optarg, &group_mask, PARSE_GROUP); - break; case 'e': options |= CT_OPT_EVENT_MASK; parse_parameter(optarg, &event_mask, PARSE_EVENT); @@ -667,30 +791,35 @@ int main(int argc, char *argv[]) case 'z': options |= CT_OPT_ZERO; break; - case '[': - options |= CT_OPT_TUPLE_SRC; + case 'k': + options |= CT_OPT_MASK_SRC; if (optarg) - tuple.src.ip = inet_addr(optarg); + mask.src.v4 = inet_addr(optarg); break; - case ']': - options |= CT_OPT_TUPLE_DST; + case 'l': + options |= CT_OPT_MASK_DST; if (optarg) - tuple.dst.ip = inet_addr(optarg); + mask.dst.v4 = inet_addr(optarg); break; - case '{': - options |= CT_OPT_MASK_SRC; + case 'x': + options |= CT_OPT_EXP_SRC; if (optarg) - mask.src.ip = inet_addr(optarg); + exptuple.src.v4 = inet_addr(optarg); break; - case '}': - options |= CT_OPT_MASK_DST; + case 'y': + options |= CT_OPT_EXP_DST; if (optarg) - mask.dst.ip = inet_addr(optarg); + exptuple.dst.v4 = inet_addr(optarg); + break; + case 'a': + options |= CT_OPT_NATRANGE; + nat_parse(optarg, 1, &range); break; default: - if (h && h->parse && !h->parse(c - h->option_offset, - argv, &orig, &reply, - &proto, &extra_flags)) + if (h && h->parse_opts + &&!h->parse_opts(c - h->option_offset, argv, &orig, + &reply, &mask, &proto, + &extra_flags)) exit_error(PARAMETER_PROBLEM, "parse error\n"); /* Unknown argument... */ @@ -703,10 +832,12 @@ int main(int argc, char *argv[]) } } + generic_cmd_check(command, options); generic_opt_check(command, options); if (!(command & CT_HELP) - && h && h->final_check && !h->final_check(extra_flags)) { + && h && h->final_check + && !h->final_check(extra_flags, &orig, &reply)) { usage(argv[0]); extension_help(h); exit_error(PARAMETER_PROBLEM, "Missing protocol arguments!\n"); @@ -716,71 +847,115 @@ int main(int argc, char *argv[]) retry--; switch(command) { case CT_LIST: - if (type == 0) { - if (options & CT_OPT_ZERO) - res = dump_conntrack_table(1); - else - res = dump_conntrack_table(0); - } else - res = dump_expect_list(); + if (options & CT_OPT_ZERO) + res = dump_conntrack_table(1); + else + res = dump_conntrack_table(0); + break; + + case EXP_LIST: + res = dump_expect_list(); break; case CT_CREATE: - if (type == 0) + if ((options & CT_OPT_ORIG) + && !(options & CT_OPT_REPL)) { + reply.src.v4 = orig.dst.v4; + reply.dst.v4 = orig.src.v4; + } else if (!(options & CT_OPT_ORIG) + && (options & CT_OPT_REPL)) { + orig.src.v4 = reply.dst.v4; + orig.dst.v4 = reply.src.v4; + } + if (options & CT_OPT_NATRANGE) res = create_conntrack(&orig, &reply, timeout, - &proto, status); + &proto, status, &range); else - res = create_expect(&tuple, &mask, &orig, - &reply, timeout); + res = create_conntrack(&orig, &reply, timeout, + &proto, status, NULL); + break; + + case EXP_CREATE: + if (options & CT_OPT_ORIG) + res = create_expectation(&orig, + CTA_TUPLE_ORIG, + &exptuple, + &mask, + timeout); + else if (options & CT_OPT_REPL) + res = create_expectation(&reply, + CTA_TUPLE_RPLY, + &exptuple, + &mask, + timeout); + break; + + case CT_UPDATE: + if ((options & CT_OPT_ORIG) + && !(options & CT_OPT_REPL)) { + reply.src.v4 = orig.dst.v4; + reply.dst.v4 = orig.src.v4; + } else if (!(options & CT_OPT_ORIG) + && (options & CT_OPT_REPL)) { + orig.src.v4 = reply.dst.v4; + orig.dst.v4 = reply.src.v4; + } + res = update_conntrack(&orig, &reply, timeout, + &proto, status); break; case CT_DELETE: - if (type == 0) { - if (options & CT_OPT_ORIG) - res =delete_conntrack(&orig, CTA_ORIG, - id); - else if (options & CT_OPT_REPL) - res = delete_conntrack(&reply, CTA_RPLY, - id); - } else - not_implemented_yet(); + if (options & CT_OPT_ORIG) + res = delete_conntrack(&orig, CTA_TUPLE_ORIG, + CTNL_DIR_ORIGINAL); + else if (options & CT_OPT_REPL) + res = delete_conntrack(&reply, CTA_TUPLE_RPLY, + CTNL_DIR_REPLY); break; - + + case EXP_DELETE: + if (options & CT_OPT_ORIG) + res = delete_expectation(&orig, CTA_TUPLE_ORIG); + else if (options & CT_OPT_REPL) + res = delete_expectation(&reply, CTA_TUPLE_RPLY); + break; + case CT_GET: - if (type == 0) { - if (options & CT_OPT_ORIG) - res = get_conntrack(&orig, CTA_ORIG, - id); - else if (options & CT_OPT_REPL) - res = get_conntrack(&reply, CTA_RPLY, - id); - } else - not_implemented_yet(); + if (options & CT_OPT_ORIG) + res = get_conntrack(&orig, CTA_TUPLE_ORIG, id); + else if (options & CT_OPT_REPL) + res = get_conntrack(&reply, CTA_TUPLE_RPLY, id); break; - + + case EXP_GET: + if (options & CT_OPT_ORIG) + res = get_expect(&orig, CTA_TUPLE_ORIG); + else if (options & CT_OPT_REPL) + res = get_expect(&reply, CTA_TUPLE_RPLY); + break; + case CT_FLUSH: - if (type == 0) - res = flush_conntrack(); - else - not_implemented_yet(); + res = flush_conntrack(); + break; + + case EXP_FLUSH: + res = flush_expectation(); break; case CT_EVENT: - if (type == 0) { - if (options & CT_OPT_GROUP_MASK) - res = event_conntrack(group_mask); - else - res = event_conntrack(~0U); - } else - not_implemented_yet(); - - case CT_ACTION: - if (type == 0) - if (options & CT_OPT_DUMP_MASK) - res = set_mask(dump_mask, 0); - else if (options & CT_OPT_EVENT_MASK) - res = set_mask(event_mask, 1); + if (options & CT_OPT_EVENT_MASK) + res = event_conntrack(event_mask); + else + res = event_conntrack(~0U); break; + + case EXP_EVENT: + if (options & CT_OPT_EVENT_MASK) + res = event_expectation(event_mask); + else + res = event_expectation(~0U); + break; + case CT_VERSION: fprintf(stdout, "%s v%s\n", PROGNAME, VERSION); break; diff --git a/src/libct.c b/src/libct.c index 0555ec8..94e5b24 100644 --- a/src/libct.c +++ b/src/libct.c @@ -1,5 +1,5 @@ /* - * (C) 2005 by Pablo Neira Ayuso , + * (C) 2005 by Pablo Neira Ayuso * Harald Welte * * This program is free software; you can redistribute it and/or modify @@ -11,9 +11,13 @@ #include #include #include +#include #include -#include +/* From kernel.h */ +#define INT_MAX ((int)(~0U>>1)) +#define INT_MIN (-INT_MAX - 1) #include +#include #include "libctnetlink.h" #include "libnfnetlink.h" #include "linux_list.h" @@ -25,6 +29,7 @@ #define DEBUGP #endif +static struct ctnl_handle cth; extern char *lib_dir; extern struct list_head proto_list; extern char *proto2str[]; @@ -37,89 +42,192 @@ static void print_status(unsigned int status) fprintf(stdout, "[UNREPLIED] "); } +static void parse_ip(struct nfattr *attr, struct ctnl_tuple *tuple) +{ + struct nfattr *tb[CTA_IP_MAX]; + + memset(tb, 0, CTA_IP_MAX * sizeof(struct nfattr *)); + + nfnl_parse_attr(tb, CTA_IP_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr)); + if (tb[CTA_IP_V4_SRC-1]) + tuple->src.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); + + if (tb[CTA_IP_V4_DST-1]) + tuple->dst.v4 = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]); +} + +static void parse_proto(struct nfattr *attr, struct ctnl_tuple *tuple) +{ + struct nfattr *tb[CTA_PROTO_MAX]; + struct ctproto_handler *h; + int dir = CTNL_DIR_REPLY; + + memset(tb, 0, CTA_PROTO_MAX * sizeof(struct nfattr *)); + + nfnl_parse_attr(tb, CTA_IP_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr)); + if (tb[CTA_PROTO_NUM-1]) + tuple->protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); + + h = findproto(proto2str[tuple->protonum]); + if (h && h->parse_proto) + h->parse_proto(tb, tuple); +} + +static void parse_tuple(struct nfattr *attr, struct ctnl_tuple *tuple) +{ + struct nfattr *tb[CTA_TUPLE_MAX]; + + memset(tb, 0, CTA_TUPLE_MAX*sizeof(struct nfattr *)); + + nfnl_parse_attr(tb, CTA_TUPLE_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr)); + if (tb[CTA_TUPLE_IP-1]) + parse_ip(tb[CTA_TUPLE_IP-1], tuple); + if (tb[CTA_TUPLE_PROTO-1]) + parse_proto(tb[CTA_TUPLE_PROTO-1], tuple); +} + +static void parse_protoinfo(struct nfattr *attr, struct ctnl_conntrack *ct) +{ + struct nfattr *tb[CTA_PROTOINFO_MAX]; + struct ctproto_handler *h; + + memset(tb, 0, CTA_PROTOINFO_MAX*sizeof(struct nfattr *)); + + nfnl_parse_attr(tb,CTA_PROTOINFO_MAX,NFA_DATA(attr), NFA_PAYLOAD(attr)); + + h = findproto(proto2str[ct->tuple[CTNL_DIR_ORIGINAL].protonum]); + if (h && h->parse_protoinfo) + h->parse_protoinfo(tb, ct); +} + +static void parse_counters(struct nfattr *attr, struct ctnl_conntrack *ct, + enum ctattr_type parent) +{ + struct nfattr *tb[CTA_COUNTERS_MAX]; + + memset(tb, 0, CTA_COUNTERS_MAX*sizeof(struct nfattr *)); + + nfnl_parse_attr(tb, CTA_COUNTERS_MAX, NFA_DATA(attr),NFA_PAYLOAD(attr)); + if (tb[CTA_COUNTERS_PACKETS_ORIG-1]) + ct->counters[CTNL_DIR_ORIGINAL].packets + = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_PACKETS_ORIG-1]); + if (tb[CTA_COUNTERS_BYTES_ORIG-1]) + ct->counters[CTNL_DIR_ORIGINAL].bytes + = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_BYTES_ORIG-1]); + if (tb[CTA_COUNTERS_PACKETS_RPLY-1]) + ct->counters[CTNL_DIR_REPLY].packets + = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_PACKETS_RPLY-1]); + if (tb[CTA_COUNTERS_BYTES_RPLY-1]) + ct->counters[CTNL_DIR_REPLY].bytes + = *(u_int64_t *)NFA_DATA(tb[CTA_COUNTERS_BYTES_RPLY-1]); +} + +#define STATUS 1 +#define PROTOINFO 2 +#define TIMEOUT 4 +#define MARK 8 +#define COUNTERS 16 +#define USE 32 + static int handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg) { struct nfgenmsg *nfmsg; struct nfattr *nfa; - int min_len = 0; + int min_len = sizeof(struct nfgenmsg);; struct ctproto_handler *h = NULL; struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); + struct ctnl_conntrack ct; + unsigned int flags = 0; - struct ip_conntrack_tuple *orig, *reply; - struct cta_counters *ctr; - unsigned long *status, *timeout; - struct cta_proto *proto; - unsigned long *id, *mark; - - DEBUGP("netlink header\n"); - DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n", - nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags, - nlh->nlmsg_seq, nlh->nlmsg_pid); + memset(&ct, 0, sizeof(struct ctnl_conntrack)); nfmsg = NLMSG_DATA(nlh); - DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family); +// min_len = sizeof(struct nfgenmsg); - min_len = sizeof(struct nfgenmsg); if (nlh->nlmsg_len < min_len) return -EINVAL; - DEBUGP("size:%d\n", nlh->nlmsg_len); - while (NFA_OK(attr, attrlen)) { switch(attr->nfa_type) { - case CTA_ORIG: - orig = NFA_DATA(attr); - fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(orig->src.ip), - NIPQUAD(orig->dst.ip)); - h = findproto(proto2str[orig->dst.protonum]); - if (h && h->print_tuple) - h->print_tuple(orig); + case CTA_TUPLE_ORIG: + parse_tuple(attr, &ct.tuple[CTNL_DIR_ORIGINAL]); break; - case CTA_RPLY: - reply = NFA_DATA(attr); - fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(reply->src.ip), - NIPQUAD(reply->dst.ip)); - h = findproto(proto2str[reply->dst.protonum]); - if (h && h->print_tuple) - h->print_tuple(reply); + case CTA_TUPLE_RPLY: + parse_tuple(attr, &ct.tuple[CTNL_DIR_REPLY]); break; case CTA_STATUS: - status = NFA_DATA(attr); - print_status(*status); + ct.status = *(unsigned int *)NFA_DATA(attr); + flags |= STATUS; break; case CTA_PROTOINFO: - proto = NFA_DATA(attr); - if (proto2str[proto->num_proto]) { - fprintf(stdout, "%s %d ", proto2str[proto->num_proto], proto->num_proto); - h = findproto(proto2str[proto->num_proto]); - if (h && h->print_proto) - h->print_proto(&proto->proto); - } else - fprintf(stdout, "unknown %d ", proto->num_proto); + parse_protoinfo(attr, &ct); + flags |= PROTOINFO; break; case CTA_TIMEOUT: - timeout = NFA_DATA(attr); - fprintf(stdout, "timeout=%lu ", *timeout); + ct.timeout = *(unsigned long *)NFA_DATA(attr); + flags |= TIMEOUT; break; case CTA_MARK: - mark = NFA_DATA(attr); - fprintf(stdout, "mark=%lu ", *mark); + ct.mark = *(unsigned long *)NFA_DATA(attr); + flags |= MARK; break; case CTA_COUNTERS: - ctr = NFA_DATA(attr); - fprintf(stdout, "orig_packets=%llu orig_bytes=%llu, " - "reply_packets=%llu reply_bytes=%llu ", - ctr->orig.packets, ctr->orig.bytes, - ctr->reply.packets, ctr->reply.bytes); + parse_counters(attr, &ct, CTA_COUNTERS-1); + flags |= COUNTERS; + break; + case CTA_USE: + ct.use = *(unsigned int *)NFA_DATA(attr); + flags |= USE; break; } - DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type); - DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len); attr = NFA_NEXT(attr, attrlen); } + + fprintf(stdout, "%-8s %u ", + proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum] == NULL ? + "unknown" : proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum], + ct.tuple[CTNL_DIR_ORIGINAL].protonum); + + if (flags & TIMEOUT) + fprintf(stdout, "%lu ", ct.timeout); + + h = findproto(proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum]); + if ((flags & PROTOINFO) && h && h->print_protoinfo) + h->print_protoinfo(&ct.protoinfo); + + fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", + NIPQUAD(ct.tuple[CTNL_DIR_ORIGINAL].src.v4), + NIPQUAD(ct.tuple[CTNL_DIR_ORIGINAL].dst.v4)); + + if (h && h->print_proto) + h->print_proto(&ct.tuple[CTNL_DIR_ORIGINAL]); + + if (flags & COUNTERS) + fprintf(stdout, "packets=%llu bytes=%llu ", + ct.counters[CTNL_DIR_ORIGINAL].packets, + ct.counters[CTNL_DIR_ORIGINAL].bytes); + + fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", + NIPQUAD(ct.tuple[CTNL_DIR_REPLY].src.v4), + NIPQUAD(ct.tuple[CTNL_DIR_REPLY].dst.v4)); + + h = findproto(proto2str[ct.tuple[CTNL_DIR_ORIGINAL].protonum]); + if (h && h->print_proto) + h->print_proto(&ct.tuple[CTNL_DIR_REPLY]); + + if (flags & COUNTERS) + fprintf(stdout, "packets=%llu bytes=%llu ", + ct.counters[CTNL_DIR_REPLY].packets, + ct.counters[CTNL_DIR_REPLY].bytes); + + print_status(ct.status); + + if (flags & MARK) + fprintf(stdout, "mark=%lu ", ct.mark); + if (flags & USE) + fprintf(stdout, "use=%u ", ct.use); + fprintf(stdout, "\n"); return 0; @@ -148,140 +256,133 @@ static int event_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, return handler(sock, nlh, arg); } +void parse_expect(struct nfattr *attr, struct ctnl_tuple *tuple, + struct ctnl_tuple *mask, unsigned long *timeout) +{ + struct nfattr *tb[CTA_EXPECT_MAX]; + + memset(tb, 0, CTA_EXPECT_MAX*sizeof(struct nfattr *)); + + nfnl_parse_attr(tb, CTA_EXPECT_MAX, NFA_DATA(attr), NFA_PAYLOAD(attr)); + if (tb[CTA_EXPECT_TUPLE-1]) + parse_tuple(tb[CTA_EXPECT_TUPLE-1], tuple); + if (tb[CTA_EXPECT_MASK-1]) + parse_tuple(tb[CTA_EXPECT_MASK-1], mask); + if (tb[CTA_EXPECT_TIMEOUT-1]) + *timeout = *(unsigned long *)NFA_DATA(tb[CTA_EXPECT_TIMEOUT-1]); +} + static int expect_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg) { struct nfgenmsg *nfmsg; struct nfattr *nfa; - int min_len = 0; + int min_len = sizeof(struct nfgenmsg);; struct ctproto_handler *h = NULL; struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh)); int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len); + struct ctnl_tuple tuple, mask; + unsigned long timeout = 0; - struct ip_conntrack_tuple *exp, *mask; - unsigned long *timeout; - - DEBUGP("netlink header\n"); - DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n", - nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags, - nlh->nlmsg_seq, nlh->nlmsg_pid); + memset(&tuple, 0, sizeof(struct ctnl_tuple)); + memset(&mask, 0, sizeof(struct ctnl_tuple)); nfmsg = NLMSG_DATA(nlh); - DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family); - min_len = sizeof(struct nfgenmsg); if (nlh->nlmsg_len < min_len) return -EINVAL; - DEBUGP("size:%d\n", nlh->nlmsg_len); - while (NFA_OK(attr, attrlen)) { switch(attr->nfa_type) { - case CTA_EXP_TUPLE: - exp = NFA_DATA(attr); - fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(exp->src.ip), - NIPQUAD(exp->dst.ip)); - h = findproto(proto2str[exp->dst.protonum]); - if (h && h->print_tuple) - h->print_tuple(exp); - break; - case CTA_EXP_MASK: - mask = NFA_DATA(attr); - fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", - NIPQUAD(mask->src.ip), - NIPQUAD(mask->dst.ip)); - h = findproto(proto2str[mask->dst.protonum]); - if (h && h->print_tuple) - h->print_tuple(mask); - break; - case CTA_EXP_TIMEOUT: - timeout = NFA_DATA(attr); - fprintf(stdout, "timeout:%lu ", *timeout); - break; + case CTA_EXPECT: + parse_expect(attr, &tuple, &mask, &timeout); + break; } - DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type); - DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len); attr = NFA_NEXT(attr, attrlen); } - fprintf(stdout, "\n"); + fprintf(stdout, "%ld proto=%d ", timeout, tuple.protonum); + + fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", + NIPQUAD(tuple.src.v4), + NIPQUAD(tuple.dst.v4)); + + fprintf(stdout, "src=%u.%u.%u.%u dst=%u.%u.%u.%u\n", + NIPQUAD(mask.src.v4), + NIPQUAD(mask.dst.v4)); return 0; } -int create_conntrack(struct ip_conntrack_tuple *orig, - struct ip_conntrack_tuple *reply, +int create_conntrack(struct ctnl_tuple *orig, + struct ctnl_tuple *reply, unsigned long timeout, - union ip_conntrack_proto *proto, - unsigned int status) + union ctnl_protoinfo *proto, + unsigned int status, + struct ctnl_nat *range) { - struct cta_proto cta; - struct nfattr *cda[CTA_MAX]; - struct ctnl_handle cth; + struct ctnl_conntrack ct; int ret; + + memset(&ct, 0, sizeof(struct ctnl_conntrack)); + ct.tuple[CTNL_DIR_ORIGINAL] = *orig; + ct.tuple[CTNL_DIR_REPLY] = *reply; + ct.timeout = timeout; + ct.status = status; + ct.protoinfo = *proto; + if (range) + ct.nat = *range; - cta.num_proto = orig->dst.protonum; - memcpy(&cta.proto, proto, sizeof(*proto)); if ((ret = ctnl_open(&cth, 0)) < 0) return ret; - if ((ret = ctnl_new_conntrack(&cth, orig, reply, timeout, &cta, - status)) < 0) - return ret; + ret = ctnl_new_conntrack(&cth, &ct); - if ((ret = ctnl_close(&cth)) < 0) - return ret; + ctnl_close(&cth); - return 0; + return ret; } -int create_expect(struct ip_conntrack_tuple *tuple, - struct ip_conntrack_tuple *mask, - struct ip_conntrack_tuple *master_tuple_orig, - struct ip_conntrack_tuple *master_tuple_reply, - unsigned long timeout) +int update_conntrack(struct ctnl_tuple *orig, + struct ctnl_tuple *reply, + unsigned long timeout, + union ctnl_protoinfo *proto, + unsigned int status) { - struct ctnl_handle cth; + struct ctnl_conntrack ct; int ret; + memset(&ct, 0, sizeof(struct ctnl_conntrack)); + ct.tuple[CTNL_DIR_ORIGINAL] = *orig; + ct.tuple[CTNL_DIR_REPLY] = *reply; + ct.timeout = timeout; + ct.status = status; + ct.protoinfo = *proto; + if ((ret = ctnl_open(&cth, 0)) < 0) return ret; - if ((ret = ctnl_new_expect(&cth, tuple, mask, master_tuple_orig, - master_tuple_reply, timeout)) < 0) - return ret; - - if ((ret = ctnl_close(&cth)) < 0) - return ret; + ret = ctnl_upd_conntrack(&cth, &ct); - return -1; + ctnl_close(&cth); + + return ret; } -int delete_conntrack(struct ip_conntrack_tuple *tuple, - enum ctattr_type_t t) +int delete_conntrack(struct ctnl_tuple *tuple, int dir) { - struct nfattr *cda[CTA_MAX]; - struct ctnl_handle cth; int ret; if ((ret = ctnl_open(&cth, 0)) < 0) return ret; - if ((ret = ctnl_del_conntrack(&cth, tuple, t)) < 0) - return ret; - - if ((ret = ctnl_close(&cth)) < 0) - return ret; + ret = ctnl_del_conntrack(&cth, tuple, dir); + ctnl_close(&cth); - return 0; + return ret; } /* get_conntrack_handler */ -int get_conntrack(struct ip_conntrack_tuple *tuple, - enum ctattr_type_t t, - unsigned long id) +int get_conntrack(struct ctnl_tuple *tuple, int dir) { - struct nfattr *cda[CTA_MAX]; - struct ctnl_handle cth; struct ctnl_msg_handler h = { .type = 0, .handler = handler @@ -293,22 +394,17 @@ int get_conntrack(struct ip_conntrack_tuple *tuple, ctnl_register_handler(&cth, &h); - /* FIXME!!!! get_conntrack_handler returns -100 */ - if ((ret = ctnl_get_conntrack(&cth, tuple, t)) != -100) - return ret; - - if ((ret = ctnl_close(&cth)) < 0) - return ret; + ret = ctnl_get_conntrack(&cth, tuple, dir); + ctnl_close(&cth); - return 0; + return ret; } int dump_conntrack_table(int zero) { int ret; - struct ctnl_handle cth; struct ctnl_msg_handler h = { - .type = 0, /* Hm... really? */ + .type = IPCTNL_MSG_CT_NEW, /* Hm... really? */ .handler = handler }; @@ -322,38 +418,37 @@ int dump_conntrack_table(int zero) } else ret = ctnl_list_conntrack(&cth, AF_INET); - if (ret != -100) - return ret; + ctnl_close(&cth); - if ((ret = ctnl_close(&cth)) < 0) - return ret; + return ret; +} - return 0; +static void event_sighandler(int s) +{ + fprintf(stdout, "Now closing conntrack event dumping...\n"); + ctnl_close(&cth); } int event_conntrack(unsigned int event_mask) { - struct ctnl_handle cth; struct ctnl_msg_handler hnew = { - .type = 0, /* new */ + .type = IPCTNL_MSG_CT_NEW, .handler = event_handler }; struct ctnl_msg_handler hdestroy = { - .type = 2, /* destroy */ + .type = IPCTNL_MSG_CT_DELETE, .handler = event_handler }; int ret; - + if ((ret = ctnl_open(&cth, event_mask)) < 0) return ret; + signal(SIGINT, event_sighandler); ctnl_register_handler(&cth, &hnew); ctnl_register_handler(&cth, &hdestroy); - if ((ret = ctnl_event_conntrack(&cth, AF_INET)) < 0) - return ret; - - if ((ret = ctnl_close(&cth)) < 0) - return ret; + ret = ctnl_event_conntrack(&cth, AF_INET); + ctnl_close(&cth); return 0; } @@ -404,9 +499,8 @@ void unregister_proto(struct ctproto_handler *h) int dump_expect_list() { - struct ctnl_handle cth; struct ctnl_msg_handler h = { - .type = 5, /* Hm... really? */ + .type = IPCTNL_MSG_EXP_NEW, .handler = expect_handler }; int ret; @@ -416,58 +510,114 @@ int dump_expect_list() ctnl_register_handler(&cth, &h); - if ((ret = ctnl_list_expect(&cth, AF_INET)) != -100) - return ret; + ret = ctnl_list_expect(&cth, AF_INET); + ctnl_close(&cth); + + return ret; +} - if ((ret = ctnl_close(&cth)) < 0) +int flush_conntrack() +{ + int ret; + + if ((ret = ctnl_open(&cth, 0)) < 0) return ret; - return 0; + ret = ctnl_flush_conntrack(&cth); + ctnl_close(&cth); + + return ret; } -int set_mask(unsigned int mask, int type) +int get_expect(struct ctnl_tuple *tuple, + enum ctattr_type t) { - struct ctnl_handle cth; - enum ctattr_type_t cta_type; + /* + struct ctnl_msg_handler h = { + .type = IPCTNL_MSG_EXP_NEW, + .handler = expect_handler + }; int ret; - switch(type) { - case 0: - cta_type = CTA_DUMPMASK; - break; - case 1: - cta_type = CTA_EVENTMASK; - break; - default: - /* Shouldn't happen */ - return -1; - } + if ((ret = ctnl_open(&cth, 0)) < 0) + return 0; + + ctnl_register_handler(&cth, &h); + + ret = ctnl_get_expect(&cth, tuple, t); + ctnl_close(&cth); + + return ret; + */ +} +int create_expectation(struct ctnl_tuple *tuple, + enum ctattr_type t, + struct ctnl_tuple *exptuple, + struct ctnl_tuple *mask, + unsigned long timeout) +{ + /* + int ret; + if ((ret = ctnl_open(&cth, 0)) < 0) return ret; + + ret = ctnl_new_expect(&cth, tuple, t, exptuple, mask, timeout); + ctnl_close(&cth); + + return ret; + */ +} + +int delete_expectation(struct ctnl_tuple *tuple, enum ctattr_type t) +{ + /* + int ret; - if ((ret = ctnl_set_mask(&cth, mask, cta_type)) < 0) + if ((ret = ctnl_open(&cth, 0)) < 0) return ret; + + ret = ctnl_del_expect(&cth, tuple, t); + ctnl_close(&cth); + + return ret; + */ +} + +int event_expectation(unsigned int event_mask) +{ + struct ctnl_msg_handler hnew = { + .type = IPCTNL_MSG_EXP_NEW, + .handler = expect_handler + }; + struct ctnl_msg_handler hdestroy = { + .type = IPCTNL_MSG_EXP_DELETE, + .handler = expect_handler + }; + int ret; - if ((ret = ctnl_close(&cth)) < 0) + if ((ret = ctnl_open(&cth, event_mask)) < 0) return ret; - return 0; + ctnl_register_handler(&cth, &hnew); + ctnl_register_handler(&cth, &hdestroy); + ret = ctnl_event_expect(&cth, AF_INET); + ctnl_close(&cth); + + return ret; } -int flush_conntrack() +int flush_expectation() { - struct ctnl_handle cth; int ret; if ((ret = ctnl_open(&cth, 0)) < 0) return ret; - if ((ret = ctnl_flush_conntrack(&cth)) < 0) - return ret; + ret = ctnl_flush_expect(&cth); + ctnl_close(&cth); - if ((ret = ctnl_close(&cth)) < 0) - return ret; - - return 0; + return ret; } + diff --git a/test.sh b/test.sh index 5999a8f..08c840f 100644 --- a/test.sh +++ b/test.sh @@ -2,27 +2,17 @@ CONNTRACK=conntrack SRC=1.1.1.1 DST=2.2.2.2 -SPORT=1980 -DPORT=2005 +SPORT=2005 +DPORT=21 case $1 in dump) - # Setting dump mask - echo "dump mask set to TUPLE" - $CONNTRACK -A -m TUPLE + echo "Dumping conntrack table" $CONNTRACK -L - echo "Press any key to continue..." - read - echo "dump mask set to TUPLE,COUNTERS" - $CONNTRACK -A -m TUPLE,COUNTERS - $CONNTRACK -L - echo "Press any key to continue..." - read - echo "dump mask set to ALL" - $CONNTRACK -A -m ALL - $CONNTRACK -L - echo "Press any key to continue..." - read + ;; + flush) + echo "Flushing conntrack table" + $CONNTRACK -F ;; new) echo "creating a new conntrack" @@ -32,6 +22,18 @@ case $1 in --reply-port-src $DPORT --reply-port-dst $SPORT \ --state LISTEN -u SEEN_REPLY -t 50 ;; + new-simple) + echo "creating a new conntrack (simplified)" + $CONNTRACK -I --orig-src $SRC --orig-dst $DST \ + -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \ + --state LISTEN -u SEEN_REPLY -t 50 + ;; + new-nat) + echo "creating a new conntrack (NAT)" + $CONNTRACK -I --orig-src $SRC --orig-dst $DST \ + -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \ + --state LISTEN -u SEEN_REPLY,SRC_NAT -t 50 -a 8.8.8.8 + ;; get) echo "getting a conntrack" $CONNTRACK -G --orig-src $SRC --orig-dst $DST \ @@ -40,7 +42,7 @@ case $1 in ;; change) echo "change a conntrack" - $CONNTRACK -I --orig-src $SRC --orig-dst $DST \ + $CONNTRACK -U --orig-src $SRC --orig-dst $DST \ --reply-src $DST --reply-dst $SRC -p tcp \ --orig-port-src $SPORT --orig-port-dst $DPORT \ --reply-port-src $DPORT --reply-port-dst $SPORT \ @@ -64,7 +66,44 @@ case $1 in fi fi ;; + dump-expect) + $CONNTRACK -L expect + ;; + flush-expect) + $CONNTRACK -F expect + ;; + create-expect) + # requires modprobe ip_conntrack_ftp + $CONNTRACK -I expect --orig-src $SRC --orig-dst $DST \ + --exp-src 4.4.4.4 --exp-dst 5.5.5.5 \ + --mask-src 255.255.255.0 --mask-dst 255.255.255.255 \ + -p tcp --orig-port-src $SPORT --orig-port-dst $DPORT \ + -t 200 --mask-port-src 10 --mask-port-dst 300 + ;; + get-expect) + $CONNTRACK -G expect --orig-src 4.4.4.4 --orig-dst 5.5.5.5 \ + --p tcp --orig-port-src 0 --orig-port-dst 0 \ + --mask-port-src 10 --mask-port-dst 11 + ;; + delete-expect) + $CONNTRACK -D expect --orig-src 4.4.4.4 \ + --orig-dst 5.5.5.5 -p tcp --orig-port-src 0 \ + --orig-port-dst 0 --mask-port-src 10 --mask-port-dst 11 + ;; *) - echo "Usage: $0 [dump|new|change|delete|output]" + echo "Usage: $0 [dump" + echo " |new" + echo " |new-simple" + echo " |new-nat" + echo " |get" + echo " |change" + echo " |delete" + echo " |output" + echo " |flush" + echo " |dump-expect" + echo " |flush-expect" + echo " |create-expect" + echo " |get-expect" + echo " |delete-expect]" ;; esac -- cgit v1.2.3