diff --git a/.gitignore b/.gitignore index 82104c4..4549012 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ modules.order Module.symvers *.so *.ko +*.a !/.github diff --git a/README.md b/README.md index d26d3d6..e977668 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,9 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--instaflush` Used with tracing. Flushes the buffer instantly, without waiting for explicit new line. Highly useful for debugging crushes. -- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything. +- `--no-gso` Disables support for TCP fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything. + +- `--no-conntrack` Disables support for conntrack in youtubeUnblock. - `--no-ipv6` Disables support for ipv6. May be useful if you don't want for ipv6 socket to be opened. @@ -332,6 +334,11 @@ Where you have to replace 192.168.. with ip of your television. * send fake sni EPERM: Fake SNI is out-of-state thing and will likely corrupt the connection (the behavior is expected). conntrack considers it as an invalid packet. By default OpenWRT set up to drop outgoing packets like this one. You may delete nftables/iptables rule that drops packets with invalid conntrack state, but I don't recommend to do this. The step 3 is better solution. * Step 3, ultimate solution. Use mark (don't confuse with connmark). The youtubeUnblock uses mark internally to avoid infinity packet loops (when the packet is sent by youtubeUnblock but on next step handled by itself). Currently it uses mark (1 << 15) = 32768. You should put iptables/nftables that ultimately accepts such marks at the very start of the filter OUTPUT chain: `iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT` or `nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept`. +### Conntrack + +youtubeUnblock *optionally* depends on conntrack. +For kernel module, if conntrack breaks dependencies, compile it with `make kmake EXTRA_CFLAGS="-DNO_CONNTRACK"` to disable it completly. + ## Compilation Before compilation make sure `gcc`, `make`, `autoconf`, `automake`, `pkg-config` and `libtool` is installed. For Fedora `glibc-static` should be installed as well. diff --git a/deps/cyclone/libcyclone.a b/deps/cyclone/libcyclone.a deleted file mode 100644 index ca3fc90..0000000 Binary files a/deps/cyclone/libcyclone.a and /dev/null differ diff --git a/kmake.mk b/kmake.mk index ea4b181..1bca027 100644 --- a/kmake.mk +++ b/kmake.mk @@ -9,11 +9,13 @@ LDFLAGS := KERNEL_BUILDER_MAKEDIR:=/lib/modules/$(shell uname -r)/build +override EXTRA_CFLAGS += -DPKG_VERSION=\"$(PKG_FULLVERSION)\" + .PHONY: kmake kload kunload kreload kclean kmclean xclean kmake: kmod kmod: - $(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) EXTRA_CFLAGS='-DPKG_VERSION=\"$(PKG_FULLVERSION)\"' modules + $(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) EXTRA_CFLAGS='$(EXTRA_CFLAGS)' modules kload: insmod kyoutubeUnblock.ko diff --git a/src/args.c b/src/args.c index 8b8eafc..2f6a2c8 100644 --- a/src/args.c +++ b/src/args.c @@ -273,6 +273,7 @@ enum { OPT_THREADS, OPT_SILENT, OPT_NO_GSO, + OPT_NO_CONNTRACK, OPT_QUEUE_NUM, OPT_UDP_MODE, OPT_UDP_FAKE_SEQ_LEN, @@ -322,6 +323,7 @@ static struct option long_opt[] = { {"trace", 0, 0, OPT_TRACE}, {"instaflush", 0, 0, OPT_INSTAFLUSH}, {"no-gso", 0, 0, OPT_NO_GSO}, + {"no-conntrack", 0, 0, OPT_NO_CONNTRACK}, {"no-ipv6", 0, 0, OPT_NO_IPV6}, {"daemonize", 0, 0, OPT_DAEMONIZE}, {"noclose", 0, 0, OPT_NOCLOSE}, @@ -386,6 +388,7 @@ void print_usage(const char *argv0) { printf("\t--trace\n"); printf("\t--instaflush\n"); printf("\t--no-gso\n"); + printf("\t--no-conntrack\n"); printf("\t--no-ipv6\n"); printf("\t--daemonize\n"); printf("\t--noclose\n"); @@ -459,7 +462,20 @@ int yparse_args(int argc, char *argv[]) { rep_config.verbose = VERBOSE_INFO; break; case OPT_NO_GSO: +#ifndef KERNEL_SPACE rep_config.use_gso = 0; +#else + lgerr("--no-gso is not supported in kernel space"); + goto invalid_opt; +#endif + break; + case OPT_NO_CONNTRACK: +#ifndef KERNEL_SPACE + rep_config.use_conntrack = 0; +#else + lgerr("--no-conntrack is not supported in kernel space. Compile with make kmake EXTRA_CFLAGS=\"-DNO_CONNTRACK\" instead." ); + goto invalid_opt; +#endif break; case OPT_NO_IPV6: rep_config.use_ipv6 = 0; @@ -1017,6 +1033,9 @@ size_t print_config(char *buffer, size_t buffer_size) { if (!config.use_gso) { print_cnf_buf("--no-gso"); } + if (!config.use_conntrack) { + print_cnf_buf("--no-conntrack"); + } #endif #ifdef KERNEL_SPACE diff --git a/src/config.h b/src/config.h index 39041eb..141d43b 100644 --- a/src/config.h +++ b/src/config.h @@ -113,6 +113,7 @@ struct config_t { int threads; int use_gso; int use_ipv6; + int use_conntrack; unsigned int mark; int daemonize; // Same as daemon() noclose @@ -269,6 +270,7 @@ enum { \ .verbose = VERBOSE_DEBUG, \ .use_gso = 1, \ + .use_conntrack = 1, \ \ .first_section = NULL, \ .last_section = NULL, \ @@ -284,4 +286,42 @@ struct config_t config = default_config_set; \ config->last_section = &(config.default_config) \ +struct ytb_conntrack { + uint32_t mask; + + uint64_t orig_packets; + uint64_t repl_packets; + uint64_t orig_bytes; + uint64_t repl_bytes; + uint32_t connmark; + uint32_t id; +}; + +enum yct_attrs { + YCTATTR_ORIG_PACKETS, + YCTATTR_REPL_PACKETS, + YCTATTR_ORIG_BYTES, + YCTATTR_REPL_BYTES, + YCTATTR_CONNMARK, + YCTATTR_CONNID, +}; +/* enum yct_attrs attr, struct ytb_conntrack * yct */ +#define yct_set_mask_attr(attr, yct) \ + ((yct)->mask |= (1 << (attr))) + +/* enum yct_attrs attr, const struct ytb_conntrack * yct */ +#define yct_is_mask_attr(attr, yct) \ + (((yct)->mask & (1 << (attr))) == (1 << (attr))) + +/* enum yct_attrs attr, struct ytb_conntrack * yct */ +#define yct_del_mask_attr(attr, yct) \ + (yct)->mask &= ~(1 << (attr)) + + +struct packet_data { + const uint8_t *payload; + size_t payload_len; + struct ytb_conntrack yct; +}; + #endif /* YTB_CONFIG_H */ diff --git a/src/kytunblock.c b/src/kytunblock.c index 013706b..483cc31 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -35,8 +35,16 @@ #include #include +#ifdef IS_ENABLED +#if !(IS_ENABLED(CONFIG_NF_CONNTRACK)) +#define NO_CONNTRACK +#endif /* IS CONNTRACK ENABLED */ +#endif /* ifdef IS_ENABLED */ + +#ifndef NO_CONNTRACK #include #include +#endif #include "mangle.h" #include "config.h" @@ -251,10 +259,12 @@ struct instance_config_t instance_config = { .send_delayed_packet = delay_packet_send, }; -static int connbytes_pkts(const struct sk_buff *skb) { +static int conntrack_parse(const struct sk_buff *skb, + struct ytb_conntrack *yct) { +#ifndef NO_CONNTRACK + const struct nf_conn *ct; enum ip_conntrack_info ctinfo; - u_int64_t pkts = 0; const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); @@ -273,9 +283,36 @@ static int connbytes_pkts(const struct sk_buff *skb) { return -1; #endif - pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + yct->orig_packets = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); + yct->orig_bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes); + yct->repl_packets = atomic64_read(&counters[IP_CT_DIR_REPLY].packets); + yct->repl_bytes = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes); +#else + yct->orig_packets = counters[IP_CT_DIR_ORIGINAL].packets; + yct->orig_bytes = counters[IP_CT_DIR_ORIGINAL].bytes; + yct->repl_packets = counters[IP_CT_DIR_REPLY].packets; + yct->repl_bytes = counters[IP_CT_DIR_REPLY].bytes; +#endif + yct_set_mask_attr(YCTATTR_ORIG_PACKETS, yct); + yct_set_mask_attr(YCTATTR_ORIG_BYTES, yct); + yct_set_mask_attr(YCTATTR_REPL_PACKETS, yct); + yct_set_mask_attr(YCTATTR_REPL_BYTES, yct); + +#if defined(CONFIG_NF_CONNTRACK_MARK) + yct->connmark = READ_ONCE(ct->mark); + yct_set_mask_attr(YCTATTR_CONNMARK, yct); +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + yct->id = nf_ct_get_id(ct); + yct_set_mask_attr(YCTATTR_CONNID, yct); +#endif + +#endif /* NO_CONNTRACK */ - return pkts; + return 0; } /* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ @@ -346,6 +383,7 @@ static int connbytes_pkts(const struct sk_buff *skb) { static NF_CALLBACK(ykb_nf_hook, skb) { int ret; + struct packet_data pd = {0}; if ((skb->mark & config.mark) == config.mark) goto accept; @@ -356,16 +394,25 @@ static NF_CALLBACK(ykb_nf_hook, skb) { if (skb->len > MAX_PACKET_SIZE) goto accept; - if (config.connbytes_limit != 0 && connbytes_pkts(skb) > config.connbytes_limit) + ret = conntrack_parse(skb, &pd.yct); + if (ret < 0) { + lgtrace("[TRACE] conntrack_parse error code\n"); + } + + if (config.connbytes_limit != 0 && yct_is_mask_attr(YCTATTR_ORIG_PACKETS, &pd.yct) && pd.yct.orig_packets > config.connbytes_limit) goto accept; + ret = skb_linearize(skb); if (ret < 0) { lgerror(ret, "Cannot linearize"); goto accept; } - int vrd = process_packet(skb->data, skb->len); + pd.payload = skb->data; + pd.payload_len = skb->len; + + int vrd = process_packet(&pd); switch(vrd) { case PKT_ACCEPT: @@ -397,6 +444,10 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = { }; static int __init ykb_init(void) { +#ifdef NO_CONNTRACK + lgwarning("Conntrack disabled."); +#endif + int ret = 0; ret = init_config(&config); if (ret < 0) goto err; diff --git a/src/mangle.c b/src/mangle.c index ae05305..a584195 100644 --- a/src/mangle.c +++ b/src/mangle.c @@ -32,7 +32,10 @@ #include "linux/inet.h" #endif -int process_packet(const uint8_t *raw_payload, size_t raw_payload_len) { +int process_packet(const struct packet_data *pd) { + const uint8_t *raw_payload = pd->payload; + uint32_t raw_payload_len = pd->payload_len; + if (raw_payload_len > MAX_PACKET_SIZE) { return PKT_ACCEPT; } diff --git a/src/mangle.h b/src/mangle.h index 51f190d..77567fd 100644 --- a/src/mangle.h +++ b/src/mangle.h @@ -22,6 +22,7 @@ #include "types.h" #include "tls.h" +#include "config.h" #define PKT_ACCEPT 0 #define PKT_DROP 1 @@ -32,7 +33,7 @@ * Processes the packet and returns verdict. * This is the primary function that traverses the packet. */ -int process_packet(const uint8_t *packet, size_t packet_len); +int process_packet(const struct packet_data *pd); /** diff --git a/src/youtubeUnblock.c b/src/youtubeUnblock.c index ce81007..1fe671f 100644 --- a/src/youtubeUnblock.c +++ b/src/youtubeUnblock.c @@ -196,6 +196,231 @@ static int close_raw6_socket(void) { return 0; } +/* + * libnetfilter_conntrack + * (C) 2005-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. + */ + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ + CTA_COUNTERS_PAD, + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + CTA_TUPLE_MASTER, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, + CTA_SECMARK, /* obsolete */ + CTA_ZONE, + CTA_SECCTX, + CTA_TIMESTAMP, + CTA_MARK_MASK, + CTA_LABELS, + CTA_LABELS_MASK, + CTA_SYNPROXY, + CTA_FILTER, + CTA_STATUS_MASK, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum { + __DIR_ORIG, + __DIR_REPL +}; + +static int +yct_parse_counters_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_COUNTERS_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_COUNTERS_PACKETS: + case CTA_COUNTERS_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + return MNL_CB_ERROR; + break; + case CTA_COUNTERS32_PACKETS: + case CTA_COUNTERS32_BYTES: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +yct_parse_counters(const struct nlattr *attr, struct ytb_conntrack *yct, + int dir) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1] = {0}; + + if (mnl_attr_parse_nested(attr, yct_parse_counters_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_COUNTERS_PACKETS] || tb[CTA_COUNTERS32_PACKETS]) { + uint64_t packets_counter; + if (tb[CTA_COUNTERS32_PACKETS]) { + packets_counter = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_PACKETS])); + } + if (tb[CTA_COUNTERS_PACKETS]) { + packets_counter = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_PACKETS])); + } + switch(dir) { + case __DIR_ORIG: + yct->orig_packets = packets_counter; + yct_set_mask_attr(YCTATTR_ORIG_PACKETS, yct); + break; + case __DIR_REPL: + yct->repl_packets = packets_counter; + yct_set_mask_attr(YCTATTR_REPL_PACKETS, yct); + break; + } + } + if (tb[CTA_COUNTERS_BYTES] || tb[CTA_COUNTERS32_BYTES]) { + uint64_t bytes_counter; + if (tb[CTA_COUNTERS32_BYTES]) { + bytes_counter = + ntohl(mnl_attr_get_u32(tb[CTA_COUNTERS32_BYTES])); + } + if (tb[CTA_COUNTERS_BYTES]) { + bytes_counter = + be64toh(mnl_attr_get_u64(tb[CTA_COUNTERS_BYTES])); + } + + switch(dir) { + case __DIR_ORIG: + yct->orig_bytes = bytes_counter; + yct_set_mask_attr(YCTATTR_ORIG_BYTES, yct); + break; + case __DIR_REPL: + yct->repl_bytes = bytes_counter; + yct_set_mask_attr(YCTATTR_REPL_BYTES, yct); + break; + } + } + + return 0; +} + +static int +yct_parse_conntrack_attr_cb(const struct nlattr *attr, void *data){ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case CTA_TUPLE_ORIG: + case CTA_TUPLE_REPLY: + case CTA_TUPLE_MASTER: + case CTA_NAT_SEQ_ADJ_ORIG: + case CTA_NAT_SEQ_ADJ_REPLY: + case CTA_PROTOINFO: + case CTA_COUNTERS_ORIG: + case CTA_COUNTERS_REPLY: + case CTA_HELP: + case CTA_SECCTX: + case CTA_TIMESTAMP: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + case CTA_STATUS: + case CTA_TIMEOUT: + case CTA_MARK: + case CTA_SECMARK: + case CTA_USE: + case CTA_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case CTA_ZONE: + if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) + return MNL_CB_ERROR; + break; + case CTA_NAT_SRC: + case CTA_NAT_DST: + /* deprecated */ + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int +yct_payload_parse(const void *payload, size_t payload_len, + uint16_t l3num, struct ytb_conntrack *yct) +{ + struct nlattr *tb[CTA_MAX+1] = {0}; + + if (mnl_attr_parse_payload(payload, payload_len, + yct_parse_conntrack_attr_cb, tb) < 0) + return -1; + + if (tb[CTA_MARK]) { + yct->connmark = ntohl(mnl_attr_get_u32(tb[CTA_MARK])); + yct_set_mask_attr(YCTATTR_CONNMARK, yct); + } + + + if (tb[CTA_COUNTERS_ORIG]) { + if (yct_parse_counters(tb[CTA_COUNTERS_ORIG], + yct, __DIR_ORIG) < 0) + return -1; + } + + if (tb[CTA_ID]) { + yct->id = ntohl(mnl_attr_get_u32(tb[CTA_ID])); + yct_set_mask_attr(YCTATTR_CONNID, yct); + } + + if (tb[CTA_COUNTERS_REPLY]) { + if (yct_parse_counters(tb[CTA_COUNTERS_REPLY], + yct, __DIR_REPL) < 0) + return -1; + } + + return 0; +} + static int send_raw_ipv4(const uint8_t *pkt, size_t pktlen) { int ret; if (pktlen > AVAILABLE_MTU) return -ENOMEM; @@ -338,15 +563,6 @@ static int send_raw_socket(const uint8_t *pkt, size_t pktlen) { return ret; } -struct packet_data { - uint32_t id; - uint16_t hw_proto; - uint8_t hook; - - void *payload; - uint16_t payload_len; -}; - // Per-queue data. Passed to queue_cb. struct queue_data { struct mnl_socket **_nl; @@ -418,6 +634,12 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { struct nfqnl_msg_packet_hdr *ph = NULL; struct nlattr *attr[NFQA_MAX+1] = {0}; struct packet_data packet = {0}; + struct ytb_conntrack *yct = &packet.yct; + struct nfgenmsg *nfg; + struct nlmsghdr *verdnlh; + int ret; + uint16_t l3num; + uint32_t id; if (nfq_nlmsg_parse(nlh, attr) < 0) { lgerror(-errno, "Attr parse"); @@ -430,39 +652,55 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { return MNL_CB_ERROR; } + nfg = mnl_nlmsg_get_payload(nlh); + l3num = nfg->nfgen_family; + ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); - packet.id = ntohl(ph->packet_id); - packet.hw_proto = ntohs(ph->hw_protocol); - packet.hook = ph->hook; + id = ntohl(ph->packet_id); + packet.payload_len = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); packet.payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); if (attr[NFQA_CAP_LEN] != NULL && ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])) != packet.payload_len) { lgerr("The packet was truncated! Skip!"); - return fallback_accept_packet(packet.id, *qdata); + return fallback_accept_packet(id, *qdata); } if (attr[NFQA_MARK] != NULL) { // Skip packets sent by rawsocket to escape infinity loop. if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & config.mark) == config.mark) { - return fallback_accept_packet(packet.id, *qdata); + return fallback_accept_packet(id, *qdata); } } + if (attr[NFQA_CT] != NULL) { + ret = yct_payload_parse( + mnl_attr_get_payload(attr[NFQA_CT]), + mnl_attr_get_payload_len(attr[NFQA_CT]), + l3num, yct); + if (ret < 0) { + lgerror(ret, "Cannot parse CT"); - struct nlmsghdr *verdnlh; + goto ct_out; + } + + lgtrace("[CONNTRACK TRACE] orig_packets=%lu repl_packets=%lu orig_bytes=%lu repl_bytes=%lu connmark=%d id=%ud\n", yct->orig_packets, yct->repl_packets, yct->orig_bytes, yct->repl_bytes, yct->connmark, yct->id); + + } + +ct_out: verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata->queue_num); - int ret = process_packet(packet.payload, packet.payload_len); + ret = process_packet(&packet); switch (ret) { case PKT_DROP: - nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP); + nfq_nlmsg_verdict_put(verdnlh, id, NF_DROP); break; default: - nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT); + nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT); break; } @@ -544,10 +782,20 @@ int init_queue(int queue_num) { nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); + unsigned int cfg_flags = NFQA_CFG_F_GSO | NFQA_CFG_F_CONNTRACK | NFQA_CFG_F_FAIL_OPEN; + unsigned int cfg_mask = 0; + if (config.use_gso) { - mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); - mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); + cfg_mask |= NFQA_CFG_F_GSO; + } + if (config.use_conntrack) { + cfg_mask |= NFQA_CFG_F_CONNTRACK; } + cfg_mask |= NFQA_CFG_F_FAIL_OPEN; + + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(cfg_flags)); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(cfg_mask)); + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { lgerror(-errno, "mnl_socket_send");