diff --git a/backend/nfqueue/main.go b/backend/nfqueue/main.go index 507bfc7..66356da 100644 --- a/backend/nfqueue/main.go +++ b/backend/nfqueue/main.go @@ -132,6 +132,8 @@ func handle_packets(packets <-chan netfilter.NFPacket, filter_table_channel chan } case p := <-packets: { + p.SetVerdict(netfilter.NF_ACCEPT) + break transport_layer := p.Packet.TransportLayer() data := transport_layer.LayerPayload() if len(data) > 0 { diff --git a/backend/nfqueue/nfqueue.cpp b/backend/nfqueue/nfqueue.cpp new file mode 100644 index 0000000..c8eb29f --- /dev/null +++ b/backend/nfqueue/nfqueue.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Tins; + +typedef bool NetFilterQueueCallback(const uint8_t*,uint32_t); +typedef struct mnl_socket* NetFilterQueueSocket; + +PDU * find_transport_layer(PDU* pkt){ + while(pkt != NULL){ + if (pkt->pdu_type() == PDU::TCP || pkt->pdu_type() == PDU::UDP) { + return pkt; + } + pkt = pkt->inner_pdu(); + } + return pkt; +} + +template +class NetfilterQueue { + public: + size_t BUF_SIZE = 0xffff + (MNL_SOCKET_BUFFER_SIZE/2); + char *buf = NULL; + unsigned int portid; + u_int16_t queue_num; + NetFilterQueueSocket nl = NULL; + + NetfilterQueue(u_int16_t queue_num): queue_num(queue_num) { + + struct nlmsghdr *nlh; + nl = mnl_socket_open(NETLINK_NETFILTER); + + if (nl == NULL) { + throw std::runtime_error( "mnl_socket_open" ); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + throw std::runtime_error( "mnl_socket_bind" ); + } + portid = mnl_socket_get_portid(nl); + + buf = (char*) malloc(BUF_SIZE); + if (!buf) { + throw std::runtime_error( "allocate receive buffer" ); + } + + nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + free(buf); + throw std::runtime_error( "mnl_socket_send" ); + } + + nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); + nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); + + 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)); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + free(buf); + throw std::runtime_error( "mnl_socket_send" ); + } + + } + + void run(){ + /* + * ENOBUFS is signalled to userspace when packets were lost + * on kernel side. In most cases, userspace isn't interested + * in this information, so turn it off. + */ + int ret = 1; + mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); + + for (;;) { + ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE); + if (ret == -1) { + throw std::runtime_error( "mnl_socket_recvfrom" ); + } + + ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, nl); + if (ret < 0){ + throw std::runtime_error( "mnl_cb_run" ); + } + } + } + + ~NetfilterQueue() { + if (buf != NULL) { + free(buf); + buf = NULL; + } + mnl_socket_close(nl); + } + private: + + static int queue_cb(const struct nlmsghdr *nlh, void *data) + { + NetFilterQueueSocket nl = (NetFilterQueueSocket)data; + //Extract attributes from the nlmsghdr + struct nlattr *attr[NFQA_MAX+1] = {}; + + if (nfq_nlmsg_parse(nlh, attr) < 0) { + perror("problems parsing"); + return MNL_CB_ERROR; + } + if (attr[NFQA_PACKET_HDR] == NULL) { + fputs("metaheader not set\n", stderr); + return MNL_CB_ERROR; + } + //Get Payload + uint16_t plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); + void *payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); + + + //Return result to the kernel + struct nfqnl_msg_packet_hdr *ph = (nfqnl_msg_packet_hdr*) mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); + struct nfgenmsg *nfg = (nfgenmsg *)mnl_nlmsg_get_payload(nlh); + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh_verdict; + struct nlattr *nest; + + nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); + + #define PKT_OPS \ + PDU *transport_layer = find_transport_layer(&packet); \ + if(transport_layer->inner_pdu() == nullptr){ \ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );\ + }else{\ + int size = transport_layer->inner_pdu()->size();\ + if(callback_func((const uint8_t*)payload+plen - size, size)){\ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT );\ + } else{\ + if (transport_layer->pdu_type() == PDU::TCP){\ + ((TCP *)transport_layer)->release_inner_pdu();\ + ((TCP *)transport_layer)->set_flag(TCP::FIN,1);\ + ((TCP *)transport_layer)->set_flag(TCP::ACK,1);\ + ((TCP *)transport_layer)->set_flag(TCP::SYN,0);\ + nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.serialize().data(), packet.size());\ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); \ + }else{\ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP );\ + }\ + }\ + }\ + + if ( (((uint8_t*)payload)[0] & 0xf0) == 0x40 ){ + IP packet = IP((uint8_t*)payload,plen); + PKT_OPS + }else{ + IPv6 packet = IPv6((uint8_t*)payload,plen); + PKT_OPS + } + + /* example to set the connmark. First, start NFQA_CT section: */ + nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT); + + /* then, add the connmark attribute: */ + mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42)); + /* more conntrack attributes, e.g. CTA_LABELS could be set here */ + + /* end conntrack section */ + mnl_attr_nest_end(nlh_verdict, nest); + + if (mnl_socket_sendto(nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) { + throw std::runtime_error( "mnl_socket_send" ); + } + + return MNL_CB_OK; + } + +}; + + +bool callb(const uint8_t *data, uint32_t len){ + return true; +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: %s [queue_num]\n", argv[0]); + exit(EXIT_FAILURE); + } + NetfilterQueue* queue = new NetfilterQueue(atoi(argv[1])); + queue->run(); + return 0; +}