From 5e8f64f46cb1dd71b0a94cb7dad87da00b8c5e32 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 15 May 2012 01:51:29 +0200 Subject: conntrackd: add cthelper infrastructure (+ example FTP helper) This patch adds the user-space helper infrastructure. It also contains the implementation of the FTP helper in user-space. There's one example file that you can use to configure conntrackd as user-space connection tracking helper under: doc/helper/conntrackd.conf Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 doc/helper/conntrackd.conf (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf new file mode 100644 index 0000000..711b309 --- /dev/null +++ b/doc/helper/conntrackd.conf @@ -0,0 +1,82 @@ +# +# Helper settings +# + +Helper { + # Before this, you have to make sure you have registered the `ftp' + # user-space helper stub via: + # + # nfct helper add ftp inet tcp + # + Type ftp inet tcp { + # + # Set NFQUEUE number you want to use to receive traffic from + # the kernel. + # + QueueNum 0 + # + # Set the Expectation policy for this helper. + # + Policy ftp { + # + # Maximum number of simultaneous expectations + # + ExpectMax 1 + # + # Maximum living time for one expectation (in seconds). + # + ExpectTimeout 300 + } + } +} + +# +# General settings +# +General { + # + # Set the nice value of the daemon, this value goes from -20 + # (most favorable scheduling) to 19 (least favorable). Using a + # very low value reduces the chances to lose state-change events. + # Default is 0 but this example file sets it to most favourable + # scheduling as this is generally a good idea. See man nice(1) for + # more information. + # + Nice -20 + + # + # Select a different scheduler for the daemon, you can select between + # RR and FIFO and the process priority (minimum is 0, maximum is 99). + # See man sched_setscheduler(2) for more information. Using a RT + # scheduler reduces the chances to overrun the Netlink buffer. + # + # Scheduler { + # Type FIFO + # Priority 99 + # } + + # + # Logfile: on (/var/log/conntrackd.log), off, or a filename + # Default: off + # + LogFile on + + # + # Syslog: on, off or a facility name (daemon (default) or local0..7) + # Default: off + # + #Syslog on + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /var/run/conntrackd.ctl + Backlog 20 + } +} -- cgit v1.2.3 From 969d93f14fffadb5cae67a7662484c1e064bbff1 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 May 2012 14:31:35 +0200 Subject: conntrackd: RPC helper added to cthelper How to use this helper in a few steps: 1) You can enable this helper via: nfct helper add rpc inet tcp nfct helper add rpc inet udp 2) Configure /etc/conntrackd/conntrackd.conf and launch it. 3) You can test this helper locally with the following rule-set: iptables -A OUTPUT -t raw -p udp -m udp --dport 111 -j CT --helper rpc iptables -A OUTPUT -t raw -p tcp -m tcp --dport 111 -j CT --helper rpc iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 111 -j ACCEPT iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -m udp --dport 111 -j ACCEPT iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -P OUTPUT DROP 4) Configure NFS and export some local directory. Then, mount it with version 3. mount.nfs -onfsvers=3 127.0.0.1:/srv/cvs /mnt/ You should see permanent expectations created for this. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 14 ++ src/helpers/Makefile.am | 7 +- src/helpers/rpc.c | 488 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 src/helpers/rpc.c (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 711b309..2bf99fa 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -28,6 +28,20 @@ Helper { ExpectTimeout 300 } } + Type rpc inet tcp { + QueueNum 1 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } + Type rpc inet udp { + QueueNum 2 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 2c9d63b..f441b29 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,9 +1,12 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la +pkglib_LTLIBRARIES = ct_helper_ftp.la \ + ct_helper_rpc.la ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) - +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) diff --git a/src/helpers/rpc.c b/src/helpers/rpc.c new file mode 100644 index 0000000..82493c2 --- /dev/null +++ b/src/helpers/rpc.c @@ -0,0 +1,488 @@ +/* + * (C) 2012 by Jozsef Kadlecsik + * (C) 2012 by Pablo Neira Ayuso + * + * Based on: RPC extension for conntrack. + * + * This port has been sponsored by Vyatta Inc. + * + * Original copyright notice: + * + * (C) 2000 by Marcelo Barbosa Lima + * (C) 2001 by Rusty Russell + * (C) 2002,2003 by Ian (Larry) Latter + * (C) 2004,2005 by David Stes + * + * 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 "network.h" /* for before and after */ +#include "helper.h" +#include "myct.h" +#include "log.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* RFC 1050: RPC: Remote Procedure Call Protocol Specification Version 2 */ +/* RFC 1014: XDR: External Data Representation Standard */ +#define SUPPORTED_RPC_VERSION 2 + +struct rpc_info { + /* XID */ + uint32_t xid; + /* program */ + uint32_t pm_prog; + /* program version */ + uint32_t pm_vers; + /* transport protocol: TCP|UDP */ + uint32_t pm_prot; +}; + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int +nf_nat_rpc(struct pkt_buff *pkt, int dir, struct nf_expect *exp, + uint8_t proto, uint32_t *port_ptr) +{ + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port, port; + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir); + + /* 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, proto); + 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. */ + 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 ret; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + ret = cthelper_add_expect(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + nfct_destroy(nat_tuple); + + if (port == 0) + return NF_DROP; + + *port_ptr = htonl(port); + + return NF_ACCEPT; +} + +#define OFFSET(o, n) ((o) += n) +#define ROUNDUP(n) ((((n) + 3)/4)*4) + +static int +rpc_call(const uint32_t *data, uint32_t offset, uint32_t datalen, + struct rpc_info *rpc_info) +{ + uint32_t p, r; + + /* RPC CALL message body */ + + /* call_body { + * rpcvers + * prog + * vers + * proc + * opaque_auth cred + * opaque_auth verf + * pmap + * } + * + * opaque_auth { + * flavour + * opaque[len] <= MAX_AUTH_BYTES + * } + */ + if (datalen < OFFSET(offset, 4*4 + 2*2*4)) { + pr_debug("RPC CALL: too short packet: %u < %u\n", + datalen, offset); + return -1; + } + /* Check rpcversion */ + p = IXDR_GET_INT32(data); + if (p != SUPPORTED_RPC_VERSION) { + pr_debug("RPC CALL: wrong rpcvers %u != %u\n", + p, SUPPORTED_RPC_VERSION); + return -1; + } + /* Skip non-portmap requests */ + p = IXDR_GET_INT32(data); + if (p != PMAPPROG) { + pr_debug("RPC CALL: not portmap %u != %lu\n", + p, PMAPPROG); + return -1; + } + /* Check portmap version */ + p = IXDR_GET_INT32(data); + if (p != PMAPVERS) { + pr_debug("RPC CALL: wrong portmap version %u != %lu\n", + p, PMAPVERS); + return -1; + } + /* Skip non PMAPPROC_GETPORT requests */ + p = IXDR_GET_INT32(data); + if (p != PMAPPROC_GETPORT) { + pr_debug("RPC CALL: not PMAPPROC_GETPORT %u != %lu\n", + p, PMAPPROC_GETPORT); + return -1; + } + /* Check and skip credentials */ + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC CALL: cred: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC CALL: invalid sized cred %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + if (datalen < OFFSET(offset, r)) { + pr_debug("RPC CALL: too short to carry cred: %u < %u, %u\n", + datalen, offset, r); + return -1; + } + data += r/4; + /* Check and skip verifier */ + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC CALL: verf: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC CALL: invalid sized verf %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + if (datalen < OFFSET(offset, r)) { + pr_debug("RPC CALL: too short to carry verf: %u < %u, %u\n", + datalen, offset, r); + return -1; + } + data += r/4; + /* pmap { + * prog + * vers + * prot + * port + * } + */ + /* Check CALL size */ + if (datalen != offset + 4*4) { + pr_debug("RPC CALL: invalid size to carry pmap: %u != %u\n", + datalen, offset + 4*4); + return -1; + } + rpc_info->pm_prog = IXDR_GET_INT32(data); + rpc_info->pm_vers = IXDR_GET_INT32(data); + rpc_info->pm_prot = IXDR_GET_INT32(data); + /* Check supported protocols */ + if (!(rpc_info->pm_prot == IPPROTO_TCP + || rpc_info->pm_prot == IPPROTO_UDP)) { + pr_debug("RPC CALL: unsupported protocol %u", + rpc_info->pm_prot); + return -1; + } + p = IXDR_GET_INT32(data); + /* Check port: must be zero */ + if (p != 0) { + pr_debug("RPC CALL: port is nonzero %u\n", + ntohl(p)); + return -1; + } + pr_debug("RPC CALL: processed: xid %u, prog %u, vers %u, prot %u\n", + rpc_info->xid, rpc_info->pm_prog, + rpc_info->pm_vers, rpc_info->pm_prot); + + return 0; +} + +static int +rpc_reply(uint32_t *data, uint32_t offset, uint32_t datalen, + struct rpc_info *rpc_info, uint32_t **port_ptr) +{ + uint16_t port; + uint32_t p, r; + + /* RPC REPLY message body */ + + /* reply_body { + * reply_stat + * xdr_union { + * accepted_reply + * rejected_reply + * } + * } + * accepted_reply { + * opaque_auth verf + * accept_stat + * xdr_union { + * port + * struct mismatch_info + * } + * } + */ + + /* Check size: reply status */ + if (datalen < OFFSET(offset, 4)) { + pr_debug("RPC REPL: too short, missing rp_stat: %u < %u\n", + datalen, offset); + return -1; + } + p = IXDR_GET_INT32(data); + /* Check accepted request */ + if (p != MSG_ACCEPTED) { + pr_debug("RPC REPL: not accepted %u != %u\n", + p, MSG_ACCEPTED); + return -1; + } + /* Check and skip verifier */ + if (datalen < OFFSET(offset, 2*4)) { + pr_debug("RPC REPL: too short, missing verf: %u < %u\n", + datalen, offset); + return -1; + } + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC REPL: verf: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC REPL: invalid sized verf %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + /* verifier + ac_stat + port */ + if (datalen != OFFSET(offset, r) + 2*4) { + pr_debug("RPC REPL: invalid size to carry verf and " + "success: %u != %u\n", + datalen, offset + 2*4); + return -1; + } + data += r/4; + /* Check success */ + p = IXDR_GET_INT32(data); + if (p != SUCCESS) { + pr_debug("RPC REPL: not success %u != %u\n", + p, SUCCESS); + return -1; + } + /* Get port */ + *port_ptr = data; + port = IXDR_GET_INT32(data); /* -Wunused-but-set-parameter */ + if (port == 0) { + pr_debug("RPC REPL: port is zero\n"); + return -1; + } + pr_debug("RPC REPL: processed: xid %u, prog %u, vers %u, " + "prot %u, port %u\n", + rpc_info->xid, rpc_info->pm_prog, rpc_info->pm_vers, + rpc_info->pm_prot, port); + return 0; +} + +static int +rpc_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + unsigned int offset = protoff, datalen; + uint32_t *data, *port_ptr = NULL, xid; + uint16_t port; + uint8_t proto = nfct_get_attr_u8(myct->ct, ATTR_L4PROTO); + enum msg_type rm_dir; + struct rpc_info *rpc_info = myct->priv_data; + union nfct_attr_grp_addr addr, daddr; + struct nf_expect *exp = NULL; + int ret = NF_ACCEPT; + + /* Until there's been traffic both ways, don't look into TCP packets. */ + if (proto == IPPROTO_TCP + && ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("TCP RPC: Conntrackinfo = %u\n", ctinfo); + return ret; + } + if (proto == IPPROTO_TCP) { + struct tcphdr *th = + (struct tcphdr *) (pktb_network_header(pkt) + protoff); + offset += th->doff * 4; + } else { + offset += sizeof(struct udphdr); + } + /* Skip broken headers */ + if (offset % 4) { + pr_debug("RPC: broken header: offset %u%%4 != 0\n", offset); + return ret; + } + + /* Take into Record Fragment header */ + if (proto == IPPROTO_TCP) + offset += 4; + + datalen = pktb_len(pkt); + data = (uint32_t *)(pktb_network_header(pkt) + offset); + + /* rpc_msg { + * xid + * direction + * xdr_union { + * call_body + * reply_body + * } + * } + */ + + /* Check minimal msg size: xid + direction */ + if (datalen < OFFSET(offset, 2*4)) { + pr_debug("RPC: too short packet: %u < %u\n", + datalen, offset); + return ret; + } + xid = IXDR_GET_INT32(data); + rm_dir = IXDR_GET_INT32(data); + + /* Check direction */ + if (!((rm_dir == CALL && dir == MYCT_DIR_ORIG) + || (rm_dir == REPLY && dir == MYCT_DIR_REPL))) { + pr_debug("RPC: rm_dir != dir %u != %u\n", rm_dir, dir); + goto out; + } + + if (rm_dir == CALL) { + if (rpc_call(data, offset, datalen, rpc_info) < 0) + goto out; + + rpc_info->xid = xid; + + return ret; + } else { + /* Check XID */ + if (xid != rpc_info->xid) { + pr_debug("RPC REPL: XID does not match: %u != %u\n", + xid, rpc_info->xid); + goto out; + } + if (rpc_reply(data, offset, datalen, rpc_info, &port_ptr) < 0) + goto out; + + port = IXDR_GET_INT32(port_ptr); + port = htons(port); + + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + cthelper_get_addr_dst(myct->ct, !dir, &daddr); + cthelper_get_addr_src(myct->ct, !dir, &addr); + + exp = nfexp_new(); + if (exp == NULL) + goto out; + + if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, + rpc_info->pm_prot, + NULL, &port, NF_CT_EXPECT_PERMANENT)) { + pr_debug("RPC: failed to init expectation\n"); + goto out_exp; + } + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. */ + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nf_nat_rpc(pkt, dir, exp, rpc_info->pm_prot, + port_ptr); + goto out_exp; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("RPC: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + } + } + +out_exp: + nfexp_destroy(exp); +out: + rpc_info->xid = 0; + return ret; +} + +static struct ctd_helper rpc_helper_tcp = { + .name = "rpc", + .l4proto = IPPROTO_TCP, + .cb = rpc_helper_cb, + .priv_data_len = sizeof(struct rpc_info), + .policy = { + { + .name = "rpc", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +static struct ctd_helper rpc_helper_udp = { + .name = "rpc", + .l4proto = IPPROTO_UDP, + .cb = rpc_helper_cb, + .priv_data_len = sizeof(struct rpc_info), + .policy = { + { + .name = "rpc", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) rpc_init(void); + +void rpc_init(void) +{ + helper_register(&rpc_helper_tcp); + helper_register(&rpc_helper_udp); +} -- cgit v1.2.3 From 40f4330e6b50ed2b198549b1006c6fcb349f5a3b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 May 2012 14:43:20 +0200 Subject: conntrackd: TNS helper added to cthelper Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 7 + src/helpers/Makefile.am | 7 +- src/helpers/tns.c | 407 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 src/helpers/tns.c (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 2bf99fa..80f1f92 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -42,6 +42,13 @@ Helper { ExpectTimeout 300 } } + Type tns inet tcp { + QueueNum 3 + Policy tns { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index f441b29..589b4f3 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,7 +1,8 @@ include $(top_srcdir)/Make_global.am pkglib_LTLIBRARIES = ct_helper_ftp.la \ - ct_helper_rpc.la + ct_helper_rpc.la \ + ct_helper_tns.la ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) @@ -10,3 +11,7 @@ ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) 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_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) diff --git a/src/helpers/tns.c b/src/helpers/tns.c new file mode 100644 index 0000000..5833fea --- /dev/null +++ b/src/helpers/tns.c @@ -0,0 +1,407 @@ +/* + * (C) 2012 by Jozsef Kadlecsik + * (C) 2012 by Pablo Neira Ayuso + * + * Sponsored by Vyatta Inc. + * + * 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 "network.h" /* for before and after */ +#include "helper.h" +#include "myct.h" +#include "log.h" + +#include /* for isdigit */ +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* TNS SQL*Net Version 2 */ +enum tns_types { + TNS_TYPE_CONNECT = 1, + TNS_TYPE_ACCEPT = 2, + TNS_TYPE_ACK = 3, + TNS_TYPE_REFUSE = 4, + TNS_TYPE_REDIRECT = 5, + TNS_TYPE_DATA = 6, + TNS_TYPE_NULL = 7, + TNS_TYPE_ABORT = 9, + TNS_TYPE_RESEND = 11, + TNS_TYPE_MARKER = 12, + TNS_TYPE_ATTENTION = 13, + TNS_TYPE_CONTROL = 14, + TNS_TYPE_MAX = 19, +}; + +struct tns_header { + uint16_t len; + uint16_t csum; + uint8_t type; + uint8_t reserved; + uint16_t header_csum; +}; + +struct tns_redirect { + uint16_t data_len; +}; + +struct tns_info { + /* Scan next DATA|REDIRECT packet */ + bool parse; +}; + +static int try_number(const char *data, size_t dlen, uint32_t array[], + int array_size, char sep, char term) +{ + uint32_t len; + int i; + + memset(array, 0, sizeof(array[0])*array_size); + + /* Keep data pointing at next char. */ + for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { + if (*data >= '0' && *data <= '9') { + array[i] = array[i]*10 + *data - '0'; + } + else if (*data == sep) + i++; + else { + /* Skip spaces. */ + if (*data == ' ') + continue; + /* Unexpected character; true if it's the + terminator and we're finished. */ + if (*data == term && i == array_size - 1) + return len; + pr_debug("Char %u (got %u nums) `%c' unexpected\n", + len, i, *data); + return 0; + } + } + pr_debug("Failed to fill %u numbers separated by %c\n", + array_size, sep); + return 0; +} + +/* Grab port: number up to delimiter */ +static int get_port(const char *data, size_t dlen, char delim, + struct myct_man *cmd) +{ + uint16_t tmp_port = 0; + uint32_t i; + + for (i = 0; i < dlen; i++) { + /* Finished? */ + if (data[i] == delim) { + if (tmp_port == 0) + break; + cmd->u.port = htons(tmp_port); + pr_debug("get_port: return %d\n", tmp_port); + return i + 1; + } + else if (data[i] >= '0' && data[i] <= '9') + tmp_port = tmp_port*10 + data[i] - '0'; + else if (data[i] == ' ') /* Skip spaces */ + continue; + else { /* Some other crap */ + pr_debug("get_port: invalid char `%c'\n", data[i]); + break; + } + } + return 0; +} + +/* (ADDRESS=(PROTOCOL=tcp)(DEV=x)(HOST=a.b.c.d)(PORT=a)) */ +/* FIXME: handle hostnames */ + +/* Returns 0, or length of port number */ +static unsigned int +find_pattern(struct pkt_buff *pkt, unsigned int dataoff, size_t dlen, + struct myct_man *cmd, unsigned int *numoff) +{ + const char *data = (const char *)pktb_network_header(pkt) + dataoff + + sizeof(struct tns_header); + int length, offset, ret; + uint32_t array[4]; + const char *p, *start; + + p = strstr(data, "("); + if (!p) + return 0; + + p = strstr(p+1, "HOST="); + if (!p) { + pr_debug("HOST= not found\n"); + return 0; + } + + start = p + strlen("HOST="); + offset = (int)(p - data) + strlen("HOST="); + *numoff = offset + sizeof(struct tns_header); + data += offset; + + length = try_number(data, dlen - offset, array, 4, '.', ')'); + if (length == 0) + return 0; + + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | + (array[2] << 8) | array[3]); + + p = strstr(data+length, "("); + if (!p) + return 0; + + p = strstr(p, "PORT="); + if (!p) { + pr_debug("PORT= not found\n"); + return 0; + } + + p += strlen("PORT="); + ret = get_port(p, dlen - offset - length, ')', cmd); + if (ret == 0) + return 0; + + p += ret; + return (int)(p - start); +} + +static inline uint16_t +nton(uint16_t len, unsigned int matchoff, unsigned int matchlen) +{ + uint32_t l = (uint32_t)ntohs(len) + matchoff - matchlen; + + return htons(l); +} + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int +nf_nat_tns(struct pkt_buff *pkt, struct tns_header *tns, struct nf_expect *exp, + struct nf_conntrack *ct, int dir, + unsigned int matchoff, unsigned int matchlen) +{ + union nfct_attr_grp_addr newip; + char buffer[sizeof("255.255.255.255)(PORT=65535)")]; + unsigned int buflen; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port, port; + + /* Connection will come from wherever this packet goes, hence !dir */ + cthelper_get_addr_dst(ct, !dir, &newip); + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir); + + /* 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. */ + 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 ret; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + ret = cthelper_add_expect(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + nfct_destroy(nat_tuple); + + if (port == 0) + return NF_DROP; + + buflen = snprintf(buffer, sizeof(buffer), + "%u.%u.%u.%u)(PORT=%u)", + ((unsigned char *)&newip.ip)[0], + ((unsigned char *)&newip.ip)[1], + ((unsigned char *)&newip.ip)[2], + ((unsigned char *)&newip.ip)[3], port); + if (!buflen) + goto out; + + if (!nfq_tcp_mangle_ipv4(pkt, matchoff, matchlen, buffer, buflen)) + goto out; + + if (buflen != matchlen) { + /* FIXME: recalculate checksum */ + tns->csum = 0; + tns->header_csum = 0; + + tns->len = nton(tns->len, matchlen, buflen); + if (tns->type == TNS_TYPE_REDIRECT) { + struct tns_redirect *r; + + r = (struct tns_redirect *)((char *)tns + sizeof(struct tns_header)); + + r->data_len = nton(r->data_len, matchlen, buflen); + } + } + + return NF_ACCEPT; + +out: + cthelper_del_expect(exp); + return NF_DROP; +} + +static int +tns_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct tcphdr *th; + struct tns_header *tns; + int dir = CTINFO2DIR(ctinfo); + unsigned int dataoff, datalen, numoff = 0, numlen; + struct tns_info *tns_info = myct->priv_data; + union nfct_attr_grp_addr addr; + struct nf_expect *exp = NULL; + struct myct_man cmd; + int ret = NF_ACCEPT; + + memset(&cmd, 0, sizeof(struct myct_man)); + memset(&addr, 0, sizeof(union nfct_attr_grp_addr)); + + /* Until there's been traffic both ways, don't look into TCP packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("TNS: Conntrackinfo = %u\n", ctinfo); + goto out; + } + /* Parse server direction only */ + if (dir != MYCT_DIR_REPL) { + pr_debug("TNS: skip client direction\n"); + goto out; + } + + th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); + + dataoff = protoff + th->doff * 4; + datalen = pktb_len(pkt); + + if (datalen < sizeof(struct tns_header)) { + pr_debug("TNS: skip packet with short header\n"); + goto out; + } + + tns = (struct tns_header *)(pktb_network_header(pkt) + dataoff); + + if (tns->type == TNS_TYPE_REDIRECT) { + struct tns_redirect *redirect; + + dataoff += sizeof(struct tns_header); + datalen -= sizeof(struct tns_header); + redirect = (struct tns_redirect *)(pktb_network_header(pkt) + dataoff); + tns_info->parse = false; + + if (ntohs(redirect->data_len) == 0) { + tns_info->parse = true; + goto out; + } + goto parse; + } + + /* Parse only the very next DATA packet */ + if (!(tns_info->parse && tns->type == TNS_TYPE_DATA)) { + tns_info->parse = false; + goto out; + } +parse: + numlen = find_pattern(pkt, dataoff, datalen, &cmd, &numoff); + tns_info->parse = false; + if (!numlen) + goto out; + + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + cthelper_get_addr_src(myct->ct, !dir, &addr); + + exp = nfexp_new(); + if (exp == NULL) + goto out; + + if (cthelper_expect_init(exp, myct->ct, 0, + &addr, &cmd.u3, + IPPROTO_TCP, + NULL, &cmd.u.port, 0)) { + pr_debug("TNS: failed to init expectation\n"); + goto out_exp; + } + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. + */ + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nf_nat_tns(pkt, tns, exp, myct->ct, dir, + numoff + sizeof(struct tns_header), numlen); + goto out_exp; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("TNS: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + goto out_exp; + } + goto out; + +out_exp: + nfexp_destroy(exp); +out: + return ret; +} + +static struct ctd_helper tns_helper = { + .name = "tns", + .l4proto = IPPROTO_TCP, + .cb = tns_helper_cb, + .priv_data_len = sizeof(struct tns_info), + .policy = { + [0] = { + .name = "tns", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) tns_init(void); + +void tns_init(void) +{ + helper_register(&tns_helper); +} -- cgit v1.2.3 From febb3cceac1889fb6558b8ef40ac733072fdcd47 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 10 Sep 2012 13:17:24 +0200 Subject: conntrackd: cthelper: add QueueLen option This patch adds the QueueLen option, that allows you to increase the maximum number of packets waiting in the nfnetlink_queue to receive a verdict from userspace. Rising the default value (1024) is useful to avoid hitting the following error message: "nf_queue: full at X entries, dropping packets(s)". Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 13 +++++++++++++ include/helper.h | 1 + src/cthelper.c | 6 ++++-- src/read_config_lex.l | 1 + src/read_config_yy.y | 23 +++++++++++++++++++++-- 5 files changed, 40 insertions(+), 4 deletions(-) (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 80f1f92..56f5162 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -14,6 +14,16 @@ Helper { # the kernel. # QueueNum 0 + + # + # Maximum number of packets waiting in the queue to receive + # a verdict from user-space. Default is 1024. + # + # Rise value if you hit the following error message: + # "nf_queue: full at X entries, dropping packets(s)" + # + QueueLen 10240 + # # Set the Expectation policy for this helper. # @@ -30,6 +40,7 @@ Helper { } Type rpc inet tcp { QueueNum 1 + QueueLen 10240 Policy rpc { ExpectMax 1 ExpectTimeout 300 @@ -37,6 +48,7 @@ Helper { } Type rpc inet udp { QueueNum 2 + QueueLen 10240 Policy rpc { ExpectMax 1 ExpectTimeout 300 @@ -44,6 +56,7 @@ Helper { } Type tns inet tcp { QueueNum 3 + QueueLen 10240 Policy tns { ExpectMax 1 ExpectTimeout 300 diff --git a/include/helper.h b/include/helper.h index 329fd2d..9d96fb7 100644 --- a/include/helper.h +++ b/include/helper.h @@ -35,6 +35,7 @@ struct ctd_helper { struct ctd_helper_instance { struct list_head head; uint32_t queue_num; + uint32_t queue_len; uint16_t l3proto; uint8_t l4proto; struct ctd_helper *helper; diff --git a/src/cthelper.c b/src/cthelper.c index c119869..307be96 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -353,8 +353,9 @@ static int cthelper_setup(struct ctd_helper_instance *cur) nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS, NFCT_HELPER_STATUS_ENABLED); - dlog(LOG_NOTICE, "configuring helper `%s' with queuenum=%d", - cur->helper->name, cur->queue_num); + dlog(LOG_NOTICE, "configuring helper `%s' with queuenum=%d and " + "queuelen=%d", cur->helper->name, cur->queue_num, + cur->queue_len); for (j=0; jqueue_len)); if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { dlog(LOG_ERR, "failed to send configuration"); diff --git a/src/read_config_lex.l b/src/read_config_lex.l index 31fa32e..bec2d81 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -144,6 +144,7 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k] "ErrorQueueLength" { return T_ERROR_QUEUE_LENGTH; } "Helper" { return T_HELPER; } "QueueNum" { return T_HELPER_QUEUE_NUM; } +"QueueLen" { return T_HELPER_QUEUE_LEN; } "Policy" { return T_HELPER_POLICY; } "ExpectMax" { return T_HELPER_EXPECT_MAX; } "ExpectTimeout" { return T_HELPER_EXPECT_TIMEOUT; } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index c9235d3..72a9654 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -56,6 +56,7 @@ struct stack symbol_stack; enum { SYMBOL_HELPER_QUEUE_NUM, + SYMBOL_HELPER_QUEUE_LEN, SYMBOL_HELPER_POLICY_EXPECT_ROOT, SYMBOL_HELPER_EXPECT_POLICY_LEAF, }; @@ -86,8 +87,8 @@ enum { %token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE %token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH %token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC -%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_POLICY T_HELPER_EXPECT_MAX -%token T_HELPER_EXPECT_TIMEOUT +%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_QUEUE_LEN T_HELPER_POLICY +%token T_HELPER_EXPECT_TIMEOUT T_HELPER_EXPECT_MAX %token T_IP T_PATH_VAL %token T_NUMBER @@ -1639,6 +1640,13 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' stack_item_free(e); break; } + case SYMBOL_HELPER_QUEUE_LEN: { + int *qlen = (int *) &e->data; + + helper_inst->queue_len = *qlen; + stack_item_free(e); + break; + } case SYMBOL_HELPER_POLICY_EXPECT_ROOT: { struct ctd_helper_policy *pol = (struct ctd_helper_policy *) &e->data; @@ -1696,6 +1704,17 @@ helper_type: T_HELPER_QUEUE_NUM T_NUMBER stack_item_push(&symbol_stack, e); }; +helper_type: T_HELPER_QUEUE_LEN T_NUMBER +{ + int *qlen; + struct stack_item *e; + + e = stack_item_alloc(SYMBOL_HELPER_QUEUE_LEN, sizeof(int)); + qlen = (int *) e->data; + *qlen = $2; + stack_item_push(&symbol_stack, e); +}; + helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}' { struct stack_item *e; -- cgit v1.2.3 From 36118bfc4901b0978d2c8f17912fe91ec66f35e8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Aug 2013 14:21:04 +0200 Subject: conntrackd: helpers: add DHCPv6 helper This patch adds support for the DHCPv6 helper. 1) nfct helper add dhcpv6 inet6 udp 2) ip6tables -I OUTPUT -t raw -p udp --sport 546 -j CT --helper dhcpv6 3) run conntrackd You should see: % conntrack -L exp -f ipv6 279 proto=17 src=:: dst=ff02::1:2 sport=0 dport=546 mask-src=:: mask-dst=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff sport=0 dport=65535 master-src=fe80::221:ccff:fe4a:7f9c master-dst=ff02::1:2 sport=546 dport=547 PERMANENT class=0 helper=dhcpv6 Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 8 +++ src/helpers/Makefile.am | 7 ++- src/helpers/dhcpv6.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/helpers/dhcpv6.c (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 56f5162..358ad10 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -62,6 +62,14 @@ Helper { ExpectTimeout 300 } } + Type dhcpv6 inet6 udp { + QueueNum 4 + QueueLen 10240 + Policy dhcpv6 { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 589b4f3..59524f5 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,9 +1,14 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la \ +pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ + ct_helper_ftp.la \ ct_helper_rpc.la \ ct_helper_tns.la +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) ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) 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 + * + * Adapted from: + * + * DHCPv6 multicast connection tracking helper. + * + * (c) 2012 Google Inc. + * + * Original author: Darren Willis + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.2.3 From 92246dcc1fdcf222302a42926e0e95af2c30463e Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Sat, 8 Mar 2014 21:13:34 +0000 Subject: conntrackd: userspace SSDP helper Here is a patch which adds a userspace conntrack helper for the SSDP protocol. This is based on the code found at: http://marc.info/?t=132945775100001&r=1&w=2 I'm not sure how to get my laptop to play at IPv6, so I've not tested this part, but I've tested the IPv4 section and it works. Signed-off-by: Ash Hughes Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 8 +++ doc/manual/conntrack-tools.tmpl | 1 + src/helpers/Makefile.am | 7 ++- src/helpers/ssdp.c | 134 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/helpers/ssdp.c (limited to 'doc/helper') diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 358ad10..d2d94a9 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -70,6 +70,14 @@ Helper { ExpectTimeout 300 } } + Type ssdp inet udp { + QueueNum 5 + QueueLen 10240 + Policy ssdp { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index f21a4ff..d23dec5 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -890,6 +890,7 @@ maintainance. Oracle*TNS, to support its special Redirect message. NFSv3, mind that version 4 does not require this helper. FTP (this helper is also available in kernel-space). +SSDP. The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers): diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index fe28e83..78ef7aa 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -6,7 +6,8 @@ pkglib_LTLIBRARIES = ct_helper_amanda.la \ ct_helper_rpc.la \ ct_helper_tftp.la \ ct_helper_tns.la \ - ct_helper_sane.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) @@ -35,3 +36,7 @@ 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/ssdp.c b/src/helpers/ssdp.c new file mode 100644 index 0000000..2713f23 --- /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 + * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c), + * :http://marc.info/?t=132945775100001&r=1&w=2 + * (C) 2012 Ian Pilcher + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + u_int16_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); +} -- cgit v1.2.3 From 882bb111285a3a4465995b4af03040a291145d7b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2015 19:18:38 +0200 Subject: nfct: update syntax in documentation Since dd73ceecdbe8 ("nfct: Update syntax to specify command before subsystem") the command comes before the object type. Update documentation accordingly. Signed-off-by: Pablo Neira Ayuso --- README.nfct | 6 +++--- doc/helper/conntrackd.conf | 2 +- doc/manual/conntrack-tools.tmpl | 4 ++-- nfct.8 | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'doc/helper') diff --git a/README.nfct b/README.nfct index 4d8e6cc..89dd328 100644 --- a/README.nfct +++ b/README.nfct @@ -9,11 +9,11 @@ more similar to `ip' and `nftables' tools (in the long run!). The `nfct' command line tool allows you to define custom timeout policies: -# nfct timeout add custom-tcp-policy1 inet tcp established 100 +# nfct add timeout custom-tcp-policy1 inet tcp established 100 You can also retrieve the existing timeout policies with: -# nfct timeout list +# nfct list timeout .tcp-policy = { .l3proto = 2, .l4proto = 6, @@ -39,7 +39,7 @@ Then, you can use the timeout policy with iptables: You can define policies for other protocols as well, eg: -# nfct timeout add custom-udp-policy1 inet udp unreplied 10 replied 20 +# nfct add timeout custom-udp-policy1 inet udp unreplied 10 replied 20 And attach them via iptables: diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index d2d94a9..5c07509 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -6,7 +6,7 @@ Helper { # Before this, you have to make sure you have registered the `ftp' # user-space helper stub via: # - # nfct helper add ftp inet tcp + # nfct add helper ftp inet tcp # Type ftp inet tcp { # diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index d23dec5..87a792e 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -899,8 +899,8 @@ maintainance. Register user-space helper: -nfct helper add rpc inet udp -nfct helper add rpc inet tcp +nfct add helper rpc inet udp +nfct add helper rpc inet tcp This registers the portmapper helper for both UDP and TCP (NFSv3 traffic goes both over TCP and UDP). diff --git a/nfct.8 b/nfct.8 index 863fe12..336d9cd 100644 --- a/nfct.8 +++ b/nfct.8 @@ -40,7 +40,7 @@ Displays the version information. Displays the help message. .SH EXAMPLE .TP -.B nfct timeout add test-tcp inet tcp established 100 close 10 close_wait 10 +.B nfct add timeout test-tcp inet tcp established 100 close 10 close_wait 10 .TP This creates a timeout policy for tcp using 100 seconds for the ESTABLISHED state, 10 seconds for CLOSE state and 10 seconds for the CLOSE_WAIT state. .TP -- cgit v1.2.3