summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c39
-rw-r--r--src/cache-ct.c13
-rw-r--r--src/cache-exp.c2
-rw-r--r--src/conntrack.c189
-rw-r--r--src/cthelper.c78
-rw-r--r--src/ctnl.c6
-rw-r--r--src/expect.c4
-rw-r--r--src/filter.c53
-rw-r--r--src/internal_bypass.c6
-rw-r--r--src/internal_cache.c11
-rw-r--r--src/main.c22
-rw-r--r--src/netlink.c12
-rw-r--r--src/parse.c77
-rw-r--r--src/read_config_lex.l4
-rw-r--r--src/read_config_yy.y9
-rw-r--r--src/run.c4
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))
diff --git a/src/ctnl.c b/src/ctnl.c
index bb54727..10b5f4c 100644
--- a/src/ctnl.c
+++ b/src/ctnl.c
@@ -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,
},
};
diff --git a/src/main.c b/src/main.c
index 831a3c2..dafeaee 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/run.c b/src/run.c
index 44a179f..7fa6889 100644
--- a/src/run.c
+++ b/src/run.c
@@ -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));