diff --git a/src/config.h b/src/config.h index 39041eb..26e7919 100644 --- a/src/config.h +++ b/src/config.h @@ -284,4 +284,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..6026ec6 100644 --- a/src/kytunblock.c +++ b/src/kytunblock.c @@ -251,10 +251,10 @@ 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) { 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 +273,20 @@ static int connbytes_pkts(const struct sk_buff *skb) { return -1; #endif - pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); + yct->orig_packets = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); + yct_set_mask_attr(YCTATTR_ORIG_PACKETS, yct); + yct->orig_bytes = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].bytes); + yct_set_mask_attr(YCTATTR_ORIG_BYTES, yct); + yct->repl_packets = atomic64_read(&counters[IP_CT_DIR_REPLY].packets); + yct_set_mask_attr(YCTATTR_REPL_PACKETS, yct); + yct->repl_bytes = atomic64_read(&counters[IP_CT_DIR_REPLY].bytes); + yct_set_mask_attr(YCTATTR_REPL_BYTES, yct); + yct->connmark = READ_ONCE(ct->mark); + yct_set_mask_attr(YCTATTR_CONNMARK, yct); + yct->id = nf_ct_get_id(ct); + yct_set_mask_attr(YCTATTR_CONNID, yct); - return pkts; + return 0; } /* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ @@ -346,6 +357,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 +368,28 @@ 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"); + } + + lgtrace("[CONNTRACK TRACE] orig_packets=%llu repl_packets=%llu orig_bytes=%llu repl_bytes=%llu connmark=%d id=%ud\n", pd.yct.orig_packets, pd.yct.repl_packets, pd.yct.orig_bytes, pd.yct.repl_bytes, pd.yct.connmark, pd.yct.id); + + 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: 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..1eb5159 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 @@ free_buffs: 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,18 @@ 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; } + 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");