diff --git a/Makefile.am b/Makefile.am index bd366bf..975c538 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,4 +10,5 @@ DIST_SUBDIRS = include src extensions LIBS = @LIBNETFILTER_CONNTRACK_LIBS@ dist-hook: - rm -rf `find $(distdir)/doc -name .svn` + rm -rf `find $(distdir)/doc -name *.orig` + rm -rf `find $(distdir)/doc -name *.rej` diff --git a/conntrack.8 b/conntrack.8 index f273434..abc26c5 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -1,4 +1,4 @@ -.TH CONNTRACK 8 "Jul 5, 2010" "" "" +.TH CONNTRACK 8 "Sep 25, 2014" "" "" .\" Man page written by Harald Welte (Dec 2007) @@ -32,7 +32,7 @@ Dump the external cache, i.e. show foreign states .TP .BI "-x " Display output in XML format. This option is only valid in combination -with "-i" and "-e" parameters. +with "\-i" and "\-e" parameters. .TP .BI "-f " "[|internal|external]" Flush the internal and/or external cache diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 56f5162..d2d94a9 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -62,6 +62,22 @@ Helper { ExpectTimeout 300 } } + Type dhcpv6 inet6 udp { + QueueNum 4 + QueueLen 10240 + Policy dhcpv6 { + ExpectMax 1 + ExpectTimeout 300 + } + } + Type ssdp inet udp { + QueueNum 5 + QueueLen 10240 + Policy ssdp { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index f21a4ff..d23dec5 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -890,6 +890,7 @@ maintainance. Oracle*TNS, to support its special Redirect message. NFSv3, mind that version 4 does not require this helper. FTP (this helper is also available in kernel-space). +SSDP. The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers): diff --git a/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c index 586c4cc..f6258ad 100644 --- a/extensions/libct_proto_dccp.c +++ b/extensions/libct_proto_dccp.c @@ -78,7 +78,7 @@ static char dccp_commands_v_options[NUMBER_OF_CMD][DCCP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0}, @@ -118,7 +118,7 @@ static int parse_options(char c, unsigned int *flags) { int i; - u_int16_t port; + uint16_t port; switch(c) { case 1: diff --git a/extensions/libct_proto_gre.c b/extensions/libct_proto_gre.c index 0274a37..2dc63d1 100644 --- a/extensions/libct_proto_gre.c +++ b/extensions/libct_proto_gre.c @@ -91,7 +91,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(strtoul(optarg, NULL, 0)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c index d04397f..2ce1c65 100644 --- a/extensions/libct_proto_icmp.c +++ b/extensions/libct_proto_icmp.c @@ -72,8 +72,8 @@ static int parse(char c, unsigned int *flags) { switch(c) { - u_int8_t tmp; - u_int16_t id; + uint8_t tmp; + uint16_t id; case '1': tmp = atoi(optarg); nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp); diff --git a/extensions/libct_proto_icmpv6.c b/extensions/libct_proto_icmpv6.c index f8c2c68..18dd3e5 100644 --- a/extensions/libct_proto_icmpv6.c +++ b/extensions/libct_proto_icmpv6.c @@ -75,8 +75,8 @@ static int parse(char c, unsigned int *flags) { switch(c) { - u_int8_t tmp; - u_int16_t id; + uint8_t tmp; + uint16_t id; case '1': tmp = atoi(optarg); nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp); diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c index f4c94df..04828bf 100644 --- a/extensions/libct_proto_sctp.c +++ b/extensions/libct_proto_sctp.c @@ -81,7 +81,7 @@ static char sctp_commands_v_options[NUMBER_OF_CMD][SCTP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0}, @@ -120,8 +120,8 @@ parse_options(char c, struct nf_conntrack *ct, unsigned int *flags) { int i; - u_int16_t port; - u_int32_t vtag; + uint16_t port; + uint32_t vtag; switch(c) { case 1: diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c index 0b43bf5..8a37a55 100644 --- a/extensions/libct_proto_tcp.c +++ b/extensions/libct_proto_tcp.c @@ -65,7 +65,7 @@ static char tcp_commands_v_options[NUMBER_OF_CMD][TCP_NUMBER_OF_OPT] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0}, @@ -106,7 +106,7 @@ static int parse_options(char c, unsigned int *flags) { int i; - u_int16_t port; + uint16_t port; switch(c) { case '1': diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c index d7c4da1..e30637c 100644 --- a/extensions/libct_proto_udp.c +++ b/extensions/libct_proto_udp.c @@ -73,7 +73,7 @@ static char udp_commands_v_options[NUMBER_OF_CMD][UDP_NUMBER_OF_OPT] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0}, @@ -87,7 +87,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(atoi(optarg)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/extensions/libct_proto_udplite.c b/extensions/libct_proto_udplite.c index bffd5fe..f46cef0 100644 --- a/extensions/libct_proto_udplite.c +++ b/extensions/libct_proto_udplite.c @@ -81,7 +81,7 @@ static char udplite_commands_v_options[NUMBER_OF_CMD][UDP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0}, @@ -95,7 +95,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(atoi(optarg)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/include/bitops.h b/include/bitops.h index 51f4289..27fe58d 100644 --- a/include/bitops.h +++ b/include/bitops.h @@ -1,34 +1,34 @@ #ifndef _BITOPS_H_ #define _BITOPS_H_ -#include +#include -static inline void set_bit_u32(int nr, u_int32_t *addr) +static inline void set_bit_u32(int nr, uint32_t *addr) { addr[nr >> 5] |= (1UL << (nr & 31)); } -static inline void unset_bit_u32(int nr, u_int32_t *addr) +static inline void unset_bit_u32(int nr, uint32_t *addr) { addr[nr >> 5] &= ~(1UL << (nr & 31)); } -static inline int test_bit_u32(int nr, const u_int32_t *addr) +static inline int test_bit_u32(int nr, const uint32_t *addr) { return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; } -static inline void set_bit_u16(int nr, u_int16_t *addr) +static inline void set_bit_u16(int nr, uint16_t *addr) { addr[nr >> 4] |= (1UL << (nr & 15)); } -static inline void unset_bit_u16(int nr, u_int16_t *addr) +static inline void unset_bit_u16(int nr, uint16_t *addr) { addr[nr >> 4] &= ~(1UL << (nr & 15)); } -static inline int test_bit_u16(int nr, const u_int16_t *addr) +static inline int test_bit_u16(int nr, const uint16_t *addr) { return ((1UL << (nr & 15)) & (addr[nr >> 4])) != 0; } diff --git a/include/conntrack.h b/include/conntrack.h index 6cd9962..c2a0c8f 100644 --- a/include/conntrack.h +++ b/include/conntrack.h @@ -10,7 +10,7 @@ #include #define NUMBER_OF_CMD 19 -#define NUMBER_OF_OPT 25 +#define NUMBER_OF_OPT 27 struct ctproto_handler { struct list_head head; diff --git a/include/helper.h b/include/helper.h index 9d96fb7..f412e55 100644 --- a/include/helper.h +++ b/include/helper.h @@ -25,7 +25,7 @@ struct ctd_helper { int (*cb)(struct pkt_buff *pkt, uint32_t protoff, struct myct *ct, - u_int32_t ctinfo); + uint32_t ctinfo); struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; @@ -49,6 +49,9 @@ 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); +void cthelper_get_port_src(struct nf_conntrack *ct, int dir, uint16_t *port); +void cthelper_get_port_dst(struct nf_conntrack *ct, int dir, uint16_t *port); + 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); diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index b64454c..c755646 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -18,6 +18,10 @@ enum nfnetlink_groups { #define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_DESTROY, #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA __NFNLGRP_MAX, }; #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) @@ -49,46 +53,14 @@ struct nfgenmsg { #define NFNL_SUBSYS_OSF 5 #define NFNL_SUBSYS_IPSET 6 #define NFNL_SUBSYS_ACCT 7 -#define NFNL_SUBSYS_COUNT 8 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_COUNT 12 -#ifdef __KERNEL__ +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 -#include -#include -#include - -struct nfnl_callback { - int (*call)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - int (*call_rcu)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - const struct nla_policy *policy; /* netlink attribute policy */ - const u_int16_t attr_count; /* number of nlattr's */ -}; - -struct nfnetlink_subsystem { - const char *name; - __u8 subsys_id; /* nfnetlink subsystem ID */ - __u8 cb_count; /* number of callbacks */ - const struct nfnl_callback *cb; /* callback for individual types */ -}; - -extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); -extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); - -extern int nfnetlink_has_listeners(struct net *net, unsigned int group); -extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, - int echo, gfp_t flags); -extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); -extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); - -extern void nfnl_lock(void); -extern void nfnl_unlock(void); - -#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ - MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) - -#endif /* __KERNEL__ */ -#endif /* _NFNETLINK_H */ +#endif /* _NFNETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_cttimeout.h b/include/linux/netfilter/nfnetlink_cttimeout.h index a2810a7..1ab0b97 100644 --- a/include/linux/netfilter/nfnetlink_cttimeout.h +++ b/include/linux/netfilter/nfnetlink_cttimeout.h @@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types { IPCTNL_MSG_TIMEOUT_NEW, IPCTNL_MSG_TIMEOUT_GET, IPCTNL_MSG_TIMEOUT_DELETE, + IPCTNL_MSG_TIMEOUT_DEFAULT_SET, + IPCTNL_MSG_TIMEOUT_DEFAULT_GET, IPCTNL_MSG_TIMEOUT_MAX }; diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index e0d8fd8..8dd819e 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -44,6 +44,11 @@ enum nfqnl_attr_type { NFQA_PAYLOAD, /* opaque data payload */ NFQA_CT, /* nf_conntrack_netlink.h */ NFQA_CT_INFO, /* enum ip_conntrack_info */ + NFQA_CAP_LEN, /* __u32 length of captured packet */ + NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nf_conntrack_netlink.h */ + NFQA_UID, /* __u32 sk uid */ + NFQA_GID, /* __u32 sk gid */ __NFQA_MAX }; @@ -95,5 +100,16 @@ enum nfqnl_attr_config { /* Flags for NFQA_CFG_FLAGS */ #define NFQA_CFG_F_FAIL_OPEN (1 << 0) #define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_GSO (1 << 2) +#define NFQA_CFG_F_UID_GID (1 << 3) +#define NFQA_CFG_F_MAX (1 << 4) + +/* flags for NFQA_SKB_INFO */ +/* packet appears to have wrong checksums, but they are ok */ +#define NFQA_SKB_CSUMNOTREADY (1 << 0) +/* packet is GSO (i.e., exceeds device mtu) */ +#define NFQA_SKB_GSO (1 << 1) +/* csum not validated (incoming device doesn't support hw checksum, etc.) */ +#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2) #endif /* _NFNETLINK_QUEUE_H */ diff --git a/include/mcast.h b/include/mcast.h index 402a033..f0225aa 100644 --- a/include/mcast.h +++ b/include/mcast.h @@ -4,6 +4,7 @@ #include #include #include +#include struct mcast_conf { int ipproto; diff --git a/include/myct.h b/include/myct.h index 45d9f29..02d695c 100644 --- a/include/myct.h +++ b/include/myct.h @@ -37,6 +37,7 @@ struct myct_tuple { struct myct { struct nf_conntrack *ct; + struct nf_expect *exp; void *priv_data; }; diff --git a/include/nfct.h b/include/nfct.h index 5548b03..dc103c6 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, @@ -17,23 +19,26 @@ enum { NFCT_CMD_GET, NFCT_CMD_FLUSH, NFCT_CMD_DISABLE, + NFCT_CMD_DEFAULT_SET, + NFCT_CMD_DEFAULT_GET, }; +#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)(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), + void *data); #endif diff --git a/include/tcp.h b/include/tcp.h index 2f0fd0a..068d43a 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -3,6 +3,7 @@ #include #include +#include struct tcp_conf { int ipproto; diff --git a/include/udp.h b/include/udp.h index 9f9c17a..53d713d 100644 --- a/include/udp.h +++ b/include/udp.h @@ -3,6 +3,7 @@ #include #include +#include struct udp_conf { int ipproto; diff --git a/src/Makefile.am b/src/Makefile.am index ec03e46..1bc3622 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,8 @@ include $(top_srcdir)/Make_global.am +if HAVE_CTHELPER SUBDIRS = helpers +endif AM_YFLAGS = -d @@ -11,17 +13,29 @@ sbin_PROGRAMS = conntrack conntrackd nfct 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} ${LIBNFNETLINK_LIBS} -nfct_SOURCES = nfct.c \ - helpers.c \ - nfct-extensions/timeout.c \ - nfct-extensions/helper.c +nfct_SOURCES = nfct.c + +if HAVE_CTHELPER +nfct_SOURCES += helpers.c \ + nfct-extensions/helper.c +endif + +if HAVE_CTTIMEOUT +nfct_SOURCES += nfct-extensions/timeout.c +endif nfct_LDADD = ${LIBMNL_LIBS} \ ${LIBNETFILTER_CONNTRACK_LIBS} \ - ${LIBNETFILTER_CTTIMEOUT_LIBS} \ - ${LIBNETFILTER_CTHELPER_LIBS} \ ${libdl_LIBS} +if HAVE_CTTIMEOUT +nfct_LDADD += ${LIBNETFILTER_CTTIMEOUT_LIBS} +endif + +if HAVE_CTHELPER +nfct_LDADD += ${LIBNETFILTER_CTHELPER_LIBS} +endif + nfct_LDFLAGS = -export-dynamic conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ @@ -29,7 +43,7 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.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 cthelper.c \ + ctnl.c \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ traffic_stats.c stats-mode.c \ network.c cidr.c \ @@ -39,15 +53,22 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ external_cache.c external_inject.c \ internal_cache.c internal_bypass.c \ read_config_yy.y read_config_lex.l \ - stack.c helpers.c utils.c expect.c + stack.c + +if HAVE_CTHELPER +conntrackd_SOURCES += cthelper.c helpers.c utils.c expect.c +endif # 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 = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \ - ${LIBNETFILTER_QUEUE_LIBS} ${LIBNETFILTER_CTHELPER_LIBS} \ ${libdl_LIBS} ${LIBNFNETLINK_LIBS} +if HAVE_CTHELPER +conntrackd_LDADD += ${LIBNETFILTER_CTHELPER_LIBS} ${LIBNETFILTER_QUEUE_LIBS} +endif + conntrackd_LDFLAGS = -export-dynamic EXTRA_DIST = read_config_yy.h diff --git a/src/build.c b/src/build.c index 5799b51..9ba8b57 100644 --- a/src/build.c +++ b/src/build.c @@ -105,14 +105,14 @@ static enum nf_conntrack_attr nat_type[] = ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ATTR_REPL_NAT_SEQ_CORRECTION_POS, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, ATTR_REPL_NAT_SEQ_OFFSET_AFTER }; +/* ICMP, UDP and TCP are always loaded with nf_conntrack_ipv4 */ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - if (!nfct_attr_is_set(ct, ATTR_TCP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE); if (CONFIG(sync).tcp_window_tracking) { ct_build_u8(ct, ATTR_TCP_WSCALE_ORIG, n, NTA_TCP_WSCALE_ORIG); @@ -122,12 +122,12 @@ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n) static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - + /* SCTP is optional, make sure nf_conntrack_sctp is loaded */ if (!nfct_attr_is_set(ct, ATTR_SCTP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE); ct_build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG); ct_build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL); @@ -135,18 +135,22 @@ static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n) static void build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - + /* DCCP is optional, make sure nf_conntrack_dccp is loaded */ if (!nfct_attr_is_set(ct, ATTR_DCCP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE); ct_build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE); } static void build_l4proto_icmp(const struct nf_conntrack *ct, struct nethdr *n) { + /* This is also used by ICMPv6 and nf_conntrack_ipv6 is optional */ + if (!nfct_attr_is_set(ct, ATTR_ICMP_TYPE)) + return; + ct_build_u8(ct, ATTR_ICMP_TYPE, n, NTA_ICMP_TYPE); ct_build_u8(ct, ATTR_ICMP_CODE, n, NTA_ICMP_CODE); ct_build_u16(ct, ATTR_ICMP_ID, n, NTA_ICMP_ID); diff --git a/src/channel.c b/src/channel.c index 8b7c319..acbfa7d 100644 --- a/src/channel.c +++ b/src/channel.c @@ -109,6 +109,7 @@ channel_open(struct channel_conf *cfg) if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { free(c); + close(fd); return NULL; } close(fd); diff --git a/src/conntrack.c b/src/conntrack.c index 7d2a365..00b09b6 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -82,6 +82,9 @@ static struct { /* Allows filtering by ctlabels */ struct nfct_bitmask *label; + + /* Allows setting/removing specific ctlabels */ + struct nfct_bitmask *label_modify; } tmpl; static int alloc_tmpl_objects(void) @@ -109,6 +112,8 @@ static void free_tmpl_objects(void) nfexp_destroy(tmpl.exp); if (tmpl.label) nfct_bitmask_destroy(tmpl.label); + if (tmpl.label_modify) + nfct_bitmask_destroy(tmpl.label_modify); } enum ct_command { @@ -255,6 +260,12 @@ enum ct_options { CT_OPT_LABEL_BIT = 24, CT_OPT_LABEL = (1 << CT_OPT_LABEL_BIT), + + CT_OPT_ADD_LABEL_BIT = 25, + CT_OPT_ADD_LABEL = (1 << CT_OPT_ADD_LABEL_BIT), + + CT_OPT_DEL_LABEL_BIT = 26, + CT_OPT_DEL_LABEL = (1 << CT_OPT_DEL_LABEL_BIT), }; /* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ @@ -289,6 +300,8 @@ static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_ANY_NAT_BIT] = "any-nat", [CT_OPT_ZONE_BIT] = "zone", [CT_OPT_LABEL_BIT] = "label", + [CT_OPT_ADD_LABEL_BIT] = "label-add", + [CT_OPT_DEL_LABEL_BIT] = "label-del", }; static struct option original_opts[] = { @@ -330,12 +343,14 @@ static struct option original_opts[] = { {"any-nat", 2, 0, 'j'}, {"zone", 1, 0, 'w'}, {"label", 1, 0, 'l'}, + {"label-add", 1, 0, '<'}, + {"label-del", 2, 0, '>'}, {0, 0, 0, 0} }; static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:" "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::" - "g::c:b:C::Sj::w:l:"; + "g::c:b:C::Sj::w:l:<:>::"; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to @@ -350,26 +365,26 @@ static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:" static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /* Well, it's better than "Re: Linux vs FreeBSD" */ { - /* s d r q p t u z e [ ] { } a m i f n g o c b j w l*/ -/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2}, -/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0}, -/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0}, -/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,0}, -/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0}, -/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2}, -/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0}, -/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0}, -/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + /* s d r q p t u z e [ ] { } a m i f n g o c b j w l < > */ +/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,0,0}, +/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0}, +/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,2,2,2}, +/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,2,0,0}, +/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0}, +/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,0,0}, +/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0}, +/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0}, +/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, }; static const int cmd2type[][2] = { @@ -402,6 +417,8 @@ static const int opt2type[] = { ['j'] = CT_OPT_ANY_NAT, ['w'] = CT_OPT_ZONE, ['l'] = CT_OPT_LABEL, + ['<'] = CT_OPT_ADD_LABEL, + ['>'] = CT_OPT_DEL_LABEL, }; static const int opt2family_attr[][2] = { @@ -420,11 +437,17 @@ static const int opt2attr[] = { ['d'] = ATTR_ORIG_L3PROTO, ['r'] = ATTR_REPL_L3PROTO, ['q'] = ATTR_REPL_L3PROTO, + ['{'] = ATTR_ORIG_L3PROTO, + ['}'] = ATTR_ORIG_L3PROTO, + ['['] = ATTR_ORIG_L3PROTO, + [']'] = ATTR_ORIG_L3PROTO, ['m'] = ATTR_MARK, ['c'] = ATTR_SECMARK, ['i'] = ATTR_ID, ['w'] = ATTR_ZONE, ['l'] = ATTR_CONNLABELS, + ['<'] = ATTR_CONNLABELS, + ['>'] = ATTR_CONNLABELS, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -472,6 +495,11 @@ static const char usage_expectation_parameters[] = " --mask-src ip\t\tSource mask address\n" " --mask-dst ip\t\tDestination mask address\n"; +static const char usage_update_parameters[] = + "Updating parameters and options:\n" + " --label-add label\tAdd label\n" + " --label-del label\tDelete label\n"; + static const char usage_parameters[] = "Common parameters and options:\n" " -s, --orig-src ip\t\tSource address from original direction\n" @@ -523,7 +551,7 @@ static struct ctproto_handler *findproto(char *name, int *pnum) /* is it in the list of supported protocol? */ list_for_each_entry(cur, &proto_list, head) { - if (strcmp(cur->name, name) == 0) { + if (strcasecmp(cur->name, name) == 0) { *pnum = cur->protonum; return cur; } @@ -890,20 +918,20 @@ add_command(unsigned int *cmd, const int newcmd) *cmd |= newcmd; } -static char *get_table(int argc, char *argv[]) +static char *get_optional_arg(int argc, char *argv[]) { - char *table = NULL; + char *arg = NULL; /* Nasty bug or feature in getopt_long ? * It seems that it behaves badly with optional arguments. * Fortunately, I just stole the fix from iptables ;) */ if (optarg) - return 0; + return arg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') - table = argv[optind++]; + arg = argv[optind++]; - return table; + return arg; } enum { @@ -915,7 +943,7 @@ enum { static unsigned int check_type(int argc, char *argv[]) { - const char *table = get_table(argc, argv); + const char *table = get_optional_arg(argc, argv); /* default to conntrack subsystem if nothing has been specified. */ if (table == NULL) @@ -1045,6 +1073,7 @@ usage(char *prog) fprintf(stdout, "\n%s", usage_tables); fprintf(stdout, "\n%s", usage_conntrack_parameters); fprintf(stdout, "\n%s", usage_expectation_parameters); + fprintf(stdout, "\n%s", usage_update_parameters); fprintf(stdout, "\n%s\n", usage_parameters); } @@ -1349,7 +1378,7 @@ static int print_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); printf("%s\n", buf); return NFCT_CB_CONTINUE; @@ -1376,6 +1405,72 @@ static void copy_status(struct nf_conntrack *tmp, const struct nf_conntrack *ct) } } +static struct nfct_bitmask *xnfct_bitmask_clone(const struct nfct_bitmask *a) +{ + struct nfct_bitmask *b = nfct_bitmask_clone(a); + if (!b) + exit_error(OTHER_PROBLEM, "out of memory"); + return b; +} + +static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct) +{ + struct nfct_bitmask *ctb, *newmask; + unsigned int i; + + if ((options & (CT_OPT_ADD_LABEL|CT_OPT_DEL_LABEL)) == 0) + return; + + nfct_copy_attr(tmp, ct, ATTR_CONNLABELS); + ctb = (void *) nfct_get_attr(tmp, ATTR_CONNLABELS); + + if (options & CT_OPT_ADD_LABEL) { + if (ctb == NULL) { + nfct_set_attr(tmp, ATTR_CONNLABELS, + xnfct_bitmask_clone(tmpl.label_modify)); + return; + } + /* If we send a bitmask shorter than the kernel sent to us, the bits we + * omit will be cleared (as "padding"). So we always have to send the + * same sized bitmask as we received. + * + * Mask has to have the same size as the labels, otherwise it will not + * be encoded by libnetfilter_conntrack, as different sizes are not + * accepted by the kernel. + */ + newmask = nfct_bitmask_new(nfct_bitmask_maxbit(ctb)); + + for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) { + if (nfct_bitmask_test_bit(tmpl.label_modify, i)) { + nfct_bitmask_set_bit(ctb, i); + nfct_bitmask_set_bit(newmask, i); + } else if (nfct_bitmask_test_bit(ctb, i)) { + /* Kernel only retains old bit values that are sent as + * zeroes in BOTH labels and mask. + */ + nfct_bitmask_unset_bit(ctb, i); + } + } + nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask); + } else if (ctb != NULL) { + /* CT_OPT_DEL_LABEL */ + if (tmpl.label_modify == NULL) { + newmask = nfct_bitmask_new(0); + if (newmask) + nfct_set_attr(tmp, ATTR_CONNLABELS, newmask); + return; + } + + for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) { + if (nfct_bitmask_test_bit(tmpl.label_modify, i)) + nfct_bitmask_unset_bit(ctb, i); + } + + newmask = xnfct_bitmask_clone(tmpl.label_modify); + nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask); + } +} + static int update_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) @@ -1395,6 +1490,9 @@ static int update_cb(enum nf_conntrack_msg_type type, if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + tmp = nfct_new(); if (tmp == NULL) exit_error(OTHER_PROBLEM, "out of memory"); @@ -1403,6 +1501,7 @@ static int update_cb(enum nf_conntrack_msg_type type, nfct_copy(tmp, obj, NFCT_CP_META); copy_mark(tmp, ct, &tmpl.mark); copy_status(tmp, ct); + copy_label(tmp, ct); /* do not send NFCT_Q_UPDATE if ct appears unchanged */ if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) { @@ -1411,12 +1510,10 @@ static int update_cb(enum nf_conntrack_msg_type type, } res = nfct_query(ith, NFCT_Q_UPDATE, tmp); - if (res < 0) { - nfct_destroy(tmp); - exit_error(OTHER_PROBLEM, - "Operation failed: %s", + if (res < 0) + fprintf(stderr, + "Operation failed: %s\n", err2str(errno, CT_UPDATE)); - } nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL); res = nfct_query(ith, NFCT_Q_GET, tmp); @@ -1601,7 +1698,8 @@ static int nfct_mnl_socket_open(void) } static struct nlmsghdr * -nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type) +nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, + uint8_t family) { struct nlmsghdr *nlh; struct nfgenmsg *nfh; @@ -1612,7 +1710,7 @@ nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type) nlh->nlmsg_seq = time(NULL); nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); - nfh->nfgen_family = AF_INET; + nfh->nfgen_family = family; nfh->version = NFNETLINK_V0; nfh->res_id = 0; @@ -1625,13 +1723,13 @@ static void nfct_mnl_socket_close(void) } static int -nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) +nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; int res; - nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); + nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family); res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); if (res < 0) @@ -1651,13 +1749,13 @@ nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) } static int -nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb) +nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; int res; - nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); + nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family); res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); if (res < 0) @@ -1818,6 +1916,65 @@ static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data) static struct ctproto_handler *h; +static void labelmap_init(void) +{ + if (labelmap) + return; + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + perror("nfct_labelmap_new"); +} + +static void merge_bitmasks(struct nfct_bitmask **current, + struct nfct_bitmask *src) +{ + unsigned int i; + + if (*current == NULL) { + *current = src; + return; + } + + /* "current" must be the larger bitmask object */ + if (nfct_bitmask_maxbit(src) > nfct_bitmask_maxbit(*current)) { + struct nfct_bitmask *tmp = *current; + *current = src; + src = tmp; + } + + for (i = 0; i <= nfct_bitmask_maxbit(src); i++) { + if (nfct_bitmask_test_bit(src, i)) + nfct_bitmask_set_bit(*current, i); + } + + nfct_bitmask_destroy(src); +} + +static void +nfct_set_addr_from_opt(int opt, struct nf_conntrack *ct, union ct_address *ad, + int *family) +{ + int l3protonum; + + options |= opt2type[opt]; + l3protonum = parse_addr(optarg, ad); + if (l3protonum == AF_UNSPEC) { + exit_error(PARAMETER_PROBLEM, + "Invalid IP address `%s'", optarg); + } + set_family(family, l3protonum); + if (l3protonum == AF_INET) { + nfct_set_attr_u32(ct, + opt2family_attr[opt][0], + ad->v4); + } else if (l3protonum == AF_INET6) { + nfct_set_attr(ct, + opt2family_attr[opt][1], + &ad->v6); + } + nfct_set_attr_u8(ct, opt2attr[opt], l3protonum); +} + int main(int argc, char *argv[]) { int c, cmd; @@ -1825,7 +1982,7 @@ int main(int argc, char *argv[]) int res = 0, partial; size_t socketbuffersize = 0; int family = AF_UNSPEC; - int l3protonum, protonum = 0; + int protonum = 0; union ct_address ad; unsigned int command = 0; @@ -1896,47 +2053,15 @@ int main(int argc, char *argv[]) case 'd': case 'r': case 'q': - options |= opt2type[c]; - - l3protonum = parse_addr(optarg, &ad); - if (l3protonum == AF_UNSPEC) { - exit_error(PARAMETER_PROBLEM, - "Invalid IP address `%s'", optarg); - } - set_family(&family, l3protonum); - if (l3protonum == AF_INET) { - nfct_set_attr_u32(tmpl.ct, - opt2family_attr[c][0], - ad.v4); - } else if (l3protonum == AF_INET6) { - nfct_set_attr(tmpl.ct, - opt2family_attr[c][1], - &ad.v6); - } - nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum); + nfct_set_addr_from_opt(c, tmpl.ct, &ad, &family); break; case '{': case '}': + nfct_set_addr_from_opt(c, tmpl.exptuple, &ad, &family); + break; case '[': case ']': - options |= opt2type[c]; - l3protonum = parse_addr(optarg, &ad); - if (l3protonum == AF_UNSPEC) { - exit_error(PARAMETER_PROBLEM, - "Invalid IP address `%s'", optarg); - } - set_family(&family, l3protonum); - if (l3protonum == AF_INET) { - nfct_set_attr_u32(tmpl.mask, - opt2family_attr[c][0], - ad.v4); - } else if (l3protonum == AF_INET6) { - nfct_set_attr(tmpl.mask, - opt2family_attr[c][1], - &ad.v6); - } - nfct_set_attr_u8(tmpl.mask, - ATTR_ORIG_L3PROTO, l3protonum); + nfct_set_addr_from_opt(c, tmpl.mask, &ad, &family); break; case 'p': options |= CT_OPT_PROTO; @@ -1970,12 +2095,8 @@ int main(int argc, char *argv[]) case 'o': options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); - if (output_mask & _O_CL) { - if (!labelmap) - labelmap = nfct_labelmap_new(NULL); - if (!labelmap) - perror("nfct_labelmap_new"); - } + if (output_mask & _O_CL) + labelmap_init(); break; case 'z': options |= CT_OPT_ZERO; @@ -1987,12 +2108,7 @@ int main(int argc, char *argv[]) options |= opt2type[c]; - if (optarg) - continue; - else if (optind < argc && argv[optind][0] != '-' - && argv[optind][0] != '!') - tmp = argv[optind++]; - + tmp = get_optional_arg(argc, argv); if (tmp == NULL) continue; @@ -2020,19 +2136,39 @@ int main(int argc, char *argv[]) tmpl.filter_mark_kernel.mask = tmpl.mark.mask; break; case 'l': + case '<': + case '>': options |= opt2type[c]; - char *optarg2 = strdup(optarg); - if (!labelmap) - labelmap = nfct_labelmap_new(NULL); - if (!labelmap) - exit_error(OTHER_PROBLEM, "unable to open labelmap file"); + labelmap_init(); + if ((options & (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL)) == + (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL)) + exit_error(OTHER_PROBLEM, "cannot use --label-add and " + "--label-del at the same time"); + + if (c == '>') { /* DELETE */ + char *tmp = get_optional_arg(argc, argv); + if (tmp == NULL) /* delete all labels */ + break; + optarg = tmp; + } + + char *optarg2 = strdup(optarg); unsigned int max = parse_label_get_max(optarg); struct nfct_bitmask * b = nfct_bitmask_new(max); + if (!b) + exit_error(OTHER_PROBLEM, "out of memory"); parse_label(b, optarg2); - tmpl.label = b; + + /* join "-l foo -l bar" into single bitmask object */ + if (c == 'l') { + merge_bitmasks(&tmpl.label, b); + } else { + merge_bitmasks(&tmpl.label_modify, b); + } + free(optarg2); break; case 'a': @@ -2114,7 +2250,7 @@ int main(int argc, char *argv[]) res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_DYING, - mnl_nfct_dump_cb); + mnl_nfct_dump_cb, family); nfct_mnl_socket_close(); break; @@ -2124,7 +2260,7 @@ int main(int argc, char *argv[]) res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_UNCONFIRMED, - mnl_nfct_dump_cb); + mnl_nfct_dump_cb, family); nfct_mnl_socket_close(); break; @@ -2191,6 +2327,10 @@ int main(int argc, char *argv[]) if (options & CT_OPT_MARK) nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value); + if (options & CT_OPT_ADD_LABEL) + nfct_set_attr(tmpl.ct, ATTR_CONNLABELS, + xnfct_bitmask_clone(tmpl.label_modify)); + cth = nfct_open(CONNTRACK, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); @@ -2389,7 +2529,7 @@ int main(int argc, char *argv[]) res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS, - nfct_global_stats_cb); + nfct_global_stats_cb, AF_UNSPEC); nfct_mnl_socket_close(); @@ -2434,7 +2574,7 @@ try_proc_count: res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS_CPU, - nfct_stats_cb); + nfct_stats_cb, AF_UNSPEC); nfct_mnl_socket_close(); @@ -2453,7 +2593,7 @@ try_proc_count: res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET_STATS_CPU, - nfexp_stats_cb); + nfexp_stats_cb, AF_UNSPEC); nfct_mnl_socket_close(); diff --git a/src/cthelper.c b/src/cthelper.c index 5a8a92a..6537515 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -31,6 +31,7 @@ #include #include #include +#define _GNU_SOURCE #include #include #include @@ -182,6 +183,15 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, nfct_nlmsg_build(nlh, myct->ct); mnl_attr_nest_end(nlh, nest); + if (myct->exp) { + nest = mnl_attr_nest_start(nlh, NFQA_EXP); + if (nest == NULL) + return -1; + + nfexp_nlmsg_build(nlh, myct->exp); + 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; @@ -315,12 +325,12 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) 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) + nfct_destroy(ct); + if (myct->exp != NULL) + nfexp_destroy(myct->exp); + if (myct->priv_data != NULL) free(myct->priv_data); - if (myct != NULL) - free(myct); + free(myct); return MNL_CB_OK; err_pktb: @@ -458,7 +468,10 @@ static int cthelper_nfqueue_setup(struct ctd_helper_instance *cur) 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)); - mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(cur->queue_len)); + if (cur->queue_len > 0) { + mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, + htonl(cur->queue_len)); + } if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { dlog(LOG_ERR, "failed to send configuration"); diff --git a/src/expect.c b/src/expect.c index 470b9ae..5add7be 100644 --- a/src/expect.c +++ b/src/expect.c @@ -39,8 +39,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, if (saddr) { switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - int i; - uint32_t addr[4] = {}; + uint32_t addr[4]; case AF_INET: nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); @@ -52,10 +51,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, 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[i], 0xffffffff, sizeof(uint32_t)); - + memset(addr, 0xff, sizeof(addr)); nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); nfct_set_attr(mask, ATTR_IPV6_SRC, addr); break; @@ -64,8 +60,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, } } else { switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - int i; - uint32_t addr[4] = {}; + uint32_t addr[4]; case AF_INET: nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); @@ -75,9 +70,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); break; case AF_INET6: - for (i=0; i<4; i++) - memset(&addr[i], 0x00000000, sizeof(uint32_t)); - + memset(addr, 0x00, sizeof(addr)); nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); nfct_set_attr(expected, ATTR_IPV6_SRC, addr); @@ -116,8 +109,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, } switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - uint32_t addr[4] = {}; - int i; + uint32_t addr[4]; case AF_INET: nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); @@ -127,10 +119,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, 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)); - + memset(addr, 0xff, sizeof(addr)); nfct_set_attr(mask, ATTR_IPV6_DST, addr); break; default: @@ -212,3 +201,27 @@ cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, break; } } + +void cthelper_get_port_src(struct nf_conntrack *ct, int dir, uint16_t *port) +{ + switch (dir) { + case MYCT_DIR_ORIG: + *port = nfct_get_attr_u16(ct, ATTR_PORT_SRC); + break; + case MYCT_DIR_REPL: + *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC); + break; + } +} + +void cthelper_get_port_dst(struct nf_conntrack *ct, int dir, uint16_t *port) +{ + switch (dir) { + case MYCT_DIR_ORIG: + *port = nfct_get_attr_u16(ct, ATTR_PORT_DST); + break; + case MYCT_DIR_REPL: + *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST); + break; + } +} diff --git a/src/filter.c b/src/filter.c index 8fac71b..1ae2cc5 100644 --- a/src/filter.c +++ b/src/filter.c @@ -33,8 +33,8 @@ struct ct_filter { int logic[CT_FILTER_MAX]; - u_int32_t l4protomap[IPPROTO_MAX/32]; - u_int16_t statemap[IPPROTO_MAX]; + uint32_t l4protomap[IPPROTO_MAX/32]; + uint16_t statemap[IPPROTO_MAX]; struct hashtable *h; struct hashtable *h6; struct vector *v; diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 589b4f3..78ef7aa 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,8 +1,21 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la \ +pkglib_LTLIBRARIES = ct_helper_amanda.la \ + ct_helper_dhcpv6.la \ + ct_helper_ftp.la \ ct_helper_rpc.la \ - ct_helper_tns.la + ct_helper_tftp.la \ + ct_helper_tns.la \ + ct_helper_sane.la \ + ct_helper_ssdp.la + +ct_helper_amanda_la_SOURCES = amanda.c +ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_dhcpv6_la_SOURCES = dhcpv6.c +ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) @@ -12,6 +25,18 @@ ct_helper_rpc_la_SOURCES = rpc.c ct_helper_rpc_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_rpc_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) +ct_helper_tftp_la_SOURCES = tftp.c +ct_helper_tftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_tftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + ct_helper_tns_la_SOURCES = tns.c ct_helper_tns_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_tns_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_sane_la_SOURCES = sane.c +ct_helper_sane_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_sane_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + +ct_helper_ssdp_la_SOURCES = ssdp.c +ct_helper_ssdp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_ssdp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c new file mode 100644 index 0000000..9e6c4e7 --- /dev/null +++ b/src/helpers/amanda.c @@ -0,0 +1,203 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Adapted from: + * + * Amanda extension for IP connection tracking + * + * (C) 2002 by Brian J. Murrell + * based on HW's ip_conntrack_irc.c as well as other modules + * (C) 2006 Patrick McHardy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_expect *exp) +{ + char buffer[sizeof("65535")]; + uint16_t port, initial_port; + unsigned int ret; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + /* Connection comes from client. */ + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); + + /* libnetfilter_conntrack needs this */ + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); + nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + + /* When you see the packet, we need to NAT it the same as the + * this one (ie. same IP: it will be TCP and master is UDP). */ + nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(initial_port); port != 0; port++) { + int res; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + res = cthelper_add_expect(exp); + if (res == 0) + break; + else if (res != -EBUSY) { + port = 0; + break; + } + } + + if (port == 0) { + pr_debug("all ports in use\n"); + return NF_DROP; + } + + sprintf(buffer, "%u", port); + ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, + strlen(buffer)); + if (ret != NF_ACCEPT) { + pr_debug("cannot mangle packet\n"); + cthelper_del_expect(exp); + } + return ret; +} + +static char amanda_buffer[65536]; +static unsigned int master_timeout = 300; + +enum amanda_strings { + SEARCH_CONNECT, + SEARCH_NEWLINE, + SEARCH_DATA, + SEARCH_MESG, + SEARCH_INDEX, +}; + +static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; + +static int +amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct nf_expect *exp; + char *data, *data_limit, *tmp; + unsigned int dataoff, i; + uint16_t port, len; + int ret = NF_ACCEPT; + struct iphdr *iph; + union nfct_attr_grp_addr saddr, daddr; + + /* Only look at packets from the Amanda server */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + /* increase the UDP timeout of the master connection as replies from + * Amanda clients to the server can be quite delayed */ + nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); + + /* No data? */ + iph = (struct iphdr *)pktb_network_header(pkt); + dataoff = iph->ihl*4 + sizeof(struct udphdr); + if (dataoff >= pktb_len(pkt)) { + pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); + return NF_ACCEPT; + } + + memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, + pktb_len(pkt) - dataoff); + data = amanda_buffer; + data_limit = amanda_buffer + pktb_len(pkt) - dataoff; + *data_limit = '\0'; + + /* Search for the CONNECT string */ + data = strstr(data, "CONNECT "); + if (!data) + goto out; + data += strlen("CONNECT "); + + /* Only search first line. */ + if ((tmp = strchr(data, '\n'))) + *tmp = '\0'; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + char *match = strstr(data, conns[i]); + if (!match) + continue; + tmp = data = match + strlen(conns[i]); + port = strtoul(data, &data, 10); + len = data - tmp; + if (port == 0 || len > 5) + break; + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_TCP, NULL, &port, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, + len, exp); + } else + myct->exp = exp; + } +out: + return ret; +} + +static struct ctd_helper amanda_helper = { + .name = "amanda", + .l4proto = IPPROTO_UDP, + .cb = amanda_helper_cb, + .policy = { + [0] = { + .name = "amanda", + .expect_max = ARRAY_SIZE(conns), + .expect_timeout = 180, + }, + }, +}; + +void __attribute__ ((constructor)) amanda_init(void); + +void amanda_init(void) +{ + helper_register(&amanda_helper); +} diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c new file mode 100644 index 0000000..73632ec --- /dev/null +++ b/src/helpers/dhcpv6.c @@ -0,0 +1,123 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Adapted from: + * + * DHCPv6 multicast connection tracking helper. + * + * (c) 2012 Google Inc. + * + * Original author: Darren Willis + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DHCPV6_CLIENT_PORT 546 + +static uint16_t dhcpv6_port; + +/* Timeouts for DHCPv6 replies, in seconds, indexed by message type. */ +static const int dhcpv6_timeouts[] = { + 0, /* No message has type 0. */ + 120, /* Solicit. */ + 0, /* Advertise. */ + 30, /* Request. */ + 4, /* Confirm. */ + 600, /* Renew. */ + 600, /* Rebind. */ + 0, /* Reply. */ + 1, /* Release. */ + 1, /* Decline. */ + 0, /* Reconfigure. */ + 120, /* Information Request. */ + 0, /* Relay-forward. */ + 0 /* Relay-reply. */ +}; + +static inline int ipv6_addr_is_multicast(const struct in6_addr *addr) +{ + return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000); +} + +static int +dhcpv6_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct iphdr *iph = (struct iphdr *)pktb_network_header(pkt); + struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb_network_header(pkt); + int dir = CTINFO2DIR(ctinfo); + union nfct_attr_grp_addr addr; + struct nf_expect *exp; + uint8_t *dhcpv6_msg_type; + + if (iph->version != 6 || !ipv6_addr_is_multicast(&ip6h->ip6_dst)) + return NF_ACCEPT; + + dhcpv6_msg_type = pktb_network_header(pkt) + protoff + sizeof(struct udphdr); + if (*dhcpv6_msg_type > ARRAY_SIZE(dhcpv6_timeouts)) { + printf("Dropping DHCPv6 message with bad type %u\n", + *dhcpv6_msg_type); + return NF_DROP; + } + + exp = nfexp_new(); + if (exp == NULL) + return NF_ACCEPT; + + cthelper_get_addr_src(myct->ct, dir, &addr); + + if (cthelper_expect_init(exp, myct->ct, 0, NULL, &addr, + IPPROTO_UDP, NULL, &dhcpv6_port, + NF_CT_EXPECT_PERMANENT)) { + nfexp_destroy(exp); + return NF_DROP; + } + + myct->exp = exp; + + if (dhcpv6_timeouts[*dhcpv6_msg_type] > 0) { + nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, + dhcpv6_timeouts[*dhcpv6_msg_type]); + } + + return NF_ACCEPT; +} + +static struct ctd_helper dhcpv6_helper = { + .name = "dhcpv6", + .l4proto = IPPROTO_UDP, + .cb = dhcpv6_helper_cb, + .policy = { + [0] = { + .name = "dhcpv6", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) dhcpv6_init(void); + +void dhcpv6_init(void) +{ + dhcpv6_port = htons(DHCPV6_CLIENT_PORT); + helper_register(&dhcpv6_helper); +} diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c index 2c8dcd6..24ee877 100644 --- a/src/helpers/ftp.c +++ b/src/helpers/ftp.c @@ -25,6 +25,7 @@ #include /* for isdigit */ #include +#define _GNU_SOURCE #include #include @@ -58,7 +59,7 @@ enum nf_ct_ftp_type { }; static int -get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) +get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, uint8_t term) { const char *end; int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), diff --git a/src/helpers/sane.c b/src/helpers/sane.c new file mode 100644 index 0000000..c30f4ba --- /dev/null +++ b/src/helpers/sane.c @@ -0,0 +1,173 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Port this helper to userspace. + */ + +/* SANE connection tracking helper + * (SANE = Scanner Access Now Easy) + * For documentation about the SANE network protocol see + * http://www.sane-project.org/html/doc015.html + */ + +/* + * Copyright (C) 2007 Red Hat, Inc. + * Author: Michal Schmidt + * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c): + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * (C) 2003,2004 USAGI/WIDE Project + * (C) 2003 Yasuyuki Kozakai @USAGI + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +enum sane_state { + SANE_STATE_NORMAL, + SANE_STATE_START_REQUESTED, +}; + +struct sane_request { + uint32_t RPC_code; +#define SANE_NET_START 7 /* RPC code */ + + uint32_t handle; +}; + +struct sane_reply_net_start { + uint32_t status; +#define SANE_STATUS_SUCCESS 0 + + uint16_t zero; + uint16_t port; + /* other fields aren't interesting for conntrack */ +}; + +struct nf_ct_sane_master { + enum sane_state state; +}; + +static int +sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + unsigned int dataoff, datalen; + const struct tcphdr *th; + void *sb_ptr; + int ret = NF_ACCEPT; + int dir = CTINFO2DIR(ctinfo); + struct nf_ct_sane_master *ct_sane_info = myct->priv_data; + struct nf_expect *exp; + struct sane_request *req; + struct sane_reply_net_start *reply; + union nfct_attr_grp_addr saddr; + union nfct_attr_grp_addr daddr; + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY) + return NF_ACCEPT; + + th = (struct tcphdr *)(pktb_network_header(pkt) + protoff); + + /* No data? */ + dataoff = protoff + th->doff * 4; + if (dataoff >= pktb_len(pkt)) + return NF_ACCEPT; + + datalen = pktb_len(pkt) - dataoff; + + sb_ptr = pktb_network_header(pkt) + dataoff; + + if (dir == MYCT_DIR_ORIG) { + if (datalen != sizeof(struct sane_request)) + goto out; + + req = sb_ptr; + if (req->RPC_code != htonl(SANE_NET_START)) { + /* Not an interesting command */ + ct_sane_info->state = SANE_STATE_NORMAL; + goto out; + } + + /* We're interested in the next reply */ + ct_sane_info->state = SANE_STATE_START_REQUESTED; + goto out; + } + + /* Is it a reply to an uninteresting command? */ + if (ct_sane_info->state != SANE_STATE_START_REQUESTED) + goto out; + + /* It's a reply to SANE_NET_START. */ + ct_sane_info->state = SANE_STATE_NORMAL; + + if (datalen < sizeof(struct sane_reply_net_start)) { + pr_debug("nf_ct_sane: NET_START reply too short\n"); + goto out; + } + + reply = sb_ptr; + if (reply->status != htonl(SANE_STATUS_SUCCESS)) { + /* saned refused the command */ + pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n", + ntohl(reply->status)); + goto out; + } + + /* Invalid saned reply? Ignore it. */ + if (reply->zero != 0) + goto out; + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_TCP, NULL, &reply->port, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + myct->exp = exp; +out: + return ret; +} + +static struct ctd_helper sane_helper = { + .name = "sane", + .l4proto = IPPROTO_TCP, + .priv_data_len = sizeof(struct nf_ct_sane_master), + .cb = sane_helper_cb, + .policy = { + [0] = { + .name = "sane", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) sane_init(void) +{ + helper_register(&sane_helper); +} diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c new file mode 100644 index 0000000..bc41087 --- /dev/null +++ b/src/helpers/ssdp.c @@ -0,0 +1,134 @@ +/* + * SSDP connection tracking helper + * (SSDP = Simple Service Discovery Protocol) + * For documentation about SSDP see + * http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol + * + * Copyright (C) 2014 Ashley Hughes + * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c), + * :http://marc.info/?t=132945775100001&r=1&w=2 + * (C) 2012 Ian Pilcher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSDP_MCAST_ADDR "239.255.255.250" +#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */ +#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */ + +#define SSDP_M_SEARCH "M-SEARCH" +#define SSDP_M_SEARCH_SIZE (sizeof SSDP_M_SEARCH - 1) + +static int ssdp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + int ret = NF_ACCEPT; + union nfct_attr_grp_addr daddr, saddr, taddr; + struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt); + int good_packet = 0; + struct nf_expect *exp; + uint16_t port; + unsigned int dataoff; + void *sb_ptr; + + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + switch (nfct_get_attr_u8(myct->ct, ATTR_L3PROTO)) { + case AF_INET: + inet_pton(AF_INET, SSDP_MCAST_ADDR, &(taddr.ip)); + if (daddr.ip == taddr.ip) + good_packet = 1; + break; + case AF_INET6: + inet_pton(AF_INET6, UPNP_MCAST_LL_ADDR, &(taddr.ip6)); + if (daddr.ip6[0] == taddr.ip6[0] && + daddr.ip6[1] == taddr.ip6[1] && + daddr.ip6[2] == taddr.ip6[2] && + daddr.ip6[3] == taddr.ip6[3]) { + good_packet = 1; + break; + } + inet_pton(AF_INET6, UPNP_MCAST_SL_ADDR, &(taddr.ip6)); + if (daddr.ip6[0] == taddr.ip6[0] && + daddr.ip6[1] == taddr.ip6[1] && + daddr.ip6[2] == taddr.ip6[2] && + daddr.ip6[3] == taddr.ip6[3]) { + good_packet = 1; + break; + } + break; + default: + break; + } + + if (!good_packet) { + pr_debug("ssdp_help: destination address not multicast; ignoring\n"); + return NF_ACCEPT; + } + + /* No data? Ignore */ + dataoff = net_hdr->ihl*4 + sizeof(struct udphdr); + if (dataoff >= pktb_len(pkt)) { + pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n"); + return NF_ACCEPT; + } + + sb_ptr = pktb_network_header(pkt) + dataoff; + + if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) { + pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n"); + return NF_ACCEPT; + } + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + if (cthelper_expect_init(exp, myct->ct, 0, NULL, &saddr, + IPPROTO_UDP, NULL, &port, + NF_CT_EXPECT_PERMANENT)) { + nfexp_destroy(exp); + return NF_DROP; + } + myct->exp = exp; + + return ret; +} + +static struct ctd_helper ssdp_helper = { + .name = "ssdp", + .l4proto = IPPROTO_UDP, + .priv_data_len = 0, + .cb = ssdp_helper_cb, + .policy = { + [0] = { + .name = "ssdp", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) ssdp_init(void) +{ + helper_register(&ssdp_helper); +} diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c new file mode 100644 index 0000000..45591c6 --- /dev/null +++ b/src/helpers/tftp.c @@ -0,0 +1,138 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Adapted from: + * + * (C) 2001-2002 Magnus Boden + * (C) 2006-2012 Patrick McHardy + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tftphdr { + uint16_t opcode; +}; + +#define TFTP_OPCODE_READ 1 +#define TFTP_OPCODE_WRITE 2 +#define TFTP_OPCODE_DATA 3 +#define TFTP_OPCODE_ACK 4 +#define TFTP_OPCODE_ERROR 5 + +static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo, + struct nf_conntrack *ct, struct nf_expect *exp) +{ + struct nf_conntrack *nat_tuple; + static uint32_t zero[4] = { 0, 0, 0, 0 }; + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) { + case AF_INET: + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); + break; + case AF_INET6: + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero); + nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero); + break; + } + nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, + nfct_get_attr_u16(ct, ATTR_PORT_SRC)); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL); + nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + return NF_ACCEPT; +} + +static int +tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + const struct tftphdr *tfh; + struct nf_expect *exp; + unsigned int ret = NF_ACCEPT; + union nfct_attr_grp_addr saddr, daddr; + uint16_t dport; + + tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr)); + + switch (ntohs(tfh->opcode)) { + case TFTP_OPCODE_READ: + case TFTP_OPCODE_WRITE: + /* RRQ and WRQ works the same way */ + exp = nfexp_new(); + if (exp == NULL) { + pr_debug("cannot alloc expectation\n"); + return NF_DROP; + } + + cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr); + cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_UDP, NULL, &dport, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) + ret = nat_tftp(pkt, ctinfo, myct->ct, exp); + + myct->exp = exp; + break; + case TFTP_OPCODE_DATA: + case TFTP_OPCODE_ACK: + pr_debug("Data/ACK opcode\n"); + break; + case TFTP_OPCODE_ERROR: + pr_debug("Error opcode\n"); + break; + default: + pr_debug("Unknown opcode\n"); + } + return ret; +} + +static struct ctd_helper tftp_helper = { + .name = "tftp", + .l4proto = IPPROTO_UDP, + .cb = tftp_helper_cb, + .policy = { + [0] = { + .name = "tftp", + .expect_max = 1, + .expect_timeout = 5 * 60, + }, + }, +}; + +static void __attribute__ ((constructor)) tftp_init(void) +{ + helper_register(&tftp_helper); +} diff --git a/src/helpers/tns.c b/src/helpers/tns.c index 5833fea..2b4fed4 100644 --- a/src/helpers/tns.c +++ b/src/helpers/tns.c @@ -18,6 +18,7 @@ #include /* for isdigit */ #include +#define _GNU_SOURCE #include #include diff --git a/src/internal_bypass.c b/src/internal_bypass.c index ce2ae46..61988c7 100644 --- a/src/internal_bypass.c +++ b/src/internal_bypass.c @@ -49,7 +49,7 @@ internal_bypass_ct_dump_cb(enum nf_conntrack_msg_type type, static void internal_bypass_ct_dump(int fd, int type) { struct nfct_handle *h; - u_int32_t family = AF_UNSPEC; + uint32_t family = AF_UNSPEC; int ret; h = nfct_open(CONFIG(netlink).subsys_id, 0); @@ -180,7 +180,7 @@ internal_bypass_exp_dump_cb(enum nf_conntrack_msg_type type, static void internal_bypass_exp_dump(int fd, int type) { struct nfct_handle *h; - u_int32_t family = AF_UNSPEC; + uint32_t family = AF_UNSPEC; int ret; h = nfct_open(CONFIG(netlink).subsys_id, 0); diff --git a/src/local.c b/src/local.c index feff608..453799a 100644 --- a/src/local.c +++ b/src/local.c @@ -117,11 +117,10 @@ void local_client_destroy(int fd) int do_local_client_step(int fd, void (*process)(char *buf)) { - int numbytes; char buf[1024]; memset(buf, 0, sizeof(buf)); - while ((numbytes = recv(fd, buf, sizeof(buf)-1, 0)) > 0) { + while (recv(fd, buf, sizeof(buf)-1, 0) > 0) { buf[sizeof(buf)-1] = '\0'; if (process) process(buf); diff --git a/src/netlink.c b/src/netlink.c index 5be102e..189f55a 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include struct nfct_handle *nl_init_event_handler(void) diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index f91fc41..bfb153f 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -37,8 +37,15 @@ 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(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; @@ -66,24 +73,25 @@ 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; } @@ -115,13 +123,11 @@ err: return MNL_CB_OK; } -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; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -132,42 +138,17 @@ 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 (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"); + 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; } -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; @@ -175,7 +156,7 @@ 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" @@ -248,47 +229,22 @@ 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 (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; } -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; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -341,48 +297,21 @@ 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 (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; } -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; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -435,46 +364,21 @@ 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 (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"); + 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; } -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; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -485,43 +389,18 @@ 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 (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; } - mnl_socket_close(nl); - return 0; } -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; @@ -529,7 +408,6 @@ 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" @@ -580,36 +458,21 @@ 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 (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; } +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..c9aa386 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -1,5 +1,5 @@ /* - * (C) 2012 by Pablo Neira Ayuso + * (C) 2012-2013 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 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,11 +32,20 @@ static void nfct_cmd_timeout_usage(char *argv[]) { fprintf(stderr, "nfct v%s: Missing command\n" - "%s timeout list|add|delete|get|flush " - "[parameters...]\n", VERSION, argv[0]); + "%s timeout " + "[, ...]\n", VERSION, argv[0]); } -int nfct_cmd_timeout_parse_params(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_default_set(struct mnl_socket *nl, int argc, char *argv[]); +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[]) { int cmd = NFCT_CMD_NONE, ret; @@ -53,6 +63,10 @@ int nfct_cmd_timeout_parse_params(int argc, char *argv[]) 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]); @@ -61,19 +75,25 @@ 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; + case NFCT_CMD_DEFAULT_SET: + ret = nfct_cmd_timeout_default_set(nl, argc, argv); + break; + case NFCT_CMD_DEFAULT_GET: + ret = nfct_cmd_timeout_default_get(nl, argc, argv); break; } @@ -105,13 +125,11 @@ err: return MNL_CB_OK; } -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; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -122,35 +140,11 @@ 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 (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_timeout_cb, 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, nfct_timeout_cb, NULL) < 0) { + nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); return 0; } @@ -167,69 +161,71 @@ static uint32_t nfct_timeout_attr_max[IPPROTO_MAX] = { [IPPROTO_RAW] = NFCT_TIMEOUT_ATTR_GENERIC_MAX, }; -int nfct_cmd_timeout_add(int argc, char *argv[]) +static int nfct_cmd_get_l3proto(char *argv[]) { - struct mnl_socket *nl; - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh; - uint32_t portid, seq; - struct nfct_timeout *t; - uint16_t l3proto; - uint8_t l4proto; - int ret, i; - unsigned int j; - - if (argc < 6) { - nfct_perror("missing parameters\n" - "syntax: nfct timeout add name " - "family protocol state1 " - "timeout1 state2 timeout2..."); - return -1; - } - - t = nfct_timeout_alloc(); - if (t == NULL) { - nfct_perror("OOM"); - return -1; - } + int l3proto; - nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]); - - if (strcmp(argv[4], "inet") == 0) + if (strcmp(*argv, "inet") == 0) l3proto = AF_INET; - else if (strcmp(argv[4], "inet6") == 0) + else if (strcmp(*argv, "inet6") == 0) l3proto = AF_INET6; else { nfct_perror("unknown layer 3 protocol"); return -1; } + return l3proto; +} + +static int nfct_cmd_get_l4proto(char *argv[]) +{ + int l4proto; + struct protoent *pent; + + pent = getprotobyname(*argv); + if (!pent) { + /* In Debian, /etc/protocols says ipv6-icmp. Support icmpv6 + * as well not to break backward compatibility. + */ + if (strcmp(*argv, "icmpv6") == 0) + l4proto = IPPROTO_ICMPV6; + else if (strcmp(*argv, "generic") == 0) + l4proto = IPPROTO_RAW; + else { + nfct_perror("unknown layer 4 protocol"); + return -1; + } + } else + l4proto = pent->p_proto; + + return l4proto; +} + +static int +nfct_cmd_timeout_parse(struct nfct_timeout *t, int argc, char *argv[]) +{ + int l3proto, l4proto; + unsigned int j; + const char *proto_name; + + l3proto = nfct_cmd_get_l3proto(argv); + if (l3proto < 0) + return -1; + nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto); - if (strcmp(argv[5], "tcp") == 0) - l4proto = IPPROTO_TCP; - else if (strcmp(argv[5], "udp") == 0) - l4proto = IPPROTO_UDP; - else if (strcmp(argv[5], "udplite") == 0) - l4proto = IPPROTO_UDPLITE; - else if (strcmp(argv[5], "sctp") == 0) - l4proto = IPPROTO_SCTP; - else if (strcmp(argv[5], "dccp") == 0) - l4proto = IPPROTO_DCCP; - else if (strcmp(argv[5], "icmp") == 0) - l4proto = IPPROTO_ICMP; - else if (strcmp(argv[5], "icmpv6") == 0) - l4proto = IPPROTO_ICMPV6; - else if (strcmp(argv[5], "gre") == 0) - l4proto = IPPROTO_GRE; - else if (strcmp(argv[5], "generic") == 0) - l4proto = IPPROTO_RAW; - else { - nfct_perror("unknown layer 4 protocol"); + argc--; + argv++; + proto_name = *argv; + + l4proto = nfct_cmd_get_l4proto(argv); + if (l4proto < 0) return -1; - } + nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); + argc--; + argv++; - for (i=6; i1; argc-=2, argv+=2) { int matching = -1; for (j=0; j= argc) { - nfct_perror("missing value for this timeout"); - return -1; - } nfct_timeout_policy_attr_set_u32(t, matching, - atoi(argv[i+1])); - matching = -1; + atoi(*(argv+1))); } else { fprintf(stderr, "nfct v%s: Wrong state name: `%s' " "for protocol `%s'\n", - VERSION, argv[i], argv[5]); + VERSION, *argv, proto_name); return -1; } } + if (argc > 0) { + nfct_perror("missing value for this timeout"); + return -1; + } - seq = time(NULL); - nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW, - NLM_F_CREATE | NLM_F_ACK, seq); - nfct_timeout_nlmsg_build_payload(nlh, t); + return 0; +} - nfct_timeout_free(t); +int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_timeout *t; - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct timeout add name " + "family protocol state1 " + "timeout1 state2 timeout2..."); return -1; } - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); + t = nfct_timeout_alloc(); + if (t == NULL) { + nfct_perror("OOM"); return -1; } - portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]); + + if (nfct_cmd_timeout_parse(t, argc-4, &argv[4]) < 0) 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"); + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_timeout_nlmsg_build_payload(nlh, t); + + nfct_timeout_free(t); + + 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; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -335,48 +333,21 @@ 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 (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; } - 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; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -401,86 +372,135 @@ 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"); + 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; } - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); - return -1; - } - portid = mnl_socket_get_portid(nl); + return 0; +} + +int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + if (argc > 3) { + nfct_perror("too many arguments"); 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"); + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, + NLM_F_ACK, seq); + + 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_flush(int argc, char *argv[]) +static int +nfct_cmd_timeout_default_set(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; - int ret; + struct nfct_timeout *t; - if (argc > 3) { - nfct_perror("too many arguments"); + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct timeout default-set " + "family protocol state1 " + "timeout1 state2 timeout2..."); return -1; } + t = nfct_timeout_alloc(); + if (t == NULL) + return -1; + + if (nfct_cmd_timeout_parse(t, argc-3, &argv[3]) < 0) + return -1; + seq = time(NULL); - nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_SET, NLM_F_ACK, seq); + nfct_timeout_nlmsg_build_payload(nlh, t); + nfct_timeout_free(t); - nl = mnl_socket_open(NETLINK_NETFILTER); - if (nl == NULL) { - nfct_perror("mnl_socket_open"); + 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; } - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - nfct_perror("mnl_socket_bind"); + return 0; +} + +static int +nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_timeout *t; + int l3proto, l4proto; + + if (argc < 5) { + nfct_perror("missing parameters\n" + "syntax: nfct timeout default-get " + "family protocol"); return -1; } - portid = mnl_socket_get_portid(nl); - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - nfct_perror("mnl_socket_send"); + t = nfct_timeout_alloc(); + if (t == NULL) 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"); + argc-=3; + argv+=3; + + l3proto = nfct_cmd_get_l3proto(argv); + if (l3proto < 0) return -1; - } - mnl_socket_close(nl); + nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto); + argc--; + argv++; + + l4proto = nfct_cmd_get_l4proto(argv); + if (l4proto < 0) + return -1; + + nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); + + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_GET, + NLM_F_ACK, seq); + nfct_timeout_nlmsg_build_payload(nlh, t); + nfct_timeout_free(t); + + 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; + } 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..533d75d 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -22,9 +22,8 @@ #include #include -#include -#include +#include "linux_list.h" #include "nfct.h" static int nfct_cmd_version(int argc, char *argv[]); @@ -46,9 +45,29 @@ 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; + struct mnl_socket *nl; if (argc < 2) { usage(argv); @@ -70,18 +89,29 @@ 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; + } + + 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; } @@ -120,3 +150,42 @@ 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; +} + +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; +} diff --git a/src/read_config_lex.l b/src/read_config_lex.l index b4d11d4..8350069 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -47,7 +47,7 @@ ip6_part {hex_255}":"? ip6_form1 {ip6_part}{0,7}"::"{ip6_part}{0,7} ip6_form2 ({hex_255}":"){0,7}{hex_255} ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}? -string [a-zA-Z][a-zA-Z0-9\.\-]* +string [a-zA-Z][a-zA-Z0-9\.\-\_]* persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] nack [N|n][A|a][C|c][K|k] alarm [A|a][L|l][A|a][R|r][M|m] diff --git a/src/read_config_yy.y b/src/read_config_yy.y index b824150..73fabbf 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1612,12 +1612,17 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' exit(EXIT_FAILURE); } - /* XXX use configure.ac definitions. */ - helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW); +#ifdef BUILD_CTHELPER + helper = helper_find(CONNTRACKD_LIB_DIR, $2, l4proto, RTLD_NOW); if (helper == NULL) { print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2); exit(EXIT_FAILURE); } +#else + print_err(CTD_CFG_ERROR, "Helper support is disabled, recompile " + "conntrackd"); + exit(EXIT_FAILURE); +#endif helper_inst = calloc(1, sizeof(struct ctd_helper_instance)); if (helper_inst == NULL) diff --git a/src/run.c b/src/run.c index 7fa6889..a9d4862 100644 --- a/src/run.c +++ b/src/run.c @@ -55,9 +55,10 @@ void killer(int signo) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) ctnl_kill(); +#ifdef BUILD_CTHELPER if (CONFIG(flags) & CTD_HELPER) cthelper_kill(); - +#endif destroy_fds(STATE(fds)); unlink(CONFIG(lockfile)); dlog(LOG_NOTICE, "---- shutdown received ----"); @@ -205,9 +206,10 @@ static int local_handler(int fd, void *data) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) return ctnl_local(fd, type, data); +#ifdef BUILD_CTHELPER if (CONFIG(flags) & CTD_HELPER) return cthelper_local(fd, type, data); - +#endif return ret; } @@ -259,11 +261,12 @@ init(void) if (ctnl_init() < 0) return -1; +#ifdef BUILD_CTHELPER if (CONFIG(flags) & CTD_HELPER) { if (cthelper_init() < 0) return -1; } - +#endif time(&STATE(stats).daemon_start_time); dlog(LOG_NOTICE, "initialization completed"); diff --git a/src/sync-notrack.c b/src/sync-notrack.c index a7df4e7..c810bbb 100644 --- a/src/sync-notrack.c +++ b/src/sync-notrack.c @@ -99,7 +99,7 @@ static int kernel_resync_cb(enum nf_conntrack_msg_type type, static void kernel_resync(void) { struct nfct_handle *h; - u_int32_t family = AF_UNSPEC; + uint32_t family = AF_UNSPEC; int ret; h = nfct_open(CONFIG(netlink).subsys_id, 0); diff --git a/src/udp.c b/src/udp.c index ecaa46e..d0a7f5b 100644 --- a/src/udp.c +++ b/src/udp.c @@ -136,14 +136,18 @@ struct udp_sock *udp_client_create(struct udp_conf *conf) m->addr.ipv4.sin_family = AF_INET; m->addr.ipv4.sin_port = htons(conf->port); m->addr.ipv4.sin_addr = conf->client.inet_addr; - m->sockaddr_len = sizeof(struct sockaddr_in); + m->sockaddr_len = sizeof(struct sockaddr_in); break; case AF_INET6: m->addr.ipv6.sin6_family = AF_INET6; m->addr.ipv6.sin6_port = htons(conf->port); memcpy(&m->addr.ipv6.sin6_addr, &conf->client.inet_addr6, sizeof(struct in6_addr)); - m->sockaddr_len = sizeof(struct sockaddr_in6); + m->sockaddr_len = sizeof(struct sockaddr_in6); + /* Bind the sender side to the same interface that we use to + * receive sync messages. + */ + m->addr.ipv6.sin6_scope_id = conf->server.ipv6.scope_id; break; default: ret = -1;