diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 39 | ||||
-rw-r--r-- | src/cache-ct.c | 13 | ||||
-rw-r--r-- | src/cache-exp.c | 2 | ||||
-rw-r--r-- | src/conntrack.c | 189 | ||||
-rw-r--r-- | src/cthelper.c | 78 | ||||
-rw-r--r-- | src/ctnl.c | 6 | ||||
-rw-r--r-- | src/expect.c | 4 | ||||
-rw-r--r-- | src/filter.c | 53 | ||||
-rw-r--r-- | src/internal_bypass.c | 6 | ||||
-rw-r--r-- | src/internal_cache.c | 11 | ||||
-rw-r--r-- | src/main.c | 22 | ||||
-rw-r--r-- | src/netlink.c | 12 | ||||
-rw-r--r-- | src/parse.c | 77 | ||||
-rw-r--r-- | src/read_config_lex.l | 4 | ||||
-rw-r--r-- | src/read_config_yy.y | 9 | ||||
-rw-r--r-- | src/run.c | 4 |
16 files changed, 421 insertions, 108 deletions
diff --git a/src/build.c b/src/build.c index 330f057..9ba8b57 100644 --- a/src/build.c +++ b/src/build.c @@ -162,6 +162,42 @@ static void build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n) sizeof(struct nfct_attr_grp_port)); } +static void ct_build_clabel(const struct nf_conntrack *ct, struct nethdr *n) +{ + const struct nfct_bitmask *b; + uint32_t *words; + unsigned int wordcount, i, maxbit; + + if (!nfct_attr_is_set(ct, ATTR_CONNLABELS)) + return; + + b = nfct_get_attr(ct, ATTR_CONNLABELS); + + maxbit = nfct_bitmask_maxbit(b); + for (i=0; i <= maxbit; i++) { + if (nfct_bitmask_test_bit(b, i)) + break; + } + + if (i > maxbit) + return; + + wordcount = (nfct_bitmask_maxbit(b) / 32) + 1; + words = put_header(n, NTA_LABELS, wordcount * sizeof(*words)); + + for (i=0; i < wordcount; i++) { + int bit = 31; + uint32_t tmp = 0; + + do { + if (nfct_bitmask_test_bit(b, (32 * i) + bit)) + tmp |= (1 << bit); + } while (--bit >= 0); + + words[i] = htonl(tmp); + } +} + #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif @@ -237,6 +273,9 @@ void ct2msg(const struct nf_conntrack *ct, struct nethdr *n) if (nfct_attr_is_set(ct, ATTR_HELPER_NAME)) ct_build_str(ct, ATTR_HELPER_NAME, n, NTA_HELPER_NAME); + + if (nfct_attr_is_set(ct, ATTR_CONNLABELS)) + ct_build_clabel(ct, n); } static void diff --git a/src/cache-ct.c b/src/cache-ct.c index 0ad8d2a..f86d143 100644 --- a/src/cache-ct.c +++ b/src/cache-ct.c @@ -59,7 +59,7 @@ cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | @@ -88,14 +88,21 @@ cache_ct_hash(const void *data, const struct hashtable *table) return ret; } +/* master conntrack of expectations have no ID */ +static inline int +cache_ct_cmp_id(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2) +{ + return nfct_attr_is_set(ct2, ATTR_ID) ? + nfct_get_attr_u32(ct1, ATTR_ID) == nfct_get_attr_u32(ct2, ATTR_ID) : 1; +} + static int cache_ct_cmp(const void *data1, const void *data2) { const struct cache_object *obj = data1; const struct nf_conntrack *ct = data2; return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG) && - nfct_get_attr_u32(obj->ptr, ATTR_ID) == - nfct_get_attr_u32(ct, ATTR_ID); + cache_ct_cmp_id(obj->ptr, ct); } static void *cache_ct_alloc(void) diff --git a/src/cache-exp.c b/src/cache-exp.c index e88877a..9183b2c 100644 --- a/src/cache-exp.c +++ b/src/cache-exp.c @@ -59,7 +59,7 @@ cache_hash6_exp(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | diff --git a/src/conntrack.c b/src/conntrack.c index 227c355..7d2a365 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -79,6 +79,9 @@ static struct { /* Allow to filter by mark from kernel-space. */ struct nfct_filter_dump_mark filter_mark_kernel; + + /* Allows filtering by ctlabels */ + struct nfct_bitmask *label; } tmpl; static int alloc_tmpl_objects(void) @@ -104,6 +107,8 @@ static void free_tmpl_objects(void) nfct_destroy(tmpl.mask); if (tmpl.exp) nfexp_destroy(tmpl.exp); + if (tmpl.label) + nfct_bitmask_destroy(tmpl.label); } enum ct_command { @@ -247,13 +252,16 @@ enum ct_options { CT_OPT_ZONE_BIT = 23, CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT), + + CT_OPT_LABEL_BIT = 24, + CT_OPT_LABEL = (1 << CT_OPT_LABEL_BIT), }; /* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ /* Update this mask to allow to filter based on new options. */ #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \ CT_OPT_MARK | CT_OPT_SECMARK | CT_OPT_STATUS | \ - CT_OPT_ID | CT_OPT_ZONE) + CT_OPT_ID | CT_OPT_ZONE | CT_OPT_LABEL) static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_ORIG_SRC_BIT] = "src", @@ -280,6 +288,7 @@ static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_BUFFERSIZE_BIT] = "buffer-size", [CT_OPT_ANY_NAT_BIT] = "any-nat", [CT_OPT_ZONE_BIT] = "zone", + [CT_OPT_LABEL_BIT] = "label", }; static struct option original_opts[] = { @@ -320,12 +329,13 @@ static struct option original_opts[] = { {"buffer-size", 1, 0, 'b'}, {"any-nat", 2, 0, 'j'}, {"zone", 1, 0, 'w'}, + {"label", 1, 0, 'l'}, {0, 0, 0, 0} }; -static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:" +static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:" "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::" - "g::c:b:C::Sj::w:"; + "g::c:b:C::Sj::w:l:"; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to @@ -340,26 +350,26 @@ static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:" 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 e [ ] { } a m i f n g o c b j w*/ -/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2}, -/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2}, -/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0}, -/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2}, -/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0}, -/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2}, -/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0}, -/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0}, -/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + /* s d r q p t u z e [ ] { } a m i f n g o c b j w l*/ +/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2}, +/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0}, +/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0}, +/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,0}, +/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0}, +/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2}, +/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0}, +/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0}, +/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }; static const int cmd2type[][2] = { @@ -391,6 +401,7 @@ static const int opt2type[] = { ['i'] = CT_OPT_ID, ['j'] = CT_OPT_ANY_NAT, ['w'] = CT_OPT_ZONE, + ['l'] = CT_OPT_LABEL, }; static const int opt2family_attr[][2] = { @@ -413,6 +424,7 @@ static const int opt2attr[] = { ['c'] = ATTR_SECMARK, ['i'] = ATTR_ID, ['w'] = ATTR_ZONE, + ['l'] = ATTR_CONNLABELS, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -450,7 +462,8 @@ static const char usage_conntrack_parameters[] = " -c, --secmark secmark\t\t\tSet selinux secmark\n" " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n" " -z, --zero \t\t\t\tZero counters while listing\n" - " -o, --output type[,...]\t\tOutput format, eg. xml\n"; + " -o, --output type[,...]\t\tOutput format, eg. xml\n" + " -l, --label label[,...]\t\tconntrack labels\n"; static const char usage_expectation_parameters[] = "Expectation parameters and options:\n" @@ -488,6 +501,7 @@ static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = { static LIST_HEAD(proto_list); static unsigned int options; +static struct nfct_labelmap *labelmap; void register_proto(struct ctproto_handler *h) { @@ -731,6 +745,7 @@ enum { _O_TMS = (1 << 2), _O_ID = (1 << 3), _O_KTMS = (1 << 4), + _O_CL = (1 << 5), }; enum { @@ -749,8 +764,8 @@ static struct parse_parameter { { IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} }, { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4, { CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } }, - { {"xml", "extended", "timestamp", "id", "ktimestamp"}, 5, - { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS }, + { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", }, 6, + { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL }, }, }; @@ -814,6 +829,59 @@ parse_u32_mask(const char *arg, struct u32_mask *m) m->mask = ~0; } +static int +get_label(char *name) +{ + int bit = nfct_labelmap_get_bit(labelmap, name); + if (bit < 0) + exit_error(PARAMETER_PROBLEM, "unknown label '%s'", name); + return bit; +} + +static void +set_label(struct nfct_bitmask *b, char *name) +{ + int bit = get_label(name); + nfct_bitmask_set_bit(b, bit); +} + +static unsigned int +set_max_label(char *name, unsigned int current_max) +{ + int bit = get_label(name); + if ((unsigned int) bit > current_max) + return (unsigned int) bit; + return current_max; +} + +static unsigned int +parse_label_get_max(char *arg) +{ + unsigned int max = 0; + char *parse; + + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + max = set_max_label(arg, max); + arg = &parse[1]; + } + + max = set_max_label(arg, max); + return max; +} + +static void +parse_label(struct nfct_bitmask *b, char *arg) +{ + char * parse; + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + set_label(b, arg); + arg = &parse[1]; + } + set_label(b, arg); +} + static void add_command(unsigned int *cmd, const int newcmd) { @@ -982,6 +1050,24 @@ usage(char *prog) static unsigned int output_mask; +static int +filter_label(const struct nf_conntrack *ct) +{ + if (tmpl.label == NULL) + return 0; + + const struct nfct_bitmask *ctb = nfct_get_attr(ct, ATTR_CONNLABELS); + if (ctb == NULL) + return 1; + + for (unsigned int i = 0; i <= nfct_bitmask_maxbit(tmpl.label); i++) { + if (nfct_bitmask_test_bit(tmpl.label, i) && + !nfct_bitmask_test_bit(ctb, i)) + return 1; + } + + return 0; +} static int filter_mark(const struct nf_conntrack *ct) @@ -992,7 +1078,6 @@ filter_mark(const struct nf_conntrack *ct) return 0; } - static int filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct) { @@ -1123,6 +1208,9 @@ static int event_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1141,7 +1229,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (!(output_mask & _O_XML)) { struct timeval tv; gettimeofday(&tv, NULL); - printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec); + printf("[%-.8ld.%-.6ld]\t", tv.tv_sec, tv.tv_usec); } else op_flags |= NFCT_OF_TIME; } @@ -1150,7 +1238,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); printf("%s\n", buf); fflush(stdout); @@ -1175,6 +1263,9 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1194,7 +1285,7 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); printf("%s\n", buf); counter++; @@ -1879,6 +1970,12 @@ int main(int argc, char *argv[]) case 'o': options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); + if (output_mask & _O_CL) { + if (!labelmap) + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + perror("nfct_labelmap_new"); + } break; case 'z': options |= CT_OPT_ZERO; @@ -1922,6 +2019,22 @@ int main(int argc, char *argv[]) tmpl.filter_mark_kernel.val = tmpl.mark.value; tmpl.filter_mark_kernel.mask = tmpl.mark.mask; break; + case 'l': + options |= opt2type[c]; + char *optarg2 = strdup(optarg); + + if (!labelmap) + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + exit_error(OTHER_PROBLEM, "unable to open labelmap file"); + + unsigned int max = parse_label_get_max(optarg); + struct nfct_bitmask * b = nfct_bitmask_new(max); + + parse_label(b, optarg2); + tmpl.label = b; + free(optarg2); + break; case 'a': fprintf(stderr, "WARNING: ignoring -%c, " "deprecated option.\n", c); @@ -1941,15 +2054,13 @@ int main(int argc, char *argv[]) socketbuffersize = atol(optarg); options |= CT_OPT_BUFFERSIZE; break; + case ':': + exit_error(PARAMETER_PROBLEM, + "option `%s' requires an " + "argument", argv[optind-1]); case '?': - if (optopt) - exit_error(PARAMETER_PROBLEM, - "option `%s' requires an " - "argument", argv[optind-1]); - else - exit_error(PARAMETER_PROBLEM, - "unknown option `%s'", - argv[optind-1]); + exit_error(PARAMETER_PROBLEM, + "unknown option `%s'", argv[optind-1]); break; default: if (h && h->parse_opts @@ -2372,6 +2483,8 @@ try_proc: free_tmpl_objects(); free_options(); + if (labelmap) + nfct_labelmap_destroy(labelmap); if (command && exit_msg[cmd][0]) { fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); diff --git a/src/cthelper.c b/src/cthelper.c index 307be96..5a8a92a 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -30,6 +30,7 @@ #include <netinet/in.h> #include <netinet/ip.h> +#include <netinet/ip6.h> #include <netinet/tcp.h> #include <netinet/udp.h> #include <net/ethernet.h> @@ -77,6 +78,8 @@ nfq_hdr_put(char *buf, int type, uint32_t queue_num) static int pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) { + uint8_t protocol; + switch(proto) { case ETHERTYPE_IP: { struct iphdr *ip = (struct iphdr *) pkt; @@ -94,41 +97,60 @@ pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) } *protoff = 4 * ip->ihl; + protocol = ip->protocol; + break; + } + case ETHERTYPE_IPV6: { + struct iphdr *ip = (struct iphdr *) pkt; + struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; - switch (ip->protocol) { - case IPPROTO_TCP: { - struct tcphdr *tcph = - (struct tcphdr *) ((char *)pkt + *protoff); - - /* No room for IPv4 header plus TCP header. */ - if (pktlen < *protoff + sizeof(struct tcphdr) - || pktlen < *protoff + tcph->doff * 4) { - dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); - return -1; - } - return 0; + /* No room for IPv6 header. */ + if (pktlen < sizeof(struct ip6_hdr)) { + dlog(LOG_ERR, "no room for IPv6 header"); + return -1; } - case IPPROTO_UDP: - /* No room for IPv4 header plus UDP header. */ - if (pktlen < *protoff + sizeof(struct udphdr)) { - dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); - return -1; - } - return 0; - default: - dlog(LOG_ERR, "not TCP/UDP, skipping"); + + /* this is not IPv6, skip. */ + if (ip->version != 6) { + dlog(LOG_ERR, "not IPv6, skipping"); return -1; } + + *protoff = sizeof(struct ip6_hdr); + protocol = ip6->ip6_nxt; break; } - case ETHERTYPE_IPV6: - dlog(LOG_ERR, "no IPv6 support sorry"); - return 0; default: /* Unknown layer 3 protocol. */ dlog(LOG_ERR, "unknown layer 3 protocol (%d), skipping", proto); return -1; } + + switch (protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = + (struct tcphdr *) ((char *)pkt + *protoff); + + /* No room for IPv4 header plus TCP header. */ + if (pktlen < *protoff + sizeof(struct tcphdr) || + pktlen < *protoff + tcph->doff * 4) { + dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); + return -1; + } + return 0; + } + case IPPROTO_UDP: + /* No room for IPv4 header plus UDP header. */ + if (pktlen < *protoff + sizeof(struct udphdr)) { + dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); + return -1; + } + return 0; + default: + dlog(LOG_ERR, "not TCP/UDP, skipping"); + return -1; + } + return 0; } @@ -143,9 +165,11 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); - /* save private data and send it back to kernel-space. */ - nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, - cur->helper->priv_data_len); + /* save private data and send it back to kernel-space, if any. */ + if (myct->priv_data) { + nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, + cur->helper->priv_data_len); + } nfq_nlmsg_verdict_put(nlh, id, verdict); if (pktb_mangled(pktb)) @@ -226,7 +226,7 @@ static int exp_event_handler(const struct nlmsghdr *nlh, STATE(stats).nl_events_filtered++; goto out; } - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; origin_type = origin_find(nlh); @@ -281,7 +281,7 @@ static int exp_dump_handler(enum nf_conntrack_msg_type type, if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; switch(type) { @@ -315,7 +315,7 @@ static int exp_get_handler(enum nf_conntrack_msg_type type, if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; STATE(get_retval) = 1; diff --git a/src/expect.c b/src/expect.c index 6069770..470b9ae 100644 --- a/src/expect.c +++ b/src/expect.c @@ -54,7 +54,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6); for (i=0; i<4; i++) - memset(addr, 0xffffffff, sizeof(uint32_t)); + memset(&addr[i], 0xffffffff, sizeof(uint32_t)); nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); nfct_set_attr(mask, ATTR_IPV6_SRC, addr); @@ -76,7 +76,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, break; case AF_INET6: for (i=0; i<4; i++) - memset(addr, 0x00000000, sizeof(uint32_t)); + memset(&addr[i], 0x00000000, sizeof(uint32_t)); nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); nfct_set_attr(expected, ATTR_IPV6_SRC, addr); diff --git a/src/filter.c b/src/filter.c index 02a8078..8fac71b 100644 --- a/src/filter.c +++ b/src/filter.c @@ -373,8 +373,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) { case AF_INET: - if (!nfct_attr_is_set(ct, ATTR_IPV4_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV4_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV4_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV4_SRC)) { dlog(LOG_ERR, "missing IPv4 address. " "You forgot to load " "nf_conntrack_ipv4?"); @@ -382,8 +382,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) } break; case AF_INET6: - if (!nfct_attr_is_set(ct, ATTR_IPV6_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV6_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV6_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV6_SRC)) { dlog(LOG_ERR, "missing IPv6 address. " "You forgot to load " "nf_conntrack_ipv6?"); @@ -407,6 +407,51 @@ int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace) return 0; } +static inline int +ct_filter_master_sanity_check(const struct nf_conntrack *master) +{ + if (master == NULL) { + dlog(LOG_ERR, "no master tuple in expectation"); + return 0; + } + + if (!nfct_attr_is_set(master, ATTR_L3PROTO)) { + dlog(LOG_ERR, "missing layer 3 protocol"); + return 0; + } + + switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { + case AF_INET: + if (!nfct_attr_is_set(master, ATTR_IPV4_SRC) || + !nfct_attr_is_set(master, ATTR_IPV4_DST)) { + dlog(LOG_ERR, "missing IPv4 address. " + "You forgot to load nf_conntrack_ipv4?"); + return 0; + } + break; + case AF_INET6: + if (!nfct_attr_is_set(master, ATTR_IPV6_SRC) || + !nfct_attr_is_set(master, ATTR_IPV6_DST)) { + dlog(LOG_ERR, "missing IPv6 address. " + "You forgot to load nf_conntrack_ipv6?"); + return 0; + } + break; + } + return 1; +} + +int ct_filter_master(const struct nf_conntrack *master) +{ + if (!ct_filter_master_sanity_check(master)) + return 1; + + /* Check if we've got a master conntrack for this expectation in our + * caches. If there is not, we don't want this expectation either. + */ + return STATE(mode)->internal->exp.find(master) ? 0 : 1; +} + struct exp_filter { struct list_head list; }; diff --git a/src/internal_bypass.c b/src/internal_bypass.c index 1194339..ce2ae46 100644 --- a/src/internal_bypass.c +++ b/src/internal_bypass.c @@ -283,6 +283,11 @@ static int internal_bypass_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_bypass_exp_master_find(const struct nf_conntrack *master) +{ + return nl_get_conntrack(STATE(get), master) == 0; +} + struct internal_handler internal_bypass = { .init = internal_bypass_init, .close = internal_bypass_close, @@ -309,5 +314,6 @@ struct internal_handler internal_bypass = { .new = internal_bypass_exp_event_new, .upd = internal_bypass_exp_event_upd, .del = internal_bypass_exp_event_del, + .find = internal_bypass_exp_master_find, }, }; diff --git a/src/internal_cache.c b/src/internal_cache.c index ba2d74b..bad31f3 100644 --- a/src/internal_cache.c +++ b/src/internal_cache.c @@ -364,6 +364,16 @@ static int internal_cache_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_cache_exp_master_find(const struct nf_conntrack *master) +{ + struct cache_object *obj; + int id; + + obj = cache_find(STATE(mode)->internal->ct.data, + (struct nf_conntrack *)master, &id); + return obj ? 1 : 0; +} + struct internal_handler internal_cache = { .flags = INTERNAL_F_POPULATE | INTERNAL_F_RESYNC, .init = internal_cache_init, @@ -391,5 +401,6 @@ struct internal_handler internal_cache = { .new = internal_cache_exp_event_new, .upd = internal_cache_exp_event_upd, .del = internal_cache_exp_event_del, + .find = internal_cache_exp_master_find, }, }; @@ -23,6 +23,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include <errno.h> #include <fcntl.h> #include <sys/utsname.h> #include <string.h> @@ -112,6 +113,23 @@ set_action_by_table(int i, int argc, char *argv[], return i; } +static void +set_nice_value(int nv) +{ + errno = 0; + if (nice(nv) == -1 && errno) /* warn only */ + fprintf(stderr, "Cannot set nice level %d: %s\n", + nv, strerror(errno)); +} + +static void +do_chdir(const char *d) +{ + if (chdir(d)) + fprintf(stderr, "Cannot change current directory to %s: %s\n", + d, strerror(errno)); +} + int main(int argc, char *argv[]) { int ret, i, action = -1; @@ -356,7 +374,7 @@ int main(int argc, char *argv[]) /* * Setting process priority and scheduler */ - nice(CONFIG(nice)); + set_nice_value(CONFIG(nice)); if (CONFIG(sched).type != SCHED_OTHER) { struct sched_param schedparam = { @@ -382,7 +400,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - chdir("/"); + do_chdir("/"); close(STDIN_FILENO); /* Daemonize conntrackd */ diff --git a/src/netlink.c b/src/netlink.c index bd38d99..5be102e 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -146,9 +146,11 @@ void nl_resize_socket_buffer(struct nfct_handle *h) "to %u bytes", CONFIG(netlink_buffer_size)); } +static const int family = AF_UNSPEC; + int nl_dump_conntrack_table(struct nfct_handle *h) { - return nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfct_query(h, NFCT_Q_DUMP, &family); } static int @@ -182,7 +184,7 @@ int nl_flush_conntrack_table_selective(void) } nfct_callback_register(h, NFCT_T_ALL, nl_flush_selective_cb, NULL); - ret = nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + ret = nfct_query(h, NFCT_Q_DUMP, &family); nfct_close(h); @@ -191,7 +193,6 @@ int nl_flush_conntrack_table_selective(void) int nl_send_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfct_send(h, NFCT_Q_DUMP, &family); } @@ -380,16 +381,15 @@ int nl_get_expect(struct nfct_handle *h, const struct nf_expect *exp) int nl_dump_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_DUMP, &family); } int nl_flush_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_FLUSH, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_FLUSH, &family); } int nl_send_expect_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfexp_send(h, NFCT_Q_DUMP, &family); } diff --git a/src/parse.c b/src/parse.c index 8ce4495..f3ec6ac 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,15 +29,19 @@ static void ct_parse_u8(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u16(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u32(struct nf_conntrack *ct, int attr, void *data); -static void ct_parse_str(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_str(struct nf_conntrack *ct, + const struct netattr *, void *data); static void ct_parse_group(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_clabel(struct nf_conntrack *ct, + const struct netattr *, void *data); struct ct_parser { void (*parse)(struct nf_conntrack *ct, int attr, void *data); - int attr; - int size; - int max_size; + void (*parse2)(struct nf_conntrack *ct, const struct netattr *, void *); + uint16_t attr; + uint16_t size; + uint16_t max_size; }; static struct ct_parser h[NTA_MAX] = { @@ -176,10 +180,15 @@ static struct ct_parser h[NTA_MAX] = { .size = NTA_SIZE(sizeof(uint8_t)), }, [NTA_HELPER_NAME] = { - .parse = ct_parse_str, + .parse2 = ct_parse_str, .attr = ATTR_HELPER_NAME, .max_size = NFCT_HELPER_NAME_MAX, }, + [NTA_LABELS] = { + .parse2 = ct_parse_clabel, + .attr = ATTR_CONNLABELS, + .max_size = NTA_SIZE(NTA_LABELS_MAX_SIZE), + }, }; static void @@ -204,9 +213,9 @@ ct_parse_u32(struct nf_conntrack *ct, int attr, void *data) } static void -ct_parse_str(struct nf_conntrack *ct, int attr, void *data) +ct_parse_str(struct nf_conntrack *ct, const struct netattr *attr, void *data) { - nfct_set_attr(ct, h[attr].attr, data); + nfct_set_attr(ct, h[attr->nta_attr].attr, data); } static void @@ -216,6 +225,44 @@ ct_parse_group(struct nf_conntrack *ct, int attr, void *data) } static void +ct_parse_clabel(struct nf_conntrack *ct, const struct netattr *attr, void *data) +{ + struct nfct_bitmask *bitm; + unsigned int i, wordcount; + const uint32_t *words; + unsigned int len; + + len = attr->nta_len - NTA_LENGTH(0); + wordcount = len / sizeof(*words); + if (!wordcount) + return; + + if (len & (sizeof(*words) - 1)) + return; + + bitm = nfct_bitmask_new((len * 8) - 1); + if (!bitm) + return; + + words = data; + for (i=0; i < wordcount; i++) { + uint32_t word; + int bit; + + if (words[i] == 0) + continue; + + word = htonl(words[i]); + bit = 31; + do { + if (word & (1 << bit)) + nfct_bitmask_set_bit(bitm, (32 * i) + bit); + } while (--bit >= 0); + } + nfct_set_attr(ct, ATTR_CONNLABELS, bitm); +} + +static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data) { struct nta_attr_natseqadj *this = data; @@ -248,14 +295,22 @@ int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain) ATTR_NETWORK2HOST(attr); if (attr->nta_len > len) return -1; + if (attr->nta_len < NTA_LENGTH(0)) + return -1; if (attr->nta_attr > NTA_MAX) return -1; if (h[attr->nta_attr].size && attr->nta_len != h[attr->nta_attr].size) return -1; - if (h[attr->nta_attr].max_size && - attr->nta_len > h[attr->nta_attr].max_size) - return -1; + + if (h[attr->nta_attr].max_size) { + if (attr->nta_len > h[attr->nta_attr].max_size) + return -1; + h[attr->nta_attr].parse2(ct, attr, NTA_DATA(attr)); + attr = NTA_NEXT(attr, len); + continue; + } + if (h[attr->nta_attr].parse == NULL) { attr = NTA_NEXT(attr, len); continue; @@ -457,6 +512,8 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain) goto err; if (attr->nta_attr > NTA_MAX) goto err; + if (attr->nta_len < NTA_LENGTH(0)) + goto err; if (exp_h[attr->nta_attr].size && attr->nta_len != exp_h[attr->nta_attr].size) goto err; diff --git a/src/read_config_lex.l b/src/read_config_lex.l index bec2d81..b4d11d4 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -44,8 +44,8 @@ ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part}{ip4_cidr}? hex_255 [0-9a-fA-F]{1,4} ip6_cidr \/[0-1]*[0-9]*[0-9]+ ip6_part {hex_255}":"? -ip6_form1 {ip6_part}{0,16}"::"{ip6_part}{0,16} -ip6_form2 ({hex_255}":"){16}{hex_255} +ip6_form1 {ip6_part}{0,7}"::"{ip6_part}{0,7} +ip6_form2 ({hex_255}":"){0,7}{hex_255} ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}? string [a-zA-Z][a-zA-Z0-9\.\-]* persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] diff --git a/src/read_config_yy.y b/src/read_config_yy.y index 72a9654..b824150 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1193,10 +1193,7 @@ scheduler_line : T_PRIO T_NUMBER family : T_FAMILY T_STRING { - if (strncmp($2, "IPv6", strlen("IPv6")) == 0) - conf.family = AF_INET6; - else - conf.family = AF_INET; + print_err(CTD_CFG_WARN, "`Family' is deprecated, ignoring"); }; event_iterations_limit : T_EVENT_ITER_LIMIT T_NUMBER @@ -1863,10 +1860,6 @@ init_config(char *filename) yyparse(); fclose(fp); - /* default to IPv4 */ - if (CONFIG(family) == 0) - CONFIG(family) = AF_INET; - /* set to default is not specified */ if (strcmp(CONFIG(lockfile), "") == 0) strncpy(CONFIG(lockfile), DEFAULT_LOCKFILE, FILENAME_MAXLEN); @@ -40,14 +40,14 @@ #include <time.h> #include <fcntl.h> -void killer(int signal) +void killer(int signo) { /* Signals are re-entrant, disable signal handling to avoid problems * in case we receive SIGINT and SIGTERM in a row. This function is * also called via -k from the unix socket context, we already disabled * signals in that path, so don't do it. */ - if (signal) + if (signo) sigprocmask(SIG_BLOCK, &STATE(block), NULL); local_server_destroy(&STATE(local)); |