diff options
Diffstat (limited to 'src/helpers')
-rw-r--r-- | src/helpers/Makefile.am | 29 | ||||
-rw-r--r-- | src/helpers/amanda.c | 203 | ||||
-rw-r--r-- | src/helpers/dhcpv6.c | 123 | ||||
-rw-r--r-- | src/helpers/ftp.c | 3 | ||||
-rw-r--r-- | src/helpers/sane.c | 173 | ||||
-rw-r--r-- | src/helpers/ssdp.c | 134 | ||||
-rw-r--r-- | src/helpers/tftp.c | 138 | ||||
-rw-r--r-- | src/helpers/tns.c | 1 |
8 files changed, 801 insertions, 3 deletions
diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 589b4f3..78ef7aa 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,8 +1,21 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la \ +pkglib_LTLIBRARIES = ct_helper_amanda.la \ + ct_helper_dhcpv6.la \ + ct_helper_ftp.la \ ct_helper_rpc.la \ - ct_helper_tns.la + ct_helper_tftp.la \ + ct_helper_tns.la \ + ct_helper_sane.la \ + ct_helper_ssdp.la + +ct_helper_amanda_la_SOURCES = amanda.c +ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_dhcpv6_la_SOURCES = dhcpv6.c +ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) @@ -12,6 +25,18 @@ ct_helper_rpc_la_SOURCES = rpc.c ct_helper_rpc_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_rpc_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) +ct_helper_tftp_la_SOURCES = tftp.c +ct_helper_tftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_tftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + ct_helper_tns_la_SOURCES = tns.c ct_helper_tns_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_tns_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_sane_la_SOURCES = sane.c +ct_helper_sane_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_sane_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_ssdp_la_SOURCES = ssdp.c +ct_helper_ssdp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_ssdp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c new file mode 100644 index 0000000..9e6c4e7 --- /dev/null +++ b/src/helpers/amanda.c @@ -0,0 +1,203 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Adapted from: + * + * Amanda extension for IP connection tracking + * + * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca> + * based on HW's ip_conntrack_irc.c as well as other modules + * (C) 2006 Patrick McHardy <kaber@trash.net> + * + * 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. + */ +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_expect *exp) +{ + char buffer[sizeof("65535")]; + uint16_t port, initial_port; + unsigned int ret; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + /* Connection comes from client. */ + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); + + /* libnetfilter_conntrack needs this */ + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); + nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + + /* When you see the packet, we need to NAT it the same as the + * this one (ie. same IP: it will be TCP and master is UDP). */ + nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(initial_port); port != 0; port++) { + int res; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + res = cthelper_add_expect(exp); + if (res == 0) + break; + else if (res != -EBUSY) { + port = 0; + break; + } + } + + if (port == 0) { + pr_debug("all ports in use\n"); + return NF_DROP; + } + + sprintf(buffer, "%u", port); + ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, + strlen(buffer)); + if (ret != NF_ACCEPT) { + pr_debug("cannot mangle packet\n"); + cthelper_del_expect(exp); + } + return ret; +} + +static char amanda_buffer[65536]; +static unsigned int master_timeout = 300; + +enum amanda_strings { + SEARCH_CONNECT, + SEARCH_NEWLINE, + SEARCH_DATA, + SEARCH_MESG, + SEARCH_INDEX, +}; + +static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; + +static int +amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct nf_expect *exp; + char *data, *data_limit, *tmp; + unsigned int dataoff, i; + uint16_t port, len; + int ret = NF_ACCEPT; + struct iphdr *iph; + union nfct_attr_grp_addr saddr, daddr; + + /* Only look at packets from the Amanda server */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + /* increase the UDP timeout of the master connection as replies from + * Amanda clients to the server can be quite delayed */ + nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); + + /* No data? */ + iph = (struct iphdr *)pktb_network_header(pkt); + dataoff = iph->ihl*4 + sizeof(struct udphdr); + if (dataoff >= pktb_len(pkt)) { + pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); + return NF_ACCEPT; + } + + memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, + pktb_len(pkt) - dataoff); + data = amanda_buffer; + data_limit = amanda_buffer + pktb_len(pkt) - dataoff; + *data_limit = '\0'; + + /* Search for the CONNECT string */ + data = strstr(data, "CONNECT "); + if (!data) + goto out; + data += strlen("CONNECT "); + + /* Only search first line. */ + if ((tmp = strchr(data, '\n'))) + *tmp = '\0'; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + char *match = strstr(data, conns[i]); + if (!match) + continue; + tmp = data = match + strlen(conns[i]); + port = strtoul(data, &data, 10); + len = data - tmp; + if (port == 0 || len > 5) + break; + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_TCP, NULL, &port, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, + len, exp); + } else + myct->exp = exp; + } +out: + return ret; +} + +static struct ctd_helper amanda_helper = { + .name = "amanda", + .l4proto = IPPROTO_UDP, + .cb = amanda_helper_cb, + .policy = { + [0] = { + .name = "amanda", + .expect_max = ARRAY_SIZE(conns), + .expect_timeout = 180, + }, + }, +}; + +void __attribute__ ((constructor)) amanda_init(void); + +void amanda_init(void) +{ + helper_register(&amanda_helper); +} diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c new file mode 100644 index 0000000..73632ec --- /dev/null +++ b/src/helpers/dhcpv6.c @@ -0,0 +1,123 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Adapted from: + * + * DHCPv6 multicast connection tracking helper. + * + * (c) 2012 Google Inc. + * + * Original author: Darren Willis <djw@google.com> + * + * 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. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +#define DHCPV6_CLIENT_PORT 546 + +static uint16_t dhcpv6_port; + +/* Timeouts for DHCPv6 replies, in seconds, indexed by message type. */ +static const int dhcpv6_timeouts[] = { + 0, /* No message has type 0. */ + 120, /* Solicit. */ + 0, /* Advertise. */ + 30, /* Request. */ + 4, /* Confirm. */ + 600, /* Renew. */ + 600, /* Rebind. */ + 0, /* Reply. */ + 1, /* Release. */ + 1, /* Decline. */ + 0, /* Reconfigure. */ + 120, /* Information Request. */ + 0, /* Relay-forward. */ + 0 /* Relay-reply. */ +}; + +static inline int ipv6_addr_is_multicast(const struct in6_addr *addr) +{ + return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); +} + +static int +dhcpv6_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct iphdr *iph = (struct iphdr *)pktb_network_header(pkt); + struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb_network_header(pkt); + int dir = CTINFO2DIR(ctinfo); + union nfct_attr_grp_addr addr; + struct nf_expect *exp; + uint8_t *dhcpv6_msg_type; + + if (iph->version != 6 || !ipv6_addr_is_multicast(&ip6h->ip6_dst)) + return NF_ACCEPT; + + dhcpv6_msg_type = pktb_network_header(pkt) + protoff + sizeof(struct udphdr); + if (*dhcpv6_msg_type > ARRAY_SIZE(dhcpv6_timeouts)) { + printf("Dropping DHCPv6 message with bad type %u\n", + *dhcpv6_msg_type); + return NF_DROP; + } + + exp = nfexp_new(); + if (exp == NULL) + return NF_ACCEPT; + + cthelper_get_addr_src(myct->ct, dir, &addr); + + if (cthelper_expect_init(exp, myct->ct, 0, NULL, &addr, + IPPROTO_UDP, NULL, &dhcpv6_port, + NF_CT_EXPECT_PERMANENT)) { + nfexp_destroy(exp); + return NF_DROP; + } + + myct->exp = exp; + + if (dhcpv6_timeouts[*dhcpv6_msg_type] > 0) { + nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, + dhcpv6_timeouts[*dhcpv6_msg_type]); + } + + return NF_ACCEPT; +} + +static struct ctd_helper dhcpv6_helper = { + .name = "dhcpv6", + .l4proto = IPPROTO_UDP, + .cb = dhcpv6_helper_cb, + .policy = { + [0] = { + .name = "dhcpv6", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) dhcpv6_init(void); + +void dhcpv6_init(void) +{ + dhcpv6_port = htons(DHCPV6_CLIENT_PORT); + helper_register(&dhcpv6_helper); +} diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c index 2c8dcd6..24ee877 100644 --- a/src/helpers/ftp.c +++ b/src/helpers/ftp.c @@ -25,6 +25,7 @@ #include <ctype.h> /* for isdigit */ #include <errno.h> +#define _GNU_SOURCE #include <netinet/tcp.h> #include <libmnl/libmnl.h> @@ -58,7 +59,7 @@ enum nf_ct_ftp_type { }; static int -get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) +get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, uint8_t term) { const char *end; int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), diff --git a/src/helpers/sane.c b/src/helpers/sane.c new file mode 100644 index 0000000..c30f4ba --- /dev/null +++ b/src/helpers/sane.c @@ -0,0 +1,173 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Port this helper to userspace. + */ + +/* SANE connection tracking helper + * (SANE = Scanner Access Now Easy) + * For documentation about the SANE network protocol see + * http://www.sane-project.org/html/doc015.html + */ + +/* + * Copyright (C) 2007 Red Hat, Inc. + * Author: Michal Schmidt <mschmidt@redhat.com> + * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c): + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> + * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> + * (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <netinet/ip.h> +#define _GNU_SOURCE +#include <netinet/tcp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +enum sane_state { + SANE_STATE_NORMAL, + SANE_STATE_START_REQUESTED, +}; + +struct sane_request { + uint32_t RPC_code; +#define SANE_NET_START 7 /* RPC code */ + + uint32_t handle; +}; + +struct sane_reply_net_start { + uint32_t status; +#define SANE_STATUS_SUCCESS 0 + + uint16_t zero; + uint16_t port; + /* other fields aren't interesting for conntrack */ +}; + +struct nf_ct_sane_master { + enum sane_state state; +}; + +static int +sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + unsigned int dataoff, datalen; + const struct tcphdr *th; + void *sb_ptr; + int ret = NF_ACCEPT; + int dir = CTINFO2DIR(ctinfo); + struct nf_ct_sane_master *ct_sane_info = myct->priv_data; + struct nf_expect *exp; + struct sane_request *req; + struct sane_reply_net_start *reply; + union nfct_attr_grp_addr saddr; + union nfct_attr_grp_addr daddr; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY) + return NF_ACCEPT; + + th = (struct tcphdr *)(pktb_network_header(pkt) + protoff); + + /* No data? */ + dataoff = protoff + th->doff * 4; + if (dataoff >= pktb_len(pkt)) + return NF_ACCEPT; + + datalen = pktb_len(pkt) - dataoff; + + sb_ptr = pktb_network_header(pkt) + dataoff; + + if (dir == MYCT_DIR_ORIG) { + if (datalen != sizeof(struct sane_request)) + goto out; + + req = sb_ptr; + if (req->RPC_code != htonl(SANE_NET_START)) { + /* Not an interesting command */ + ct_sane_info->state = SANE_STATE_NORMAL; + goto out; + } + + /* We're interested in the next reply */ + ct_sane_info->state = SANE_STATE_START_REQUESTED; + goto out; + } + + /* Is it a reply to an uninteresting command? */ + if (ct_sane_info->state != SANE_STATE_START_REQUESTED) + goto out; + + /* It's a reply to SANE_NET_START. */ + ct_sane_info->state = SANE_STATE_NORMAL; + + if (datalen < sizeof(struct sane_reply_net_start)) { + pr_debug("nf_ct_sane: NET_START reply too short\n"); + goto out; + } + + reply = sb_ptr; + if (reply->status != htonl(SANE_STATUS_SUCCESS)) { + /* saned refused the command */ + pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n", + ntohl(reply->status)); + goto out; + } + + /* Invalid saned reply? Ignore it. */ + if (reply->zero != 0) + goto out; + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_TCP, NULL, &reply->port, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + myct->exp = exp; +out: + return ret; +} + +static struct ctd_helper sane_helper = { + .name = "sane", + .l4proto = IPPROTO_TCP, + .priv_data_len = sizeof(struct nf_ct_sane_master), + .cb = sane_helper_cb, + .policy = { + [0] = { + .name = "sane", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) sane_init(void) +{ + helper_register(&sane_helper); +} diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c new file mode 100644 index 0000000..bc41087 --- /dev/null +++ b/src/helpers/ssdp.c @@ -0,0 +1,134 @@ +/* + * SSDP connection tracking helper + * (SSDP = Simple Service Discovery Protocol) + * For documentation about SSDP see + * http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol + * + * Copyright (C) 2014 Ashley Hughes <ashley.hughes@blueyonder.co.uk> + * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c), + * :http://marc.info/?t=132945775100001&r=1&w=2 + * (C) 2012 Ian Pilcher <arequipeno@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <arpa/inet.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_tcp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +#define SSDP_MCAST_ADDR "239.255.255.250" +#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */ +#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */ + +#define SSDP_M_SEARCH "M-SEARCH" +#define SSDP_M_SEARCH_SIZE (sizeof SSDP_M_SEARCH - 1) + +static int ssdp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + int ret = NF_ACCEPT; + union nfct_attr_grp_addr daddr, saddr, taddr; + struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt); + int good_packet = 0; + struct nf_expect *exp; + uint16_t port; + unsigned int dataoff; + void *sb_ptr; + + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + switch (nfct_get_attr_u8(myct->ct, ATTR_L3PROTO)) { + case AF_INET: + inet_pton(AF_INET, SSDP_MCAST_ADDR, &(taddr.ip)); + if (daddr.ip == taddr.ip) + good_packet = 1; + break; + case AF_INET6: + inet_pton(AF_INET6, UPNP_MCAST_LL_ADDR, &(taddr.ip6)); + if (daddr.ip6[0] == taddr.ip6[0] && + daddr.ip6[1] == taddr.ip6[1] && + daddr.ip6[2] == taddr.ip6[2] && + daddr.ip6[3] == taddr.ip6[3]) { + good_packet = 1; + break; + } + inet_pton(AF_INET6, UPNP_MCAST_SL_ADDR, &(taddr.ip6)); + if (daddr.ip6[0] == taddr.ip6[0] && + daddr.ip6[1] == taddr.ip6[1] && + daddr.ip6[2] == taddr.ip6[2] && + daddr.ip6[3] == taddr.ip6[3]) { + good_packet = 1; + break; + } + break; + default: + break; + } + + if (!good_packet) { + pr_debug("ssdp_help: destination address not multicast; ignoring\n"); + return NF_ACCEPT; + } + + /* No data? Ignore */ + dataoff = net_hdr->ihl*4 + sizeof(struct udphdr); + if (dataoff >= pktb_len(pkt)) { + pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n"); + return NF_ACCEPT; + } + + sb_ptr = pktb_network_header(pkt) + dataoff; + + if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) { + pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n"); + return NF_ACCEPT; + } + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + if (cthelper_expect_init(exp, myct->ct, 0, NULL, &saddr, + IPPROTO_UDP, NULL, &port, + NF_CT_EXPECT_PERMANENT)) { + nfexp_destroy(exp); + return NF_DROP; + } + myct->exp = exp; + + return ret; +} + +static struct ctd_helper ssdp_helper = { + .name = "ssdp", + .l4proto = IPPROTO_UDP, + .priv_data_len = 0, + .cb = ssdp_helper_cb, + .policy = { + [0] = { + .name = "ssdp", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) ssdp_init(void) +{ + helper_register(&ssdp_helper); +} diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c new file mode 100644 index 0000000..45591c6 --- /dev/null +++ b/src/helpers/tftp.c @@ -0,0 +1,138 @@ +/* + * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Adapted from: + * + * (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu> + * (C) 2006-2012 Patrick McHardy <kaber@trash.net> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include <errno.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/udp.h> +#include <libmnl/libmnl.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_queue/libnetfilter_queue.h> +#include <libnetfilter_queue/libnetfilter_queue_udp.h> +#include <libnetfilter_queue/pktbuff.h> +#include <linux/netfilter.h> + +struct tftphdr { + uint16_t opcode; +}; + +#define TFTP_OPCODE_READ 1 +#define TFTP_OPCODE_WRITE 2 +#define TFTP_OPCODE_DATA 3 +#define TFTP_OPCODE_ACK 4 +#define TFTP_OPCODE_ERROR 5 + +static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo, + struct nf_conntrack *ct, struct nf_expect *exp) +{ + struct nf_conntrack *nat_tuple; + static uint32_t zero[4] = { 0, 0, 0, 0 }; + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) { + case AF_INET: + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); + break; + case AF_INET6: + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero); + nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero); + break; + } + nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, + nfct_get_attr_u16(ct, ATTR_PORT_SRC)); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL); + nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + return NF_ACCEPT; +} + +static int +tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + const struct tftphdr *tfh; + struct nf_expect *exp; + unsigned int ret = NF_ACCEPT; + union nfct_attr_grp_addr saddr, daddr; + uint16_t dport; + + tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr)); + + switch (ntohs(tfh->opcode)) { + case TFTP_OPCODE_READ: + case TFTP_OPCODE_WRITE: + /* RRQ and WRQ works the same way */ + exp = nfexp_new(); + if (exp == NULL) { + pr_debug("cannot alloc expectation\n"); + return NF_DROP; + } + + cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr); + cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_UDP, NULL, &dport, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) + ret = nat_tftp(pkt, ctinfo, myct->ct, exp); + + myct->exp = exp; + break; + case TFTP_OPCODE_DATA: + case TFTP_OPCODE_ACK: + pr_debug("Data/ACK opcode\n"); + break; + case TFTP_OPCODE_ERROR: + pr_debug("Error opcode\n"); + break; + default: + pr_debug("Unknown opcode\n"); + } + return ret; +} + +static struct ctd_helper tftp_helper = { + .name = "tftp", + .l4proto = IPPROTO_UDP, + .cb = tftp_helper_cb, + .policy = { + [0] = { + .name = "tftp", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) tftp_init(void) +{ + helper_register(&tftp_helper); +} diff --git a/src/helpers/tns.c b/src/helpers/tns.c index 5833fea..2b4fed4 100644 --- a/src/helpers/tns.c +++ b/src/helpers/tns.c @@ -18,6 +18,7 @@ #include <ctype.h> /* for isdigit */ #include <errno.h> +#define _GNU_SOURCE #include <netinet/tcp.h> #include <libmnl/libmnl.h> |