From d2e942c76f87ea061d5e8643007f1d4c3ed39694 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 10 May 2012 10:15:09 +0200 Subject: src: integrate nfct into the conntrack-tools tree I'll need for the upcoming cthelper infrastructure. Moreover, we avoid more fragmentation in the netfilter user-space utilities. And the plan is that `nfct' will replace `conntrack' at some point. Signed-off-by: Pablo Neira Ayuso --- src/nfct.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/nfct.c (limited to 'src/nfct.c') diff --git a/src/nfct.c b/src/nfct.c new file mode 100644 index 0000000..db629e7 --- /dev/null +++ b/src/nfct.c @@ -0,0 +1,116 @@ +/* + * (C) 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nfct.h" + +static int nfct_cmd_version(int argc, char *argv[]); +static int nfct_cmd_help(int argc, char *argv[]); + +static void usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s subsystem command [parameters]...\n", + argv[0]); +} + +void nfct_perror(const char *msg) +{ + if (errno == 0) { + fprintf(stderr, "nfct v%s: %s\n", VERSION, msg); + } else { + fprintf(stderr, "nfct v%s: %s: %s\n", + VERSION, msg, strerror(errno)); + } +} + +int main(int argc, char *argv[]) +{ + int subsys = NFCT_SUBSYS_NONE, ret = 0; + + if (argc < 2) { + usage(argv); + exit(EXIT_FAILURE); + } + if (strncmp(argv[1], "timeout", strlen(argv[1])) == 0) { + subsys = NFCT_SUBSYS_TIMEOUT; + } else if (strncmp(argv[1], "version", strlen(argv[1])) == 0) + subsys = NFCT_SUBSYS_VERSION; + else if (strncmp(argv[1], "help", strlen(argv[1])) == 0) + subsys = NFCT_SUBSYS_HELP; + else { + fprintf(stderr, "nfct v%s: Unknown subsystem: %s\n", + VERSION, argv[1]); + usage(argv); + exit(EXIT_FAILURE); + } + + switch(subsys) { + case NFCT_SUBSYS_TIMEOUT: + ret = nfct_cmd_timeout_parse_params(argc, argv); + break; + case NFCT_SUBSYS_VERSION: + ret = nfct_cmd_version(argc, argv); + break; + case NFCT_SUBSYS_HELP: + ret = nfct_cmd_help(argc, argv); + break; + } + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +static const char version_msg[] = + "nfct v%s: utility for the Netfilter's Connection Tracking System\n" + "Copyright (C) 2012 Pablo Neira Ayuso \n" + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it under " + "certain \nconditions; see LICENSE file distributed in this package " + "for details.\n"; + +static int nfct_cmd_version(int argc, char *argv[]) +{ + printf(version_msg, VERSION); + return 0; +} + +static const char help_msg[] = + "nfct v%s: utility for the Netfilter's Connection Tracking System\n" + "Usage: %s command [parameters]...\n\n" + "Subsystem:\n" + " timeout\t\tAllows definition of fine-grain timeout policies\n" + " version\t\tDisplay version and disclaimer\n" + " help\t\t\tDisplay this help message\n" + "Commands:\n" + " list [reset]\t\tList the accounting object table (and reset)\n" + " add object-name\tAdd new accounting object to table\n" + " delete object-name\tDelete existing accounting object\n" + " get object-name\tGet existing accounting object\n" + " flush\t\t\tFlush accounting object table\n"; + +static int nfct_cmd_help(int argc, char *argv[]) +{ + printf(help_msg, VERSION, argv[0]); + return 0; +} -- cgit v1.2.3 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 --- configure.ac | 9 +- doc/helper/conntrackd.conf | 82 ++++ include/Makefile.am | 3 +- include/conntrackd.h | 21 +- include/helper.h | 104 +++++ include/linux/netfilter/Makefile.am | 2 +- include/linux/netfilter/nfnetlink_cthelper.h | 55 +++ include/linux/netfilter/nfnetlink_queue.h | 99 +++++ include/myct.h | 43 ++ include/nfct.h | 10 + include/stack.h | 28 ++ src/Makefile.am | 24 +- src/cthelper.c | 521 ++++++++++++++++++++++ src/expect.c | 214 +++++++++ src/helpers.c | 76 ++++ src/helpers/Makefile.am | 9 + src/helpers/ftp.c | 599 ++++++++++++++++++++++++++ src/main.c | 3 +- src/nfct-extensions/helper.c | 619 +++++++++++++++++++++++++++ src/nfct.c | 6 + src/read_config_lex.l | 5 + src/read_config_yy.y | 200 +++++++++ src/run.c | 11 + src/stack.c | 56 +++ src/utils.c | 243 +++++++++++ 25 files changed, 3030 insertions(+), 12 deletions(-) create mode 100644 doc/helper/conntrackd.conf create mode 100644 include/helper.h create mode 100644 include/linux/netfilter/nfnetlink_cthelper.h create mode 100644 include/linux/netfilter/nfnetlink_queue.h create mode 100644 include/myct.h create mode 100644 include/stack.h create mode 100644 src/cthelper.c create mode 100644 src/expect.c create mode 100644 src/helpers.c create mode 100644 src/helpers/Makefile.am create mode 100644 src/helpers/ftp.c create mode 100644 src/nfct-extensions/helper.c create mode 100644 src/stack.c create mode 100644 src/utils.c (limited to 'src/nfct.c') diff --git a/configure.ac b/configure.ac index 8cd5626..5d1860d 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,9 @@ AM_INIT_AUTOMAKE([-Wall foreign subdir-objects dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""]) +AC_SUBST([libdl_LIBS]) + AC_PROG_CC AC_DISABLE_STATIC AM_PROG_LIBTOOL @@ -52,9 +55,11 @@ else fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) -PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.0]) +PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.1]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) @@ -114,5 +119,5 @@ dnl debug/src/Makefile dnl extensions/Makefile dnl src/Makefile]) -AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile]) AC_OUTPUT 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 + } +} diff --git a/include/Makefile.am b/include/Makefile.am index 138005d..6bd0f7f 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -5,5 +5,6 @@ noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \ debug.h log.h hash.h mcast.h conntrack.h \ network.h filter.h queue.h vector.h cidr.h \ traffic_stats.h netlink.h fds.h event.h bitops.h channel.h \ - process.h origin.h internal.h external.h date.h nfct.h + process.h origin.h internal.h external.h date.h nfct.h \ + helper.h myct.h stack.h diff --git a/include/conntrackd.h b/include/conntrackd.h index ec720ec..19e613c 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -69,6 +69,7 @@ #define CTD_SYNC_NOTRACK (1UL << 4) #define CTD_POLL (1UL << 5) #define CTD_EXPECT (1UL << 6) +#define CTD_HELPER (1UL << 7) /* FILENAME_MAX is 4096 on my system, perhaps too much? */ #ifndef FILENAME_MAXLEN @@ -134,6 +135,9 @@ struct ct_conf { int syslog_facility; size_t buffer_size; } stats; + struct { + struct list_head list; + } cthelper; }; #define STATE(x) st.x @@ -252,13 +256,21 @@ struct ct_stats_state { struct cache *cache; /* internal events cache (netlink) */ }; -union ct_state { +#define STATE_CTH(x) state.cthelper->x + +struct ct_helper_state { + struct mnl_socket *nl; + uint32_t portid; +}; + +struct ct_state { struct ct_sync_state *sync; struct ct_stats_state *stats; + struct ct_helper_state *cthelper; }; extern struct ct_conf conf; -extern union ct_state state; +extern struct ct_state state; extern struct ct_general_state st; struct ct_mode { @@ -273,6 +285,11 @@ void ctnl_kill(void); int ctnl_local(int fd, int type, void *data); int ctnl_init(void); +/* basic cthelper functions */ +void cthelper_kill(void); +int cthelper_local(int fd, int type, void *data); +int cthelper_init(void); + /* conntrackd ctnl modes */ extern struct ct_mode sync_mode; extern struct ct_mode stats_mode; diff --git a/include/helper.h b/include/helper.h new file mode 100644 index 0000000..02ff3df --- /dev/null +++ b/include/helper.h @@ -0,0 +1,104 @@ +#ifndef _CTD_HELPER_H_ +#define _CTD_HELPER_H_ + +#include +#include "linux_list.h" +#include "myct.h" + +#include + +struct pkt_buff; + +#define CTD_HELPER_NAME_LEN 16 +#define CTD_HELPER_POLICY_MAX 4 + +struct ctd_helper_policy { + char name[CTD_HELPER_NAME_LEN]; + uint32_t expect_timeout; + uint32_t expect_max; +}; + +struct ctd_helper { + struct list_head head; + char name[CTD_HELPER_NAME_LEN]; + uint8_t l4proto; + int (*cb)(struct pkt_buff *pkt, + uint32_t protoff, + struct myct *ct, + u_int32_t ctinfo); + + struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; + + int priv_data_len; +}; + +struct ctd_helper_instance { + struct list_head head; + uint32_t queue_num; + uint16_t l3proto; + uint8_t l4proto; + struct ctd_helper *helper; + struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; +}; + +extern int cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, uint32_t class, union nfct_attr_grp_addr *saddr, union nfct_attr_grp_addr *daddr, uint8_t l4proto, uint16_t *sport, uint16_t *dport, uint32_t flags); +extern int cthelper_add_expect(struct nf_expect *exp); +extern int cthelper_del_expect(struct nf_expect *exp); + +extern void cthelper_get_addr_src(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); +extern void cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); + +extern int in4_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); +extern int in6_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); + +extern void helper_register(struct ctd_helper *helper); +struct ctd_helper *helper_find(const char *libdir_path, const char *name, uint8_t l4proto, int flags); + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1: __min2; }) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1: __max2; }) + +#define ARRAY_SIZE MNL_ARRAY_SIZE + +enum ip_conntrack_dir { + IP_CT_DIR_ORIGINAL, + IP_CT_DIR_REPLY, + IP_CT_DIR_MAX +}; + +/* Connection state tracking for netfilter. This is separated from, + but required by, the NAT layer; it can also be used by an iptables + extension. */ +enum ip_conntrack_info { + /* Part of an established connection (either direction). */ + IP_CT_ESTABLISHED, + + /* Like NEW, but related to an existing connection, or ICMP error + (in either direction). */ + IP_CT_RELATED, + + /* Started a new connection to track (only + IP_CT_DIR_ORIGINAL); may be a retransmission. */ + IP_CT_NEW, + + /* >= this indicates reply direction */ + IP_CT_IS_REPLY, + + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY, + /* Number of distinct IP_CT types (no NEW in reply dirn). */ + IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 +}; + +#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) + +#define pr_debug printf + +#endif diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am index 84315e3..6574060 100644 --- a/include/linux/netfilter/Makefile.am +++ b/include/linux/netfilter/Makefile.am @@ -1 +1 @@ -noinst_HEADERS = nfnetlink.h nfnetlink_cttimeout.h +noinst_HEADERS = nfnetlink.h nfnetlink_cttimeout.h nfnetlink_queue.h nfnetlink_cthelper.h diff --git a/include/linux/netfilter/nfnetlink_cthelper.h b/include/linux/netfilter/nfnetlink_cthelper.h new file mode 100644 index 0000000..33659f6 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_cthelper.h @@ -0,0 +1,55 @@ +#ifndef _NFNL_CTHELPER_H_ +#define _NFNL_CTHELPER_H_ + +#define NFCT_HELPER_STATUS_DISABLED 0 +#define NFCT_HELPER_STATUS_ENABLED 1 + +enum nfnl_acct_msg_types { + NFNL_MSG_CTHELPER_NEW, + NFNL_MSG_CTHELPER_GET, + NFNL_MSG_CTHELPER_DEL, + NFNL_MSG_CTHELPER_MAX +}; + +enum nfnl_cthelper_type { + NFCTH_UNSPEC, + NFCTH_NAME, + NFCTH_TUPLE, + NFCTH_QUEUE_NUM, + NFCTH_POLICY, + NFCTH_PRIV_DATA_LEN, + NFCTH_STATUS, + __NFCTH_MAX +}; +#define NFCTH_MAX (__NFCTH_MAX - 1) + +enum nfnl_cthelper_policy_type { + NFCTH_POLICY_SET_UNSPEC, + NFCTH_POLICY_SET_NUM, + NFCTH_POLICY_SET, + NFCTH_POLICY_SET1 = NFCTH_POLICY_SET, + NFCTH_POLICY_SET2, + NFCTH_POLICY_SET3, + NFCTH_POLICY_SET4, + __NFCTH_POLICY_SET_MAX +}; +#define NFCTH_POLICY_SET_MAX (__NFCTH_POLICY_SET_MAX - 1) + +enum nfnl_cthelper_pol_type { + NFCTH_POLICY_UNSPEC, + NFCTH_POLICY_NAME, + NFCTH_POLICY_EXPECT_MAX, + NFCTH_POLICY_EXPECT_TIMEOUT, + __NFCTH_POLICY_MAX +}; +#define NFCTH_POLICY_MAX (__NFCTH_POLICY_MAX - 1) + +enum nfnl_cthelper_tuple_type { + NFCTH_TUPLE_UNSPEC, + NFCTH_TUPLE_L3PROTONUM, + NFCTH_TUPLE_L4PROTONUM, + __NFCTH_TUPLE_MAX, +}; +#define NFCTH_TUPLE_MAX (__NFCTH_TUPLE_MAX - 1) + +#endif /* _NFNL_CTHELPER_H */ diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h new file mode 100644 index 0000000..e0d8fd8 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -0,0 +1,99 @@ +#ifndef _NFNETLINK_QUEUE_H +#define _NFNETLINK_QUEUE_H + +#include +#include + +enum nfqnl_msg_types { + NFQNL_MSG_PACKET, /* packet from kernel to userspace */ + NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ + NFQNL_MSG_CONFIG, /* connect to a particular queue */ + NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */ + + NFQNL_MSG_MAX +}; + +struct nfqnl_msg_packet_hdr { + __be32 packet_id; /* unique ID of packet in queue */ + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ +} __attribute__ ((packed)); + +struct nfqnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfqnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfqnl_attr_type { + NFQA_UNSPEC, + NFQA_PACKET_HDR, + NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ + NFQA_MARK, /* __u32 nfmark */ + NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ + NFQA_IFINDEX_INDEV, /* __u32 ifindex */ + NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFQA_HWADDR, /* nfqnl_msg_packet_hw */ + NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nf_conntrack_netlink.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ + + __NFQA_MAX +}; +#define NFQA_MAX (__NFQA_MAX - 1) + +struct nfqnl_msg_verdict_hdr { + __be32 verdict; + __be32 id; +}; + + +enum nfqnl_msg_config_cmds { + NFQNL_CFG_CMD_NONE, + NFQNL_CFG_CMD_BIND, + NFQNL_CFG_CMD_UNBIND, + NFQNL_CFG_CMD_PF_BIND, + NFQNL_CFG_CMD_PF_UNBIND, +}; + +struct nfqnl_msg_config_cmd { + __u8 command; /* nfqnl_msg_config_cmds */ + __u8 _pad; + __be16 pf; /* AF_xxx for PF_[UN]BIND */ +}; + +enum nfqnl_config_mode { + NFQNL_COPY_NONE, + NFQNL_COPY_META, + NFQNL_COPY_PACKET, +}; + +struct nfqnl_msg_config_params { + __be32 copy_range; + __u8 copy_mode; /* enum nfqnl_config_mode */ +} __attribute__ ((packed)); + + +enum nfqnl_attr_config { + NFQA_CFG_UNSPEC, + NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ + NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ + NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ + __NFQA_CFG_MAX +}; +#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) + +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) + +#endif /* _NFNETLINK_QUEUE_H */ diff --git a/include/myct.h b/include/myct.h new file mode 100644 index 0000000..45d9f29 --- /dev/null +++ b/include/myct.h @@ -0,0 +1,43 @@ +#ifndef _MYCT_H_ +#define _MYCT_H_ + +#include "linux_list.h" + +#include + +struct nf_conntrack; + +enum { + MYCT_NONE = 0, + MYCT_ESTABLISHED = (1 << 0), +}; + +enum { + MYCT_DIR_ORIG = 0, + MYCT_DIR_REPL, + MYCT_DIR_MAX, +}; + +union myct_proto { + uint16_t port; + uint16_t all; +}; + +struct myct_man { + union nfct_attr_grp_addr u3; + union myct_proto u; + uint16_t l3num; + uint8_t protonum; +}; + +struct myct_tuple { + struct myct_man src; + struct myct_man dst; +}; + +struct myct { + struct nf_conntrack *ct; + void *priv_data; +}; + +#endif diff --git a/include/nfct.h b/include/nfct.h index d6271cf..5548b03 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -4,6 +4,7 @@ enum { NFCT_SUBSYS_NONE = 0, NFCT_SUBSYS_TIMEOUT, + NFCT_SUBSYS_HELPER, NFCT_SUBSYS_VERSION, NFCT_SUBSYS_HELP, }; @@ -15,6 +16,7 @@ enum { NFCT_CMD_DELETE, NFCT_CMD_GET, NFCT_CMD_FLUSH, + NFCT_CMD_DISABLE, }; void nfct_perror(const char *msg); @@ -26,4 +28,12 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]); int nfct_cmd_timeout_get(int argc, char *argv[]); int nfct_cmd_timeout_flush(int argc, char *argv[]); +int nfct_cmd_helper_parse_params(int argc, char *argv[]); +int nfct_cmd_helper_list(int argc, char *argv[]); +int nfct_cmd_helper_add(int argc, char *argv[]); +int nfct_cmd_helper_delete(int argc, char *argv[]); +int nfct_cmd_helper_get(int argc, char *argv[]); +int nfct_cmd_helper_flush(int argc, char *argv[]); +int nfct_cmd_helper_disable(int argc, char *argv[]); + #endif diff --git a/include/stack.h b/include/stack.h new file mode 100644 index 0000000..512a30f --- /dev/null +++ b/include/stack.h @@ -0,0 +1,28 @@ +#ifndef _STACK_H_ +#define _STACK_H_ + +#include "linux_list.h" + +struct stack { + struct list_head list; + int items; +}; + +static inline void stack_init(struct stack *s) +{ + INIT_LIST_HEAD(&s->list); +} + +struct stack_item { + struct list_head head; + int type; + int data_len; + char data[0]; +}; + +struct stack_item *stack_item_alloc(int type, size_t data_len); +void stack_item_free(struct stack_item *e); +void stack_item_push(struct stack *s, struct stack_item *e); +struct stack_item *stack_item_pop(struct stack *s, int type); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index fa4ab75..d8074d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,7 @@ include $(top_srcdir)/Make_global.am +SUBDIRS = helpers + AM_YFLAGS = -d CLEANFILES = read_config_yy.c read_config_lex.c @@ -10,17 +12,24 @@ conntrack_SOURCES = conntrack.c conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS} ${LIBMNL_LIBS} nfct_SOURCES = nfct.c \ - nfct-extensions/timeout.c + helpers.c \ + nfct-extensions/timeout.c \ + nfct-extensions/helper.c + nfct_LDADD = ${LIBMNL_LIBS} \ ${LIBNETFILTER_CONNTRACK_LIBS} \ - ${LIBNETFILTER_CTTIMEOUT_LIBS} + ${LIBNETFILTER_CTTIMEOUT_LIBS} \ + ${LIBNETFILTER_CTHELPER_LIBS} \ + ${libdl_LIBS} + +nfct_LDFLAGS = -export-dynamic conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ local.c log.c mcast.c udp.c netlink.c vector.c \ filter.c fds.c event.c process.c origin.c date.c \ cache.c cache-ct.c cache-exp.c \ cache_timer.c \ - ctnl.c \ + ctnl.c cthelper.c \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ traffic_stats.c stats-mode.c \ network.c cidr.c \ @@ -29,11 +38,16 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ tcp.c channel_tcp.c \ external_cache.c external_inject.c \ internal_cache.c internal_bypass.c \ - read_config_yy.y read_config_lex.l + read_config_yy.y read_config_lex.l \ + stack.c helpers.c utils.c expect.c # yacc and lex generate dirty code read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls -conntrackd_LDADD = ${LIBNETFILTER_CONNTRACK_LIBS} +conntrackd_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \ + ${LIBNETFILTER_QUEUE_LIBS} ${LIBNETFILTER_CTHELPER_LIBS} \ + ${libdl_LIBS} + +conntrackd_LDFLAGS = -export-dynamic EXTRA_DIST = read_config_yy.h diff --git a/src/cthelper.c b/src/cthelper.c new file mode 100644 index 0000000..c119869 --- /dev/null +++ b/src/cthelper.c @@ -0,0 +1,521 @@ +/* + * (C) 2006-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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "conntrackd.h" +#include "log.h" +#include "fds.h" +#include "helper.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef __aligned_be64 +#define __aligned_be64 unsigned long long __attribute__((aligned(8))) +#endif + +#include + +#include +#include +#include +#include +#include +#include + +void cthelper_kill(void) +{ + mnl_socket_close(STATE_CTH(nl)); + free(state.cthelper); +} + +int cthelper_local(int fd, int type, void *data) +{ + /* No services to obtain information on helpers yet, sorry */ + return LOCAL_RET_OK; +} + +static struct nlmsghdr * +nfq_hdr_put(char *buf, int type, uint32_t queue_num) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + return nlh; +} + +static int +pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) +{ + switch(proto) { + case ETHERTYPE_IP: { + struct iphdr *ip = (struct iphdr *) pkt; + + /* No room for IPv4 header. */ + if (pktlen < sizeof(struct iphdr)) { + dlog(LOG_ERR, "no room for IPv4 header"); + return -1; + } + + /* this is not IPv4, skip. */ + if (ip->version != 4) { + dlog(LOG_ERR, "not IPv4, skipping"); + return -1; + } + + *protoff = 4 * ip->ihl; + + switch (ip->protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = + (struct tcphdr *) ((char *)pkt + *protoff); + + /* No room for IPv4 header plus TCP header. */ + if (pktlen < *protoff + sizeof(struct tcphdr) + || pktlen < *protoff + tcph->doff * 4) { + dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); + return -1; + } + return 0; + } + case IPPROTO_UDP: + /* No room for IPv4 header plus UDP header. */ + if (pktlen < *protoff + sizeof(struct udphdr)) { + dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); + return -1; + } + return 0; + default: + dlog(LOG_ERR, "not TCP/UDP, skipping"); + return -1; + } + break; + } + case ETHERTYPE_IPV6: + dlog(LOG_ERR, "no IPv6 support sorry"); + return 0; + default: + /* Unknown layer 3 protocol. */ + dlog(LOG_ERR, "unknown layer 3 protocol (%d), skipping", proto); + return -1; + } + return 0; +} + +static int +pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, + uint16_t queue_num, uint32_t id, uint32_t verdict, + struct pkt_buff *pktb) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); + + /* save private data and send it back to kernel-space. */ + nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, + cur->helper->priv_data_len); + + nfq_nlmsg_verdict_put(nlh, id, verdict); + if (pktb_mangled(pktb)) + nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb)); + + nest = mnl_attr_nest_start(nlh, NFQA_CT); + if (nest == NULL) + return -1; + + nfct_nlmsg_build(nlh, myct->ct); + mnl_attr_nest_end(nlh, nest); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int +pkt_verdict_error(uint16_t queue_num, uint32_t id) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); + nfq_nlmsg_verdict_put(nlh, id, NF_ACCEPT); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno)); + return -1; + } + return 0; +} + +static struct ctd_helper_instance * +helper_run(struct pkt_buff *pktb, uint32_t protoff, struct myct *myct, + uint32_t ctinfo, uint32_t queue_num, int *verdict) +{ + struct ctd_helper_instance *cur, *helper = NULL; + + list_for_each_entry(cur, &CONFIG(cthelper).list, head) { + if (cur->queue_num == queue_num) { + const void *priv_data; + + /* retrieve helper private data. */ + priv_data = nfct_get_attr(myct->ct, ATTR_HELPER_INFO); + if (priv_data != NULL) { + myct->priv_data = + calloc(1, cur->helper->priv_data_len); + + if (myct->priv_data == NULL) + continue; + + memcpy(myct->priv_data, priv_data, + cur->helper->priv_data_len); + } + + *verdict = cur->helper->cb(pktb, protoff, myct, ctinfo); + helper = cur; + break; + } + } + return helper; +} + +static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfqnl_msg_packet_hdr *ph = NULL; + struct nlattr *attr[NFQA_MAX+1] = {}; + struct nfgenmsg *nfg; + uint8_t *pkt; + uint16_t l3num; + uint32_t id, ctinfo, queue_num = 0, protoff = 0, pktlen; + struct nf_conntrack *ct = NULL; + struct myct *myct; + struct ctd_helper_instance *helper; + struct pkt_buff *pktb; + int verdict = NF_ACCEPT; + + if (nfq_nlmsg_parse(nlh, attr) < 0) { + dlog(LOG_ERR, "problems parsing message from kernel"); + return MNL_CB_ERROR; + } + + ph = (struct nfqnl_msg_packet_hdr *) + mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); + if (ph == NULL) { + dlog(LOG_ERR, "problems retrieving metaheader"); + return MNL_CB_ERROR; + } + + id = ntohl(ph->packet_id); + + if (!attr[NFQA_PAYLOAD]) { + dlog(LOG_ERR, "packet with no payload"); + goto err; + } + if (!attr[NFQA_CT] || !attr[NFQA_CT_INFO]) { + dlog(LOG_ERR, "no CT attached to this packet"); + goto err; + } + + pkt = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); + pktlen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); + + nfg = mnl_nlmsg_get_payload(nlh); + l3num = nfg->nfgen_family; + queue_num = ntohs(nfg->res_id); + + if (pkt_get(pkt, pktlen, ntohs(ph->hw_protocol), &protoff)) + goto err; + + ct = nfct_new(); + if (ct == NULL) + goto err; + + if (nfct_payload_parse(mnl_attr_get_payload(attr[NFQA_CT]), + mnl_attr_get_payload_len(attr[NFQA_CT]), + l3num, ct) < 0) { + dlog(LOG_ERR, "cannot convert message to CT"); + goto err; + } + + myct = calloc(1, sizeof(struct myct)); + if (myct == NULL) + goto err; + + myct->ct = ct; + ctinfo = ntohl(mnl_attr_get_u32(attr[NFQA_CT_INFO])); + + /* XXX: 256 bytes enough for possible NAT mangling in helpers? */ + pktb = pktb_alloc(AF_INET, pkt, pktlen, 256); + if (pktb == NULL) + goto err; + + /* Misconfiguration: if no helper found, accept the packet. */ + helper = helper_run(pktb, protoff, myct, ctinfo, queue_num, &verdict); + if (!helper) + goto err_pktb; + + if (pkt_verdict_issue(helper, myct, queue_num, id, verdict, pktb) < 0) + goto err_pktb; + + if (ct != NULL) + nfct_destroy(ct); + if (myct && myct->priv_data != NULL) + free(myct->priv_data); + if (myct != NULL) + free(myct); + + return MNL_CB_OK; +err_pktb: + pktb_free(pktb); +err: + /* In case of error, we don't want to disrupt traffic. We accept all. + * This is connection tracking after all. The policy is not to drop + * packet unless we enter some inconsistent state. + */ + pkt_verdict_error(queue_num, id); + + if (ct != NULL) + nfct_destroy(ct); + + return MNL_CB_OK; +} + +static void nfq_cb(void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + if (ret == -1) { + dlog(LOG_ERR, "failed to receive message: %s", strerror(errno)); + return; + } + + ret = mnl_cb_run(buf, ret, 0, STATE_CTH(portid), nfq_queue_cb, NULL); + if (ret < 0){ + dlog(LOG_ERR, "failed to process message"); + return; + } +} + +static int cthelper_setup(struct ctd_helper_instance *cur) +{ + struct nfct_helper *t; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq; + int j, ret; + + t = nfct_helper_alloc(); + if (t == NULL) { + dlog(LOG_ERR, "cannot allocate object for helper"); + return -1; + } + + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, cur->helper->name); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_QUEUE_NUM, cur->queue_num); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, cur->l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, cur->l4proto); + 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); + + for (j=0; jhelper->policy[j].name[0]) + break; + + p = nfct_helper_policy_alloc(); + if (p == NULL) { + dlog(LOG_ERR, "cannot allocate object for helper"); + return -1; + } + /* FIXME: get existing policy values from the kernel first. */ + nfct_helper_policy_attr_set(p, NFCTH_ATTR_POLICY_NAME, + cur->helper->policy[j].name); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_TIMEOUT, + cur->helper->policy[j].expect_timeout); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_MAX, + cur->helper->policy[j].expect_max); + + dlog(LOG_NOTICE, "policy name=%s expect_timeout=%d expect_max=%d", + cur->helper->policy[j].name, + cur->helper->policy[j].expect_timeout, + cur->helper->policy[j].expect_max); + + nfct_helper_attr_set(t, NFCTH_ATTR_POLICY+j, p); + } + + if (j == 0) { + dlog(LOG_ERR, "you have to define one policy for helper"); + return -1; + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "sending cthelper configuration"); + return -1; + } + + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, STATE_CTH(portid), NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + } + if (ret == -1) { + dlog(LOG_ERR, "trying to configure cthelper `%s': %s", + cur->helper->name, strerror(errno)); + return -1; + } + + return 0; +} + +static int cthelper_nfqueue_setup(struct ctd_helper_instance *cur) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, cur->queue_num); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send bind command"); + return -1; + } + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, cur->queue_num); + nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_CONNTRACK)); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(0xffffffff)); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send configuration"); + return -1; + } + + return 0; +} + +static int cthelper_configure(struct ctd_helper_instance *cur) +{ + /* First, configure cthelper. */ + if (cthelper_setup(cur) < 0) + return -1; + + /* Now, we are ready to configure nfqueue attached to this helper. */ + if (cthelper_nfqueue_setup(cur) < 0) + return -1; + + dlog(LOG_NOTICE, "helper `%s' configured successfully", + cur->helper->name); + + return 0; +} + +static int nfq_configure(void) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send pf unbind command"); + return -1; + } + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send pf bind command"); + return -1; + } + + return 0; +} + +int cthelper_init(void) +{ + struct ctd_helper_instance *cur; + int ret; + + state.cthelper = calloc(1, sizeof(struct ct_helper_state)); + if (state.cthelper == NULL) { + dlog(LOG_ERR, "can't allocate memory for cthelper struct"); + return -1; + } + + STATE_CTH(nl) = mnl_socket_open(NETLINK_NETFILTER); + if (STATE_CTH(nl) == NULL) { + dlog(LOG_ERR, "cannot open nfq socket"); + return -1; + } + fcntl(mnl_socket_get_fd(STATE_CTH(nl)), F_SETFL, O_NONBLOCK); + + if (mnl_socket_bind(STATE_CTH(nl), 0, MNL_SOCKET_AUTOPID) < 0) { + dlog(LOG_ERR, "cannot bind nfq socket"); + return -1; + } + STATE_CTH(portid) = mnl_socket_get_portid(STATE_CTH(nl)); + + if (nfq_configure()) + return -1; + + list_for_each_entry(cur, &CONFIG(cthelper).list, head) { + ret = cthelper_configure(cur); + if (ret < 0) + return ret; + } + + register_fd(mnl_socket_get_fd(STATE_CTH(nl)), nfq_cb, NULL, STATE(fds)); + + return 0; +} diff --git a/src/expect.c b/src/expect.c new file mode 100644 index 0000000..6069770 --- /dev/null +++ b/src/expect.c @@ -0,0 +1,214 @@ +/* + * (C) 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 version 2 as + * published by the Free Software Foundation (or any later at your option). + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "helper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int +cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + uint32_t class, + union nfct_attr_grp_addr *saddr, + union nfct_attr_grp_addr *daddr, + uint8_t l4proto, uint16_t *sport, uint16_t *dport, + uint32_t flags) +{ + struct nf_conntrack *expected, *mask; + + expected = nfct_new(); + if (!expected) + return -1; + + mask = nfct_new(); + if (!mask) + return -1; + + if (saddr) { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, saddr->ip); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0xffffffff); + break; + case AF_INET6: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6); + + for (i=0; i<4; i++) + memset(addr, 0xffffffff, sizeof(uint32_t)); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } else { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, 0x00000000); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); + break; + case AF_INET6: + for (i=0; i<4; i++) + memset(addr, 0x00000000, sizeof(uint32_t)); + + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, addr); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } + + if (sport) { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, *sport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0xffff); + break; + default: + break; + } + } else { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, 0x0000); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0x0000); + break; + default: + break; + } + } + + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + uint32_t addr[4] = {}; + int i; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_DST, daddr->ip); + nfct_set_attr_u32(mask, ATTR_IPV4_DST, 0xffffffff); + break; + case AF_INET6: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_DST, daddr->ip6); + + for (i=0; i<4; i++) + memset(addr, 0xffffffff, sizeof(uint32_t)); + + nfct_set_attr(mask, ATTR_IPV6_DST, addr); + break; + default: + break; + } + + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_DST, *dport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_DST, 0xffff); + break; + default: + break; + } + + nfexp_set_attr(exp, ATTR_EXP_MASTER, master); + nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected); + nfexp_set_attr(exp, ATTR_EXP_MASK, mask); + nfexp_set_attr_u32(exp, ATTR_EXP_FLAGS, flags); + + nfct_destroy(expected); + nfct_destroy(mask); + + return 0; +} + +static int cthelper_expect_cmd(struct nf_expect *exp, int cmd) +{ + int ret; + struct nfct_handle *h; + + h = nfct_open(EXPECT, 0); + if (!h) + return -1; + + ret = nfexp_query(h, cmd, exp); + + nfct_close(h); + return ret; +} + +int cthelper_add_expect(struct nf_expect *exp) +{ + return cthelper_expect_cmd(exp, NFCT_Q_CREATE_UPDATE); +} + +int cthelper_del_expect(struct nf_expect *exp) +{ + return cthelper_expect_cmd(exp, NFCT_Q_DESTROY); +} + +void +cthelper_get_addr_src(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_SRC, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_SRC, addr); + break; + } +} + +void +cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_DST, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_DST, addr); + break; + } +} diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..3e4e6c8 --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,76 @@ +/* + * (C) 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 version 2 as + * published by the Free Software Foundation (or any later at your option). + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "helper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(helper_list); + +void helper_register(struct ctd_helper *helper) +{ + list_add(&helper->head, &helper_list); +} + +static struct ctd_helper * +__helper_find(const char *helper_name, uint8_t l4proto) +{ + struct ctd_helper *cur, *helper = NULL; + + list_for_each_entry(cur, &helper_list, head) { + if (strncmp(cur->name, helper_name, CTD_HELPER_NAME_LEN) != 0) + continue; + + if (cur->l4proto != l4proto) + continue; + + helper = cur; + break; + } + return helper; +} + +struct ctd_helper * +helper_find(const char *libdir_path, + const char *helper_name, uint8_t l4proto, int flag) +{ + char path[PATH_MAX]; + struct ctd_helper *helper; + struct stat sb; + + helper = __helper_find(helper_name, l4proto); + if (helper != NULL) + return helper; + + snprintf(path, sizeof(path), "%s/ct_helper_%s.so", + libdir_path, helper_name); + + if (stat(path, &sb) != 0) { + if (errno == ENOENT) + return NULL; + fprintf(stderr, "%s: %s\n", path, + strerror(errno)); + return NULL; + } + + if (dlopen(path, flag) == NULL) { + fprintf(stderr, "%s: %s\n", path, dlerror()); + return NULL; + } + + return __helper_find(helper_name, l4proto); +} diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am new file mode 100644 index 0000000..2c9d63b --- /dev/null +++ b/src/helpers/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Make_global.am + +pkglib_LTLIBRARIES = ct_helper_ftp.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) + + diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c new file mode 100644 index 0000000..962020b --- /dev/null +++ b/src/helpers/ftp.c @@ -0,0 +1,599 @@ +/* + * (C) 2010-2012 by Pablo Neira Ayuso + * + * Based on: kernel-space FTP extension for connection tracking. + * + * This port has been sponsored by Vyatta Inc. + * + * Original copyright notice: + * + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * (C) 2003,2004 USAGI/WIDE Project + * + * 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 + +static bool loose; /* XXX: export this as config option. */ + +#define NUM_SEQ_TO_REMEMBER 2 + +/* This structure exists only once per master */ +struct ftp_info { + /* Valid seq positions for cmd matching after newline */ + uint32_t seq_aft_nl[MYCT_DIR_MAX][NUM_SEQ_TO_REMEMBER]; + /* 0 means seq_match_aft_nl not set */ + int seq_aft_nl_num[MYCT_DIR_MAX]; +}; + +enum nf_ct_ftp_type { + /* PORT command from client */ + NF_CT_FTP_PORT, + /* PASV response from server */ + NF_CT_FTP_PASV, + /* EPRT command from client */ + NF_CT_FTP_EPRT, + /* EPSV response from server */ + NF_CT_FTP_EPSV, +}; + +static int +get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) +{ + const char *end; + int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), + (uint8_t *)dst, term, &end); + if (ret > 0) + return (int)(end - src); + return 0; +} + +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 { + /* 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) `%u' 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, int start, size_t dlen, char delim, + struct myct_man *cmd) +{ + uint16_t tmp_port = 0; + uint32_t i; + + for (i = start; 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 { /* Some other crap */ + pr_debug("get_port: invalid char.\n"); + break; + } + } + return 0; +} + +/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ +static int try_rfc959(const char *data, size_t dlen, struct myct_man *cmd, + uint16_t l3protonum, char term) +{ + int length; + uint32_t array[6]; + + length = try_number(data, dlen, array, 6, ',', term); + if (length == 0) + return 0; + + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | + (array[2] << 8) | array[3]); + cmd->u.port = htons((array[4] << 8) | array[5]); + return length; +} + +/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */ +static int try_eprt(const char *data, size_t dlen, + struct myct_man *cmd, uint16_t l3protonum, char term) +{ + char delim; + int length; + + /* First character is delimiter, then "1" for IPv4 or "2" for IPv6, + then delimiter again. */ + if (dlen <= 3) { + pr_debug("EPRT: too short\n"); + return 0; + } + delim = data[0]; + if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) { + pr_debug("try_eprt: invalid delimitter.\n"); + return 0; + } + + if ((l3protonum == PF_INET && data[1] != '1') || + (l3protonum == PF_INET6 && data[1] != '2')) { + pr_debug("EPRT: invalid protocol number.\n"); + return 0; + } + + pr_debug("EPRT: Got %c%c%c\n", delim, data[1], delim); + if (data[1] == '1') { + uint32_t array[4]; + + /* Now we have IP address. */ + length = try_number(data + 3, dlen - 3, array, 4, '.', delim); + if (length != 0) + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) + | (array[2] << 8) | array[3]); + } else { + /* Now we have IPv6 address. */ + length = get_ipv6_addr(data + 3, dlen - 3, + (struct in6_addr *)cmd->u3.ip6, delim); + } + + if (length == 0) + return 0; + pr_debug("EPRT: Got IP address!\n"); + /* Start offset includes initial "|1|", and trailing delimiter */ + return get_port(data, 3 + length + 1, dlen, delim, cmd); +} + +/* Returns 0, or length of numbers: |||6446| */ +static int try_epsv_response(const char *data, size_t dlen, + struct myct_man *cmd, + uint16_t l3protonum, char term) +{ + char delim; + + /* Three delimiters. */ + if (dlen <= 3) return 0; + delim = data[0]; + if (isdigit(delim) || delim < 33 || delim > 126 || + data[1] != delim || data[2] != delim) + return 0; + + return get_port(data, 3, dlen, delim, cmd); +} + +static struct ftp_search { + const char *pattern; + size_t plen; + char skip; + char term; + enum nf_ct_ftp_type ftptype; + int (*getnum)(const char *, size_t, struct myct_man *, uint16_t, char); +} search[MYCT_DIR_MAX][2] = { + [MYCT_DIR_ORIG] = { + { + .pattern = "PORT", + .plen = sizeof("PORT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = NF_CT_FTP_PORT, + .getnum = try_rfc959, + }, + { + .pattern = "EPRT", + .plen = sizeof("EPRT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = NF_CT_FTP_EPRT, + .getnum = try_eprt, + }, + }, + [MYCT_DIR_REPL] = { + { + .pattern = "227 ", + .plen = sizeof("227 ") - 1, + .skip = '(', + .term = ')', + .ftptype = NF_CT_FTP_PASV, + .getnum = try_rfc959, + }, + { + .pattern = "229 ", + .plen = sizeof("229 ") - 1, + .skip = '(', + .term = ')', + .ftptype = NF_CT_FTP_EPSV, + .getnum = try_epsv_response, + }, + }, +}; + +static int ftp_find_pattern(struct pkt_buff *pkt, + unsigned int dataoff, unsigned int dlen, + const char *pattern, size_t plen, + char skip, char term, + unsigned int *matchoff, unsigned int *matchlen, + struct myct_man *cmd, + int (*getnum)(const char *, size_t, + struct myct_man *cmd, + uint16_t, char), + int dir) +{ + char *data = (char *)pktb_network_header(pkt) + dataoff; + int numlen; + uint32_t i; + + if (dlen == 0) + return 0; + + /* short packet, skip partial matching. */ + if (dlen <= plen) + return 0; + + if (strncmp(data, pattern, plen) != 0) + return 0; + + pr_debug("Pattern matches!\n"); + + /* Now we've found the constant string, try to skip + to the 'skip' character */ + for (i = plen; data[i] != skip; i++) + if (i == dlen - 1) return 0; + + /* Skip over the last character */ + i++; + + pr_debug("Skipped up to `%c'!\n", skip); + + numlen = getnum(data + i, dlen - i, cmd, PF_INET, term); + if (!numlen) + return 0; + + pr_debug("Match succeded!\n"); + return 1; +} + +/* Look up to see if we're just after a \n. */ +static int find_nl_seq(uint32_t seq, struct ftp_info *info, int dir) +{ + int i; + + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) + if (info->seq_aft_nl[dir][i] == seq) + return 1; + return 0; +} + +/* We don't update if it's older than what we have. */ +static void update_nl_seq(uint32_t nl_seq, struct ftp_info *info, int dir) +{ + int i, oldest; + + /* Look for oldest: if we find exact match, we're done. */ + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) { + if (info->seq_aft_nl[dir][i] == nl_seq) + return; + } + + if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { + info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; + } else { + if (before(info->seq_aft_nl[dir][0], info->seq_aft_nl[dir][1])) + oldest = 0; + else + oldest = 1; + + if (after(nl_seq, info->seq_aft_nl[dir][oldest])) + info->seq_aft_nl[dir][oldest] = nl_seq; + } +} + +static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, + char *buffer, size_t buflen, + uint32_t addr, uint16_t port) +{ + switch (type) { + case NF_CT_FTP_PORT: + case NF_CT_FTP_PASV: + return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", + ((unsigned char *)&addr)[0], + ((unsigned char *)&addr)[1], + ((unsigned char *)&addr)[2], + ((unsigned char *)&addr)[3], + port >> 8, + port & 0xFF); + case NF_CT_FTP_EPRT: + return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port); + case NF_CT_FTP_EPSV: + return snprintf(buffer, buflen, "|||%u|", port); + } + + return 0; +} + +/* 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_ftp(struct pkt_buff *pkt, + int dir, + int ctinfo, + enum nf_ct_ftp_type type, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack *ct, + struct nf_expect *exp) +{ + union nfct_attr_grp_addr newip; + uint16_t port; + char buffer[sizeof("|1|255.255.255.255|65535|")]; + unsigned int buflen; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port; + + pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); + + /* 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; + } + } + + if (port == 0) + return NF_DROP; + + buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), + newip.ip, port); + if (!buflen) + goto out; + + if (!nfq_tcp_mangle_ipv4(pkt, matchoff, matchlen, buffer, buflen)) + goto out; + + return NF_ACCEPT; + +out: + cthelper_del_expect(exp); + return NF_DROP; +} + +static int +ftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct tcphdr *th; + unsigned int dataoff; + unsigned int matchoff = 0, matchlen = 0; /* makes gcc happy. */ + unsigned int datalen; + unsigned int i; + int found = 0, ends_in_nl; + uint32_t seq; + int ret = NF_ACCEPT; + struct myct_man cmd; + union nfct_attr_grp_addr addr; + union nfct_attr_grp_addr daddr; + int dir = CTINFO2DIR(ctinfo); + struct ftp_info *ftp_info = myct->priv_data; + struct nf_expect *exp = NULL; + + 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 in packets. */ + if (ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("ftp: Conntrackinfo = %u\n", ctinfo); + goto out; + } + + th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); + + dataoff = protoff + th->doff * 4; + datalen = pktb_len(pkt) - dataoff; + + ends_in_nl = (pktb_network_header(pkt)[pktb_len(pkt) - 1] == '\n'); + seq = ntohl(th->seq) + datalen; + + /* Look up to see if we're just after a \n. */ + if (!find_nl_seq(ntohl(th->seq), ftp_info, dir)) { + /* Now if this ends in \n, update ftp info. */ + pr_debug("nf_conntrack_ftp: wrong seq pos %s(%u) or %s(%u)\n", + ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)", + ftp_info->seq_aft_nl[dir][0], + ftp_info->seq_aft_nl_num[dir] > 1 ? "" : "(UNSET)", + ftp_info->seq_aft_nl[dir][1]); + goto out_update_nl; + } + + /* Initialize IP/IPv6 addr to expected address (it's not mentioned + in EPSV responses) */ + cmd.l3num = nfct_get_attr_u16(myct->ct, ATTR_L3PROTO); + nfct_get_attr_grp(myct->ct, ATTR_GRP_ORIG_ADDR_SRC, &cmd.u3); + + for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { + found = ftp_find_pattern(pkt, dataoff, datalen, + search[dir][i].pattern, + search[dir][i].plen, + search[dir][i].skip, + search[dir][i].term, + &matchoff, &matchlen, + &cmd, + search[dir][i].getnum, + dir); + if (found) break; + } + if (found == 0) /* No match */ + goto out_update_nl; + + pr_debug("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", + matchlen, pktb_network_header(pkt) + matchoff, + matchlen, ntohl(th->seq) + matchoff); + + /* 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); + + /* Update the ftp info */ + if ((cmd.l3num == nfct_get_attr_u16(myct->ct, ATTR_L3PROTO)) && + memcmp(&cmd.u3, &addr, sizeof(addr)) != 0) { + /* Enrico Scholz's passive FTP to partially RNAT'd ftp + server: it really wants us to connect to a + different IP address. Simply don't record it for + NAT. */ + if (cmd.l3num == PF_INET) { + pr_debug("conntrack_ftp: NOT RECORDING: %pI4 != %pI4\n", + &cmd.u3.ip, &addr); + } else { + pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n", + cmd.u3.ip6, &addr); + } + /* Thanks to Cristiano Lincoln Mattos + for reporting this potential + problem (DMZ machines opening holes to internal + networks, or the packet filter itself). */ + if (!loose) { + ret = NF_ACCEPT; + goto out; + } + memcpy(&daddr, &cmd.u3, sizeof(cmd.u3)); + } + + exp = nfexp_new(); + if (exp == NULL) + goto out_update_nl; + + cthelper_get_addr_src(myct->ct, !dir, &addr); + + if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, IPPROTO_TCP, + NULL, &cmd.u.port, 0)) { + pr_debug("conntrack_ftp: failed to init expectation\n"); + goto out_update_nl; + } + + /* 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_ftp(pkt, dir, ctinfo, search[dir][i].ftptype, + matchoff, matchlen, myct->ct, exp); + goto out_update_nl; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("conntrack_ftp: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + goto out_update_nl; + } + +out_update_nl: + if (exp != NULL) + nfexp_destroy(exp); + + /* Now if this ends in \n, update ftp info. Seq may have been + * adjusted by NAT code. */ + if (ends_in_nl) + update_nl_seq(seq, ftp_info, dir); +out: + return ret; +} + +static struct ctd_helper ftp_helper = { + .name = "ftp", + .l4proto = IPPROTO_TCP, + .cb = ftp_helper_cb, + .priv_data_len = sizeof(struct ftp_info), + .policy = { + [0] = { + .name = "ftp", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) ftp_init(void); + +void ftp_init(void) +{ + helper_register(&ftp_helper); +} diff --git a/src/main.c b/src/main.c index 26f6c14..831a3c2 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ #include "conntrackd.h" #include "log.h" +#include "helper.h" #include #include @@ -31,7 +32,7 @@ #include struct ct_general_state st; -union ct_state state; +struct ct_state state; static const char usage_daemon_commands[] = "Daemon mode commands:\n" diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c new file mode 100644 index 0000000..e8f85bb --- /dev/null +++ b/src/nfct-extensions/helper.c @@ -0,0 +1,619 @@ +/* + * (C) 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nfct.h" +#include "helper.h" + +static void +nfct_cmd_helper_usage(char *argv[]) +{ + fprintf(stderr, "nfct v%s: Missing command\n" + "%s helper list|add|delete|get|flush " + "[parameters...]\n", VERSION, argv[0]); +} + +int +nfct_cmd_helper_parse_params(int argc, char *argv[]) +{ + int cmd = NFCT_CMD_NONE, ret = 0; + + if (argc < 3) { + fprintf(stderr, "nfct v%s: Missing command\n" + "%s helper list|add|delete|get|flush " + "[parameters...]\n", VERSION, argv[0]); + exit(EXIT_FAILURE); + } + if (strncmp(argv[2], "list", strlen(argv[2])) == 0) + cmd = NFCT_CMD_LIST; + else if (strncmp(argv[2], "add", strlen(argv[2])) == 0) + cmd = NFCT_CMD_ADD; + else if (strncmp(argv[2], "delete", strlen(argv[2])) == 0) + cmd = NFCT_CMD_DELETE; + else if (strncmp(argv[2], "get", strlen(argv[2])) == 0) + cmd = NFCT_CMD_GET; + else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0) + cmd = NFCT_CMD_FLUSH; + else if (strncmp(argv[2], "disable", strlen(argv[2])) == 0) + cmd = NFCT_CMD_DISABLE; + else { + fprintf(stderr, "nfct v%s: Unknown command: %s\n", + VERSION, argv[2]); + nfct_cmd_helper_usage(argv); + exit(EXIT_FAILURE); + } + switch(cmd) { + case NFCT_CMD_LIST: + ret = nfct_cmd_helper_list(argc, argv); + break; + case NFCT_CMD_ADD: + ret = nfct_cmd_helper_add(argc, argv); + break; + case NFCT_CMD_DELETE: + ret = nfct_cmd_helper_delete(argc, argv); + break; + case NFCT_CMD_GET: + ret = nfct_cmd_helper_get(argc, argv); + break; + case NFCT_CMD_FLUSH: + ret = nfct_cmd_helper_flush(argc, argv); + break; + case NFCT_CMD_DISABLE: + ret = nfct_cmd_helper_disable(argc, argv); + break; + } + + return ret; +} + +static int nfct_helper_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfct_helper *t; + char buf[4096]; + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + goto err; + } + + if (nfct_helper_nlmsg_parse_payload(nlh, t) < 0) { + nfct_perror("nfct_helper_nlmsg_parse_payload"); + goto err_free; + } + + nfct_helper_snprintf(buf, sizeof(buf), t, 0, 0); + printf("%s\n", buf); + +err_free: + nfct_helper_free(t); +err: + return MNL_CB_OK; +} + +int nfct_cmd_helper_list(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + unsigned int seq, portid; + int ret; + + if (argc > 3) { + nfct_perror("too many arguments"); + return -1; + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET, + NLM_F_DUMP, seq); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_add(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; + uint16_t l3proto; + uint8_t l4proto; + struct ctd_helper *helper; + int ret, j; + + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct helper add name " + "family protocol"); + return -1; + } + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + + /* XXX use prefix defined in configure.ac. */ + helper = helper_find("/usr/lib/conntrack-tools", + argv[3], l4proto, RTLD_LAZY); + if (helper == NULL) { + nfct_perror("that helper is not supported"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PRIV_DATA_LEN, + helper->priv_data_len); + + for (j=0; jpolicy[j].name[0]) + break; + + p = nfct_helper_policy_alloc(); + if (p == NULL) { + nfct_perror("OOM"); + return -1; + } + + nfct_helper_policy_attr_set(p, NFCTH_ATTR_POLICY_NAME, + helper->policy[j].name); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_TIMEOUT, + helper->policy[j].expect_timeout); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_MAX, + helper->policy[j].expect_max); + + nfct_helper_attr_set(t, NFCTH_ATTR_POLICY+j, p); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_delete(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; + int ret; + + if (argc < 4) { + nfct_perror("missing helper policy name"); + return -1; + } else if (argc > 6) { + nfct_perror("too many arguments"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + + if (argc >= 5) { + uint16_t l3proto; + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + } + + if (argc == 6) { + uint8_t l4proto; + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL, + NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_get(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; + int ret; + + if (argc < 4) { + nfct_perror("missing helper policy name"); + return -1; + } else if (argc > 6) { + nfct_perror("too many arguments"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + + if (argc >= 5) { + uint16_t l3proto; + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + } + + if (argc == 6) { + uint8_t l4proto; + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET, + NLM_F_ACK, seq); + + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_flush(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + int ret; + + if (argc > 3) { + nfct_perror("too many arguments"); + return -1; + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL, + NLM_F_ACK, seq); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_disable(int argc, char *argv[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; + uint16_t l3proto; + uint8_t l4proto; + struct ctd_helper *helper; + int ret; + + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct helper add name " + "family protocol"); + return -1; + } + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + + /* XXX use prefix defined in configure.ac. */ + helper = helper_find("/usr/lib/conntrack-tools", + argv[3], l4proto, RTLD_LAZY); + if (helper == NULL) { + nfct_perror("that helper is not supported"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS, + NFCT_HELPER_STATUS_DISABLED); + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + nfct_perror("mnl_socket_open"); + return -1; + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + nfct_perror("mnl_socket_bind"); + return -1; + } + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + nfct_perror("mnl_socket_send"); + return -1; + } + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) { + nfct_perror("error"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + diff --git a/src/nfct.c b/src/nfct.c index db629e7..b5c9654 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -56,6 +56,8 @@ int main(int argc, char *argv[]) } if (strncmp(argv[1], "timeout", strlen(argv[1])) == 0) { subsys = NFCT_SUBSYS_TIMEOUT; + } else if (strncmp(argv[1], "helper", strlen(argv[1])) == 0) { + subsys = NFCT_SUBSYS_HELPER; } else if (strncmp(argv[1], "version", strlen(argv[1])) == 0) subsys = NFCT_SUBSYS_VERSION; else if (strncmp(argv[1], "help", strlen(argv[1])) == 0) @@ -71,6 +73,9 @@ int main(int argc, char *argv[]) case NFCT_SUBSYS_TIMEOUT: ret = nfct_cmd_timeout_parse_params(argc, argv); break; + case NFCT_SUBSYS_HELPER: + ret = nfct_cmd_helper_parse_params(argc, argv); + break; case NFCT_SUBSYS_VERSION: ret = nfct_cmd_version(argc, argv); break; @@ -99,6 +104,7 @@ static const char help_msg[] = "nfct v%s: utility for the Netfilter's Connection Tracking System\n" "Usage: %s command [parameters]...\n\n" "Subsystem:\n" + " helper\t\tAllows to configure user-space helper\n" " timeout\t\tAllows definition of fine-grain timeout policies\n" " version\t\tDisplay version and disclaimer\n" " help\t\t\tDisplay this help message\n" diff --git a/src/read_config_lex.l b/src/read_config_lex.l index 01fe4fc..31fa32e 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -142,6 +142,11 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k] "TCPWindowTracking" { return T_TCP_WINDOW_TRACKING; } "ExpectationSync" { return T_EXPECT_SYNC; } "ErrorQueueLength" { return T_ERROR_QUEUE_LENGTH; } +"Helper" { return T_HELPER; } +"QueueNum" { return T_HELPER_QUEUE_NUM; } +"Policy" { return T_HELPER_POLICY; } +"ExpectMax" { return T_HELPER_EXPECT_MAX; } +"ExpectTimeout" { return T_HELPER_EXPECT_TIMEOUT; } {is_on} { return T_ON; } {is_off} { return T_OFF; } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index b22784c..c9235d3 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -28,8 +28,11 @@ #include "conntrackd.h" #include "bitops.h" #include "cidr.h" +#include "helper.h" +#include "stack.h" #include #include +#include #include #include @@ -48,6 +51,15 @@ static void print_err(int err, const char *msg, ...); static void __kernel_filter_start(void); static void __kernel_filter_add_state(int value); static void __max_dedicated_links_reached(void); + +struct stack symbol_stack; + +enum { + SYMBOL_HELPER_QUEUE_NUM, + SYMBOL_HELPER_POLICY_EXPECT_ROOT, + SYMBOL_HELPER_EXPECT_POLICY_LEAF, +}; + %} %union { @@ -74,6 +86,8 @@ static void __max_dedicated_links_reached(void); %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_IP T_PATH_VAL %token T_NUMBER @@ -96,6 +110,7 @@ line : ignore_protocol | general | sync | stats + | helper ; logfile_bool : T_LOG T_ON @@ -1561,6 +1576,186 @@ buffer_size: T_STAT_BUFFER_SIZE T_NUMBER print_err(CTD_CFG_WARN, "`LogFileBufferSize' is deprecated"); }; +helper: T_HELPER '{' helper_list '}' +{ + conf.flags |= CTD_HELPER; +}; + +helper_list: + | helper_list helper_line + ; + +helper_line: helper_type + ; + +helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' +{ + struct ctd_helper_instance *helper_inst; + struct ctd_helper *helper; + struct stack_item *e; + uint16_t l3proto; + uint8_t l4proto; + + if (strcmp($3, "inet") == 0) + l3proto = AF_INET; + else if (strcmp($3, "inet6") == 0) + l3proto = AF_INET6; + else { + print_err(CTD_CFG_ERROR, "unknown layer 3 protocol"); + exit(EXIT_FAILURE); + } + + if (strcmp($4, "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp($4, "udp") == 0) + l4proto = IPPROTO_UDP; + else { + print_err(CTD_CFG_ERROR, "unknown layer 4 protocol"); + exit(EXIT_FAILURE); + } + + /* XXX use configure.ac definitions. */ + helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW); + if (helper == NULL) { + print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2); + exit(EXIT_FAILURE); + } + + helper_inst = calloc(1, sizeof(struct ctd_helper_instance)); + if (helper_inst == NULL) + break; + + helper_inst->l3proto = l3proto; + helper_inst->l4proto = l4proto; + helper_inst->helper = helper; + + while ((e = stack_item_pop(&symbol_stack, -1)) != NULL) { + + switch(e->type) { + case SYMBOL_HELPER_QUEUE_NUM: { + int *qnum = (int *) &e->data; + + helper_inst->queue_num = *qnum; + stack_item_free(e); + break; + } + case SYMBOL_HELPER_POLICY_EXPECT_ROOT: { + struct ctd_helper_policy *pol = + (struct ctd_helper_policy *) &e->data; + struct ctd_helper_policy *matching = NULL; + int i; + + for (i=0; ipolicy[i].name, + pol->name) != 0) + continue; + + matching = pol; + break; + } + if (matching == NULL) { + print_err(CTD_CFG_ERROR, + "Unknown policy `%s' in helper " + "configuration", pol->name); + exit(EXIT_FAILURE); + } + /* FIXME: First set default policy, then change only + * tuned fields, not everything. + */ + memcpy(&helper->policy[i], pol, + sizeof(struct ctd_helper_policy)); + + stack_item_free(e); + break; + } + default: + print_err(CTD_CFG_ERROR, + "Unexpected symbol parsing helper policy"); + exit(EXIT_FAILURE); + break; + } + } + list_add(&helper_inst->head, &CONFIG(cthelper).list); +}; + +helper_type_list: + | helper_type_list helper_type_line + ; + +helper_type_line: helper_type + ; + +helper_type: T_HELPER_QUEUE_NUM T_NUMBER +{ + int *qnum; + struct stack_item *e; + + e = stack_item_alloc(SYMBOL_HELPER_QUEUE_NUM, sizeof(int)); + qnum = (int *) e->data; + *qnum = $2; + stack_item_push(&symbol_stack, e); +}; + +helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}' +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + print_err(CTD_CFG_ERROR, + "Helper policy configuration empty, fix your " + "configuration file, please"); + exit(EXIT_FAILURE); + break; + } + + policy = (struct ctd_helper_policy *) &e->data; + strncpy(policy->name, $2, CTD_HELPER_NAME_LEN); + policy->name[CTD_HELPER_NAME_LEN-1] = '\0'; + /* Now object is complete. */ + e->type = SYMBOL_HELPER_POLICY_EXPECT_ROOT; + stack_item_push(&symbol_stack, e); +}; + +helper_policy_list: + | helper_policy_list helper_policy_line + ; + +helper_policy_line: helper_policy_expect_max + | helper_policy_expect_timeout + ; + +helper_policy_expect_max: T_HELPER_EXPECT_MAX T_NUMBER +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF, + sizeof(struct ctd_helper_policy)); + } + policy = (struct ctd_helper_policy *) &e->data; + policy->expect_max = $2; + stack_item_push(&symbol_stack, e); +}; + +helper_policy_expect_timeout: T_HELPER_EXPECT_TIMEOUT T_NUMBER +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF, + sizeof(struct ctd_helper_policy)); + } + policy = (struct ctd_helper_policy *) &e->data; + policy->expect_timeout = $2; + stack_item_push(&symbol_stack, e); +}; + %% int __attribute__((noreturn)) @@ -1640,6 +1835,11 @@ init_config(char *filename) CONFIG(stats).syslog_facility = -1; CONFIG(netlink).subsys_id = -1; + /* Initialize list of user-space helpers */ + INIT_LIST_HEAD(&CONFIG(cthelper).list); + + stack_init(&symbol_stack); + yyrestart(fp); yyparse(); fclose(fp); diff --git a/src/run.c b/src/run.c index 852bec6..3337694 100644 --- a/src/run.c +++ b/src/run.c @@ -50,6 +50,9 @@ void killer(int foo) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) ctnl_kill(); + if (CONFIG(flags) & CTD_HELPER) + cthelper_kill(); + destroy_fds(STATE(fds)); unlink(CONFIG(lockfile)); dlog(LOG_NOTICE, "---- shutdown received ----"); @@ -199,6 +202,9 @@ static int local_handler(int fd, void *data) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) return ctnl_local(fd, type, data); + if (CONFIG(flags) & CTD_HELPER) + return cthelper_local(fd, type, data); + return ret; } @@ -250,6 +256,11 @@ init(void) if (ctnl_init() < 0) return -1; + if (CONFIG(flags) & CTD_HELPER) { + if (cthelper_init() < 0) + return -1; + } + time(&STATE(stats).daemon_start_time); dlog(LOG_NOTICE, "initialization completed"); diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 0000000..104b7ba --- /dev/null +++ b/src/stack.c @@ -0,0 +1,56 @@ +/* + * (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. + */ + +#include +#include +#include +#include + +#include "stack.h" + +struct stack_item * +stack_item_alloc(int type, size_t data_len) +{ + struct stack_item *e; + + e = calloc(1, sizeof(struct stack_item) + data_len); + if (e == NULL) + return NULL; + + e->data_len = data_len; + e->type = type; + + return e; +} + +void stack_item_free(struct stack_item *e) +{ + free(e); +} + +void stack_item_push(struct stack *s, struct stack_item *e) +{ + list_add(&e->head, &s->list); +} + +struct stack_item *stack_item_pop(struct stack *s, int type) +{ + struct stack_item *cur, *tmp, *found = NULL; + + list_for_each_entry_safe(cur, tmp, &s->list, head) { + if (cur->type != type && type != -1) + continue; + + list_del(&cur->head); + found = cur; + break; + } + + return found; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..fabec24 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,243 @@ +/* The following code has been extracted from the kenrel sources, if there is + * any problem, blame for mangling it. --pablo */ + +/* + * Generic address resultion entity + * + * Authors: + * net_random Alan Cox + * net_ratelimit Andi Kleen + * in{4,6}_pton YOSHIFUJI Hideaki, Copyright (C)2006 USAGI/WIDE Project + * + * Created by Alexey Kuznetsov + * + * 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. + */ + +/* + * lib/hexdump.c + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include /* for memcpy */ + +#include "helper.h" + +static int hex_to_bin(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + return -1; +} + +#define IN6PTON_XDIGIT 0x00010000 +#define IN6PTON_DIGIT 0x00020000 +#define IN6PTON_COLON_MASK 0x00700000 +#define IN6PTON_COLON_1 0x00100000 /* single : requested */ +#define IN6PTON_COLON_2 0x00200000 /* second : requested */ +#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ +#define IN6PTON_DOT 0x00800000 /* . */ +#define IN6PTON_DELIM 0x10000000 +#define IN6PTON_NULL 0x20000000 /* first/tail */ +#define IN6PTON_UNKNOWN 0x40000000 + +static inline int xdigit2bin(char c, int delim) +{ + int val; + + if (c == delim || c == '\0') + return IN6PTON_DELIM; + if (c == ':') + return IN6PTON_COLON_MASK; + if (c == '.') + return IN6PTON_DOT; + + val = hex_to_bin(c); + if (val >= 0) + return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); + + if (delim == -1) + return IN6PTON_DELIM; + return IN6PTON_UNKNOWN; +} + +int in4_pton(const char *src, int srclen, + uint8_t *dst, + int delim, const char **end) +{ + const char *s; + uint8_t *d; + uint8_t dbuf[4]; + int ret = 0; + int i; + int w = 0; + + if (srclen < 0) + srclen = strlen(src); + s = src; + d = dbuf; + i = 0; + while(1) { + int c; + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK))) { + goto out; + } + if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (w == 0) + goto out; + *d++ = w & 0xff; + w = 0; + i++; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (i != 4) + goto out; + break; + } + goto cont; + } + w = (w * 10) + c; + if ((w & 0xffff) > 255) { + goto out; + } +cont: + if (i >= 4) + goto out; + s++; + srclen--; + } + ret = 1; + memcpy(dst, dbuf, sizeof(dbuf)); +out: + if (end) + *end = s; + return ret; +} + +int in6_pton(const char *src, int srclen, + uint8_t *dst, + int delim, const char **end) +{ + const char *s, *tok = NULL; + uint8_t *d, *dc = NULL; + uint8_t dbuf[16]; + int ret = 0; + int i; + int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; + int w = 0; + + memset(dbuf, 0, sizeof(dbuf)); + + s = src; + d = dbuf; + if (srclen < 0) + srclen = strlen(src); + + while (1) { + int c; + + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (!(c & state)) + goto out; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + /* process one 16-bit word */ + if (!(state & IN6PTON_NULL)) { + *d++ = (w >> 8) & 0xff; + *d++ = w & 0xff; + } + w = 0; + if (c & IN6PTON_DELIM) { + /* We've processed last word */ + break; + } + /* + * COLON_1 => XDIGIT + * COLON_2 => XDIGIT|DELIM + * COLON_1_2 => COLON_2 + */ + switch (state & IN6PTON_COLON_MASK) { + case IN6PTON_COLON_2: + dc = d; + state = IN6PTON_XDIGIT | IN6PTON_DELIM; + if (dc - dbuf >= (int)sizeof(dbuf)) + state |= IN6PTON_NULL; + break; + case IN6PTON_COLON_1|IN6PTON_COLON_1_2: + state = IN6PTON_XDIGIT | IN6PTON_COLON_2; + break; + case IN6PTON_COLON_1: + state = IN6PTON_XDIGIT; + break; + case IN6PTON_COLON_1_2: + state = IN6PTON_COLON_2; + break; + default: + state = 0; + } + tok = s + 1; + goto cont; + } + + if (c & IN6PTON_DOT) { + ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok), d, delim, &s); + if (ret > 0) { + d += 4; + break; + } + goto out; + } + + w = (w << 4) | (0xff & c); + state = IN6PTON_COLON_1 | IN6PTON_DELIM; + if (!(w & 0xf000)) { + state |= IN6PTON_XDIGIT; + } + if (!dc && d + 2 < dbuf + sizeof(dbuf)) { + state |= IN6PTON_COLON_1_2; + state &= ~IN6PTON_DELIM; + } + if (d + 2 >= dbuf + sizeof(dbuf)) { + state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); + } +cont: + if ((dc && d + 4 < dbuf + sizeof(dbuf)) || + d + 4 == dbuf + sizeof(dbuf)) { + state |= IN6PTON_DOT; + } + if (d >= dbuf + sizeof(dbuf)) { + state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); + } + s++; + srclen--; + } + + i = 15; d--; + + if (dc) { + while(d >= dc) + dst[i--] = *d--; + while(i >= dc - dbuf) + dst[i--] = 0; + while(i >= 0) + dst[i--] = *d--; + } else + memcpy(dst, dbuf, sizeof(dbuf)); + + ret = 1; +out: + if (end) + *end = s; + return ret; +} -- cgit v1.2.3 From 0cf75aaf19ffd08e7c63fee737423d01343f4cb9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 26 Sep 2013 18:25:45 +0200 Subject: nfct: modularize extensions Modularize timeout and helper extensions. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 25 +++++++++++-------------- src/nfct-extensions/helper.c | 31 +++++++++++++++++++++++-------- src/nfct-extensions/timeout.c | 20 ++++++++++++++++++-- src/nfct.c | 35 +++++++++++++++++++++++++++++------ 4 files changed, 81 insertions(+), 30 deletions(-) (limited to 'src/nfct.c') diff --git a/include/nfct.h b/include/nfct.h index 5548b03..ddf9038 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -1,6 +1,8 @@ #ifndef _NFCT_H_ #define _NFCT_H_ +#include "linux_list.h" + enum { NFCT_SUBSYS_NONE = 0, NFCT_SUBSYS_TIMEOUT, @@ -19,21 +21,16 @@ enum { NFCT_CMD_DISABLE, }; +#define __init __attribute__((constructor)) + void nfct_perror(const char *msg); -int nfct_cmd_timeout_parse_params(int argc, char *argv[]); -int nfct_cmd_timeout_list(int argc, char *argv[]); -int nfct_cmd_timeout_add(int argc, char *argv[]); -int nfct_cmd_timeout_delete(int argc, char *argv[]); -int nfct_cmd_timeout_get(int argc, char *argv[]); -int nfct_cmd_timeout_flush(int argc, char *argv[]); - -int nfct_cmd_helper_parse_params(int argc, char *argv[]); -int nfct_cmd_helper_list(int argc, char *argv[]); -int nfct_cmd_helper_add(int argc, char *argv[]); -int nfct_cmd_helper_delete(int argc, char *argv[]); -int nfct_cmd_helper_get(int argc, char *argv[]); -int nfct_cmd_helper_flush(int argc, char *argv[]); -int nfct_cmd_helper_disable(int argc, char *argv[]); +struct nfct_extension { + struct list_head head; + int type; + int (*parse_params)(int argc, char *argv[]); +}; + +void nfct_extension_register(struct nfct_extension *ext); #endif diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index f91fc41..98ccde2 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -37,8 +37,14 @@ nfct_cmd_helper_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -int -nfct_cmd_helper_parse_params(int argc, char *argv[]) +static int nfct_cmd_helper_list(int argc, char *argv[]); +static int nfct_cmd_helper_add(int argc, char *argv[]); +static int nfct_cmd_helper_delete(int argc, char *argv[]); +static int nfct_cmd_helper_get(int argc, char *argv[]); +static int nfct_cmd_helper_flush(int argc, char *argv[]); +static int nfct_cmd_helper_disable(int argc, char *argv[]); + +static int nfct_cmd_helper_parse_params(int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret = 0; @@ -115,7 +121,7 @@ err: return MNL_CB_OK; } -int nfct_cmd_helper_list(int argc, char *argv[]) +static int nfct_cmd_helper_list(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -165,7 +171,7 @@ int nfct_cmd_helper_list(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_add(int argc, char *argv[]) +static int nfct_cmd_helper_add(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -281,7 +287,7 @@ int nfct_cmd_helper_add(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_delete(int argc, char *argv[]) +static int nfct_cmd_helper_delete(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -375,7 +381,7 @@ int nfct_cmd_helper_delete(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_get(int argc, char *argv[]) +static int nfct_cmd_helper_get(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -468,7 +474,7 @@ int nfct_cmd_helper_get(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_flush(int argc, char *argv[]) +static int nfct_cmd_helper_flush(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -519,7 +525,7 @@ int nfct_cmd_helper_flush(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_disable(int argc, char *argv[]) +static int nfct_cmd_helper_disable(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -613,3 +619,12 @@ int nfct_cmd_helper_disable(int argc, char *argv[]) return 0; } +static struct nfct_extension helper = { + .type = NFCT_SUBSYS_HELPER, + .parse_params = nfct_cmd_helper_parse_params, +}; + +static void __init helper_init(void) +{ + nfct_extension_register(&helper); +} diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index 5b32023..dde489a 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -35,7 +35,13 @@ nfct_cmd_timeout_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -int nfct_cmd_timeout_parse_params(int argc, char *argv[]) +static int nfct_cmd_timeout_list(int argc, char *argv[]); +static int nfct_cmd_timeout_add(int argc, char *argv[]); +static int nfct_cmd_timeout_delete(int argc, char *argv[]); +static int nfct_cmd_timeout_get(int argc, char *argv[]); +static int nfct_cmd_timeout_flush(int argc, char *argv[]); + +static int nfct_cmd_timeout_parse_params(int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret; @@ -105,7 +111,7 @@ err: return MNL_CB_OK; } -int nfct_cmd_timeout_list(int argc, char *argv[]) +static int nfct_cmd_timeout_list(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -484,3 +490,13 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) return 0; } + +static struct nfct_extension timeout = { + .type = NFCT_SUBSYS_TIMEOUT, + .parse_params = nfct_cmd_timeout_parse_params, +}; + +static void __init timeout_init(void) +{ + nfct_extension_register(&timeout); +} diff --git a/src/nfct.c b/src/nfct.c index b5c9654..4795570 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -25,6 +25,7 @@ #include #include +#include "linux_list.h" #include "nfct.h" static int nfct_cmd_version(int argc, char *argv[]); @@ -46,9 +47,28 @@ void nfct_perror(const char *msg) } } +static LIST_HEAD(nfct_extension_list); + +void nfct_extension_register(struct nfct_extension *ext) +{ + list_add(&ext->head, &nfct_extension_list); +} + +static struct nfct_extension *nfct_extension_lookup(int type) +{ + struct nfct_extension *ext; + + list_for_each_entry(ext, &nfct_extension_list, head) { + if (ext->type == type) + return ext; + } + return NULL; +} + int main(int argc, char *argv[]) { int subsys = NFCT_SUBSYS_NONE, ret = 0; + struct nfct_extension *ext; if (argc < 2) { usage(argv); @@ -70,18 +90,21 @@ int main(int argc, char *argv[]) } switch(subsys) { - case NFCT_SUBSYS_TIMEOUT: - ret = nfct_cmd_timeout_parse_params(argc, argv); - break; - case NFCT_SUBSYS_HELPER: - ret = nfct_cmd_helper_parse_params(argc, argv); - break; case NFCT_SUBSYS_VERSION: ret = nfct_cmd_version(argc, argv); break; case NFCT_SUBSYS_HELP: ret = nfct_cmd_help(argc, argv); break; + default: + ext = nfct_extension_lookup(subsys); + if (ext == NULL) { + fprintf(stderr, "nfct v%s: subsystem %s not supported\n", + VERSION, argv[1]); + return EXIT_FAILURE; + } + ret = ext->parse_params(argc, argv); + break; } return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3 From 386968d321d02571b593b3bbbf39891f44397469 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Sep 2013 20:09:57 +0200 Subject: nfct: src: add nfct_mnl_talk and use it Add helper function nfct_mnl_talk and use it. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 5 +++ src/nfct-extensions/helper.c | 99 ++++++------------------------------------- src/nfct-extensions/timeout.c | 82 +++++------------------------------ src/nfct.c | 25 +++++++++++ 4 files changed, 53 insertions(+), 158 deletions(-) (limited to 'src/nfct.c') diff --git a/include/nfct.h b/include/nfct.h index ddf9038..93717c5 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -33,4 +33,9 @@ struct nfct_extension { void nfct_extension_register(struct nfct_extension *ext); +int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, + uint32_t seq, uint32_t portid, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *data); + #endif diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index 98ccde2..4171a47 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -127,7 +127,6 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -150,22 +149,11 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -181,7 +169,7 @@ static int nfct_cmd_helper_add(int argc, char *argv[]) uint16_t l3proto; uint8_t l4proto; struct ctd_helper *helper; - int ret, j; + int j; if (argc < 6) { nfct_perror("missing parameters\n" @@ -266,22 +254,11 @@ static int nfct_cmd_helper_add(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -294,7 +271,6 @@ static int nfct_cmd_helper_delete(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -359,20 +335,8 @@ static int nfct_cmd_helper_delete(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); - return -1; - } - - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } @@ -388,7 +352,6 @@ static int nfct_cmd_helper_get(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -453,22 +416,11 @@ static int nfct_cmd_helper_get(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -480,7 +432,6 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -503,20 +454,8 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); - return -1; - } - - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } @@ -535,7 +474,6 @@ static int nfct_cmd_helper_disable(int argc, char *argv[]) uint16_t l3proto; uint8_t l4proto; struct ctd_helper *helper; - int ret; if (argc < 6) { nfct_perror("missing parameters\n" @@ -598,22 +536,11 @@ static int nfct_cmd_helper_disable(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index 0cf92bb..c361dab 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -118,7 +118,6 @@ static int nfct_cmd_timeout_list(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -141,22 +140,11 @@ static int nfct_cmd_timeout_list(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -281,7 +269,6 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 6) { nfct_perror("missing parameters\n" @@ -321,22 +308,11 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -349,7 +325,6 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -386,20 +361,8 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); - return -1; - } - - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } @@ -415,7 +378,6 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -452,22 +414,11 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); - return -1; - } mnl_socket_close(nl); return 0; @@ -479,7 +430,6 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -502,20 +452,8 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) } portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); - return -1; - } - - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - while (ret > 0) { - ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); - if (ret <= 0) - break; - ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); - } - if (ret == -1) { - nfct_perror("error"); + if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { + nfct_perror("netlink error"); return -1; } diff --git a/src/nfct.c b/src/nfct.c index 4795570..84bb1b7 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -143,3 +143,28 @@ static int nfct_cmd_help(int argc, char *argv[]) printf(help_msg, VERSION, argv[0]); return 0; } + +int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, + uint32_t seq, uint32_t portid, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *data) +{ + int ret; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + return -1; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, cb, data); + if (ret <= 0) + break; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + if (ret == -1) + return -1; + + return 0; +} -- cgit v1.2.3 From 3c78a4543e12f5e82bdd771971d3534fa452117b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 1 Oct 2013 13:23:39 +0200 Subject: nfct: src: consolidate netlink socket creation Open the socket from the main function, then pass it as parameter to the corresponding interpreter. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 3 +- src/nfct-extensions/helper.c | 125 +++++++++--------------------------------- src/nfct-extensions/timeout.c | 103 ++++++---------------------------- src/nfct.c | 25 ++++++++- 4 files changed, 68 insertions(+), 188 deletions(-) (limited to 'src/nfct.c') diff --git a/include/nfct.h b/include/nfct.h index 93717c5..682fe3a 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -28,11 +28,12 @@ void nfct_perror(const char *msg); struct nfct_extension { struct list_head head; int type; - int (*parse_params)(int argc, char *argv[]); + int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[]); }; void nfct_extension_register(struct nfct_extension *ext); +struct mnl_socket *nfct_mnl_open(void); int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq, uint32_t portid, int (*cb)(const struct nlmsghdr *nlh, void *data), diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index 4171a47..7544ed7 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -37,14 +37,15 @@ nfct_cmd_helper_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -static int nfct_cmd_helper_list(int argc, char *argv[]); -static int nfct_cmd_helper_add(int argc, char *argv[]); -static int nfct_cmd_helper_delete(int argc, char *argv[]); -static int nfct_cmd_helper_get(int argc, char *argv[]); -static int nfct_cmd_helper_flush(int argc, char *argv[]); -static int nfct_cmd_helper_disable(int argc, char *argv[]); - -static int nfct_cmd_helper_parse_params(int argc, char *argv[]) +static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]); + +static int +nfct_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret = 0; @@ -72,24 +73,25 @@ static int nfct_cmd_helper_parse_params(int argc, char *argv[]) nfct_cmd_helper_usage(argv); exit(EXIT_FAILURE); } + switch(cmd) { case NFCT_CMD_LIST: - ret = nfct_cmd_helper_list(argc, argv); + ret = nfct_cmd_helper_list(nl, argc, argv); break; case NFCT_CMD_ADD: - ret = nfct_cmd_helper_add(argc, argv); + ret = nfct_cmd_helper_add(nl, argc, argv); break; case NFCT_CMD_DELETE: - ret = nfct_cmd_helper_delete(argc, argv); + ret = nfct_cmd_helper_delete(nl, argc, argv); break; case NFCT_CMD_GET: - ret = nfct_cmd_helper_get(argc, argv); + ret = nfct_cmd_helper_get(nl, argc, argv); break; case NFCT_CMD_FLUSH: - ret = nfct_cmd_helper_flush(argc, argv); + ret = nfct_cmd_helper_flush(nl, argc, argv); break; case NFCT_CMD_DISABLE: - ret = nfct_cmd_helper_disable(argc, argv); + ret = nfct_cmd_helper_disable(nl, argc, argv); break; } @@ -121,9 +123,8 @@ err: return MNL_CB_OK; } -static int nfct_cmd_helper_list(int argc, char *argv[]) +static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; @@ -137,18 +138,7 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET, NLM_F_DUMP, seq); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; @@ -159,9 +149,8 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) return 0; } -static int nfct_cmd_helper_add(int argc, char *argv[]) +static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -242,31 +231,18 @@ static int nfct_cmd_helper_add(int argc, char *argv[]) nfct_helper_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static int nfct_cmd_helper_delete(int argc, char *argv[]) +static int +nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -323,31 +299,17 @@ static int nfct_cmd_helper_delete(int argc, char *argv[]) nfct_helper_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static int nfct_cmd_helper_get(int argc, char *argv[]) +static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -404,31 +366,18 @@ static int nfct_cmd_helper_get(int argc, char *argv[]) nfct_helper_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static int nfct_cmd_helper_flush(int argc, char *argv[]) +static int +nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -442,18 +391,7 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL, NLM_F_ACK, seq); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; @@ -464,9 +402,9 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) return 0; } -static int nfct_cmd_helper_disable(int argc, char *argv[]) +static int +nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -524,25 +462,12 @@ static int nfct_cmd_helper_disable(int argc, char *argv[]) nfct_helper_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index c361dab..9f74eca 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -36,13 +36,14 @@ nfct_cmd_timeout_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -static int nfct_cmd_timeout_list(int argc, char *argv[]); -static int nfct_cmd_timeout_add(int argc, char *argv[]); -static int nfct_cmd_timeout_delete(int argc, char *argv[]); -static int nfct_cmd_timeout_get(int argc, char *argv[]); -static int nfct_cmd_timeout_flush(int argc, char *argv[]); +static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]); +static int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]); -static int nfct_cmd_timeout_parse_params(int argc, char *argv[]) +static int +nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret; @@ -68,19 +69,19 @@ static int nfct_cmd_timeout_parse_params(int argc, char *argv[]) } switch(cmd) { case NFCT_CMD_LIST: - ret = nfct_cmd_timeout_list(argc, argv); + ret = nfct_cmd_timeout_list(nl, argc, argv); break; case NFCT_CMD_ADD: - ret = nfct_cmd_timeout_add(argc, argv); + ret = nfct_cmd_timeout_add(nl, argc, argv); break; case NFCT_CMD_DELETE: - ret = nfct_cmd_timeout_delete(argc, argv); + ret = nfct_cmd_timeout_delete(nl, argc, argv); break; case NFCT_CMD_GET: - ret = nfct_cmd_timeout_get(argc, argv); + ret = nfct_cmd_timeout_get(nl, argc, argv); break; case NFCT_CMD_FLUSH: - ret = nfct_cmd_timeout_flush(argc, argv); + ret = nfct_cmd_timeout_flush(nl, argc, argv); break; } @@ -112,9 +113,8 @@ err: return MNL_CB_OK; } -static int nfct_cmd_timeout_list(int argc, char *argv[]) +static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; @@ -128,25 +128,12 @@ static int nfct_cmd_timeout_list(int argc, char *argv[]) nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_GET, NLM_F_DUMP, seq); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } @@ -262,9 +249,8 @@ nfct_cmd_timeout_parse(struct nfct_timeout *t, int argc, char *argv[]) return 0; } -int nfct_cmd_timeout_add(int argc, char *argv[]) +int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -296,31 +282,17 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) nfct_timeout_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -int nfct_cmd_timeout_delete(int argc, char *argv[]) +int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -349,31 +321,17 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) nfct_timeout_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -int nfct_cmd_timeout_get(int argc, char *argv[]) +int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -402,31 +360,17 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) nfct_timeout_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -int nfct_cmd_timeout_flush(int argc, char *argv[]) +int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]) { - struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; @@ -440,25 +384,12 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, NLM_F_ACK, seq); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); - return -1; - } - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } portid = mnl_socket_get_portid(nl); - if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } diff --git a/src/nfct.c b/src/nfct.c index 84bb1b7..19e44be 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -69,6 +69,7 @@ int main(int argc, char *argv[]) { int subsys = NFCT_SUBSYS_NONE, ret = 0; struct nfct_extension *ext; + struct mnl_socket *nl; if (argc < 2) { usage(argv); @@ -103,7 +104,15 @@ int main(int argc, char *argv[]) VERSION, argv[1]); return EXIT_FAILURE; } - ret = ext->parse_params(argc, argv); + + nl = nfct_mnl_open(); + if (nl == NULL) { + nfct_perror("cannot open netlink"); + return -1; + } + + ret = ext->parse_params(nl, argc, argv); + mnl_socket_close(nl); break; } return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -168,3 +177,17 @@ int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, return 0; } + +struct mnl_socket *nfct_mnl_open(void) +{ + struct mnl_socket *nl; + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) + return NULL; + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + return NULL; + + return nl; +} -- cgit v1.2.3 From 34a8e200eee54b4bbecadf52bba8901cae129795 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 12 May 2014 17:21:21 +0200 Subject: nfct: remove unneeded included header This fixes a compilation breakage when libnetfilter_cttimeout.h is not installed. Reported-by: Hangbin Liu Signed-off-by: Pablo Neira Ayuso --- src/nfct.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/nfct.c') diff --git a/src/nfct.c b/src/nfct.c index 19e44be..533d75d 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -22,8 +22,6 @@ #include #include -#include -#include #include "linux_list.h" #include "nfct.h" -- cgit v1.2.3 From dd73ceecdbe87b6ecf9e96643cd5326e520d7a1c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2015 19:18:38 +0200 Subject: nfct: Update syntax to specify command before subsystem This patch gets the nfct syntax in sync with nft so it looks like this: nfct object ... instead of: nfct object ... This patch retains backward compatibility so you can still use the old syntax. The manpage and tests have been also updated to promote the adoption of this syntax. We should have little existing clients of this tool as we can only use this to configure the cttimeout and cthelper infrastructures. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 4 +- nfct.8 | 36 +++++++++------- src/nfct-extensions/helper.c | 46 ++++++++++---------- src/nfct-extensions/timeout.c | 53 ++++++++++------------- src/nfct.c | 97 +++++++++++++++++++++++++++++++++++-------- tests/nfct/test-live.sh | 14 +++---- tests/nfct/timeout/00tcp | 16 +++---- tests/nfct/timeout/01udp | 16 +++---- tests/nfct/timeout/02generic | 16 +++---- tests/nfct/timeout/03udplite | 16 +++---- tests/nfct/timeout/04icmp | 16 +++---- tests/nfct/timeout/05icmpv6 | 16 +++---- tests/nfct/timeout/06sctp | 16 +++---- tests/nfct/timeout/07dccp | 16 +++---- tests/nfct/timeout/08gre | 16 +++---- 15 files changed, 225 insertions(+), 169 deletions(-) (limited to 'src/nfct.c') diff --git a/include/nfct.h b/include/nfct.h index dc103c6..bfffdd6 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -9,6 +9,7 @@ enum { NFCT_SUBSYS_HELPER, NFCT_SUBSYS_VERSION, NFCT_SUBSYS_HELP, + NFCT_SUBSYS_MAX }; enum { @@ -21,6 +22,7 @@ enum { NFCT_CMD_DISABLE, NFCT_CMD_DEFAULT_SET, NFCT_CMD_DEFAULT_GET, + NFCT_CMD_MAX, }; #define __init __attribute__((constructor)) @@ -30,7 +32,7 @@ void nfct_perror(const char *msg); struct nfct_extension { struct list_head head; int type; - int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[]); + int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[], int cmd); }; void nfct_extension_register(struct nfct_extension *ext); diff --git a/nfct.8 b/nfct.8 index 6f5190a..863fe12 100644 --- a/nfct.8 +++ b/nfct.8 @@ -3,12 +3,26 @@ .\" Man page written by Pablo Neira Ayuso (Feb 2012) .SH NAME -nfct \- command line tool to interact with the connection tracking system +nfct \- command line tool to configure with the connection tracking system .SH SYNOPSIS -.BR "nfct subsystem command [parameters]" +.BR "nfct command subsystem [parameters]" .SH DESCRIPTION .B nfct -is the command line tool that allows you Netfilter's manipulate Connection Tracking System. +is the command line tool that allows to configure the Connection Tracking +System. +.SH COMMANDS +.TP +.BI "list " +List the existing objects. +.TP +.BI "add " +Add new object. +.TP +.BI "delete " +Delete an object. +.TP +.BI "get " +Get an existing object. .SH SUBSYS By the time this manpage has been written, the supported subsystem are .B timeout @@ -16,24 +30,14 @@ By the time this manpage has been written, the supported subsystem are .BI "timeout " The timeout subsystem allows you to define fine-grain timeout policies. .TP +.BI "helper " +The helper subsystem allows you to configure userspace helpers. +.TP .BI "version " Displays the version information. .TP .BI "help " Displays the help message. -.SH TIMEOUT SUBSYSTEM -.TP -.BI "list " -List the existing timeout policies. -.TP -.BI "add " -Add new timeout policy. -.TP -.BI "delete " -Delete timeout policy. -.TP -.BI "get " -Get existing timeout policy. .SH EXAMPLE .TP .B nfct timeout add test-tcp inet tcp established 100 close 10 close_wait 10 diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index bfb153f..dfc55e7 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -45,36 +45,31 @@ static int nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]); static int nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]); static int -nfct_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) +nfct_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[], int cmd) { - int cmd = NFCT_CMD_NONE, ret = 0; + int ret; if (argc < 3) { - fprintf(stderr, "nfct v%s: Missing command\n" - "%s helper list|add|delete|get|flush " - "[parameters...]\n", VERSION, argv[0]); - exit(EXIT_FAILURE); + nfct_cmd_helper_usage(argv); + return -1; } - if (strncmp(argv[2], "list", strlen(argv[2])) == 0) - cmd = NFCT_CMD_LIST; - else if (strncmp(argv[2], "add", strlen(argv[2])) == 0) - cmd = NFCT_CMD_ADD; - else if (strncmp(argv[2], "delete", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DELETE; - else if (strncmp(argv[2], "get", strlen(argv[2])) == 0) - cmd = NFCT_CMD_GET; - else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0) - cmd = NFCT_CMD_FLUSH; - else if (strncmp(argv[2], "disable", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DISABLE; - else { + + switch (cmd) { + case NFCT_CMD_LIST: + case NFCT_CMD_ADD: + case NFCT_CMD_DELETE: + case NFCT_CMD_GET: + case NFCT_CMD_FLUSH: + case NFCT_CMD_DISABLE: + break; + default: fprintf(stderr, "nfct v%s: Unknown command: %s\n", VERSION, argv[2]); nfct_cmd_helper_usage(argv); exit(EXIT_FAILURE); } - switch(cmd) { + switch (cmd) { case NFCT_CMD_LIST: ret = nfct_cmd_helper_list(nl, argc, argv); break; @@ -93,6 +88,9 @@ nfct_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) case NFCT_CMD_DISABLE: ret = nfct_cmd_helper_disable(nl, argc, argv); break; + default: + nfct_cmd_helper_usage(argv); + return -1; } return ret; @@ -160,8 +158,7 @@ static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct helper add name " - "family protocol"); + "syntax: nfct add helper name family protocol"); return -1; } @@ -411,8 +408,7 @@ nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct helper add name " - "family protocol"); + "syntax: nfct add helper name family protocol"); return -1; } @@ -469,7 +465,7 @@ nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) static struct nfct_extension helper = { .type = NFCT_SUBSYS_HELPER, - .parse_params = nfct_cmd_helper_parse_params, + .parse_params = nfct_helper_parse_params, }; static void __init helper_init(void) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index c9aa386..1cb04a1 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -32,7 +32,7 @@ static void nfct_cmd_timeout_usage(char *argv[]) { fprintf(stderr, "nfct v%s: Missing command\n" - "%s timeout " + "%s timeout " "[, ...]\n", VERSION, argv[0]); } @@ -45,35 +45,30 @@ static int nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *a static int nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]); static int -nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) +nfct_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[], int cmd) { - int cmd = NFCT_CMD_NONE, ret; + int ret; if (argc < 3) { nfct_cmd_timeout_usage(argv); return -1; } - if (strncmp(argv[2], "list", strlen(argv[2])) == 0) - cmd = NFCT_CMD_LIST; - else if (strncmp(argv[2], "add", strlen(argv[2])) == 0) - cmd = NFCT_CMD_ADD; - else if (strncmp(argv[2], "delete", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DELETE; - else if (strncmp(argv[2], "get", strlen(argv[2])) == 0) - cmd = NFCT_CMD_GET; - else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0) - cmd = NFCT_CMD_FLUSH; - else if (strncmp(argv[2], "default-set", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DEFAULT_SET; - else if (strncmp(argv[2], "default-get", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DEFAULT_GET; - else { - fprintf(stderr, "nfct v%s: Unknown command: %s\n", - VERSION, argv[2]); + + switch (cmd) { + case NFCT_CMD_LIST: + case NFCT_CMD_ADD: + case NFCT_CMD_DELETE: + case NFCT_CMD_GET: + case NFCT_CMD_FLUSH: + case NFCT_CMD_DEFAULT_SET: + case NFCT_CMD_DEFAULT_GET: + break; + default: nfct_cmd_timeout_usage(argv); return -1; } - switch(cmd) { + + switch (cmd) { case NFCT_CMD_LIST: ret = nfct_cmd_timeout_list(nl, argc, argv); break; @@ -95,6 +90,9 @@ nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) case NFCT_CMD_DEFAULT_GET: ret = nfct_cmd_timeout_default_get(nl, argc, argv); break; + default: + nfct_cmd_timeout_usage(argv); + return -1; } return ret; @@ -270,9 +268,7 @@ int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct timeout add name " - "family protocol state1 " - "timeout1 state2 timeout2..."); + "syntax: nfct add timeout name family protocol state1 timeout1 ..."); return -1; } @@ -415,9 +411,7 @@ nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct timeout default-set " - "family protocol state1 " - "timeout1 state2 timeout2..."); + "syntax: nfct default-set timeout family protocol state1 timeout1..."); return -1; } @@ -454,8 +448,7 @@ nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 5) { nfct_perror("missing parameters\n" - "syntax: nfct timeout default-get " - "family protocol"); + "syntax: nfct default-get timeout family protocol"); return -1; } @@ -497,7 +490,7 @@ nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) static struct nfct_extension timeout = { .type = NFCT_SUBSYS_TIMEOUT, - .parse_params = nfct_cmd_timeout_parse_params, + .parse_params = nfct_timeout_parse_params, }; static void __init timeout_init(void) diff --git a/src/nfct.c b/src/nfct.c index 533d75d..3331e5b 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -31,7 +31,7 @@ static int nfct_cmd_help(int argc, char *argv[]); static void usage(char *argv[]) { - fprintf(stderr, "Usage: %s subsystem command [parameters]...\n", + fprintf(stderr, "Usage: %s command subsystem [parameters]...\n", argv[0]); } @@ -63,32 +63,93 @@ static struct nfct_extension *nfct_extension_lookup(int type) return NULL; } +static const char *nfct_cmd_array[NFCT_CMD_MAX] = { + [NFCT_CMD_LIST] = "list", + [NFCT_CMD_ADD] = "add", + [NFCT_CMD_DELETE] = "delete", + [NFCT_CMD_GET] = "get", + [NFCT_CMD_FLUSH] = "flush", + [NFCT_CMD_DISABLE] = "disable", + [NFCT_CMD_DEFAULT_SET] = "default-set", + [NFCT_CMD_DEFAULT_GET] = "default-get", +}; + +static int nfct_cmd_parse(const char *cmdstr) +{ + int i; + + for (i = 1; i < NFCT_CMD_MAX; i++) { + if (strncmp(nfct_cmd_array[i], cmdstr, strlen(cmdstr)) == 0) + return i; + } + return -1; +} + +static int nfct_cmd_error(char *argv[]) +{ + fprintf(stderr, "nfct v%s: Unknown command: %s\n", VERSION, argv[1]); + usage(argv); + + return EXIT_FAILURE; +} + +static const char *nfct_subsys_array[NFCT_SUBSYS_MAX] = { + [NFCT_SUBSYS_TIMEOUT] = "timeout", + [NFCT_SUBSYS_HELPER] = "helper", + [NFCT_SUBSYS_VERSION] = "version", + [NFCT_SUBSYS_HELP] = "help", +}; + +static int nfct_subsys_parse(const char *cmdstr) +{ + int i; + + for (i = 1; i < NFCT_SUBSYS_MAX; i++) { + if (strncmp(nfct_subsys_array[i], cmdstr, strlen(cmdstr)) == 0) + return i; + } + return -1; +} + +static int nfct_subsys_error(char *argv[]) +{ + fprintf(stderr, "nfct v%s: Unknown subsystem: %s\n", VERSION, argv[1]); + usage(argv); + + return EXIT_FAILURE; +} + int main(int argc, char *argv[]) { - int subsys = NFCT_SUBSYS_NONE, ret = 0; + int subsys, cmd, ret = 0; struct nfct_extension *ext; struct mnl_socket *nl; - if (argc < 2) { + if (argc < 3) { usage(argv); exit(EXIT_FAILURE); } - if (strncmp(argv[1], "timeout", strlen(argv[1])) == 0) { - subsys = NFCT_SUBSYS_TIMEOUT; - } else if (strncmp(argv[1], "helper", strlen(argv[1])) == 0) { - subsys = NFCT_SUBSYS_HELPER; - } else if (strncmp(argv[1], "version", strlen(argv[1])) == 0) - subsys = NFCT_SUBSYS_VERSION; - else if (strncmp(argv[1], "help", strlen(argv[1])) == 0) - subsys = NFCT_SUBSYS_HELP; - else { - fprintf(stderr, "nfct v%s: Unknown subsystem: %s\n", - VERSION, argv[1]); - usage(argv); - exit(EXIT_FAILURE); + + cmd = nfct_cmd_parse(argv[1]); + if (cmd < 0) { + /* Workaround not to break backward compatibility and to get + * the syntax in sync with nft. Old nfct versions allow to + * specify the subsystem before the command. + */ + subsys = nfct_subsys_parse(argv[1]); + if (subsys < 0) + return nfct_subsys_error(argv); + + cmd = nfct_cmd_parse(argv[2]); + if (cmd < 0) + return nfct_cmd_error(argv); + } else { + subsys = nfct_subsys_parse(argv[2]); + if (subsys < 0) + return nfct_subsys_error(argv); } - switch(subsys) { + switch (subsys) { case NFCT_SUBSYS_VERSION: ret = nfct_cmd_version(argc, argv); break; @@ -109,7 +170,7 @@ int main(int argc, char *argv[]) return -1; } - ret = ext->parse_params(nl, argc, argv); + ret = ext->parse_params(nl, argc, argv, cmd); mnl_socket_close(nl); break; } diff --git a/tests/nfct/test-live.sh b/tests/nfct/test-live.sh index c338e63..2257087 100644 --- a/tests/nfct/test-live.sh +++ b/tests/nfct/test-live.sh @@ -6,7 +6,7 @@ WAIT_BETWEEN_TESTS=10 # flush cttimeout table -nfct timeout flush +nfct flush timeout # flush the conntrack table conntrack -F @@ -19,7 +19,7 @@ echo "---- test no. 1 ----" conntrack -E -p 13 & -nfct timeout add test-generic inet generic timeout 100 +nfct add timeout test-generic inet generic timeout 100 iptables -I OUTPUT -t raw -p all -j CT --timeout test-generic hping3 -c 1 -V -I eth0 -0 8.8.8.8 -H 13 @@ -30,7 +30,7 @@ echo "---- end test no. 1 ----" sleep $WAIT_BETWEEN_TESTS iptables -D OUTPUT -t raw -p all -j CT --timeout test-generic -nfct timeout del test-generic +nfct del timeout test-generic # # No.2: test TCP timeout policy @@ -40,14 +40,14 @@ echo "---- test no. 2 ----" conntrack -E -p tcp & -nfct timeout add test-tcp inet tcp syn_sent 100 +nfct add timeout test-tcp inet tcp syn_sent 100 iptables -I OUTPUT -t raw -p tcp -j CT --timeout test-tcp hping3 -V -S -p 80 -s 5050 8.8.8.8 -c 1 sleep $WAIT_BETWEEN_TESTS iptables -D OUTPUT -t raw -p tcp -j CT --timeout test-tcp -nfct timeout del test-tcp +nfct del timeout test-tcp killall -15 conntrack @@ -61,12 +61,12 @@ echo "---- test no. 3 ----" conntrack -E -p icmp & -nfct timeout add test-icmp inet icmp timeout 50 +nfct add timeout test-icmp inet icmp timeout 50 iptables -I OUTPUT -t raw -p icmp -j CT --timeout test-icmp hping3 -1 8.8.8.8 -c 2 iptables -D OUTPUT -t raw -p icmp -j CT --timeout test-icmp -nfct timeout del test-icmp +nfct del timeout test-icmp killall -15 conntrack diff --git a/tests/nfct/timeout/00tcp b/tests/nfct/timeout/00tcp index c9d7d24..ab2e6fc 100644 --- a/tests/nfct/timeout/00tcp +++ b/tests/nfct/timeout/00tcp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet tcp established 100 ; OK +nfct add timeout test inet tcp established 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK +nfct add timeout test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/01udp b/tests/nfct/timeout/01udp index 952526c..f8097d6 100644 --- a/tests/nfct/timeout/01udp +++ b/tests/nfct/timeout/01udp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet udp unreplied 10 ; OK +nfct add timeout test inet udp unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet udp unreplied 1 replied 2 ; OK +nfct add timeout test inet udp unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/02generic b/tests/nfct/timeout/02generic index b6ca699..ffba138 100644 --- a/tests/nfct/timeout/02generic +++ b/tests/nfct/timeout/02generic @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet generic timeout 10 ; OK +nfct add timeout test inet generic timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet generic timeout 1 ; OK +nfct add timeout test inet generic timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/03udplite b/tests/nfct/timeout/03udplite index 69dda15..8ed3459 100644 --- a/tests/nfct/timeout/03udplite +++ b/tests/nfct/timeout/03udplite @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet udplite unreplied 10 ; OK +nfct add timeout test inet udplite unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet udplite unreplied 1 replied 2 ; OK +nfct add timeout test inet udplite unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/04icmp b/tests/nfct/timeout/04icmp index 606e8b9..edb1c99 100644 --- a/tests/nfct/timeout/04icmp +++ b/tests/nfct/timeout/04icmp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet icmp timeout 10 ; OK +nfct add timeout test inet icmp timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet icmp timeout 1 ; OK +nfct add timeout test inet icmp timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/05icmpv6 b/tests/nfct/timeout/05icmpv6 index 16541f5..40ccc49 100644 --- a/tests/nfct/timeout/05icmpv6 +++ b/tests/nfct/timeout/05icmpv6 @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet6 icmpv6 timeout 10 ; OK +nfct add timeout test inet6 icmpv6 timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet6 icmpv6 timeout 1 ; OK +nfct add timeout test inet6 icmpv6 timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/06sctp b/tests/nfct/timeout/06sctp index f475215..62b44c6 100644 --- a/tests/nfct/timeout/06sctp +++ b/tests/nfct/timeout/06sctp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet sctp established 100 ; OK +nfct add timeout test inet sctp established 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK +nfct add timeout test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/07dccp b/tests/nfct/timeout/07dccp index 1bd4fa5..1d88585 100644 --- a/tests/nfct/timeout/07dccp +++ b/tests/nfct/timeout/07dccp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet dccp request 100 ; OK +nfct add timeout test inet dccp request 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK +nfct add timeout test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/08gre b/tests/nfct/timeout/08gre index 7ef4bdb..709b943 100644 --- a/tests/nfct/timeout/08gre +++ b/tests/nfct/timeout/08gre @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet gre unreplied 10 ; OK +nfct add timeout test inet gre unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet gre unreplied 1 replied 2 ; OK +nfct add timeout test inet gre unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK -- cgit v1.2.3