diff options
Diffstat (limited to 'src/conntrack.c')
-rw-r--r-- | src/conntrack.c | 542 |
1 files changed, 244 insertions, 298 deletions
diff --git a/src/conntrack.c b/src/conntrack.c index fe5574d..0d71352 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -68,10 +68,14 @@ #include <linux/netfilter/nf_conntrack_common.h> #include <libnetfilter_conntrack/libnetfilter_conntrack.h> -static struct nfct_mnl_socket { +struct nfct_mnl_socket { struct mnl_socket *mnl; uint32_t portid; -} _sock; +}; + +static struct nfct_mnl_socket _sock; +static struct nfct_mnl_socket _modifier_sock; +static struct nfct_mnl_socket _event_sock; struct u32_mask { uint32_t value; @@ -135,6 +139,8 @@ static int alloc_tmpl_objects(struct ct_tmpl *tmpl) static void free_tmpl_objects(struct ct_tmpl *tmpl) { + if (!tmpl) + return; if (tmpl->ct) nfct_destroy(tmpl->ct); if (tmpl->exptuple) @@ -149,68 +155,6 @@ static void free_tmpl_objects(struct ct_tmpl *tmpl) nfct_bitmask_destroy(tmpl->label_modify); } -enum ct_command { - 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 = 3, - CT_DELETE = (1 << CT_DELETE_BIT), - - CT_GET_BIT = 4, - CT_GET = (1 << CT_GET_BIT), - - CT_FLUSH_BIT = 5, - CT_FLUSH = (1 << CT_FLUSH_BIT), - - CT_EVENT_BIT = 6, - CT_EVENT = (1 << CT_EVENT_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), - - CT_COUNT_BIT = 15, - CT_COUNT = (1 << CT_COUNT_BIT), - - EXP_COUNT_BIT = 16, - EXP_COUNT = (1 << EXP_COUNT_BIT), - - CT_STATS_BIT = 17, - CT_STATS = (1 << CT_STATS_BIT), - - EXP_STATS_BIT = 18, - EXP_STATS = (1 << EXP_STATS_BIT), -}; -/* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */ - enum ct_options { CT_OPT_ORIG_SRC_BIT = 0, CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT), @@ -349,6 +293,7 @@ static const char *optflags[NUMBER_OF_OPT] = { static struct option original_opts[] = { {"dump", 2, 0, 'L'}, {"create", 2, 0, 'I'}, + {"add", 2, 0, 'A'}, {"delete", 2, 0, 'D'}, {"update", 2, 0, 'U'}, {"get", 2, 0, 'G'}, @@ -392,7 +337,7 @@ static struct option original_opts[] = { {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::A::hVs:d:r:q:" "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::" "g::c:b:C::Sj::w:l:<:>::(:):"; @@ -409,26 +354,27 @@ 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 l < > ( ) */ -/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,2,2,0,2,0,2,2,2,2,2,0,2,2,2,0,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,0,2,0,2,2}, -/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,0,2,2,2,0,0}, -/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,2,2,0,0,2,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,2,0,0,0,0}, -/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,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,2,2,0,2,0,2,2,2,2,2,2,2,2,2,0,0,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,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,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,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,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,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,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,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,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,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,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,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,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_BIT] = {2,2,2,2,2,0,2,2,0,0,0,2,2,0,2,0,2,2,2,2,2,0,2,2,2,0,0,2,2}, + [CT_CREATE_BIT] = {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,2,0,2,2}, + [CT_UPDATE_BIT] = {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,0,2,2,2,0,0}, + [CT_DELETE_BIT] = {2,2,2,2,2,2,2,0,0,0,0,2,2,0,2,2,2,2,2,2,0,0,0,2,2,0,0,2,2}, + [CT_GET_BIT] = {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0,0,0}, + [CT_FLUSH_BIT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0}, + [CT_EVENT_BIT] = {2,2,2,2,2,0,0,0,2,0,0,2,2,0,2,0,2,2,2,2,2,2,2,2,2,0,0,2,2}, + [CT_VERSION_BIT]= {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,0,0,0,0}, + [CT_HELP_BIT] = {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,0,0,0,0}, + [EXP_LIST_BIT] = {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,0,0,0,0}, + [EXP_CREATE_BIT]= {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,0,0,0,0}, + [EXP_DELETE_BIT]= {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,0,0,0,0}, + [EXP_GET_BIT] = {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,0,0,0,0}, + [EXP_FLUSH_BIT] = {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,0,0,0,0}, + [EXP_EVENT_BIT] = {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,0,0,0,0}, + [CT_COUNT_BIT] = {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,0,0,0,0}, + [EXP_COUNT_BIT] = {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,0,0,0,0}, + [CT_STATS_BIT] = {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,0,0,0,0}, + [EXP_STATS_BIT] = {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,0,0,0,0}, + [CT_ADD_BIT] = {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,2,0,2,2}, }; static const int cmd2type[][2] = { @@ -442,6 +388,8 @@ static const int cmd2type[][2] = { ['h'] = { CT_HELP, CT_HELP }, ['C'] = { CT_COUNT, EXP_COUNT }, ['S'] = { CT_STATS, EXP_STATS }, + ['U'] = { CT_UPDATE, 0 }, + ['A'] = { CT_ADD, 0 }, }; static const int opt2type[] = { @@ -545,6 +493,7 @@ static char exit_msg[NUMBER_OF_CMD][64] = { [CT_EVENT_BIT] = "%d flow events have been shown.\n", [EXP_LIST_BIT] = "%d expectations have been shown.\n", [EXP_DELETE_BIT] = "%d expectations have been shown.\n", + [CT_ADD_BIT] = "%d flow entries have been added.\n", }; static const char usage_commands[] = @@ -589,7 +538,7 @@ static const char usage_parameters[] = "Common parameters and options:\n" " -s, --src, --orig-src ip\t\tSource address from original direction\n" " -d, --dst, --orig-dst ip\t\tDestination address from original direction\n" - " -r, --reply-src ip\t\tSource addres from reply direction\n" + " -r, --reply-src ip\t\tSource address from reply direction\n" " -q, --reply-dst ip\t\tDestination address from reply direction\n" " -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n" " -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n" @@ -605,7 +554,7 @@ static const char usage_parameters[] = #define OPTION_OFFSET 256 -static struct nfct_handle *cth, *ith; +static struct nfct_handle *cth; static struct option *opts = original_opts; static unsigned int global_option_offset = 0; @@ -796,12 +745,13 @@ static int ct_save_snprintf(char *buf, size_t len, struct ctproto_handler *cur; uint8_t l3proto, l4proto; int tuple_attrs[4] = {}; + bool l4proto_set; unsigned i; int ret; switch (type) { case NFCT_T_NEW: - ret = snprintf(buf + offset, len, "-I "); + ret = snprintf(buf + offset, len, "-A "); BUFFER_SIZE(ret, size, len, offset); break; case NFCT_T_UPDATE: @@ -813,7 +763,6 @@ static int ct_save_snprintf(char *buf, size_t len, BUFFER_SIZE(ret, size, len, offset); break; default: - ret = 0; break; } @@ -856,6 +805,7 @@ static int ct_save_snprintf(char *buf, size_t len, l4proto = nfct_get_attr_u8(ct, ATTR_L4PROTO); + l4proto_set = false; /* is it in the list of supported protocol? */ list_for_each_entry(cur, &proto_list, head) { if (cur->protonum != l4proto) @@ -866,9 +816,16 @@ static int ct_save_snprintf(char *buf, size_t len, ret = ct_snprintf_opts(buf + offset, len, ct, cur->print_opts); BUFFER_SIZE(ret, size, len, offset); + + l4proto_set = true; break; } + if (!l4proto_set) { + ret = snprintf(buf + offset, len, "-p %d ", l4proto); + BUFFER_SIZE(ret, size, len, offset); + } + /* skip trailing space, if any */ for (; size && buf[size-1] == ' '; --size) buf[size-1] = '\0'; @@ -878,6 +835,20 @@ static int ct_save_snprintf(char *buf, size_t len, extern struct ctproto_handler ct_proto_unknown; +static int parse_proto_num(const char *str) +{ + unsigned long val; + char *endptr; + + val = strtoul(str, &endptr, 0); + if (val > IPPROTO_RAW || + endptr == str || + *endptr != '\0') + return -1; + + return val; +} + static struct ctproto_handler *findproto(char *name, int *pnum) { struct ctproto_handler *cur; @@ -897,8 +868,8 @@ static struct ctproto_handler *findproto(char *name, int *pnum) return &ct_proto_unknown; } /* using a protocol number? */ - protonum = atoi(name); - if (protonum >= 0 && protonum <= IPPROTO_MAX) { + protonum = parse_proto_num(name); + if (protonum >= 0) { /* try lookup by number, perhaps this protocol is supported */ list_for_each_entry(cur, &proto_list, head) { if (cur->protonum == protonum) { @@ -1087,11 +1058,11 @@ err2str(int err, enum ct_command command) { { CT_LIST, ENOTSUPP, "function not implemented" }, { 0xFFFF, EINVAL, "invalid parameters" }, { CT_CREATE, EEXIST, "Such conntrack exists, try -U to update" }, - { CT_CREATE|CT_GET|CT_DELETE, ENOENT, + { CT_CREATE|CT_GET|CT_DELETE|CT_ADD, ENOENT, "such conntrack doesn't exist" }, - { CT_CREATE|CT_GET, ENOMEM, "not enough memory" }, + { CT_CREATE|CT_GET|CT_ADD, ENOMEM, "not enough memory" }, { CT_GET, EAFNOSUPPORT, "protocol not supported" }, - { CT_CREATE, ETIME, "conntrack has expired" }, + { CT_CREATE|CT_ADD, ETIME, "conntrack has expired" }, { EXP_CREATE, ENOENT, "master conntrack not found" }, { EXP_CREATE, EINVAL, "invalid parameters" }, { ~0U, EPERM, "sorry, you must be root or get " @@ -1124,8 +1095,7 @@ enum { _O_ID = (1 << 3), _O_KTMS = (1 << 4), _O_CL = (1 << 5), - _O_US = (1 << 6), - _O_SAVE = (1 << 7), + _O_SAVE = (1 << 6), }; enum { @@ -1145,7 +1115,7 @@ static struct parse_parameter { { {"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", "labels", "userspace", "save"}, 8, - { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, _O_US, _O_SAVE }, + { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL, 0, _O_SAVE }, }, }; @@ -1800,7 +1770,7 @@ static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode) continue; rl = readlink(procname, tmp, sizeof(tmp)); - if (rl <= 0 || rl > (ssize_t)sizeof(tmp)) + if (rl <= 0 || rl >= (ssize_t)sizeof(tmp)) continue; tmp[rl] = 0; @@ -1974,7 +1944,7 @@ static int event_cb(const struct nlmsghdr *nlh, void *data) nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); done: - if ((output_mask & _O_US) && nlh->nlmsg_pid) { + if (nlh->nlmsg_pid && !(output_mask & _O_XML)) { char *prog = get_progname(nlh->nlmsg_pid); if (prog) @@ -1992,65 +1962,41 @@ out: return MNL_CB_OK; } -static int dump_cb(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) -{ - unsigned int op_type = NFCT_O_DEFAULT; - unsigned int op_flags = 0; - struct ct_cmd *cmd = data; - char buf[1024]; - - if (nfct_filter(cmd, ct, cur_tmpl)) - return NFCT_CB_CONTINUE; - - if (output_mask & _O_SAVE) { - ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW); - goto done; - } - - if (output_mask & _O_XML) { - op_type = NFCT_O_XML; - if (dump_xml_header_done) { - dump_xml_header_done = 0; - printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" - "<conntrack>\n"); - } - } - if (output_mask & _O_EXT) - op_flags = NFCT_OF_SHOW_LAYER3; - if (output_mask & _O_KTMS) - op_flags |= NFCT_OF_TIMESTAMP; - if (output_mask & _O_ID) - op_flags |= NFCT_OF_ID; - - nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); -done: - printf("%s\n", buf); - - counter++; - - return NFCT_CB_CONTINUE; -} +static int nfct_mnl_request(struct nfct_mnl_socket *sock, uint16_t subsys, + int family, uint16_t type, uint16_t flags, + mnl_cb_t cb, const struct nf_conntrack *ct, + const struct ct_cmd *cmd); -static int delete_cb(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) +static int mnl_nfct_delete_cb(const struct nlmsghdr *nlh, void *data) { + struct nfct_mnl_socket *modifier_sock = &_modifier_sock; unsigned int op_type = NFCT_O_DEFAULT; unsigned int op_flags = 0; struct ct_cmd *cmd = data; + struct nf_conntrack *ct; char buf[1024]; int res; + ct = nfct_new(); + if (ct == NULL) + return MNL_CB_OK; + + nfct_nlmsg_parse(nlh, ct); + if (nfct_filter(cmd, ct, cur_tmpl)) - return NFCT_CB_CONTINUE; + goto destroy_ok; - res = nfct_query(ith, NFCT_Q_DESTROY, ct); - if (res < 0) + res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK, + nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO), + IPCTNL_MSG_CT_DELETE, NLM_F_ACK, NULL, ct, NULL); + if (res < 0) { + /* the entry has vanish in middle of the delete */ + if (errno == ENOENT) + goto done; exit_error(OTHER_PROBLEM, "Operation failed: %s", err2str(errno, CT_DELETE)); + } if (output_mask & _O_SAVE) { ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_DESTROY); @@ -2070,16 +2016,23 @@ done: counter++; - return NFCT_CB_CONTINUE; +destroy_ok: + nfct_destroy(ct); + return MNL_CB_OK; } -static int print_cb(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) +static int mnl_nfct_print_cb(const struct nlmsghdr *nlh, void *data) { - char buf[1024]; unsigned int op_type = NFCT_O_DEFAULT; unsigned int op_flags = 0; + struct nf_conntrack *ct; + char buf[1024]; + + ct = nfct_new(); + if (ct == NULL) + return MNL_CB_OK; + + nfct_nlmsg_parse(nlh, ct); if (output_mask & _O_SAVE) { ct_save_snprintf(buf, sizeof(buf), ct, labelmap, NFCT_T_NEW); @@ -2097,7 +2050,9 @@ static int print_cb(enum nf_conntrack_msg_type type, done: printf("%s\n", buf); - return NFCT_CB_CONTINUE; + nfct_destroy(ct); + + return MNL_CB_OK; } static void copy_mark(const struct ct_cmd *cmd, struct nf_conntrack *tmp, @@ -2190,29 +2145,34 @@ static void copy_label(const struct ct_cmd *cmd, struct nf_conntrack *tmp, } } -static int update_cb(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) +static int mnl_nfct_update_cb(const struct nlmsghdr *nlh, void *data) { struct ct_cmd *cmd = data; - struct nf_conntrack *obj = cmd->tmpl.ct, *tmp; + struct nfct_mnl_socket *modifier_sock = &_modifier_sock; + struct nf_conntrack *ct, *obj = cmd->tmpl.ct, *tmp = NULL; int res; + ct = nfct_new(); + if (ct == NULL) + return MNL_CB_OK; + + nfct_nlmsg_parse(nlh, ct); + if (filter_nat(cmd, ct) || filter_label(ct, cur_tmpl) || filter_network(cmd, ct)) - return NFCT_CB_CONTINUE; + goto destroy_ok; if (nfct_attr_is_set(obj, ATTR_ID) && nfct_attr_is_set(ct, ATTR_ID) && nfct_get_attr_u32(obj, ATTR_ID) != nfct_get_attr_u32(ct, ATTR_ID)) - return NFCT_CB_CONTINUE; + goto destroy_ok; if (cmd->options & CT_OPT_TUPLE_ORIG && !nfct_cmp(obj, ct, NFCT_CMP_ORIG)) - return NFCT_CB_CONTINUE; + goto destroy_ok; if (cmd->options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL)) - return NFCT_CB_CONTINUE; + goto destroy_ok; tmp = nfct_new(); if (tmp == NULL) @@ -2225,36 +2185,47 @@ static int update_cb(enum nf_conntrack_msg_type type, copy_label(cmd, tmp, ct, cur_tmpl); /* do not send NFCT_Q_UPDATE if ct appears unchanged */ - if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) { - nfct_destroy(tmp); - return NFCT_CB_CONTINUE; - } + if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) + goto destroy_ok; - res = nfct_query(ith, NFCT_Q_UPDATE, tmp); - if (res < 0) - fprintf(stderr, - "Operation failed: %s\n", + res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK, + nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO), + IPCTNL_MSG_CT_NEW, NLM_F_ACK, NULL, tmp, NULL); + if (res < 0) { + /* the entry has vanish in middle of the update */ + if (errno == ENOENT) + goto destroy_ok; + else if (cmd->options & (CT_OPT_ADD_LABEL | CT_OPT_DEL_LABEL) && + !nfct_attr_is_set(ct, ATTR_CONNLABELS) && + errno == ENOSPC) + goto destroy_ok; + + exit_error(OTHER_PROBLEM, + "Operation failed: %s", err2str(errno, CT_UPDATE)); - nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL); + } - res = nfct_query(ith, NFCT_Q_GET, tmp); + res = nfct_mnl_request(modifier_sock, NFNL_SUBSYS_CTNETLINK, + nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO), + IPCTNL_MSG_CT_GET, 0, + mnl_nfct_print_cb, tmp, NULL); if (res < 0) { - nfct_destroy(tmp); /* the entry has vanish in middle of the update */ - if (errno == ENOENT) { - nfct_callback_unregister(ith); - return NFCT_CB_CONTINUE; - } + if (errno == ENOENT) + goto destroy_ok; exit_error(OTHER_PROBLEM, "Operation failed: %s", err2str(errno, CT_UPDATE)); } - nfct_destroy(tmp); - nfct_callback_unregister(ith); counter++; - return NFCT_CB_CONTINUE; +destroy_ok: + if (tmp) + nfct_destroy(tmp); + nfct_destroy(ct); + + return MNL_CB_OK; } static int dump_exp_cb(enum nf_conntrack_msg_type type, @@ -2400,7 +2371,7 @@ out_err: } static int nfct_mnl_socket_open(struct nfct_mnl_socket *socket, - unsigned int events) + unsigned int events) { socket->mnl = mnl_socket_open(NETLINK_NETFILTER); if (socket->mnl == NULL) { @@ -2441,8 +2412,25 @@ static void nfct_mnl_socket_close(const struct nfct_mnl_socket *sock) mnl_socket_close(sock->mnl); } -static int nfct_mnl_recv(struct nfct_mnl_socket *sock, - const struct nlmsghdr *nlh, mnl_cb_t cb, void *data) +static int nfct_mnl_socket_check_open(struct nfct_mnl_socket *socket, + unsigned int events) +{ + if (socket->mnl != NULL) + return 0; + + return nfct_mnl_socket_open(socket, events); +} + +static void nfct_mnl_socket_check_close(struct nfct_mnl_socket *sock) +{ + if (sock->mnl) { + nfct_mnl_socket_close(sock); + memset(sock, 0, sizeof(*sock)); + } +} + +static int __nfct_mnl_dump(struct nfct_mnl_socket *sock, + const struct nlmsghdr *nlh, mnl_cb_t cb, void *data) { char buf[MNL_SOCKET_BUFFER_SIZE]; int res; @@ -2478,11 +2466,12 @@ nfct_mnl_dump(struct nfct_mnl_socket *sock, uint16_t subsys, uint16_t type, if (filter_dump) nfct_nlmsg_build_filter(nlh, filter_dump); - return nfct_mnl_recv(sock, nlh, cb, cmd); + return __nfct_mnl_dump(sock, nlh, cb, cmd); } static int nfct_mnl_talk(struct nfct_mnl_socket *sock, - const struct nlmsghdr *nlh, mnl_cb_t cb) + const struct nlmsghdr *nlh, mnl_cb_t cb, + const struct ct_cmd *cmd) { char buf[MNL_SOCKET_BUFFER_SIZE]; int ret; @@ -2495,38 +2484,27 @@ static int nfct_mnl_talk(struct nfct_mnl_socket *sock, if (ret < 0) return ret; - return mnl_cb_run(buf, ret, nlh->nlmsg_seq, sock->portid, cb, NULL); + return mnl_cb_run(buf, ret, nlh->nlmsg_seq, sock->portid, cb, (void *)cmd); } -static int -nfct_mnl_get(struct nfct_mnl_socket *sock, uint16_t subsys, uint16_t type, - mnl_cb_t cb, uint8_t family) -{ - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - - nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, 0, family); - - return nfct_mnl_talk(sock, nlh, cb); -} - -static int -nfct_mnl_create(struct nfct_mnl_socket *sock, uint16_t subsys, uint16_t type, - const struct nf_conntrack *ct) +static int nfct_mnl_request(struct nfct_mnl_socket *sock, uint16_t subsys, + int family, uint16_t type, uint16_t flags, + mnl_cb_t cb, const struct nf_conntrack *ct, + const struct ct_cmd *cmd) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; int err; - nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, - NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL, - nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO)); + nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, flags, family); - err = nfct_nlmsg_build(nlh, ct); - if (err < 0) - return err; + if (ct) { + err = nfct_nlmsg_build(nlh, ct); + if (err < 0) + return err; + } - return nfct_mnl_talk(sock, nlh, NULL); + return nfct_mnl_talk(sock, nlh, cb, cmd); } #define UNKNOWN_STATS_NUM 4 @@ -2922,7 +2900,8 @@ static int print_stats(const struct ct_cmd *cmd) if (cmd->command && exit_msg[cmd->cmd][0]) { fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); fprintf(stderr, exit_msg[cmd->cmd], counter); - if (counter == 0 && !(cmd->command & (CT_LIST | EXP_LIST))) + if (counter == 0 && + !(cmd->command & (CT_LIST | EXP_LIST))) return -1; } @@ -2975,27 +2954,24 @@ static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[]) case 'h': case 'C': case 'S': - type = check_type(argc, argv); - if (type == CT_TABLE_DYING || - type == CT_TABLE_UNCONFIRMED) { - exit_error(PARAMETER_PROBLEM, - "Can't do that command with " - "tables `dying' and `unconfirmed'"); - } - add_command(&command, cmd2type[c][type]); - break; case 'U': + case 'A': type = check_type(argc, argv); if (type == CT_TABLE_DYING || type == CT_TABLE_UNCONFIRMED) { exit_error(PARAMETER_PROBLEM, "Can't do that command with " "tables `dying' and `unconfirmed'"); - } else if (type == CT_TABLE_CONNTRACK) - add_command(&command, CT_UPDATE); - else + } + if (cmd2type[c][type]) + add_command(&command, cmd2type[c][type]); + else { exit_error(PARAMETER_PROBLEM, - "Can't update expectations"); + "Can't do --%s on %s", + get_long_opt(c), + type == CT_TABLE_CONNTRACK ? + "conntrack" : "expectations"); + } break; /* options */ case 's': @@ -3046,8 +3022,7 @@ static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[]) if (tmpl->filter_status_kernel.mask == 0) tmpl->filter_status_kernel.mask = status; - tmpl->mark.value = status; - tmpl->filter_status_kernel.val = tmpl->mark.value; + tmpl->filter_status_kernel.val = status; tmpl->filter_status_kernel_set = true; break; case 'e': @@ -3096,6 +3071,8 @@ static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[]) nfct_set_nat_details(c, tmpl->ct, &ad, port_str, family); } + free(port_str); + free(nat_address); } break; case 'w': @@ -3250,28 +3227,26 @@ static void do_parse(struct ct_cmd *ct_cmd, int argc, char *argv[]) ct_cmd->socketbuffersize = socketbuffersize; } -static int do_command_ct(const char *progname, struct ct_cmd *cmd) +static int do_command_ct(const char *progname, struct ct_cmd *cmd, + struct nfct_mnl_socket *sock) { - struct nfct_mnl_socket *sock = &_sock; + struct nfct_mnl_socket *modifier_sock = &_modifier_sock; + struct nfct_mnl_socket *event_sock = &_event_sock; struct nfct_filter_dump *filter_dump; + uint16_t nl_flags = 0; int res = 0; switch(cmd->command) { case CT_LIST: - if (nfct_mnl_socket_open(sock, 0) < 0) - exit_error(OTHER_PROBLEM, "Can't open handler"); - if (cmd->type == CT_TABLE_DYING) { res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_DYING, mnl_nfct_dump_cb, cmd, NULL); - nfct_mnl_socket_close(sock); break; } else if (cmd->type == CT_TABLE_UNCONFIRMED) { res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_UNCONFIRMED, mnl_nfct_dump_cb, cmd, NULL); - nfct_mnl_socket_close(sock); break; } @@ -3315,10 +3290,7 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) printf("</conntrack>\n"); fflush(stdout); } - - nfct_mnl_socket_close(sock); break; - case EXP_LIST: cth = nfct_open(EXPECT, 0); if (!cth) @@ -3335,6 +3307,7 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) break; case CT_CREATE: + case CT_ADD: if ((cmd->options & CT_OPT_ORIG) && !(cmd->options & CT_OPT_REPL)) nfct_setobjopt(cmd->tmpl.ct, NFCT_SOPT_SETUP_REPLY); else if (!(cmd->options & CT_OPT_ORIG) && (cmd->options & CT_OPT_REPL)) @@ -3347,16 +3320,15 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) nfct_set_attr(cmd->tmpl.ct, ATTR_CONNLABELS, xnfct_bitmask_clone(cmd->tmpl.label_modify)); - res = nfct_mnl_socket_open(sock, 0); - if (res < 0) - exit_error(OTHER_PROBLEM, "Can't open netlink socket"); + if (cmd->command == CT_CREATE) + nl_flags = NLM_F_EXCL; - res = nfct_mnl_create(sock, NFNL_SUBSYS_CTNETLINK, - IPCTNL_MSG_CT_NEW, cmd->tmpl.ct); + res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family, + IPCTNL_MSG_CT_NEW, + NLM_F_CREATE | NLM_F_ACK | nl_flags, + NULL, cmd->tmpl.ct, NULL); if (res >= 0) counter++; - - nfct_mnl_socket_close(sock); break; case EXP_CREATE: @@ -3373,31 +3345,21 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) break; case CT_UPDATE: - cth = nfct_open(CONNTRACK, 0); - /* internal handler for delete_cb, otherwise we hit EILSEQ */ - ith = nfct_open(CONNTRACK, 0); - if (!cth || !ith) + if (nfct_mnl_socket_check_open(modifier_sock, 0) < 0) exit_error(OTHER_PROBLEM, "Can't open handler"); nfct_filter_init(cmd); - - nfct_callback_register(cth, NFCT_T_ALL, update_cb, cmd); - - res = nfct_query(cth, NFCT_Q_DUMP, &cmd->family); - nfct_close(ith); - nfct_close(cth); + res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET, mnl_nfct_update_cb, + cmd, NULL); break; case CT_DELETE: - cth = nfct_open(CONNTRACK, 0); - ith = nfct_open(CONNTRACK, 0); - if (!cth || !ith) + if (nfct_mnl_socket_check_open(modifier_sock, 0) < 0) exit_error(OTHER_PROBLEM, "Can't open handler"); nfct_filter_init(cmd); - nfct_callback_register(cth, NFCT_T_ALL, delete_cb, cmd); - filter_dump = nfct_filter_dump_create(); if (filter_dump == NULL) exit_error(OTHER_PROBLEM, "OOM"); @@ -3411,12 +3373,11 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) NFCT_FILTER_DUMP_L3NUM, cmd->family); - res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump); + res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET, mnl_nfct_delete_cb, + cmd, filter_dump); nfct_filter_dump_destroy(filter_dump); - - nfct_close(ith); - nfct_close(cth); break; case EXP_DELETE: @@ -3431,13 +3392,9 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) break; case CT_GET: - cth = nfct_open(CONNTRACK, 0); - if (!cth) - exit_error(OTHER_PROBLEM, "Can't open handler"); - - nfct_callback_register(cth, NFCT_T_ALL, dump_cb, cmd); - res = nfct_query(cth, NFCT_Q_GET, cmd->tmpl.ct); - nfct_close(cth); + res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family, + IPCTNL_MSG_CT_GET, 0, + mnl_nfct_dump_cb, cmd->tmpl.ct, cmd); break; case EXP_GET: @@ -3453,11 +3410,9 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) break; case CT_FLUSH: - cth = nfct_open(CONNTRACK, 0); - if (!cth) - exit_error(OTHER_PROBLEM, "Can't open handler"); - res = nfct_query(cth, NFCT_Q_FLUSH_FILTER, &cmd->family); - nfct_close(cth); + res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, cmd->family, + IPCTNL_MSG_CT_DELETE, NLM_F_ACK, NULL, NULL, NULL); + fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); fprintf(stderr,"connection tracking table has been emptied.\n"); break; @@ -3483,9 +3438,9 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) if (cmd->event_mask & CT_EVENT_F_DEL) nl_events |= NF_NETLINK_CONNTRACK_DESTROY; - res = nfct_mnl_socket_open(sock, nl_events); + res = nfct_mnl_socket_open(event_sock, nl_events); } else { - res = nfct_mnl_socket_open(sock, + res = nfct_mnl_socket_open(event_sock, NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); @@ -3524,7 +3479,7 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) while (1) { char buf[MNL_SOCKET_BUFFER_SIZE]; - res = mnl_socket_recvfrom(sock->mnl, buf, sizeof(buf)); + res = mnl_socket_recvfrom(event_sock->mnl, buf, sizeof(buf)); if (res < 0) { if (errno == ENOBUFS) { fprintf(stderr, @@ -3541,9 +3496,9 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) strerror(errno)); break; } - res = mnl_cb_run(buf, res, 0, 0, event_cb, cmd); + mnl_cb_run(buf, res, 0, 0, event_cb, cmd); } - mnl_socket_close(sock->mnl); + mnl_socket_close(event_sock->mnl); break; case EXP_EVENT: @@ -3577,21 +3532,14 @@ static int do_command_ct(const char *progname, struct ct_cmd *cmd) /* If we fail with netlink, fall back to /proc to ensure * backward compatibility. */ - if (nfct_mnl_socket_open(sock, 0) < 0) - goto try_proc_count; - - res = nfct_mnl_get(sock, - NFNL_SUBSYS_CTNETLINK, - IPCTNL_MSG_CT_GET_STATS, - nfct_global_stats_cb, AF_UNSPEC); - - nfct_mnl_socket_close(sock); + res = nfct_mnl_request(sock, NFNL_SUBSYS_CTNETLINK, AF_UNSPEC, + IPCTNL_MSG_CT_GET_STATS, 0, + nfct_global_stats_cb, NULL, NULL); /* don't look at /proc, we got the information via ctnetlink */ if (res >= 0) break; -try_proc_count: { #define NF_CONNTRACK_COUNT_PROC "/proc/sys/net/netfilter/nf_conntrack_count" FILE *fd; @@ -3623,15 +3571,10 @@ try_proc_count: /* If we fail with netlink, fall back to /proc to ensure * backward compatibility. */ - if (nfct_mnl_socket_open(sock, 0) < 0) - goto try_proc; - res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, nfct_stats_cb, NULL, NULL); - nfct_mnl_socket_close(sock); - /* don't look at /proc, we got the information via ctnetlink */ if (res >= 0) break; @@ -3642,15 +3585,10 @@ try_proc_count: /* If we fail with netlink, fall back to /proc to ensure * backward compatibility. */ - if (nfct_mnl_socket_open(sock, 0) < 0) - goto try_proc; - res = nfct_mnl_dump(sock, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET_STATS_CPU, nfexp_stats_cb, NULL, NULL); - nfct_mnl_socket_close(sock); - /* don't look at /proc, we got the information via ctnetlink */ if (res >= 0) break; @@ -3872,6 +3810,8 @@ static const char *ct_unsupp_cmd_file(const struct ct_cmd *cmd) int main(int argc, char *argv[]) { + struct nfct_mnl_socket *modifier_sock = &_modifier_sock; + struct nfct_mnl_socket *sock = &_sock; struct ct_cmd *cmd, *next; LIST_HEAD(cmd_list); int res = 0; @@ -3886,18 +3826,22 @@ int main(int argc, char *argv[]) register_gre(); register_unknown(); + if (nfct_mnl_socket_open(sock, 0) < 0) + exit_error(OTHER_PROBLEM, "Can't open handler"); + if (argc > 2 && (!strcmp(argv[1], "-R") || !strcmp(argv[1], "--load-file"))) { ct_parse_file(&cmd_list, argv[0], argv[2]); list_for_each_entry(cmd, &cmd_list, list) { - if (!(cmd->command & (CT_CREATE | CT_UPDATE | CT_DELETE | CT_FLUSH))) + if (!(cmd->command & + (CT_CREATE | CT_ADD | CT_UPDATE | CT_DELETE | CT_FLUSH))) exit_error(PARAMETER_PROBLEM, "Cannot use command `%s' with --load-file", ct_unsupp_cmd_file(cmd)); } list_for_each_entry_safe(cmd, next, &cmd_list, list) { - res |= do_command_ct(argv[0], cmd); + res |= do_command_ct(argv[0], cmd, sock); list_del(&cmd->list); free(cmd); } @@ -3907,10 +3851,12 @@ int main(int argc, char *argv[]) exit_error(OTHER_PROBLEM, "OOM"); do_parse(cmd, argc, argv); - do_command_ct(argv[0], cmd); - res = print_stats(cmd); + res = do_command_ct(argv[0], cmd, sock); + res |= print_stats(cmd); free(cmd); } + nfct_mnl_socket_close(sock); + nfct_mnl_socket_check_close(modifier_sock); return res < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |