* [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support @ 2012-02-10 22:10 Richard Weinberger 2012-02-10 22:10 ` [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG Richard Weinberger ` (5 more replies) 0 siblings, 6 replies; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti This patch set merges ipt_LOG and ip6t_LOG and adds ring buffer support to xt_LOG. Using "--ring" a user can create LOG rules which log messages into one or more ring buffers. Each ring buffer is represented as pipe-like file in /proc/net/netfilter/xt_LOG_ring/. Ring buffer support can be enabled/disabled using CONFIG_NETFILTER_XT_TARGET_LOG_RING. Changes since v1: - Merged ipt_LOG and ip6t_LOG - Implemented ring buffer support as part of xt_LOG. Changes since v2: - Fixed struct xt_log_info_v1's data layout - Removed the sizeof(void *) hack - Replaced BUG() by WARN_ON_ONCE() Changes since v3: - Merged xt_LOG_core.c and xt_LOG_ring.c into xt_LOG.c - Made checkpatch.pl happy Changes since v4: - Fixed a typo in include/linux/netfilter/Kbuild (Thanks Bastian!) All patches can also be found at: git://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git xt_LOG git://git.kernel.org/pub/scm/linux/kernel/git/rw/iptables.git xt_LOG [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into [PATCH 2/2] iptables: xt_LOG: Add ring buffer support [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() [PATCH 3/4] xt_log: Make printk() in sb_close() optional [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-25 13:38 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG Richard Weinberger ` (4 subsequent siblings) 5 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger libip6t_LOG and libipt_LOG share a lot of common code, merge them to recude duplicate code. Signed-off-by: Richard Weinberger <richard@nod.at> --- extensions/libip6t_LOG.c | 184 ---------------------------------- extensions/libip6t_LOG.man | 31 ------ extensions/libipt_LOG.c | 186 ----------------------------------- extensions/libipt_LOG.man | 31 ------ extensions/libxt_LOG.c | 201 ++++++++++++++++++++++++++++++++++++++ extensions/libxt_LOG.man | 31 ++++++ include/linux/netfilter/xt_LOG.h | 19 ++++ 7 files changed, 251 insertions(+), 432 deletions(-) delete mode 100644 extensions/libip6t_LOG.c delete mode 100644 extensions/libip6t_LOG.man delete mode 100644 extensions/libipt_LOG.c delete mode 100644 extensions/libipt_LOG.man create mode 100644 extensions/libxt_LOG.c create mode 100644 extensions/libxt_LOG.man create mode 100644 include/linux/netfilter/xt_LOG.h diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c deleted file mode 100644 index 2b1ae28..0000000 --- a/extensions/libip6t_LOG.c +++ /dev/null @@ -1,184 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <syslog.h> -#include <xtables.h> -#include <linux/netfilter_ipv6/ip6t_LOG.h> - -#ifndef IP6T_LOG_UID /* Old kernel */ -#define IP6T_LOG_UID 0x08 -#undef IP6T_LOG_MASK -#define IP6T_LOG_MASK 0x0f -#endif - -#define LOG_DEFAULT_LEVEL LOG_WARNING - -enum { - O_LOG_LEVEL = 0, - O_LOG_PREFIX, - O_LOG_TCPSEQ, - O_LOG_TCPOPTS, - O_LOG_IPOPTS, - O_LOG_UID, - O_LOG_MAC, -}; - -static void LOG_help(void) -{ - printf( -"LOG target options:\n" -" --log-level level Level of logging (numeric or see syslog.conf)\n" -" --log-prefix prefix Prefix log messages with this prefix.\n" -" --log-tcp-sequence Log TCP sequence numbers.\n" -" --log-tcp-options Log TCP options.\n" -" --log-ip-options Log IP options.\n" -" --log-uid Log UID owning the local socket.\n" -" --log-macdecode Decode MAC addresses and protocol.\n"); -} - -#define s struct ip6t_log_info -static const struct xt_option_entry LOG_opts[] = { - {.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL, - .flags = XTOPT_PUT, XTOPT_POINTER(s, level)}, - {.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING, - .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1}, - {.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE}, - {.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE}, - {.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE}, - {.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE}, - {.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE}, - XTOPT_TABLEEND, -}; -#undef s - -static void LOG_init(struct xt_entry_target *t) -{ - struct ip6t_log_info *loginfo = (struct ip6t_log_info *)t->data; - - loginfo->level = LOG_DEFAULT_LEVEL; - -} - -struct ip6t_log_names { - const char *name; - unsigned int level; -}; - -static const struct ip6t_log_names ip6t_log_names[] -= { { .name = "alert", .level = LOG_ALERT }, - { .name = "crit", .level = LOG_CRIT }, - { .name = "debug", .level = LOG_DEBUG }, - { .name = "emerg", .level = LOG_EMERG }, - { .name = "error", .level = LOG_ERR }, /* DEPRECATED */ - { .name = "info", .level = LOG_INFO }, - { .name = "notice", .level = LOG_NOTICE }, - { .name = "panic", .level = LOG_EMERG }, /* DEPRECATED */ - { .name = "warning", .level = LOG_WARNING } -}; - -static void LOG_parse(struct xt_option_call *cb) -{ - struct ip6t_log_info *info = cb->data; - - xtables_option_parse(cb); - switch (cb->entry->id) { - case O_LOG_PREFIX: - if (strchr(cb->arg, '\n') != NULL) - xtables_error(PARAMETER_PROBLEM, - "Newlines not allowed in --log-prefix"); - break; - case O_LOG_TCPSEQ: - info->logflags |= IP6T_LOG_TCPSEQ; - break; - case O_LOG_TCPOPTS: - info->logflags |= IP6T_LOG_TCPOPT; - break; - case O_LOG_IPOPTS: - info->logflags |= IP6T_LOG_IPOPT; - break; - case O_LOG_UID: - info->logflags |= IP6T_LOG_UID; - break; - case O_LOG_MAC: - info->logflags |= IP6T_LOG_MACDECODE; - break; - } -} - -static void LOG_print(const void *ip, const struct xt_entry_target *target, - int numeric) -{ - const struct ip6t_log_info *loginfo - = (const struct ip6t_log_info *)target->data; - unsigned int i = 0; - - printf(" LOG"); - if (numeric) - printf(" flags %u level %u", - loginfo->logflags, loginfo->level); - else { - for (i = 0; i < ARRAY_SIZE(ip6t_log_names); ++i) - if (loginfo->level == ip6t_log_names[i].level) { - printf(" level %s", ip6t_log_names[i].name); - break; - } - if (i == ARRAY_SIZE(ip6t_log_names)) - printf(" UNKNOWN level %u", loginfo->level); - if (loginfo->logflags & IP6T_LOG_TCPSEQ) - printf(" tcp-sequence"); - if (loginfo->logflags & IP6T_LOG_TCPOPT) - printf(" tcp-options"); - if (loginfo->logflags & IP6T_LOG_IPOPT) - printf(" ip-options"); - if (loginfo->logflags & IP6T_LOG_UID) - printf(" uid"); - if (loginfo->logflags & IP6T_LOG_MACDECODE) - printf(" macdecode"); - if (loginfo->logflags & ~(IP6T_LOG_MASK)) - printf(" unknown-flags"); - } - - if (strcmp(loginfo->prefix, "") != 0) - printf(" prefix \"%s\"", loginfo->prefix); -} - -static void LOG_save(const void *ip, const struct xt_entry_target *target) -{ - const struct ip6t_log_info *loginfo - = (const struct ip6t_log_info *)target->data; - - if (strcmp(loginfo->prefix, "") != 0) - printf(" --log-prefix \"%s\"", loginfo->prefix); - - if (loginfo->level != LOG_DEFAULT_LEVEL) - printf(" --log-level %d", loginfo->level); - - if (loginfo->logflags & IP6T_LOG_TCPSEQ) - printf(" --log-tcp-sequence"); - if (loginfo->logflags & IP6T_LOG_TCPOPT) - printf(" --log-tcp-options"); - if (loginfo->logflags & IP6T_LOG_IPOPT) - printf(" --log-ip-options"); - if (loginfo->logflags & IP6T_LOG_UID) - printf(" --log-uid"); - if (loginfo->logflags & IP6T_LOG_MACDECODE) - printf(" --log-macdecode"); -} - -static struct xtables_target log_tg6_reg = { - .name = "LOG", - .version = XTABLES_VERSION, - .family = NFPROTO_IPV6, - .size = XT_ALIGN(sizeof(struct ip6t_log_info)), - .userspacesize = XT_ALIGN(sizeof(struct ip6t_log_info)), - .help = LOG_help, - .init = LOG_init, - .print = LOG_print, - .save = LOG_save, - .x6_parse = LOG_parse, - .x6_options = LOG_opts, -}; - -void _init(void) -{ - xtables_register_target(&log_tg6_reg); -} diff --git a/extensions/libip6t_LOG.man b/extensions/libip6t_LOG.man deleted file mode 100644 index b7803fe..0000000 --- a/extensions/libip6t_LOG.man +++ /dev/null @@ -1,31 +0,0 @@ -Turn on kernel logging of matching packets. When this option is set -for a rule, the Linux kernel will print some information on all -matching packets (like most IPv6 IPv6-header fields) via the kernel log -(where it can be read with -.I dmesg -or -.IR syslogd (8)). -This is a "non-terminating target", i.e. rule traversal continues at -the next rule. So if you want to LOG the packets you refuse, use two -separate rules with the same matching criteria, first using target LOG -then DROP (or REJECT). -.TP -\fB\-\-log\-level\fP \fIlevel\fP -Level of logging (numeric or see \fIsyslog.conf\fP(5)). -.TP -\fB\-\-log\-prefix\fP \fIprefix\fP -Prefix log messages with the specified prefix; up to 29 letters long, -and useful for distinguishing messages in the logs. -.TP -\fB\-\-log\-tcp\-sequence\fP -Log TCP sequence numbers. This is a security risk if the log is -readable by users. -.TP -\fB\-\-log\-tcp\-options\fP -Log options from the TCP packet header. -.TP -\fB\-\-log\-ip\-options\fP -Log options from the IPv6 packet header. -.TP -\fB\-\-log\-uid\fP -Log the userid of the process which generated the packet. diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c deleted file mode 100644 index 77f16d1..0000000 --- a/extensions/libipt_LOG.c +++ /dev/null @@ -1,186 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <syslog.h> -#include <xtables.h> -#include <linux/netfilter_ipv4/ipt_LOG.h> - -#define LOG_DEFAULT_LEVEL LOG_WARNING - -#ifndef IPT_LOG_UID /* Old kernel */ -#define IPT_LOG_UID 0x08 /* Log UID owning local socket */ -#undef IPT_LOG_MASK -#define IPT_LOG_MASK 0x0f -#endif - -enum { - O_LOG_LEVEL = 0, - O_LOG_PREFIX, - O_LOG_TCPSEQ, - O_LOG_TCPOPTS, - O_LOG_IPOPTS, - O_LOG_UID, - O_LOG_MAC, -}; - -static void LOG_help(void) -{ - printf( -"LOG target options:\n" -" --log-level level Level of logging (numeric or see syslog.conf)\n" -" --log-prefix prefix Prefix log messages with this prefix.\n\n" -" --log-tcp-sequence Log TCP sequence numbers.\n\n" -" --log-tcp-options Log TCP options.\n\n" -" --log-ip-options Log IP options.\n\n" -" --log-uid Log UID owning the local socket.\n\n" -" --log-macdecode Decode MAC addresses and protocol.\n\n"); -} - -#define s struct ipt_log_info -static const struct xt_option_entry LOG_opts[] = { - {.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL, - .flags = XTOPT_PUT, XTOPT_POINTER(s, level)}, - {.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING, - .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1}, - {.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE}, - {.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE}, - {.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE}, - {.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE}, - {.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE}, - XTOPT_TABLEEND, -}; -#undef s - -static void LOG_init(struct xt_entry_target *t) -{ - struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data; - - loginfo->level = LOG_DEFAULT_LEVEL; - -} - -struct ipt_log_names { - const char *name; - unsigned int level; -}; - -static const struct ipt_log_names ipt_log_names[] -= { { .name = "alert", .level = LOG_ALERT }, - { .name = "crit", .level = LOG_CRIT }, - { .name = "debug", .level = LOG_DEBUG }, - { .name = "emerg", .level = LOG_EMERG }, - { .name = "error", .level = LOG_ERR }, /* DEPRECATED */ - { .name = "info", .level = LOG_INFO }, - { .name = "notice", .level = LOG_NOTICE }, - { .name = "panic", .level = LOG_EMERG }, /* DEPRECATED */ - { .name = "warning", .level = LOG_WARNING } -}; - -static void LOG_parse(struct xt_option_call *cb) -{ - struct ipt_log_info *info = cb->data; - - xtables_option_parse(cb); - switch (cb->entry->id) { - case O_LOG_PREFIX: - if (strchr(cb->arg, '\n') != NULL) - xtables_error(PARAMETER_PROBLEM, - "Newlines not allowed in --log-prefix"); - break; - case O_LOG_TCPSEQ: - info->logflags |= IPT_LOG_TCPSEQ; - break; - case O_LOG_TCPOPTS: - info->logflags |= IPT_LOG_TCPOPT; - break; - case O_LOG_IPOPTS: - info->logflags |= IPT_LOG_IPOPT; - break; - case O_LOG_UID: - info->logflags |= IPT_LOG_UID; - break; - case O_LOG_MAC: - info->logflags |= IPT_LOG_MACDECODE; - break; - } -} - -static void LOG_print(const void *ip, const struct xt_entry_target *target, - int numeric) -{ - const struct ipt_log_info *loginfo - = (const struct ipt_log_info *)target->data; - unsigned int i = 0; - - printf(" LOG"); - if (numeric) - printf(" flags %u level %u", - loginfo->logflags, loginfo->level); - else { - for (i = 0; i < ARRAY_SIZE(ipt_log_names); ++i) - if (loginfo->level == ipt_log_names[i].level) { - printf(" level %s", ipt_log_names[i].name); - break; - } - if (i == ARRAY_SIZE(ipt_log_names)) - printf(" UNKNOWN level %u", loginfo->level); - if (loginfo->logflags & IPT_LOG_TCPSEQ) - printf(" tcp-sequence"); - if (loginfo->logflags & IPT_LOG_TCPOPT) - printf(" tcp-options"); - if (loginfo->logflags & IPT_LOG_IPOPT) - printf(" ip-options"); - if (loginfo->logflags & IPT_LOG_UID) - printf(" uid"); - if (loginfo->logflags & IPT_LOG_MACDECODE) - printf(" macdecode"); - if (loginfo->logflags & ~(IPT_LOG_MASK)) - printf(" unknown-flags"); - } - - if (strcmp(loginfo->prefix, "") != 0) - printf(" prefix \"%s\"", loginfo->prefix); -} - -static void LOG_save(const void *ip, const struct xt_entry_target *target) -{ - const struct ipt_log_info *loginfo - = (const struct ipt_log_info *)target->data; - - if (strcmp(loginfo->prefix, "") != 0) { - printf(" --log-prefix"); - xtables_save_string(loginfo->prefix); - } - - if (loginfo->level != LOG_DEFAULT_LEVEL) - printf(" --log-level %d", loginfo->level); - - if (loginfo->logflags & IPT_LOG_TCPSEQ) - printf(" --log-tcp-sequence"); - if (loginfo->logflags & IPT_LOG_TCPOPT) - printf(" --log-tcp-options"); - if (loginfo->logflags & IPT_LOG_IPOPT) - printf(" --log-ip-options"); - if (loginfo->logflags & IPT_LOG_UID) - printf(" --log-uid"); - if (loginfo->logflags & IPT_LOG_MACDECODE) - printf(" --log-macdecode"); -} - -static struct xtables_target log_tg_reg = { - .name = "LOG", - .version = XTABLES_VERSION, - .family = NFPROTO_IPV4, - .size = XT_ALIGN(sizeof(struct ipt_log_info)), - .userspacesize = XT_ALIGN(sizeof(struct ipt_log_info)), - .help = LOG_help, - .init = LOG_init, - .print = LOG_print, - .save = LOG_save, - .x6_parse = LOG_parse, - .x6_options = LOG_opts, -}; - -void _init(void) -{ - xtables_register_target(&log_tg_reg); -} diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man deleted file mode 100644 index 47c35e0..0000000 --- a/extensions/libipt_LOG.man +++ /dev/null @@ -1,31 +0,0 @@ -Turn on kernel logging of matching packets. When this option is set -for a rule, the Linux kernel will print some information on all -matching packets (like most IP header fields) via the kernel log -(where it can be read with -.I dmesg -or -.IR syslogd (8)). -This is a "non-terminating target", i.e. rule traversal continues at -the next rule. So if you want to LOG the packets you refuse, use two -separate rules with the same matching criteria, first using target LOG -then DROP (or REJECT). -.TP -\fB\-\-log\-level\fP \fIlevel\fP -Level of logging (numeric or see \fIsyslog.conf\fP(5)). -.TP -\fB\-\-log\-prefix\fP \fIprefix\fP -Prefix log messages with the specified prefix; up to 29 letters long, -and useful for distinguishing messages in the logs. -.TP -\fB\-\-log\-tcp\-sequence\fP -Log TCP sequence numbers. This is a security risk if the log is -readable by users. -.TP -\fB\-\-log\-tcp\-options\fP -Log options from the TCP packet header. -.TP -\fB\-\-log\-ip\-options\fP -Log options from the IP packet header. -.TP -\fB\-\-log\-uid\fP -Log the userid of the process which generated the packet. diff --git a/extensions/libxt_LOG.c b/extensions/libxt_LOG.c new file mode 100644 index 0000000..a4c11b6 --- /dev/null +++ b/extensions/libxt_LOG.c @@ -0,0 +1,201 @@ +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <xtables.h> +#include <linux/netfilter/xt_LOG.h> + +#define LOG_DEFAULT_LEVEL LOG_WARNING + +#ifndef XT_LOG_UID /* Old kernel */ +#define XT_LOG_UID 0x08 /* Log UID owning local socket */ +#undef XT_LOG_MASK +#define XT_LOG_MASK 0x0f +#endif + +enum { + O_LOG_LEVEL = 0, + O_LOG_PREFIX, + O_LOG_TCPSEQ, + O_LOG_TCPOPTS, + O_LOG_IPOPTS, + O_LOG_UID, + O_LOG_MAC, +}; + +static void LOG_help(void) +{ + printf( +"LOG target options:\n" +" --log-level level Level of logging (numeric or see syslog.conf)\n" +" --log-prefix prefix Prefix log messages with this prefix.\n\n" +" --log-tcp-sequence Log TCP sequence numbers.\n\n" +" --log-tcp-options Log TCP options.\n\n" +" --log-ip-options Log IP options.\n\n" +" --log-uid Log UID owning the local socket.\n\n" +" --log-macdecode Decode MAC addresses and protocol.\n\n"); +} + +#define s struct xt_log_info +static const struct xt_option_entry LOG_opts[] = { + {.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL, + .flags = XTOPT_PUT, XTOPT_POINTER(s, level)}, + {.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING, + .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1}, + {.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE}, + {.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE}, + {.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE}, + {.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE}, + {.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE}, + XTOPT_TABLEEND, +}; +#undef s + +static void LOG_init(struct xt_entry_target *t) +{ + struct xt_log_info *loginfo = (struct xt_log_info *)t->data; + + loginfo->level = LOG_DEFAULT_LEVEL; + +} + +struct ipt_log_names { + const char *name; + unsigned int level; +}; + +static const struct ipt_log_names ipt_log_names[] += { { .name = "alert", .level = LOG_ALERT }, + { .name = "crit", .level = LOG_CRIT }, + { .name = "debug", .level = LOG_DEBUG }, + { .name = "emerg", .level = LOG_EMERG }, + { .name = "error", .level = LOG_ERR }, /* DEPRECATED */ + { .name = "info", .level = LOG_INFO }, + { .name = "notice", .level = LOG_NOTICE }, + { .name = "panic", .level = LOG_EMERG }, /* DEPRECATED */ + { .name = "warning", .level = LOG_WARNING } +}; + +static void LOG_parse(struct xt_option_call *cb) +{ + struct xt_log_info *info = cb->data; + + xtables_option_parse(cb); + switch (cb->entry->id) { + case O_LOG_PREFIX: + if (strchr(cb->arg, '\n') != NULL) + xtables_error(PARAMETER_PROBLEM, + "Newlines not allowed in --log-prefix"); + break; + case O_LOG_TCPSEQ: + info->logflags |= XT_LOG_TCPSEQ; + break; + case O_LOG_TCPOPTS: + info->logflags |= XT_LOG_TCPOPT; + break; + case O_LOG_IPOPTS: + info->logflags |= XT_LOG_IPOPT; + break; + case O_LOG_UID: + info->logflags |= XT_LOG_UID; + break; + case O_LOG_MAC: + info->logflags |= XT_LOG_MACDECODE; + break; + } +} + +static void LOG_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_log_info *loginfo + = (const struct xt_log_info *)target->data; + unsigned int i = 0; + + printf(" LOG"); + if (numeric) + printf(" flags %u level %u", + loginfo->logflags, loginfo->level); + else { + for (i = 0; i < ARRAY_SIZE(ipt_log_names); ++i) + if (loginfo->level == ipt_log_names[i].level) { + printf(" level %s", ipt_log_names[i].name); + break; + } + if (i == ARRAY_SIZE(ipt_log_names)) + printf(" UNKNOWN level %u", loginfo->level); + if (loginfo->logflags & XT_LOG_TCPSEQ) + printf(" tcp-sequence"); + if (loginfo->logflags & XT_LOG_TCPOPT) + printf(" tcp-options"); + if (loginfo->logflags & XT_LOG_IPOPT) + printf(" ip-options"); + if (loginfo->logflags & XT_LOG_UID) + printf(" uid"); + if (loginfo->logflags & XT_LOG_MACDECODE) + printf(" macdecode"); + if (loginfo->logflags & ~(XT_LOG_MASK)) + printf(" unknown-flags"); + } + + if (strcmp(loginfo->prefix, "") != 0) + printf(" prefix \"%s\"", loginfo->prefix); +} + +static void LOG_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_log_info *loginfo + = (const struct xt_log_info *)target->data; + + if (strcmp(loginfo->prefix, "") != 0) { + printf(" --log-prefix"); + xtables_save_string(loginfo->prefix); + } + + if (loginfo->level != LOG_DEFAULT_LEVEL) + printf(" --log-level %d", loginfo->level); + + if (loginfo->logflags & XT_LOG_TCPSEQ) + printf(" --log-tcp-sequence"); + if (loginfo->logflags & XT_LOG_TCPOPT) + printf(" --log-tcp-options"); + if (loginfo->logflags & XT_LOG_IPOPT) + printf(" --log-ip-options"); + if (loginfo->logflags & XT_LOG_UID) + printf(" --log-uid"); + if (loginfo->logflags & XT_LOG_MACDECODE) + printf(" --log-macdecode"); +} + +static struct xtables_target log_tg_regs[] = { + { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_log_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_log_info)), + .help = LOG_help, + .init = LOG_init, + .print = LOG_print, + .save = LOG_save, + .x6_parse = LOG_parse, + .x6_options = LOG_opts, + }, + { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_log_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_log_info)), + .help = LOG_help, + .init = LOG_init, + .print = LOG_print, + .save = LOG_save, + .x6_parse = LOG_parse, + .x6_options = LOG_opts, + }, +}; + +void _init(void) +{ + xtables_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); +} diff --git a/extensions/libxt_LOG.man b/extensions/libxt_LOG.man new file mode 100644 index 0000000..47c35e0 --- /dev/null +++ b/extensions/libxt_LOG.man @@ -0,0 +1,31 @@ +Turn on kernel logging of matching packets. When this option is set +for a rule, the Linux kernel will print some information on all +matching packets (like most IP header fields) via the kernel log +(where it can be read with +.I dmesg +or +.IR syslogd (8)). +This is a "non-terminating target", i.e. rule traversal continues at +the next rule. So if you want to LOG the packets you refuse, use two +separate rules with the same matching criteria, first using target LOG +then DROP (or REJECT). +.TP +\fB\-\-log\-level\fP \fIlevel\fP +Level of logging (numeric or see \fIsyslog.conf\fP(5)). +.TP +\fB\-\-log\-prefix\fP \fIprefix\fP +Prefix log messages with the specified prefix; up to 29 letters long, +and useful for distinguishing messages in the logs. +.TP +\fB\-\-log\-tcp\-sequence\fP +Log TCP sequence numbers. This is a security risk if the log is +readable by users. +.TP +\fB\-\-log\-tcp\-options\fP +Log options from the TCP packet header. +.TP +\fB\-\-log\-ip\-options\fP +Log options from the IP packet header. +.TP +\fB\-\-log\-uid\fP +Log the userid of the process which generated the packet. diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h new file mode 100644 index 0000000..cac0790 --- /dev/null +++ b/include/linux/netfilter/xt_LOG.h @@ -0,0 +1,19 @@ +#ifndef _XT_LOG_H +#define _XT_LOG_H + +/* make sure not to change this without changing nf_log.h:NF_LOG_* (!) */ +#define XT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ +#define XT_LOG_TCPOPT 0x02 /* Log TCP options */ +#define XT_LOG_IPOPT 0x04 /* Log IP options */ +#define XT_LOG_UID 0x08 /* Log UID owning local socket */ +#define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ +#define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ +#define XT_LOG_MASK 0x2f + +struct xt_log_info { + unsigned char level; + unsigned char logflags; + char prefix[30]; +}; + +#endif /* _XT_LOG_H */ -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG 2012-02-10 22:10 ` [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG Richard Weinberger @ 2012-02-25 13:38 ` Pablo Neira Ayuso 2012-02-28 16:06 ` Richard Weinberger 0 siblings, 1 reply; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-25 13:38 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Fri, Feb 10, 2012 at 11:10:51PM +0100, Richard Weinberger wrote: > libip6t_LOG and libipt_LOG share a lot of common code, merge them > to recude duplicate code. I have applied this to the net-next branch of iptables. See git.netfilter.org I'll merge it to master once linux kernel 3.3 is released. Please, send follow up patches if you find any issue. Thanks. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG 2012-02-25 13:38 ` Pablo Neira Ayuso @ 2012-02-28 16:06 ` Richard Weinberger 2012-02-28 16:15 ` Dave Taht 0 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-28 16:06 UTC (permalink / raw) To: Pablo Neira Ayuso Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti [-- Attachment #1: Type: text/plain, Size: 477 bytes --] Am 25.02.2012 14:38, schrieb Pablo Neira Ayuso: > On Fri, Feb 10, 2012 at 11:10:51PM +0100, Richard Weinberger wrote: >> libip6t_LOG and libipt_LOG share a lot of common code, merge them >> to recude duplicate code. > > I have applied this to the net-next branch of iptables. > > See git.netfilter.org > > I'll merge it to master once linux kernel 3.3 is released. Okay. What about the ring buffer feature? Is there anything missing? Thanks, //richard [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 490 bytes --] ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG 2012-02-28 16:06 ` Richard Weinberger @ 2012-02-28 16:15 ` Dave Taht 0 siblings, 0 replies; 21+ messages in thread From: Dave Taht @ 2012-02-28 16:15 UTC (permalink / raw) To: Richard Weinberger Cc: Pablo Neira Ayuso, netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Tue, Feb 28, 2012 at 5:06 PM, Richard Weinberger <richard@nod.at> wrote: > Am 25.02.2012 14:38, schrieb Pablo Neira Ayuso: >> On Fri, Feb 10, 2012 at 11:10:51PM +0100, Richard Weinberger wrote: >>> libip6t_LOG and libipt_LOG share a lot of common code, merge them >>> to recude duplicate code. >> >> I have applied this to the net-next branch of iptables. >> >> See git.netfilter.org >> >> I'll merge it to master once linux kernel 3.3 is released. > > Okay. What about the ring buffer feature? > Is there anything missing? I'm under the impression that the ecn related changes didn't make it as yet. I'm in a position to test that stuff this month, if it needs further testing and hasn't actually made it in. > Thanks, > //richard > > -- Dave Täht SKYPE: davetaht US Tel: 1-239-829-5608 http://www.bufferbloat.net ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG @ 2012-02-28 16:15 ` Dave Taht 0 siblings, 0 replies; 21+ messages in thread From: Dave Taht @ 2012-02-28 16:15 UTC (permalink / raw) To: Richard Weinberger Cc: Pablo Neira Ayuso, netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Tue, Feb 28, 2012 at 5:06 PM, Richard Weinberger <richard@nod.at> wrote: > Am 25.02.2012 14:38, schrieb Pablo Neira Ayuso: >> On Fri, Feb 10, 2012 at 11:10:51PM +0100, Richard Weinberger wrote: >>> libip6t_LOG and libipt_LOG share a lot of common code, merge them >>> to recude duplicate code. >> >> I have applied this to the net-next branch of iptables. >> >> See git.netfilter.org >> >> I'll merge it to master once linux kernel 3.3 is released. > > Okay. What about the ring buffer feature? > Is there anything missing? I'm under the impression that the ecn related changes didn't make it as yet. I'm in a position to test that stuff this month, if it needs further testing and hasn't actually made it in. > Thanks, > //richard > > -- Dave Täht SKYPE: davetaht US Tel: 1-239-829-5608 http://www.bufferbloat.net -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG 2012-02-28 16:15 ` Dave Taht (?) @ 2012-02-28 22:54 ` Pablo Neira Ayuso -1 siblings, 0 replies; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-28 22:54 UTC (permalink / raw) To: Dave Taht Cc: Richard Weinberger, netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Tue, Feb 28, 2012 at 05:15:59PM +0100, Dave Taht wrote: > On Tue, Feb 28, 2012 at 5:06 PM, Richard Weinberger <richard@nod.at> wrote: > > Am 25.02.2012 14:38, schrieb Pablo Neira Ayuso: > >> On Fri, Feb 10, 2012 at 11:10:51PM +0100, Richard Weinberger wrote: > >>> libip6t_LOG and libipt_LOG share a lot of common code, merge them > >>> to recude duplicate code. > >> > >> I have applied this to the net-next branch of iptables. > >> > >> See git.netfilter.org > >> > >> I'll merge it to master once linux kernel 3.3 is released. > > > > Okay. What about the ring buffer feature? > > Is there anything missing? > > I'm under the impression that the ecn related changes didn't make it > as yet. I'm in a position to test that stuff this month, if it needs > further testing and hasn't actually made it in. You mean this? commit af0d29cd2a732f70882e6122b9f9df8b0d84515e Author: Patrick McHardy <kaber@trash.net> Date: Thu Jun 9 15:20:27 2011 +0200 netfilter: xtables: add an IPv6 capable version of the ECN match It's already 3.3-rc. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger 2012-02-10 22:10 ` [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-13 23:56 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 2/2] iptables: xt_LOG: Add ring buffer support Richard Weinberger ` (3 subsequent siblings) 5 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger ipt_LOG and ip6_LOG have a lot of common code, merge them to reduce duplicate code. Signed-off-by: Richard Weinberger <richard@nod.at> --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_LOG.h | 19 + include/linux/netfilter_ipv4/ipt_LOG.h | 2 + include/linux/netfilter_ipv6/ip6t_LOG.h | 2 + net/ipv4/netfilter/Kconfig | 9 - net/ipv4/netfilter/Makefile | 1 - net/ipv4/netfilter/ipt_LOG.c | 516 ----------------- net/ipv6/netfilter/Kconfig | 9 - net/ipv6/netfilter/Makefile | 1 - net/ipv6/netfilter/ip6t_LOG.c | 527 ------------------ net/netfilter/Kconfig | 9 + net/netfilter/Makefile | 1 + net/netfilter/xt_LOG.c | 921 +++++++++++++++++++++++++++++++ 13 files changed, 955 insertions(+), 1063 deletions(-) create mode 100644 include/linux/netfilter/xt_LOG.h delete mode 100644 net/ipv4/netfilter/ipt_LOG.c delete mode 100644 net/ipv6/netfilter/ip6t_LOG.c create mode 100644 net/netfilter/xt_LOG.c diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index e144f54..aaa72aa 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -22,6 +22,7 @@ header-y += xt_CT.h header-y += xt_DSCP.h header-y += xt_IDLETIMER.h header-y += xt_LED.h +header-y += xt_LOG.h header-y += xt_MARK.h header-y += xt_nfacct.h header-y += xt_NFLOG.h diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h new file mode 100644 index 0000000..cac0790 --- /dev/null +++ b/include/linux/netfilter/xt_LOG.h @@ -0,0 +1,19 @@ +#ifndef _XT_LOG_H +#define _XT_LOG_H + +/* make sure not to change this without changing nf_log.h:NF_LOG_* (!) */ +#define XT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ +#define XT_LOG_TCPOPT 0x02 /* Log TCP options */ +#define XT_LOG_IPOPT 0x04 /* Log IP options */ +#define XT_LOG_UID 0x08 /* Log UID owning local socket */ +#define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ +#define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ +#define XT_LOG_MASK 0x2f + +struct xt_log_info { + unsigned char level; + unsigned char logflags; + char prefix[30]; +}; + +#endif /* _XT_LOG_H */ diff --git a/include/linux/netfilter_ipv4/ipt_LOG.h b/include/linux/netfilter_ipv4/ipt_LOG.h index dcdbadf..5d81520 100644 --- a/include/linux/netfilter_ipv4/ipt_LOG.h +++ b/include/linux/netfilter_ipv4/ipt_LOG.h @@ -1,6 +1,8 @@ #ifndef _IPT_LOG_H #define _IPT_LOG_H +#warning "Please update iptables, this file will be removed soon!" + /* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */ #define IPT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ #define IPT_LOG_TCPOPT 0x02 /* Log TCP options */ diff --git a/include/linux/netfilter_ipv6/ip6t_LOG.h b/include/linux/netfilter_ipv6/ip6t_LOG.h index 9dd5579..3dd0bc4 100644 --- a/include/linux/netfilter_ipv6/ip6t_LOG.h +++ b/include/linux/netfilter_ipv6/ip6t_LOG.h @@ -1,6 +1,8 @@ #ifndef _IP6T_LOG_H #define _IP6T_LOG_H +#warning "Please update iptables, this file will be removed soon!" + /* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */ #define IP6T_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */ #define IP6T_LOG_TCPOPT 0x02 /* Log TCP options */ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 74dfc9e..fcc543c 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -123,15 +123,6 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. -config IP_NF_TARGET_LOG - tristate "LOG target support" - default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. - - To compile it as a module, choose M here. If unsure, say N. - config IP_NF_TARGET_ULOG tristate "ULOG target support" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 213a462..240b684 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o # targets obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o -obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c deleted file mode 100644 index d76d6c9..0000000 --- a/net/ipv4/netfilter/ipt_LOG.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * This is a module which is used for logging packets. - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <net/icmp.h> -#include <net/udp.h> -#include <net/tcp.h> -#include <net/route.h> - -#include <linux/netfilter.h> -#include <linux/netfilter/x_tables.h> -#include <linux/netfilter_ipv4/ipt_LOG.h> -#include <net/netfilter/nf_log.h> -#include <net/netfilter/xt_log.h> - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); -MODULE_DESCRIPTION("Xtables: IPv4 packet logging to syslog"); - -/* One level of recursion won't kill us */ -static void dump_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, - unsigned int iphoff) -{ - struct iphdr _iph; - const struct iphdr *ih; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Important fields: - * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ - /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ - sb_add(m, "SRC=%pI4 DST=%pI4 ", - &ih->saddr, &ih->daddr); - - /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ - sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", - ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, - ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); - - /* Max length: 6 "CE DF MF " */ - if (ntohs(ih->frag_off) & IP_CE) - sb_add(m, "CE "); - if (ntohs(ih->frag_off) & IP_DF) - sb_add(m, "DF "); - if (ntohs(ih->frag_off) & IP_MF) - sb_add(m, "MF "); - - /* Max length: 11 "FRAG:65535 " */ - if (ntohs(ih->frag_off) & IP_OFFSET) - sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); - - if ((logflags & IPT_LOG_IPOPT) && - ih->ihl * 4 > sizeof(struct iphdr)) { - const unsigned char *op; - unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; - unsigned int i, optsize; - - optsize = ih->ihl * 4 - sizeof(struct iphdr); - op = skb_header_pointer(skb, iphoff+sizeof(_iph), - optsize, _opt); - if (op == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - - switch (ih->protocol) { - case IPPROTO_TCP: { - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_tcph), &_tcph); - if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", - ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & IPT_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", - ntohl(th->seq), ntohl(th->ack_seq)); - /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3F " */ - sb_add(m, "RES=0x%02x ", (u8)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - sb_add(m, "CWR "); - if (th->ece) - sb_add(m, "ECE "); - if (th->urg) - sb_add(m, "URG "); - if (th->ack) - sb_add(m, "ACK "); - if (th->psh) - sb_add(m, "PSH "); - if (th->rst) - sb_add(m, "RST "); - if (th->syn) - sb_add(m, "SYN "); - if (th->fin) - sb_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & IPT_LOG_TCPOPT) && - th->doff * 4 > sizeof(struct tcphdr)) { - unsigned char _opt[4 * 15 - sizeof(struct tcphdr)]; - const unsigned char *op; - unsigned int i, optsize; - - optsize = th->doff * 4 - sizeof(struct tcphdr); - op = skb_header_pointer(skb, - iphoff+ih->ihl*4+sizeof(_tcph), - optsize, _opt); - if (op == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - break; - } - case IPPROTO_UDP: - case IPPROTO_UDPLITE: { - struct udphdr _udph; - const struct udphdr *uh; - - if (ih->protocol == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP " ); - else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_udph), &_udph); - if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", - ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); - break; - } - case IPPROTO_ICMP: { - struct icmphdr _icmph; - const struct icmphdr *ich; - static const size_t required_len[NR_ICMP_TYPES+1] - = { [ICMP_ECHOREPLY] = 4, - [ICMP_DEST_UNREACH] - = 8 + sizeof(struct iphdr), - [ICMP_SOURCE_QUENCH] - = 8 + sizeof(struct iphdr), - [ICMP_REDIRECT] - = 8 + sizeof(struct iphdr), - [ICMP_ECHO] = 4, - [ICMP_TIME_EXCEEDED] - = 8 + sizeof(struct iphdr), - [ICMP_PARAMETERPROB] - = 8 + sizeof(struct iphdr), - [ICMP_TIMESTAMP] = 20, - [ICMP_TIMESTAMPREPLY] = 20, - [ICMP_ADDRESS] = 12, - [ICMP_ADDRESSREPLY] = 12 }; - - /* Max length: 11 "PROTO=ICMP " */ - sb_add(m, "PROTO=ICMP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_icmph), &_icmph); - if (ich == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - if (ich->type <= NR_ICMP_TYPES && - required_len[ich->type] && - skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - switch (ich->type) { - case ICMP_ECHOREPLY: - case ICMP_ECHO: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ich->un.echo.id), - ntohs(ich->un.echo.sequence)); - break; - - case ICMP_PARAMETERPROB: - /* Max length: 14 "PARAMETER=255 " */ - sb_add(m, "PARAMETER=%u ", - ntohl(ich->un.gateway) >> 24); - break; - case ICMP_REDIRECT: - /* Max length: 24 "GATEWAY=255.255.255.255 " */ - sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); - /* Fall through */ - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - /* Max length: 3+maxlen */ - if (!iphoff) { /* Only recurse once. */ - sb_add(m, "["); - dump_packet(m, info, skb, - iphoff + ih->ihl*4+sizeof(_icmph)); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ich->type == ICMP_DEST_UNREACH && - ich->code == ICMP_FRAG_NEEDED) - sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); - } - break; - } - /* Max Length */ - case IPPROTO_AH: { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 9 "PROTO=AH " */ - sb_add(m, "PROTO=AH "); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ah = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_ahdr), &_ahdr); - if (ah == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - break; - } - case IPPROTO_ESP: { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 10 "PROTO=ESP " */ - sb_add(m, "PROTO=ESP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - eh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_esph), &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); - break; - } - /* Max length: 10 "PROTO 255 " */ - default: - sb_add(m, "PROTO=%u ", ih->protocol); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & IPT_LOG_UID) && !iphoff && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!iphoff && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); - - /* Proto Max log string length */ - /* IP: 40+46+6+11+127 = 230 */ - /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ - /* UDP: 10+max(25,20) = 35 */ - /* UDPLITE: 14+max(25,20) = 39 */ - /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ - /* ESP: 10+max(25)+15 = 50 */ - /* AH: 9+max(25)+15 = 49 */ - /* unknown: 10 */ - - /* (ICMP allows recursion one level deep) */ - /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ - /* maxlen = 230+ 91 + 230 + 252 = 803 */ -} - -static void dump_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & IPT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int i; - - sb_add(m, "%02x", *p++); - for (i = 1; i < dev->hard_header_len; i++, p++) - sb_add(m, ":%02x", *p); - } - sb_add(m, " "); -} - -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static void -ipt_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", - out ? out->name : ""); -#ifdef CONFIG_BRIDGE_NETFILTER - if (skb->nf_bridge) { - const struct net_device *physindev; - const struct net_device *physoutdev; - - physindev = skb->nf_bridge->physindev; - if (physindev && in != physindev) - sb_add(m, "PHYSIN=%s ", physindev->name); - physoutdev = skb->nf_bridge->physoutdev; - if (physoutdev && out != physoutdev) - sb_add(m, "PHYSOUT=%s ", physoutdev->name); - } -#endif - - if (in != NULL) - dump_mac_header(m, loginfo, skb); - - dump_packet(m, loginfo, skb, 0); - - sb_close(m); -} - -static unsigned int -log_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct ipt_log_info *loginfo = par->targinfo; - struct nf_loginfo li; - - li.type = NF_LOG_TYPE_LOG; - li.u.log.level = loginfo->level; - li.u.log.logflags = loginfo->logflags; - - ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, par->out, &li, - loginfo->prefix); - return XT_CONTINUE; -} - -static int log_tg_check(const struct xt_tgchk_param *par) -{ - const struct ipt_log_info *loginfo = par->targinfo; - - if (loginfo->level >= 8) { - pr_debug("level %u >= 8\n", loginfo->level); - return -EINVAL; - } - if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { - pr_debug("prefix is not null-terminated\n"); - return -EINVAL; - } - return 0; -} - -static struct xt_target log_tg_reg __read_mostly = { - .name = "LOG", - .family = NFPROTO_IPV4, - .target = log_tg, - .targetsize = sizeof(struct ipt_log_info), - .checkentry = log_tg_check, - .me = THIS_MODULE, -}; - -static struct nf_logger ipt_log_logger __read_mostly = { - .name = "ipt_LOG", - .logfn = &ipt_log_packet, - .me = THIS_MODULE, -}; - -static int __init log_tg_init(void) -{ - int ret; - - ret = xt_register_target(&log_tg_reg); - if (ret < 0) - return ret; - nf_log_register(NFPROTO_IPV4, &ipt_log_logger); - return 0; -} - -static void __exit log_tg_exit(void) -{ - nf_log_unregister(&ipt_log_logger); - xt_unregister_target(&log_tg_reg); -} - -module_init(log_tg_init); -module_exit(log_tg_exit); diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 9a68fb5..d33cddd 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -154,15 +154,6 @@ config IP6_NF_TARGET_HL (e.g. when running oldconfig). It selects CONFIG_NETFILTER_XT_TARGET_HL. -config IP6_NF_TARGET_LOG - tristate "LOG target support" - default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. - - To compile it as a module, choose M here. If unsure, say N. - config IP6_NF_FILTER tristate "Packet filtering" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2eaed96..d4dfd0a 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -31,5 +31,4 @@ obj-$(CONFIG_IP6_NF_MATCH_RPFILTER) += ip6t_rpfilter.o obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o # targets -obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c deleted file mode 100644 index e6af8d7..0000000 --- a/net/ipv6/netfilter/ip6t_LOG.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * This is a module which is used for logging packets. - */ - -/* (C) 2001 Jan Rekorajski <baggins@pld.org.pl> - * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> - * - * 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. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <linux/spinlock.h> -#include <linux/icmpv6.h> -#include <net/udp.h> -#include <net/tcp.h> -#include <net/ipv6.h> -#include <linux/netfilter.h> -#include <linux/netfilter/x_tables.h> -#include <linux/netfilter_ipv6/ip6_tables.h> -#include <net/netfilter/nf_log.h> -#include <net/netfilter/xt_log.h> - -MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); -MODULE_DESCRIPTION("Xtables: IPv6 packet logging to syslog"); -MODULE_LICENSE("GPL"); - -struct in_device; -#include <net/route.h> -#include <linux/netfilter_ipv6/ip6t_LOG.h> - -/* One level of recursion won't kill us */ -static void dump_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int ip6hoff, - int recurse) -{ - u_int8_t currenthdr; - int fragment; - struct ipv6hdr _ip6h; - const struct ipv6hdr *ih; - unsigned int ptr; - unsigned int hdrlen = 0; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ - sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); - - /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ - sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", - ntohs(ih->payload_len) + sizeof(struct ipv6hdr), - (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, - ih->hop_limit, - (ntohl(*(__be32 *)ih) & 0x000fffff)); - - fragment = 0; - ptr = ip6hoff + sizeof(struct ipv6hdr); - currenthdr = ih->nexthdr; - while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { - struct ipv6_opt_hdr _hdr; - const struct ipv6_opt_hdr *hp; - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - if (hp == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 48 "OPT (...) " */ - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, "OPT ( "); - - switch (currenthdr) { - case IPPROTO_FRAGMENT: { - struct frag_hdr _fhdr; - const struct frag_hdr *fh; - - sb_add(m, "FRAG:"); - fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), - &_fhdr); - if (fh == NULL) { - sb_add(m, "TRUNCATED "); - return; - } - - /* Max length: 6 "65535 " */ - sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); - - /* Max length: 11 "INCOMPLETE " */ - if (fh->frag_off & htons(0x0001)) - sb_add(m, "INCOMPLETE "); - - sb_add(m, "ID:%08x ", ntohl(fh->identification)); - - if (ntohs(fh->frag_off) & 0xFFF8) - fragment = 1; - - hdrlen = 8; - - break; - } - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_HOPOPTS: - if (fragment) { - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, ")"); - return; - } - hdrlen = ipv6_optlen(hp); - break; - /* Max Length */ - case IPPROTO_AH: - if (logflags & IP6T_LOG_IPOPT) { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - /* Max length: 3 "AH " */ - sb_add(m, "AH "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), - &_ahdr); - if (ah == NULL) { - /* - * Max length: 26 "INCOMPLETE [65535 - * bytes] )" - */ - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 15 "SPI=0xF1234567 */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - - } - - hdrlen = (hp->hdrlen+2)<<2; - break; - case IPPROTO_ESP: - if (logflags & IP6T_LOG_IPOPT) { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 4 "ESP " */ - sb_add(m, "ESP "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - /* - * Max length: 26 "INCOMPLETE [65535 bytes] )" - */ - eh = skb_header_pointer(skb, ptr, sizeof(_esph), - &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 16 "SPI=0xF1234567 )" */ - sb_add(m, "SPI=0x%x )", ntohl(eh->spi) ); - - } - return; - default: - /* Max length: 20 "Unknown Ext Hdr 255" */ - sb_add(m, "Unknown Ext Hdr %u", currenthdr); - return; - } - if (logflags & IP6T_LOG_IPOPT) - sb_add(m, ") "); - - currenthdr = hp->nexthdr; - ptr += hdrlen; - } - - switch (currenthdr) { - case IPPROTO_TCP: { - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, ptr, sizeof(_tcph), &_tcph); - if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", - ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & IP6T_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", - ntohl(th->seq), ntohl(th->ack_seq)); - /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3C " */ - sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - sb_add(m, "CWR "); - if (th->ece) - sb_add(m, "ECE "); - if (th->urg) - sb_add(m, "URG "); - if (th->ack) - sb_add(m, "ACK "); - if (th->psh) - sb_add(m, "PSH "); - if (th->rst) - sb_add(m, "RST "); - if (th->syn) - sb_add(m, "SYN "); - if (th->fin) - sb_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & IP6T_LOG_TCPOPT) && - th->doff * 4 > sizeof(struct tcphdr)) { - u_int8_t _opt[60 - sizeof(struct tcphdr)]; - const u_int8_t *op; - unsigned int i; - unsigned int optsize = th->doff * 4 - - sizeof(struct tcphdr); - - op = skb_header_pointer(skb, - ptr + sizeof(struct tcphdr), - optsize, _opt); - if (op == NULL) { - sb_add(m, "OPT (TRUNCATED)"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i =0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - break; - } - case IPPROTO_UDP: - case IPPROTO_UDPLITE: { - struct udphdr _udph; - const struct udphdr *uh; - - if (currenthdr == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP " ); - else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, ptr, sizeof(_udph), &_udph); - if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", - ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); - break; - } - case IPPROTO_ICMPV6: { - struct icmp6hdr _icmp6h; - const struct icmp6hdr *ic; - - /* Max length: 13 "PROTO=ICMPv6 " */ - sb_add(m, "PROTO=ICMPv6 "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); - if (ic == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); - - switch (ic->icmp6_type) { - case ICMPV6_ECHO_REQUEST: - case ICMPV6_ECHO_REPLY: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ic->icmp6_identifier), - ntohs(ic->icmp6_sequence)); - break; - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - break; - - case ICMPV6_PARAMPROB: - /* Max length: 17 "POINTER=ffffffff " */ - sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); - /* Fall through */ - case ICMPV6_DEST_UNREACH: - case ICMPV6_PKT_TOOBIG: - case ICMPV6_TIME_EXCEED: - /* Max length: 3+maxlen */ - if (recurse) { - sb_add(m, "["); - dump_packet(m, info, skb, - ptr + sizeof(_icmp6h), 0); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) - sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); - } - break; - } - /* Max length: 10 "PROTO=255 " */ - default: - sb_add(m, "PROTO=%u ", currenthdr); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & IP6T_LOG_UID) && recurse && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!recurse && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); -} - -static void dump_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & IP6T_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int len = dev->hard_header_len; - unsigned int i; - - if (dev->type == ARPHRD_SIT && - (p -= ETH_HLEN) < skb->head) - p = NULL; - - if (p != NULL) { - sb_add(m, "%02x", *p++); - for (i = 1; i < len; i++) - sb_add(m, ":%02x", *p++); - } - sb_add(m, " "); - - if (dev->type == ARPHRD_SIT) { - const struct iphdr *iph = - (struct iphdr *)skb_mac_header(skb); - sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, &iph->daddr); - } - } else - sb_add(m, " "); -} - -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static void -ip6t_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", - out ? out->name : ""); - - if (in != NULL) - dump_mac_header(m, loginfo, skb); - - dump_packet(m, loginfo, skb, skb_network_offset(skb), 1); - - sb_close(m); -} - -static unsigned int -log_tg6(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct ip6t_log_info *loginfo = par->targinfo; - struct nf_loginfo li; - - li.type = NF_LOG_TYPE_LOG; - li.u.log.level = loginfo->level; - li.u.log.logflags = loginfo->logflags; - - ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, par->out, - &li, loginfo->prefix); - return XT_CONTINUE; -} - - -static int log_tg6_check(const struct xt_tgchk_param *par) -{ - const struct ip6t_log_info *loginfo = par->targinfo; - - if (loginfo->level >= 8) { - pr_debug("level %u >= 8\n", loginfo->level); - return -EINVAL; - } - if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { - pr_debug("prefix not null-terminated\n"); - return -EINVAL; - } - return 0; -} - -static struct xt_target log_tg6_reg __read_mostly = { - .name = "LOG", - .family = NFPROTO_IPV6, - .target = log_tg6, - .targetsize = sizeof(struct ip6t_log_info), - .checkentry = log_tg6_check, - .me = THIS_MODULE, -}; - -static struct nf_logger ip6t_logger __read_mostly = { - .name = "ip6t_LOG", - .logfn = &ip6t_log_packet, - .me = THIS_MODULE, -}; - -static int __init log_tg6_init(void) -{ - int ret; - - ret = xt_register_target(&log_tg6_reg); - if (ret < 0) - return ret; - nf_log_register(NFPROTO_IPV6, &ip6t_logger); - return 0; -} - -static void __exit log_tg6_exit(void) -{ - nf_log_unregister(&ip6t_logger); - xt_unregister_target(&log_tg6_reg); -} - -module_init(log_tg6_init); -module_exit(log_tg6_exit); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f8ac4ef..b895d8b 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -524,6 +524,15 @@ config NETFILTER_XT_TARGET_LED For more information on the LEDs available on your system, see Documentation/leds/leds-class.txt +config NETFILTER_XT_TARGET_LOG + tristate "LOG target support" + default m if NETFILTER_ADVANCED=n + help + This option adds a `LOG' target, which allows you to create rules in + any iptables table which records the packet header to the syslog. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 40f4c3d..a28c2a6 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c new file mode 100644 index 0000000..1595608 --- /dev/null +++ b/net/netfilter/xt_LOG.c @@ -0,0 +1,921 @@ +/* + * This is a module which is used for logging packets. + */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ip.h> +#include <net/ipv6.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/tcp.h> +#include <net/route.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_LOG.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <net/netfilter/nf_log.h> +#include <net/netfilter/xt_log.h> + +static struct nf_loginfo default_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 5, + .logflags = NF_LOG_MASK, + }, + }, +}; + +static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset) +{ + struct udphdr _udph; + const struct udphdr *uh; + + if (proto == IPPROTO_UDP) + /* Max length: 10 "PROTO=UDP " */ + sb_add(m, "PROTO=UDP "); + else /* Max length: 14 "PROTO=UDPLITE " */ + sb_add(m, "PROTO=UDPLITE "); + + if (fragment) + goto out; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest), + ntohs(uh->len)); + +out: + return 0; +} + +static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset, + unsigned int logflags) +{ + struct tcphdr _tcph; + const struct tcphdr *th; + + /* Max length: 10 "PROTO=TCP " */ + sb_add(m, "PROTO=TCP "); + + if (fragment) + return 0; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + if (logflags & XT_LOG_TCPSEQ) + sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq)); + + /* Max length: 13 "WINDOW=65535 " */ + sb_add(m, "WINDOW=%u ", ntohs(th->window)); + /* Max length: 9 "RES=0x3C " */ + sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & + TCP_RESERVED_BITS) >> 22)); + /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ + if (th->cwr) + sb_add(m, "CWR "); + if (th->ece) + sb_add(m, "ECE "); + if (th->urg) + sb_add(m, "URG "); + if (th->ack) + sb_add(m, "ACK "); + if (th->psh) + sb_add(m, "PSH "); + if (th->rst) + sb_add(m, "RST "); + if (th->syn) + sb_add(m, "SYN "); + if (th->fin) + sb_add(m, "FIN "); + /* Max length: 11 "URGP=65535 " */ + sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); + + if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { + u_int8_t _opt[60 - sizeof(struct tcphdr)]; + const u_int8_t *op; + unsigned int i; + unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); + + op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), + optsize, _opt); + if (op == NULL) { + sb_add(m, "OPT (TRUNCATED)"); + return 1; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + + sb_add(m, ") "); + } + + return 0; +} + +/* One level of recursion won't kill us */ +static void dump_ipv4_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, + unsigned int iphoff) +{ + struct iphdr _iph; + const struct iphdr *ih; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + sb_add(m, "SRC=%pI4 DST=%pI4 ", + &ih->saddr, &ih->daddr); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, + ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(ih->frag_off) & IP_CE) + sb_add(m, "CE "); + if (ntohs(ih->frag_off) & IP_DF) + sb_add(m, "DF "); + if (ntohs(ih->frag_off) & IP_MF) + sb_add(m, "MF "); + + /* Max length: 11 "FRAG:65535 " */ + if (ntohs(ih->frag_off) & IP_OFFSET) + sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); + + if ((logflags & XT_LOG_IPOPT) && + ih->ihl * 4 > sizeof(struct iphdr)) { + const unsigned char *op; + unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; + unsigned int i, optsize; + + optsize = ih->ihl * 4 - sizeof(struct iphdr); + op = skb_header_pointer(skb, iphoff+sizeof(_iph), + optsize, _opt); + if (op == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + sb_add(m, ") "); + } + + switch (ih->protocol) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4, logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4)) + return; + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ich; + static const size_t required_len[NR_ICMP_TYPES+1] + = { [ICMP_ECHOREPLY] = 4, + [ICMP_DEST_UNREACH] + = 8 + sizeof(struct iphdr), + [ICMP_SOURCE_QUENCH] + = 8 + sizeof(struct iphdr), + [ICMP_REDIRECT] + = 8 + sizeof(struct iphdr), + [ICMP_ECHO] = 4, + [ICMP_TIME_EXCEEDED] + = 8 + sizeof(struct iphdr), + [ICMP_PARAMETERPROB] + = 8 + sizeof(struct iphdr), + [ICMP_TIMESTAMP] = 20, + [ICMP_TIMESTAMPREPLY] = 20, + [ICMP_ADDRESS] = 12, + [ICMP_ADDRESSREPLY] = 12 }; + + /* Max length: 11 "PROTO=ICMP " */ + sb_add(m, "PROTO=ICMP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, + sizeof(_icmph), &_icmph); + if (ich == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (ich->type <= NR_ICMP_TYPES && + required_len[ich->type] && + skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + switch (ich->type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ich->un.echo.id), + ntohs(ich->un.echo.sequence)); + break; + + case ICMP_PARAMETERPROB: + /* Max length: 14 "PARAMETER=255 " */ + sb_add(m, "PARAMETER=%u ", + ntohl(ich->un.gateway) >> 24); + break; + case ICMP_REDIRECT: + /* Max length: 24 "GATEWAY=255.255.255.255 " */ + sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); + /* Fall through */ + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + /* Max length: 3+maxlen */ + if (!iphoff) { /* Only recurse once. */ + sb_add(m, "["); + dump_ipv4_packet(m, info, skb, + iphoff + ih->ihl*4+sizeof(_icmph)); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ich->type == ICMP_DEST_UNREACH && + ich->code == ICMP_FRAG_NEEDED) + sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); + } + break; + } + /* Max Length */ + case IPPROTO_AH: { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 9 "PROTO=AH " */ + sb_add(m, "PROTO=AH "); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ah = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_ahdr), &_ahdr); + if (ah == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + break; + } + case IPPROTO_ESP: { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 10 "PROTO=ESP " */ + sb_add(m, "PROTO=ESP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + eh = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_esph), &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); + break; + } + /* Max length: 10 "PROTO 255 " */ + default: + sb_add(m, "PROTO=%u ", ih->protocol); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!iphoff && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ + /* UDP: 10+max(25,20) = 35 */ + /* UDPLITE: 14+max(25,20) = 39 */ + /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ + /* ESP: 10+max(25)+15 = 50 */ + /* AH: 9+max(25)+15 = 49 */ + /* unknown: 10 */ + + /* (ICMP allows recursion one level deep) */ + /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ + /* maxlen = 230+ 91 + 230 + 252 = 803 */ +} + +static void dump_ipv4_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int i; + + sb_add(m, "%02x", *p++); + for (i = 1; i < dev->hard_header_len; i++, p++) + sb_add(m, ":%02x", *p); + } + sb_add(m, " "); +} + +static void +log_packet_common(struct sbuff *m, + u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, + prefix, + in ? in->name : "", + out ? out->name : ""); +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge) { + const struct net_device *physindev; + const struct net_device *physoutdev; + + physindev = skb->nf_bridge->physindev; + if (physindev && in != physindev) + sb_add(m, "PHYSIN=%s ", physindev->name); + physoutdev = skb->nf_bridge->physoutdev; + if (physoutdev && out != physoutdev) + sb_add(m, "PHYSOUT=%s ", physoutdev->name); + } +#endif +} + + +static void +ipt_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv4_mac_header(m, loginfo, skb); + + dump_ipv4_packet(m, loginfo, skb, 0); + + sb_close(m); +} + +#if IS_ENABLED(CONFIG_IPV6) +/* One level of recursion won't kill us */ +static void dump_ipv6_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int ip6hoff, + int recurse) +{ + u_int8_t currenthdr; + int fragment; + struct ipv6hdr _ip6h; + const struct ipv6hdr *ih; + unsigned int ptr; + unsigned int hdrlen = 0; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ + sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); + + /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ + sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), + (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, + ih->hop_limit, + (ntohl(*(__be32 *)ih) & 0x000fffff)); + + fragment = 0; + ptr = ip6hoff + sizeof(struct ipv6hdr); + currenthdr = ih->nexthdr; + while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { + struct ipv6_opt_hdr _hdr; + const struct ipv6_opt_hdr *hp; + + hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); + if (hp == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 48 "OPT (...) " */ + if (logflags & XT_LOG_IPOPT) + sb_add(m, "OPT ( "); + + switch (currenthdr) { + case IPPROTO_FRAGMENT: { + struct frag_hdr _fhdr; + const struct frag_hdr *fh; + + sb_add(m, "FRAG:"); + fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), + &_fhdr); + if (fh == NULL) { + sb_add(m, "TRUNCATED "); + return; + } + + /* Max length: 6 "65535 " */ + sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); + + /* Max length: 11 "INCOMPLETE " */ + if (fh->frag_off & htons(0x0001)) + sb_add(m, "INCOMPLETE "); + + sb_add(m, "ID:%08x ", ntohl(fh->identification)); + + if (ntohs(fh->frag_off) & 0xFFF8) + fragment = 1; + + hdrlen = 8; + + break; + } + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + if (fragment) { + if (logflags & XT_LOG_IPOPT) + sb_add(m, ")"); + return; + } + hdrlen = ipv6_optlen(hp); + break; + /* Max Length */ + case IPPROTO_AH: + if (logflags & XT_LOG_IPOPT) { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + /* Max length: 3 "AH " */ + sb_add(m, "AH "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), + &_ahdr); + if (ah == NULL) { + /* + * Max length: 26 "INCOMPLETE [65535 + * bytes] )" + */ + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 15 "SPI=0xF1234567 */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + + } + + hdrlen = (hp->hdrlen+2)<<2; + break; + case IPPROTO_ESP: + if (logflags & XT_LOG_IPOPT) { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 4 "ESP " */ + sb_add(m, "ESP "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + /* + * Max length: 26 "INCOMPLETE [65535 bytes] )" + */ + eh = skb_header_pointer(skb, ptr, sizeof(_esph), + &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 16 "SPI=0xF1234567 )" */ + sb_add(m, "SPI=0x%x )", ntohl(eh->spi)); + + } + return; + default: + /* Max length: 20 "Unknown Ext Hdr 255" */ + sb_add(m, "Unknown Ext Hdr %u", currenthdr); + return; + } + if (logflags & XT_LOG_IPOPT) + sb_add(m, ") "); + + currenthdr = hp->nexthdr; + ptr += hdrlen; + } + + switch (currenthdr) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, currenthdr, fragment, ptr, + logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, currenthdr, fragment, ptr)) + return; + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmp6h; + const struct icmp6hdr *ic; + + /* Max length: 13 "PROTO=ICMPv6 " */ + sb_add(m, "PROTO=ICMPv6 "); + + if (fragment) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); + if (ic == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); + return; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); + + switch (ic->icmp6_type) { + case ICMPV6_ECHO_REQUEST: + case ICMPV6_ECHO_REPLY: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ic->icmp6_identifier), + ntohs(ic->icmp6_sequence)); + break; + case ICMPV6_MGM_QUERY: + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_REDUCTION: + break; + + case ICMPV6_PARAMPROB: + /* Max length: 17 "POINTER=ffffffff " */ + sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); + /* Fall through */ + case ICMPV6_DEST_UNREACH: + case ICMPV6_PKT_TOOBIG: + case ICMPV6_TIME_EXCEED: + /* Max length: 3+maxlen */ + if (recurse) { + sb_add(m, "["); + dump_ipv6_packet(m, info, skb, + ptr + sizeof(_icmp6h), 0); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) + sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); + } + break; + } + /* Max length: 10 "PROTO=255 " */ + default: + sb_add(m, "PROTO=%u ", currenthdr); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && recurse && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!recurse && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); +} + +static void dump_ipv6_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int len = dev->hard_header_len; + unsigned int i; + + if (dev->type == ARPHRD_SIT) { + p -= ETH_HLEN; + + if (p < skb->head) + p = NULL; + } + + if (p != NULL) { + sb_add(m, "%02x", *p++); + for (i = 1; i < len; i++) + sb_add(m, ":%02x", *p++); + } + sb_add(m, " "); + + if (dev->type == ARPHRD_SIT) { + const struct iphdr *iph = + (struct iphdr *)skb_mac_header(skb); + sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, + &iph->daddr); + } + } else + sb_add(m, " "); +} + +static void +ip6t_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv6_mac_header(m, loginfo, skb); + + dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + + sb_close(m); +} +#endif + +static unsigned int +log_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + struct nf_loginfo li; + + li.type = NF_LOG_TYPE_LOG; + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else + WARN_ON_ONCE(1); + + return XT_CONTINUE; +} + +static int log_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + return 0; +} + +static struct xt_target log_tg_regs[] __read_mostly = { + { + .name = "LOG", + .family = NFPROTO_IPV4, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, +#if IS_ENABLED(CONFIG_IPV6) + { + .name = "LOG", + .family = NFPROTO_IPV6, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, +#endif +}; + +static struct nf_logger ipt_log_logger __read_mostly = { + .name = "ipt_LOG", + .logfn = &ipt_log_packet, + .me = THIS_MODULE, +}; + +#if IS_ENABLED(CONFIG_IPV6) +static struct nf_logger ip6t_log_logger __read_mostly = { + .name = "ip6t_LOG", + .logfn = &ip6t_log_packet, + .me = THIS_MODULE, +}; +#endif + +static int __init log_tg_init(void) +{ + int ret; + + ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_IPV4, &ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_register(NFPROTO_IPV6, &ip6t_log_logger); +#endif + return 0; +} + +static void __exit log_tg_exit(void) +{ + nf_log_unregister(&ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_unregister(&ip6t_log_logger); +#endif + xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); +} + +module_init(log_tg_init); +module_exit(log_tg_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); +MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); +MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); +MODULE_ALIAS("ipt_LOG"); +MODULE_ALIAS("ip6t_LOG"); -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG 2012-02-10 22:10 ` [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG Richard Weinberger @ 2012-02-13 23:56 ` Pablo Neira Ayuso 0 siblings, 0 replies; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-13 23:56 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Fri, Feb 10, 2012 at 11:10:52PM +0100, Richard Weinberger wrote: > ipt_LOG and ip6_LOG have a lot of common code, merge them > to reduce duplicate code. Applied, thanks. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 2/2] iptables: xt_LOG: Add ring buffer support 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger 2012-02-10 22:10 ` [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG Richard Weinberger 2012-02-10 22:10 ` [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-10 22:10 ` [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() Richard Weinberger ` (2 subsequent siblings) 5 siblings, 0 replies; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger This patch adds "--ring" and "--ring-size" to xt_LOG. Signed-off-by: Richard Weinberger <richard@nod.at> --- extensions/libxt_LOG.c | 129 ++++++++++++++++++++++++++++++++++++++ extensions/libxt_LOG.man | 9 +++ include/linux/netfilter/xt_LOG.h | 13 ++++- 3 files changed, 150 insertions(+), 1 deletions(-) diff --git a/extensions/libxt_LOG.c b/extensions/libxt_LOG.c index a4c11b6..a9afbb5 100644 --- a/extensions/libxt_LOG.c +++ b/extensions/libxt_LOG.c @@ -1,10 +1,12 @@ #include <stdio.h> #include <string.h> +#include <ctype.h> #include <syslog.h> #include <xtables.h> #include <linux/netfilter/xt_LOG.h> #define LOG_DEFAULT_LEVEL LOG_WARNING +#define DEFAULT_RING_SIZE 1024 #ifndef XT_LOG_UID /* Old kernel */ #define XT_LOG_UID 0x08 /* Log UID owning local socket */ @@ -20,6 +22,9 @@ enum { O_LOG_IPOPTS, O_LOG_UID, O_LOG_MAC, + O_LOG_TIMESTAMP, + O_LOG_RING_NAME, + O_LOG_RING_SIZE, }; static void LOG_help(void) @@ -35,6 +40,17 @@ static void LOG_help(void) " --log-macdecode Decode MAC addresses and protocol.\n\n"); } +static void LOG_help_v1(void) +{ + LOG_help(); + + printf( +" --log-timestamp Add the current time to the log message.\n\n" +" --ring NAME Log messages into a ring buffer NAME instead of kernel syslog.\n\n" +" --ring-size SIZE Size of the ring buffer in KiB.\n\n" +); +} + #define s struct xt_log_info static const struct xt_option_entry LOG_opts[] = { {.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL, @@ -50,6 +66,26 @@ static const struct xt_option_entry LOG_opts[] = { }; #undef s +#define s struct xt_log_info_v1 +static const struct xt_option_entry LOG_opts_v1[] = { + {.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL, + .flags = XTOPT_PUT, XTOPT_POINTER(s, level)}, + {.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING, + .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1}, + {.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE}, + {.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE}, + {.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE}, + {.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE}, + {.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE}, + {.name = "log-timestamp", .id = O_LOG_TIMESTAMP, .type = XTTYPE_NONE}, + {.name = "ring", .id = O_LOG_RING_NAME, .type = XTTYPE_STRING, + .flags = XTOPT_PUT, XTOPT_POINTER(s, ring_name), .min = 1}, + {.name = "ring-size", .id = O_LOG_RING_SIZE, .type = XTTYPE_UINT64, + .flags = XTOPT_PUT, XTOPT_POINTER(s, ring_size)}, + XTOPT_TABLEEND, +}; +#undef s + static void LOG_init(struct xt_entry_target *t) { struct xt_log_info *loginfo = (struct xt_log_info *)t->data; @@ -104,6 +140,29 @@ static void LOG_parse(struct xt_option_call *cb) } } +static void LOG_parse_v1(struct xt_option_call *cb) +{ + struct xt_log_info_v1 *info = cb->data; + int i; + + LOG_parse(cb); + + switch (cb->entry->id) { + case O_LOG_TIMESTAMP: + info->logflags |= XT_LOG_ADD_TIMESTAMP; + break; + case O_LOG_RING_NAME: + if (!info->ring_size) + info->ring_size = DEFAULT_RING_SIZE; + + for (i = 0; i < strlen(info->ring_name); i++) + if (!isalnum(cb->arg[i])) + xtables_error(PARAMETER_PROBLEM, + "Ring name is not alphanumeric"); + break; + } +} + static void LOG_print(const void *ip, const struct xt_entry_target *target, int numeric) { @@ -141,6 +200,27 @@ static void LOG_print(const void *ip, const struct xt_entry_target *target, printf(" prefix \"%s\"", loginfo->prefix); } +static void LOG_print_v1(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_log_info_v1 *loginfo + = (const struct xt_log_info_v1 *)target->data; + + LOG_print(ip, target, numeric); + + if (!numeric) { + if (loginfo->logflags & XT_LOG_ADD_TIMESTAMP) + printf(" log-timestamp"); + } + + if (loginfo->ring_name[0]) { + printf(" ring \"%s\"", loginfo->ring_name); + + if (loginfo->ring_size != DEFAULT_RING_SIZE) + printf(" ring-size %lu", (unsigned long)loginfo->ring_size); + } +} + static void LOG_save(const void *ip, const struct xt_entry_target *target) { const struct xt_log_info *loginfo @@ -166,6 +246,25 @@ static void LOG_save(const void *ip, const struct xt_entry_target *target) printf(" --log-macdecode"); } +static void LOG_save_v1(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_log_info_v1 *loginfo + = (const struct xt_log_info_v1 *)target->data; + + LOG_save(ip, target); + + if (loginfo->logflags & XT_LOG_ADD_TIMESTAMP) + printf(" --log-timestamp"); + + if (loginfo->ring_name[0]) { + printf(" --ring"); + xtables_save_string(loginfo->ring_name); + + if (loginfo->ring_size != DEFAULT_RING_SIZE) + printf(" --ring-size %lu", (unsigned long)loginfo->ring_size); + } +} + static struct xtables_target log_tg_regs[] = { { .name = "LOG", @@ -176,6 +275,7 @@ static struct xtables_target log_tg_regs[] = { .help = LOG_help, .init = LOG_init, .print = LOG_print, + .revision = 0, .save = LOG_save, .x6_parse = LOG_parse, .x6_options = LOG_opts, @@ -189,10 +289,39 @@ static struct xtables_target log_tg_regs[] = { .help = LOG_help, .init = LOG_init, .print = LOG_print, + .revision = 0, .save = LOG_save, .x6_parse = LOG_parse, .x6_options = LOG_opts, }, + { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_log_info_v1)), + .userspacesize = offsetof(struct xt_log_info_v1, rctx), + .help = LOG_help_v1, + .init = LOG_init, + .print = LOG_print_v1, + .revision = 1, + .save = LOG_save_v1, + .x6_parse = LOG_parse_v1, + .x6_options = LOG_opts_v1, + }, + { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_log_info_v1)), + .userspacesize = offsetof(struct xt_log_info_v1, rctx), + .help = LOG_help_v1, + .init = LOG_init, + .print = LOG_print_v1, + .revision = 1, + .save = LOG_save_v1, + .x6_parse = LOG_parse_v1, + .x6_options = LOG_opts_v1, + }, }; void _init(void) diff --git a/extensions/libxt_LOG.man b/extensions/libxt_LOG.man index 47c35e0..2f0b2ff 100644 --- a/extensions/libxt_LOG.man +++ b/extensions/libxt_LOG.man @@ -29,3 +29,12 @@ Log options from the IP packet header. .TP \fB\-\-log\-uid\fP Log the userid of the process which generated the packet. +\fB\-\-log\-timestamp\fP +Add a timestamp to each log message. +\fB\-\-ring\fP \fIname\fP +Log all messages into a ring buffer identified by \fIname\fP. +Each ring buffer is represented as file in +/proc/net/netfilter/xt_LOG_ring/. +\fB\-\-ring\-size\fP \fIsize\fP +Set the ring buffer size in KiB. +Default is 1024 KiB. diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h index cac0790..d84710c 100644 --- a/include/linux/netfilter/xt_LOG.h +++ b/include/linux/netfilter/xt_LOG.h @@ -8,7 +8,8 @@ #define XT_LOG_UID 0x08 /* Log UID owning local socket */ #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ -#define XT_LOG_MASK 0x2f +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ +#define XT_LOG_MASK 0x6f struct xt_log_info { unsigned char level; @@ -16,4 +17,14 @@ struct xt_log_info { char prefix[30]; }; +struct xt_log_info_v1 { + unsigned char level; + unsigned char logflags; + char prefix[30]; + + char ring_name[30]; + __aligned_u64 ring_size; + struct xt_LOG_ring_ctx *rctx __attribute__((aligned(8))); +}; + #endif /* _XT_LOG_H */ -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger ` (2 preceding siblings ...) 2012-02-10 22:10 ` [PATCH 2/2] iptables: xt_LOG: Add ring buffer support Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-14 0:05 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 3/4] xt_log: Make printk() in sb_close() optional Richard Weinberger 2012-02-10 22:10 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 5 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger In order to make ring_buffer usable to other modules for_each_buffer_cpu() needs to be exported. Signed-off-by: Richard Weinberger <richard@nod.at> Acked-by: Steven Rostedt <rostedt@goodmis.org> --- include/linux/ring_buffer.h | 5 +++++ kernel/trace/ring_buffer.c | 7 +++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 67be037..5c1c29f 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -185,4 +185,9 @@ enum ring_buffer_flags { RB_FL_OVERWRITE = 1 << 0, }; +cpumask_var_t *ring_buffer_mask(struct ring_buffer *buffer); + +#define for_each_buffer_cpu(buffer, cpu) \ + for_each_cpu(cpu, *(ring_buffer_mask(buffer))) + #endif /* _LINUX_RING_BUFFER_H */ diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index f5b7b5c..a4117c5 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -348,6 +348,7 @@ void *ring_buffer_event_data(struct ring_buffer_event *event) } EXPORT_SYMBOL_GPL(ring_buffer_event_data); +#undef for_each_buffer_cpu #define for_each_buffer_cpu(buffer, cpu) \ for_each_cpu(cpu, buffer->cpumask) @@ -4139,3 +4140,9 @@ static int rb_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } #endif + +cpumask_var_t *ring_buffer_mask(struct ring_buffer *buffer) +{ + return &buffer->cpumask; +} +EXPORT_SYMBOL_GPL(ring_buffer_mask); -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() 2012-02-10 22:10 ` [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() Richard Weinberger @ 2012-02-14 0:05 ` Pablo Neira Ayuso 0 siblings, 0 replies; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-14 0:05 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Fri, Feb 10, 2012 at 11:10:54PM +0100, Richard Weinberger wrote: > In order to make ring_buffer usable to other modules > for_each_buffer_cpu() needs to be exported. > > Signed-off-by: Richard Weinberger <richard@nod.at> > Acked-by: Steven Rostedt <rostedt@goodmis.org> I've taken this one as well since we need it for the ring_buffer support for your extension for xt_LOG. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 3/4] xt_log: Make printk() in sb_close() optional 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger ` (3 preceding siblings ...) 2012-02-10 22:10 ` [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-14 0:12 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 5 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger Make printk() in sb_close() optional such that other modules can build a log string without printing it using printk(). Signed-off-by: Richard Weinberger <richard@nod.at> --- include/net/netfilter/xt_log.h | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/net/netfilter/xt_log.h b/include/net/netfilter/xt_log.h index 0dfb34a..767e08d 100644 --- a/include/net/netfilter/xt_log.h +++ b/include/net/netfilter/xt_log.h @@ -39,10 +39,12 @@ static struct sbuff *sb_open(void) return m; } -static void sb_close(struct sbuff *m) +static void __sb_close(struct sbuff *m, int print) { - m->buf[m->count] = 0; - printk("%s\n", m->buf); + if (print) { + m->buf[m->count] = 0; + printk("%s\n", m->buf); + } if (likely(m != &emergency)) kfree(m); @@ -52,3 +54,4 @@ static void sb_close(struct sbuff *m) } } +#define sb_close(m) __sb_close(m, 1) -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 3/4] xt_log: Make printk() in sb_close() optional 2012-02-10 22:10 ` [PATCH 3/4] xt_log: Make printk() in sb_close() optional Richard Weinberger @ 2012-02-14 0:12 ` Pablo Neira Ayuso 0 siblings, 0 replies; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-14 0:12 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Fri, Feb 10, 2012 at 11:10:55PM +0100, Richard Weinberger wrote: > Make printk() in sb_close() optional such that other modules > can build a log string without printing it using printk(). Applied, thanks. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger ` (4 preceding siblings ...) 2012-02-10 22:10 ` [PATCH 3/4] xt_log: Make printk() in sb_close() optional Richard Weinberger @ 2012-02-10 22:10 ` Richard Weinberger 2012-02-14 0:11 ` Pablo Neira Ayuso 5 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-10 22:10 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, basti, Richard Weinberger This patch introduces NETFILTER_XT_TARGET_LOG_RING. It allows logging into various ring buffers which are represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. Signed-off-by: Richard Weinberger <richard@nod.at> --- include/linux/netfilter/xt_LOG.h | 13 +- include/net/netfilter/nf_log.h | 1 + include/net/netfilter/xt_log_ring.h | 24 ++ net/netfilter/Kconfig | 13 + net/netfilter/xt_LOG.c | 642 ++++++++++++++++++++++++++++++++++- 5 files changed, 674 insertions(+), 19 deletions(-) create mode 100644 include/net/netfilter/xt_log_ring.h diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h index cac0790..d84710c 100644 --- a/include/linux/netfilter/xt_LOG.h +++ b/include/linux/netfilter/xt_LOG.h @@ -8,7 +8,8 @@ #define XT_LOG_UID 0x08 /* Log UID owning local socket */ #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ -#define XT_LOG_MASK 0x2f +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ +#define XT_LOG_MASK 0x6f struct xt_log_info { unsigned char level; @@ -16,4 +17,14 @@ struct xt_log_info { char prefix[30]; }; +struct xt_log_info_v1 { + unsigned char level; + unsigned char logflags; + char prefix[30]; + + char ring_name[30]; + __aligned_u64 ring_size; + struct xt_LOG_ring_ctx *rctx __attribute__((aligned(8))); +}; + #endif /* _XT_LOG_H */ diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index e991bd0..18a94f9 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -14,6 +14,7 @@ #define NF_LOG_TYPE_LOG 0x01 #define NF_LOG_TYPE_ULOG 0x02 +#define NF_LOG_TYPE_RING 0x04 struct nf_loginfo { u_int8_t type; diff --git a/include/net/netfilter/xt_log_ring.h b/include/net/netfilter/xt_log_ring.h new file mode 100644 index 0000000..c531381 --- /dev/null +++ b/include/net/netfilter/xt_log_ring.h @@ -0,0 +1,24 @@ +#ifndef XT_LOG_RING_H +#define XT_LOG_RING_H + +struct xt_LOG_ring_ctx; +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size); +int xt_LOG_ring_add_record(const struct xt_LOG_ring_ctx *rctx, const char *buf, unsigned int len); +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx); +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx); +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name); + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +void xt_LOG_ring_exit(void); +int xt_LOG_ring_init(void); +#else +static inline void xt_LOG_ring_exit(void) +{ +} + +static inline int xt_LOG_ring_init(void) +{ + return 0; +} +#endif +#endif diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index b895d8b..ac62599 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -533,6 +533,19 @@ config NETFILTER_XT_TARGET_LOG To compile it as a module, choose M here. If unsure, say N. +if NETFILTER_XT_TARGET_LOG + +config NETFILTER_XT_TARGET_LOG_RING + bool 'Ring buffer support' + default y + select RING_BUFFER + help + Using this it is possible to record packets into one or more ring buffers + instead of the kernel syslog. + Each ring buffer is represented as file in /proc/net/netfilter/xt_LOG_ring/. + +endif + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 1595608..62738be 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -16,6 +16,21 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/ip.h> +#include <linux/ctype.h> +#include <linux/ring_buffer.h> +#include <linux/cpu.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/atomic.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> #include <net/ipv6.h> #include <net/icmp.h> #include <net/udp.h> @@ -28,6 +43,45 @@ #include <linux/netfilter_ipv6/ip6_tables.h> #include <net/netfilter/nf_log.h> #include <net/netfilter/xt_log.h> +#include <net/netfilter/xt_log_ring.h> + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +#define RING_DIR "xt_LOG_ring" + +struct xt_LOG_ring_ctx { + char name[32]; + struct ring_buffer *buffer; + atomic_t pipe_in_use; + atomic_t refcnt; + + struct list_head list; +}; + +struct rlog_entry { + size_t count; + char msg[0]; +}; + +struct rlog_iter { + struct ring_buffer *buffer; + const char *buffer_name; + struct rlog_entry *ent; + + char print_buf[PAGE_SIZE]; + size_t print_buf_len; + size_t print_buf_pos; + + unsigned long lost_events; + int cpu; + + struct mutex lock; +}; + +static DEFINE_SPINLOCK(ring_list_lock); +static LIST_HEAD(ring_list); +static DECLARE_WAIT_QUEUE_HEAD(rlog_wait); +static struct proc_dir_entry *prlog; +#endif static struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, @@ -155,7 +209,7 @@ static void dump_ipv4_packet(struct sbuff *m, const struct iphdr *ih; unsigned int logflags; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; else logflags = NF_LOG_MASK; @@ -394,7 +448,7 @@ static void dump_ipv4_mac_header(struct sbuff *m, struct net_device *dev = skb->dev; unsigned int logflags = 0; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; if (!(logflags & XT_LOG_MACDECODE)) @@ -434,9 +488,20 @@ log_packet_common(struct sbuff *m, const struct nf_loginfo *loginfo, const char *prefix) { - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", + if (loginfo->type == NF_LOG_TYPE_LOG) + sb_add(m, "<%d>", loginfo->u.log.level); + + if (loginfo->u.log.logflags & XT_LOG_ADD_TIMESTAMP) { + static struct timespec tv; + unsigned int msec; + + getnstimeofday(&tv); + msec = tv.tv_nsec; + do_div(msec, 1000000); + sb_add(m, "TIMESTAMP=%li.%03li ", tv.tv_sec, msec); + } + + sb_add(m, "%sIN=%s OUT=%s ", prefix, in ? in->name : "", out ? out->name : ""); #ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) { @@ -454,8 +519,7 @@ log_packet_common(struct sbuff *m, } -static void -ipt_log_packet(u_int8_t pf, +static struct sbuff *ipt_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -475,6 +539,19 @@ ipt_log_packet(u_int8_t pf, dump_ipv4_packet(m, loginfo, skb, 0); + return m; +} + +static void ipt_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ipt_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + sb_close(m); } @@ -493,7 +570,7 @@ static void dump_ipv6_packet(struct sbuff *m, unsigned int hdrlen = 0; unsigned int logflags; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; else logflags = NF_LOG_MASK; @@ -734,7 +811,7 @@ static void dump_ipv6_mac_header(struct sbuff *m, struct net_device *dev = skb->dev; unsigned int logflags = 0; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; if (!(logflags & XT_LOG_MACDECODE)) @@ -782,8 +859,7 @@ fallback: sb_add(m, " "); } -static void -ip6t_log_packet(u_int8_t pf, +static struct sbuff *ip6t_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -803,6 +879,19 @@ ip6t_log_packet(u_int8_t pf, dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + return m; +} + +static void ip6t_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ip6t_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + sb_close(m); } #endif @@ -812,22 +901,28 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_log_info *loginfo = par->targinfo; struct nf_loginfo li; + struct sbuff *m; li.type = NF_LOG_TYPE_LOG; li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; if (par->family == NFPROTO_IPV4) - ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #if IS_ENABLED(CONFIG_IPV6) else if (par->family == NFPROTO_IPV6) - ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #endif - else + else { WARN_ON_ONCE(1); + goto out; + } + + sb_close(m); +out: return XT_CONTINUE; } @@ -851,37 +946,542 @@ static int log_tg_check(const struct xt_tgchk_param *par) return 0; } +static unsigned int +log_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct nf_loginfo li; + struct sbuff *m; + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) + li.type = NF_LOG_TYPE_RING; + else +#endif + li.type = NF_LOG_TYPE_LOG; + + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else { + WARN_ON_ONCE(1); + goto out; + } + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) { + sb_add(m, "\n"); + xt_LOG_ring_add_record(loginfo->rctx, m->buf, m->count); + __sb_close(m, 0); + } else +#endif + sb_close(m); + +out: + return XT_CONTINUE; +} + +static int log_tg_check_v1(const struct xt_tgchk_param *par) +{ + struct xt_log_info_v1 *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + /* a non-empty ring_size indicates that we're using ring_buffer */ + if (loginfo->ring_size) { +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + int i; + struct xt_LOG_ring_ctx *rctx; + + if (loginfo->ring_name[sizeof(loginfo->ring_name)-1] != '\0') { + pr_debug("ring_name is not null-terminated\n"); + return -EINVAL; + } + + if (!loginfo->ring_name[0]) { + pr_debug("ring_name is empty\n"); + return -EINVAL; + } + + for (i = 0; i < strlen(loginfo->ring_name); i++) { + if (!isalnum(loginfo->ring_name[i])) { + pr_debug("ring_name contains " + "a non-alphanumeric character\n"); + return -EINVAL; + } + } + + rctx = xt_LOG_ring_find_ctx(loginfo->ring_name); + if (!rctx) { + rctx = xt_LOG_ring_new_ctx(loginfo->ring_name, + loginfo->ring_size); + if (IS_ERR(rctx)) + return PTR_ERR(rctx); + } + + xt_LOG_ring_get(rctx); + loginfo->rctx = rctx; +#else + pr_debug("Sorry, this kernel was built without CONFIG_NETFILTER_XT_TARGET_LOG_RING\n"); + return -EINVAL; +#endif + } + + return 0; +} + +static void log_tg_destroy_v1(const struct xt_tgdtor_param *par) +{ +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct xt_LOG_ring_ctx *rctx = loginfo->rctx; + + if (loginfo->ring_size) + xt_LOG_ring_put(rctx); +#endif +} + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +static void wakeup_work_handler(struct work_struct *work) +{ + wake_up(&rlog_wait); +} + +static DECLARE_WORK(wakeup_work, wakeup_work_handler); + +static void rlog_wake_up(void) +{ + schedule_work(&wakeup_work); +} + +int xt_LOG_ring_add_record(const struct xt_LOG_ring_ctx *rctx, + const char *buf, unsigned int len) +{ + struct rlog_entry *entry; + struct ring_buffer_event *event; + + event = ring_buffer_lock_reserve(rctx->buffer, sizeof(*entry) + len); + if (!event) + return 1; + + entry = ring_buffer_event_data(event); + memcpy(entry->msg, buf, len); + entry->count = len; + + ring_buffer_unlock_commit(rctx->buffer, event); + rlog_wake_up(); + + return 0; +} + +static struct rlog_entry *peek_next_entry(struct rlog_iter *iter, int cpu, + unsigned long long *ts) +{ + struct ring_buffer_event *event; + + event = ring_buffer_peek(iter->buffer, cpu, ts, &iter->lost_events); + + if (event) + return ring_buffer_event_data(event); + + return NULL; +} + +static struct rlog_entry *find_next_entry(struct rlog_iter *iter) +{ + struct rlog_entry *ent, *next = NULL; + unsigned long long next_ts = 0, ts; + int cpu, next_cpu = -1; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (ring_buffer_empty_cpu(iter->buffer, cpu)) + continue; + + ent = peek_next_entry(iter, cpu, &ts); + + if (ent && (!next || ts < next_ts)) { + next = ent; + next_cpu = cpu; + next_ts = ts; + } + } + + iter->cpu = next_cpu; + + return next; +} + +static struct rlog_iter *find_next_entry_inc(struct rlog_iter *iter) +{ + iter->ent = find_next_entry(iter); + + if (iter->ent) + return iter; + + return NULL; +} + +static int buffer_empty(struct rlog_iter *iter) +{ + int cpu; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (!ring_buffer_empty_cpu(iter->buffer, cpu)) + return 0; + } + + return 1; +} + +static ssize_t rlog_to_user(struct rlog_iter *iter, char __user *ubuf, + size_t cnt) +{ + int ret; + int len; + + if (!cnt) + goto out; + + len = iter->print_buf_len - iter->print_buf_pos; + if (len < 1) + return -EBUSY; + + if (cnt > len) + cnt = len; + + ret = copy_to_user(ubuf, iter->print_buf + iter->print_buf_pos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + iter->print_buf_pos += cnt; + +out: + return cnt; +} + +static int rlog_open_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + int ret = 0; + + /* only one consuming reader is allowed */ + if (atomic_cmpxchg(&tgt->pipe_in_use, 0, 1)) { + ret = -EBUSY; + goto out; + } + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) { + ret = -ENOMEM; + goto out; + } + + mutex_init(&iter->lock); + iter->buffer = tgt->buffer; + iter->buffer_name = tgt->name; + + file->private_data = iter; +out: + return ret; +} + +static unsigned int rlog_poll_pipe(struct file *file, poll_table *poll_table) +{ + struct rlog_iter *iter = file->private_data; + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + poll_wait(file, &rlog_wait, poll_table); + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int rlog_release_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter = file->private_data; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + + mutex_destroy(&iter->lock); + kfree(iter); + atomic_set(&tgt->pipe_in_use, 0); + + return 0; +} + +static void wait_pipe(struct rlog_iter *iter) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(&rlog_wait, &wait, TASK_INTERRUPTIBLE); + + if (buffer_empty(iter)) + schedule(); + + finish_wait(&rlog_wait, &wait); +} + +static int rlog_wait_pipe(struct file *file) +{ + struct rlog_iter *iter = file->private_data; + + while (buffer_empty(iter)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + mutex_unlock(&iter->lock); + + wait_pipe(iter); + + mutex_lock(&iter->lock); + + if (signal_pending(current)) + return -EINTR; + } + + return 1; +} + +static ssize_t rlog_read_pipe(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct rlog_iter *iter = file->private_data; + ssize_t ret; + + ret = rlog_to_user(iter, ubuf, cnt); + if (ret != -EBUSY) + goto out; + + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + mutex_lock(&iter->lock); +again: + ret = rlog_wait_pipe(file); + if (ret <= 0) + goto out_unlock; + + while (find_next_entry_inc(iter) != NULL) { + struct rlog_entry *ent; + ent = iter->ent; + + if (ent->count >= PAGE_SIZE - iter->print_buf_len) + break; + + memcpy(iter->print_buf + iter->print_buf_len, ent->msg, + ent->count); + iter->print_buf_len += ent->count; + + ring_buffer_consume(iter->buffer, iter->cpu, NULL, + &iter->lost_events); + if (iter->lost_events) + printk(KERN_WARNING KBUILD_MODNAME ": Ring %s " + "lost %lu events\n", iter->buffer_name, + iter->lost_events); + + if (iter->print_buf_len >= cnt) + break; + } + + ret = rlog_to_user(iter, ubuf, cnt); + + if (iter->print_buf_pos >= iter->print_buf_len) { + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + } + + if (ret == -EBUSY) + goto again; +out_unlock: + mutex_unlock(&iter->lock); +out: + return ret; +} + +static const struct file_operations rlog_pipe_fops = { + .open = rlog_open_pipe, + .poll = rlog_poll_pipe, + .read = rlog_read_pipe, + .release = rlog_release_pipe, + .llseek = no_llseek, +}; + +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size) +{ + struct xt_LOG_ring_ctx *new; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + new = ERR_PTR(-ENOMEM); + + goto out; + } + + new->buffer = ring_buffer_alloc(rb_size << 10, RB_FL_OVERWRITE); + if (!new->buffer) { + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + strlcpy(new->name, name, sizeof new->name); + + if (!proc_create_data(name, 0400, prlog, &rlog_pipe_fops, new)) { + ring_buffer_free(new->buffer); + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + atomic_set(&new->pipe_in_use, 0); + atomic_set(&new->refcnt, 0); + + spin_lock(&ring_list_lock); + list_add(&new->list, &ring_list); + spin_unlock(&ring_list_lock); +out: + return new; +} + +static void free_xt_LOG_ring_ctx(struct xt_LOG_ring_ctx *ctx) +{ + remove_proc_entry(ctx->name, prlog); + ring_buffer_free(ctx->buffer); + list_del(&ctx->list); + kfree(ctx); +} + +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx) +{ + atomic_inc(&ctx->refcnt); +} + +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx) +{ + if (atomic_dec_and_test(&ctx->refcnt)) + free_xt_LOG_ring_ctx(ctx); +} + +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name) +{ + struct list_head *e; + struct xt_LOG_ring_ctx *tmp, *victim = NULL; + + spin_lock(&ring_list_lock); + + list_for_each(e, &ring_list) { + tmp = list_entry(e, struct xt_LOG_ring_ctx, list); + if (strcmp(tmp->name, name) == 0) { + victim = tmp; + + goto out; + } + } + +out: + spin_unlock(&ring_list_lock); + + return victim; +} + +void __exit xt_LOG_ring_exit(void) +{ + WARN_ON(!list_empty(&ring_list)); + remove_proc_entry(RING_DIR, proc_net_netfilter); +} + +int __init xt_LOG_ring_init(void) +{ + prlog = proc_mkdir(RING_DIR, proc_net_netfilter); + if (!prlog) + return -ENOMEM; + + return 0; +} +#endif /* CONFIG_NETFILTER_XT_TARGET_LOG_RING */ + static struct xt_target log_tg_regs[] __read_mostly = { { .name = "LOG", .family = NFPROTO_IPV4, + .revision = 0, .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, .me = THIS_MODULE, }, + { + .name = "LOG", + .family = NFPROTO_IPV4, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, #if IS_ENABLED(CONFIG_IPV6) { .name = "LOG", .family = NFPROTO_IPV6, + .revision = 0, .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, .me = THIS_MODULE, }, + { + .name = "LOG", + .family = NFPROTO_IPV6, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, #endif }; static struct nf_logger ipt_log_logger __read_mostly = { .name = "ipt_LOG", - .logfn = &ipt_log_packet, + .logfn = &ipt_log_packet_logger, .me = THIS_MODULE, }; #if IS_ENABLED(CONFIG_IPV6) static struct nf_logger ip6t_log_logger __read_mostly = { .name = "ip6t_LOG", - .logfn = &ip6t_log_packet, + .logfn = &ip6t_log_packet_logger, .me = THIS_MODULE, }; #endif @@ -890,6 +1490,10 @@ static int __init log_tg_init(void) { int ret; + ret = xt_LOG_ring_init(); + if (ret < 0) + return ret; + ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); if (ret < 0) return ret; @@ -908,6 +1512,7 @@ static void __exit log_tg_exit(void) nf_log_unregister(&ip6t_log_logger); #endif xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + xt_LOG_ring_exit(); } module_init(log_tg_init); @@ -916,6 +1521,7 @@ module_exit(log_tg_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); +MODULE_AUTHOR("Richard Weinberger <richard@nod.at>"); MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); MODULE_ALIAS("ipt_LOG"); MODULE_ALIAS("ip6t_LOG"); -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-10 22:10 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger @ 2012-02-14 0:11 ` Pablo Neira Ayuso 2012-02-14 0:12 ` Richard Weinberger 0 siblings, 1 reply; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-14 0:11 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti On Fri, Feb 10, 2012 at 11:10:56PM +0100, Richard Weinberger wrote: > This patch introduces NETFILTER_XT_TARGET_LOG_RING. > It allows logging into various ring buffers which are > represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. > > Signed-off-by: Richard Weinberger <richard@nod.at> > --- > include/linux/netfilter/xt_LOG.h | 13 +- > include/net/netfilter/nf_log.h | 1 + > include/net/netfilter/xt_log_ring.h | 24 ++ Could merge xt_log_ring.h with xt_log.h? > net/netfilter/Kconfig | 13 + > net/netfilter/xt_LOG.c | 642 ++++++++++++++++++++++++++++++++++- > 5 files changed, 674 insertions(+), 19 deletions(-) > create mode 100644 include/net/netfilter/xt_log_ring.h > > diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h > index cac0790..d84710c 100644 > --- a/include/linux/netfilter/xt_LOG.h > +++ b/include/linux/netfilter/xt_LOG.h > @@ -8,7 +8,8 @@ > #define XT_LOG_UID 0x08 /* Log UID owning local socket */ > #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ > #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ > -#define XT_LOG_MASK 0x2f > +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ Please, I'd appreciate if you can split this timestamp support in a separated patch. Thanks. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-14 0:11 ` Pablo Neira Ayuso @ 2012-02-14 0:12 ` Richard Weinberger 0 siblings, 0 replies; 21+ messages in thread From: Richard Weinberger @ 2012-02-14 0:12 UTC (permalink / raw) To: Pablo Neira Ayuso Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt, basti Am 14.02.2012 01:11, schrieb Pablo Neira Ayuso: > On Fri, Feb 10, 2012 at 11:10:56PM +0100, Richard Weinberger wrote: >> This patch introduces NETFILTER_XT_TARGET_LOG_RING. >> It allows logging into various ring buffers which are >> represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. >> >> Signed-off-by: Richard Weinberger<richard@nod.at> >> --- >> include/linux/netfilter/xt_LOG.h | 13 +- >> include/net/netfilter/nf_log.h | 1 + >> include/net/netfilter/xt_log_ring.h | 24 ++ > > Could merge xt_log_ring.h with xt_log.h? Sure. >> net/netfilter/Kconfig | 13 + >> net/netfilter/xt_LOG.c | 642 ++++++++++++++++++++++++++++++++++- >> 5 files changed, 674 insertions(+), 19 deletions(-) >> create mode 100644 include/net/netfilter/xt_log_ring.h >> >> diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h >> index cac0790..d84710c 100644 >> --- a/include/linux/netfilter/xt_LOG.h >> +++ b/include/linux/netfilter/xt_LOG.h >> @@ -8,7 +8,8 @@ >> #define XT_LOG_UID 0x08 /* Log UID owning local socket */ >> #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ >> #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ >> -#define XT_LOG_MASK 0x2f >> +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ > > Please, I'd appreciate if you can split this timestamp support in a > separated patch. > Will do. Thanks, //richard ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4] Merge ipt_LOG and ip6t_LOG, add ring bufffer support @ 2012-02-09 21:54 Richard Weinberger 2012-02-09 21:54 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 0 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-09 21:54 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo This patch set merges ipt_LOG and ip6t_LOG and adds ring buffer support to xt_LOG. Using "--ring" a user can create LOG rules which log messages into one or more ring buffers. Each ring buffer is represented as pipe-like file in /proc/net/netfilter/xt_LOG_ring/. Ring buffer support can be enabled/disabled using CONFIG_NETFILTER_XT_TARGET_LOG_RING. Changes since v1: - Merged ipt_LOG and ip6t_LOG - Implemented ring buffer support as part of xt_LOG. Changes since v2: - Fixed struct xt_log_info_v1's data layout - Removed the sizeof(void *) hack - Replaced BUG() by WARN_ON_ONCE() Changes since v3: - Merged xt_LOG_core.c and xt_LOG_ring.c into xt_LOG.c - Made checkpatch.pl happy All patches can also be found at: git://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git xt_LOG git://git.kernel.org/pub/scm/linux/kernel/git/rw/iptables.git xt_LOG [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into [PATCH 2/2] iptables: xt_LOG: Add ring buffer support [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() [PATCH 3/4] xt_log: Make printk() in sb_close() optional [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-09 21:54 [PATCH v4] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger @ 2012-02-09 21:54 ` Richard Weinberger 0 siblings, 0 replies; 21+ messages in thread From: Richard Weinberger @ 2012-02-09 21:54 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, pablo, Richard Weinberger This patch introduces NETFILTER_XT_TARGET_LOG_RING. It allows logging into various ring buffers which are represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. Signed-off-by: Richard Weinberger <richard@nod.at> --- include/linux/netfilter/xt_LOG.h | 13 +- include/net/netfilter/nf_log.h | 1 + include/net/netfilter/xt_log_ring.h | 24 ++ net/netfilter/Kconfig | 13 + net/netfilter/xt_LOG.c | 642 ++++++++++++++++++++++++++++++++++- 5 files changed, 674 insertions(+), 19 deletions(-) create mode 100644 include/net/netfilter/xt_log_ring.h diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h index cac0790..d84710c 100644 --- a/include/linux/netfilter/xt_LOG.h +++ b/include/linux/netfilter/xt_LOG.h @@ -8,7 +8,8 @@ #define XT_LOG_UID 0x08 /* Log UID owning local socket */ #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ -#define XT_LOG_MASK 0x2f +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ +#define XT_LOG_MASK 0x6f struct xt_log_info { unsigned char level; @@ -16,4 +17,14 @@ struct xt_log_info { char prefix[30]; }; +struct xt_log_info_v1 { + unsigned char level; + unsigned char logflags; + char prefix[30]; + + char ring_name[30]; + __aligned_u64 ring_size; + struct xt_LOG_ring_ctx *rctx __attribute__((aligned(8))); +}; + #endif /* _XT_LOG_H */ diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index e991bd0..18a94f9 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -14,6 +14,7 @@ #define NF_LOG_TYPE_LOG 0x01 #define NF_LOG_TYPE_ULOG 0x02 +#define NF_LOG_TYPE_RING 0x04 struct nf_loginfo { u_int8_t type; diff --git a/include/net/netfilter/xt_log_ring.h b/include/net/netfilter/xt_log_ring.h new file mode 100644 index 0000000..c531381 --- /dev/null +++ b/include/net/netfilter/xt_log_ring.h @@ -0,0 +1,24 @@ +#ifndef XT_LOG_RING_H +#define XT_LOG_RING_H + +struct xt_LOG_ring_ctx; +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size); +int xt_LOG_ring_add_record(const struct xt_LOG_ring_ctx *rctx, const char *buf, unsigned int len); +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx); +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx); +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name); + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +void xt_LOG_ring_exit(void); +int xt_LOG_ring_init(void); +#else +static inline void xt_LOG_ring_exit(void) +{ +} + +static inline int xt_LOG_ring_init(void) +{ + return 0; +} +#endif +#endif diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index b895d8b..ac62599 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -533,6 +533,19 @@ config NETFILTER_XT_TARGET_LOG To compile it as a module, choose M here. If unsure, say N. +if NETFILTER_XT_TARGET_LOG + +config NETFILTER_XT_TARGET_LOG_RING + bool 'Ring buffer support' + default y + select RING_BUFFER + help + Using this it is possible to record packets into one or more ring buffers + instead of the kernel syslog. + Each ring buffer is represented as file in /proc/net/netfilter/xt_LOG_ring/. + +endif + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c index 1595608..62738be 100644 --- a/net/netfilter/xt_LOG.c +++ b/net/netfilter/xt_LOG.c @@ -16,6 +16,21 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> #include <linux/ip.h> +#include <linux/ctype.h> +#include <linux/ring_buffer.h> +#include <linux/cpu.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/atomic.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> #include <net/ipv6.h> #include <net/icmp.h> #include <net/udp.h> @@ -28,6 +43,45 @@ #include <linux/netfilter_ipv6/ip6_tables.h> #include <net/netfilter/nf_log.h> #include <net/netfilter/xt_log.h> +#include <net/netfilter/xt_log_ring.h> + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +#define RING_DIR "xt_LOG_ring" + +struct xt_LOG_ring_ctx { + char name[32]; + struct ring_buffer *buffer; + atomic_t pipe_in_use; + atomic_t refcnt; + + struct list_head list; +}; + +struct rlog_entry { + size_t count; + char msg[0]; +}; + +struct rlog_iter { + struct ring_buffer *buffer; + const char *buffer_name; + struct rlog_entry *ent; + + char print_buf[PAGE_SIZE]; + size_t print_buf_len; + size_t print_buf_pos; + + unsigned long lost_events; + int cpu; + + struct mutex lock; +}; + +static DEFINE_SPINLOCK(ring_list_lock); +static LIST_HEAD(ring_list); +static DECLARE_WAIT_QUEUE_HEAD(rlog_wait); +static struct proc_dir_entry *prlog; +#endif static struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, @@ -155,7 +209,7 @@ static void dump_ipv4_packet(struct sbuff *m, const struct iphdr *ih; unsigned int logflags; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; else logflags = NF_LOG_MASK; @@ -394,7 +448,7 @@ static void dump_ipv4_mac_header(struct sbuff *m, struct net_device *dev = skb->dev; unsigned int logflags = 0; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; if (!(logflags & XT_LOG_MACDECODE)) @@ -434,9 +488,20 @@ log_packet_common(struct sbuff *m, const struct nf_loginfo *loginfo, const char *prefix) { - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", + if (loginfo->type == NF_LOG_TYPE_LOG) + sb_add(m, "<%d>", loginfo->u.log.level); + + if (loginfo->u.log.logflags & XT_LOG_ADD_TIMESTAMP) { + static struct timespec tv; + unsigned int msec; + + getnstimeofday(&tv); + msec = tv.tv_nsec; + do_div(msec, 1000000); + sb_add(m, "TIMESTAMP=%li.%03li ", tv.tv_sec, msec); + } + + sb_add(m, "%sIN=%s OUT=%s ", prefix, in ? in->name : "", out ? out->name : ""); #ifdef CONFIG_BRIDGE_NETFILTER if (skb->nf_bridge) { @@ -454,8 +519,7 @@ log_packet_common(struct sbuff *m, } -static void -ipt_log_packet(u_int8_t pf, +static struct sbuff *ipt_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -475,6 +539,19 @@ ipt_log_packet(u_int8_t pf, dump_ipv4_packet(m, loginfo, skb, 0); + return m; +} + +static void ipt_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ipt_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + sb_close(m); } @@ -493,7 +570,7 @@ static void dump_ipv6_packet(struct sbuff *m, unsigned int hdrlen = 0; unsigned int logflags; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; else logflags = NF_LOG_MASK; @@ -734,7 +811,7 @@ static void dump_ipv6_mac_header(struct sbuff *m, struct net_device *dev = skb->dev; unsigned int logflags = 0; - if (info->type == NF_LOG_TYPE_LOG) + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) logflags = info->u.log.logflags; if (!(logflags & XT_LOG_MACDECODE)) @@ -782,8 +859,7 @@ fallback: sb_add(m, " "); } -static void -ip6t_log_packet(u_int8_t pf, +static struct sbuff *ip6t_log_packet(u_int8_t pf, unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, @@ -803,6 +879,19 @@ ip6t_log_packet(u_int8_t pf, dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + return m; +} + +static void ip6t_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ip6t_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + sb_close(m); } #endif @@ -812,22 +901,28 @@ log_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct xt_log_info *loginfo = par->targinfo; struct nf_loginfo li; + struct sbuff *m; li.type = NF_LOG_TYPE_LOG; li.u.log.level = loginfo->level; li.u.log.logflags = loginfo->logflags; if (par->family == NFPROTO_IPV4) - ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #if IS_ENABLED(CONFIG_IPV6) else if (par->family == NFPROTO_IPV6) - ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); #endif - else + else { WARN_ON_ONCE(1); + goto out; + } + + sb_close(m); +out: return XT_CONTINUE; } @@ -851,37 +946,542 @@ static int log_tg_check(const struct xt_tgchk_param *par) return 0; } +static unsigned int +log_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct nf_loginfo li; + struct sbuff *m; + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) + li.type = NF_LOG_TYPE_RING; + else +#endif + li.type = NF_LOG_TYPE_LOG; + + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else { + WARN_ON_ONCE(1); + goto out; + } + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) { + sb_add(m, "\n"); + xt_LOG_ring_add_record(loginfo->rctx, m->buf, m->count); + __sb_close(m, 0); + } else +#endif + sb_close(m); + +out: + return XT_CONTINUE; +} + +static int log_tg_check_v1(const struct xt_tgchk_param *par) +{ + struct xt_log_info_v1 *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + /* a non-empty ring_size indicates that we're using ring_buffer */ + if (loginfo->ring_size) { +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + int i; + struct xt_LOG_ring_ctx *rctx; + + if (loginfo->ring_name[sizeof(loginfo->ring_name)-1] != '\0') { + pr_debug("ring_name is not null-terminated\n"); + return -EINVAL; + } + + if (!loginfo->ring_name[0]) { + pr_debug("ring_name is empty\n"); + return -EINVAL; + } + + for (i = 0; i < strlen(loginfo->ring_name); i++) { + if (!isalnum(loginfo->ring_name[i])) { + pr_debug("ring_name contains " + "a non-alphanumeric character\n"); + return -EINVAL; + } + } + + rctx = xt_LOG_ring_find_ctx(loginfo->ring_name); + if (!rctx) { + rctx = xt_LOG_ring_new_ctx(loginfo->ring_name, + loginfo->ring_size); + if (IS_ERR(rctx)) + return PTR_ERR(rctx); + } + + xt_LOG_ring_get(rctx); + loginfo->rctx = rctx; +#else + pr_debug("Sorry, this kernel was built without CONFIG_NETFILTER_XT_TARGET_LOG_RING\n"); + return -EINVAL; +#endif + } + + return 0; +} + +static void log_tg_destroy_v1(const struct xt_tgdtor_param *par) +{ +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct xt_LOG_ring_ctx *rctx = loginfo->rctx; + + if (loginfo->ring_size) + xt_LOG_ring_put(rctx); +#endif +} + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +static void wakeup_work_handler(struct work_struct *work) +{ + wake_up(&rlog_wait); +} + +static DECLARE_WORK(wakeup_work, wakeup_work_handler); + +static void rlog_wake_up(void) +{ + schedule_work(&wakeup_work); +} + +int xt_LOG_ring_add_record(const struct xt_LOG_ring_ctx *rctx, + const char *buf, unsigned int len) +{ + struct rlog_entry *entry; + struct ring_buffer_event *event; + + event = ring_buffer_lock_reserve(rctx->buffer, sizeof(*entry) + len); + if (!event) + return 1; + + entry = ring_buffer_event_data(event); + memcpy(entry->msg, buf, len); + entry->count = len; + + ring_buffer_unlock_commit(rctx->buffer, event); + rlog_wake_up(); + + return 0; +} + +static struct rlog_entry *peek_next_entry(struct rlog_iter *iter, int cpu, + unsigned long long *ts) +{ + struct ring_buffer_event *event; + + event = ring_buffer_peek(iter->buffer, cpu, ts, &iter->lost_events); + + if (event) + return ring_buffer_event_data(event); + + return NULL; +} + +static struct rlog_entry *find_next_entry(struct rlog_iter *iter) +{ + struct rlog_entry *ent, *next = NULL; + unsigned long long next_ts = 0, ts; + int cpu, next_cpu = -1; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (ring_buffer_empty_cpu(iter->buffer, cpu)) + continue; + + ent = peek_next_entry(iter, cpu, &ts); + + if (ent && (!next || ts < next_ts)) { + next = ent; + next_cpu = cpu; + next_ts = ts; + } + } + + iter->cpu = next_cpu; + + return next; +} + +static struct rlog_iter *find_next_entry_inc(struct rlog_iter *iter) +{ + iter->ent = find_next_entry(iter); + + if (iter->ent) + return iter; + + return NULL; +} + +static int buffer_empty(struct rlog_iter *iter) +{ + int cpu; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (!ring_buffer_empty_cpu(iter->buffer, cpu)) + return 0; + } + + return 1; +} + +static ssize_t rlog_to_user(struct rlog_iter *iter, char __user *ubuf, + size_t cnt) +{ + int ret; + int len; + + if (!cnt) + goto out; + + len = iter->print_buf_len - iter->print_buf_pos; + if (len < 1) + return -EBUSY; + + if (cnt > len) + cnt = len; + + ret = copy_to_user(ubuf, iter->print_buf + iter->print_buf_pos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + iter->print_buf_pos += cnt; + +out: + return cnt; +} + +static int rlog_open_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + int ret = 0; + + /* only one consuming reader is allowed */ + if (atomic_cmpxchg(&tgt->pipe_in_use, 0, 1)) { + ret = -EBUSY; + goto out; + } + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) { + ret = -ENOMEM; + goto out; + } + + mutex_init(&iter->lock); + iter->buffer = tgt->buffer; + iter->buffer_name = tgt->name; + + file->private_data = iter; +out: + return ret; +} + +static unsigned int rlog_poll_pipe(struct file *file, poll_table *poll_table) +{ + struct rlog_iter *iter = file->private_data; + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + poll_wait(file, &rlog_wait, poll_table); + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int rlog_release_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter = file->private_data; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + + mutex_destroy(&iter->lock); + kfree(iter); + atomic_set(&tgt->pipe_in_use, 0); + + return 0; +} + +static void wait_pipe(struct rlog_iter *iter) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(&rlog_wait, &wait, TASK_INTERRUPTIBLE); + + if (buffer_empty(iter)) + schedule(); + + finish_wait(&rlog_wait, &wait); +} + +static int rlog_wait_pipe(struct file *file) +{ + struct rlog_iter *iter = file->private_data; + + while (buffer_empty(iter)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + mutex_unlock(&iter->lock); + + wait_pipe(iter); + + mutex_lock(&iter->lock); + + if (signal_pending(current)) + return -EINTR; + } + + return 1; +} + +static ssize_t rlog_read_pipe(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct rlog_iter *iter = file->private_data; + ssize_t ret; + + ret = rlog_to_user(iter, ubuf, cnt); + if (ret != -EBUSY) + goto out; + + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + mutex_lock(&iter->lock); +again: + ret = rlog_wait_pipe(file); + if (ret <= 0) + goto out_unlock; + + while (find_next_entry_inc(iter) != NULL) { + struct rlog_entry *ent; + ent = iter->ent; + + if (ent->count >= PAGE_SIZE - iter->print_buf_len) + break; + + memcpy(iter->print_buf + iter->print_buf_len, ent->msg, + ent->count); + iter->print_buf_len += ent->count; + + ring_buffer_consume(iter->buffer, iter->cpu, NULL, + &iter->lost_events); + if (iter->lost_events) + printk(KERN_WARNING KBUILD_MODNAME ": Ring %s " + "lost %lu events\n", iter->buffer_name, + iter->lost_events); + + if (iter->print_buf_len >= cnt) + break; + } + + ret = rlog_to_user(iter, ubuf, cnt); + + if (iter->print_buf_pos >= iter->print_buf_len) { + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + } + + if (ret == -EBUSY) + goto again; +out_unlock: + mutex_unlock(&iter->lock); +out: + return ret; +} + +static const struct file_operations rlog_pipe_fops = { + .open = rlog_open_pipe, + .poll = rlog_poll_pipe, + .read = rlog_read_pipe, + .release = rlog_release_pipe, + .llseek = no_llseek, +}; + +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size) +{ + struct xt_LOG_ring_ctx *new; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + new = ERR_PTR(-ENOMEM); + + goto out; + } + + new->buffer = ring_buffer_alloc(rb_size << 10, RB_FL_OVERWRITE); + if (!new->buffer) { + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + strlcpy(new->name, name, sizeof new->name); + + if (!proc_create_data(name, 0400, prlog, &rlog_pipe_fops, new)) { + ring_buffer_free(new->buffer); + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + atomic_set(&new->pipe_in_use, 0); + atomic_set(&new->refcnt, 0); + + spin_lock(&ring_list_lock); + list_add(&new->list, &ring_list); + spin_unlock(&ring_list_lock); +out: + return new; +} + +static void free_xt_LOG_ring_ctx(struct xt_LOG_ring_ctx *ctx) +{ + remove_proc_entry(ctx->name, prlog); + ring_buffer_free(ctx->buffer); + list_del(&ctx->list); + kfree(ctx); +} + +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx) +{ + atomic_inc(&ctx->refcnt); +} + +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx) +{ + if (atomic_dec_and_test(&ctx->refcnt)) + free_xt_LOG_ring_ctx(ctx); +} + +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name) +{ + struct list_head *e; + struct xt_LOG_ring_ctx *tmp, *victim = NULL; + + spin_lock(&ring_list_lock); + + list_for_each(e, &ring_list) { + tmp = list_entry(e, struct xt_LOG_ring_ctx, list); + if (strcmp(tmp->name, name) == 0) { + victim = tmp; + + goto out; + } + } + +out: + spin_unlock(&ring_list_lock); + + return victim; +} + +void __exit xt_LOG_ring_exit(void) +{ + WARN_ON(!list_empty(&ring_list)); + remove_proc_entry(RING_DIR, proc_net_netfilter); +} + +int __init xt_LOG_ring_init(void) +{ + prlog = proc_mkdir(RING_DIR, proc_net_netfilter); + if (!prlog) + return -ENOMEM; + + return 0; +} +#endif /* CONFIG_NETFILTER_XT_TARGET_LOG_RING */ + static struct xt_target log_tg_regs[] __read_mostly = { { .name = "LOG", .family = NFPROTO_IPV4, + .revision = 0, .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, .me = THIS_MODULE, }, + { + .name = "LOG", + .family = NFPROTO_IPV4, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, #if IS_ENABLED(CONFIG_IPV6) { .name = "LOG", .family = NFPROTO_IPV6, + .revision = 0, .target = log_tg, .targetsize = sizeof(struct xt_log_info), .checkentry = log_tg_check, .me = THIS_MODULE, }, + { + .name = "LOG", + .family = NFPROTO_IPV6, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, #endif }; static struct nf_logger ipt_log_logger __read_mostly = { .name = "ipt_LOG", - .logfn = &ipt_log_packet, + .logfn = &ipt_log_packet_logger, .me = THIS_MODULE, }; #if IS_ENABLED(CONFIG_IPV6) static struct nf_logger ip6t_log_logger __read_mostly = { .name = "ip6t_LOG", - .logfn = &ip6t_log_packet, + .logfn = &ip6t_log_packet_logger, .me = THIS_MODULE, }; #endif @@ -890,6 +1490,10 @@ static int __init log_tg_init(void) { int ret; + ret = xt_LOG_ring_init(); + if (ret < 0) + return ret; + ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); if (ret < 0) return ret; @@ -908,6 +1512,7 @@ static void __exit log_tg_exit(void) nf_log_unregister(&ip6t_log_logger); #endif xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + xt_LOG_ring_exit(); } module_init(log_tg_init); @@ -916,6 +1521,7 @@ module_exit(log_tg_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); +MODULE_AUTHOR("Richard Weinberger <richard@nod.at>"); MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); MODULE_ALIAS("ipt_LOG"); MODULE_ALIAS("ip6t_LOG"); -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v3] Merge ipt_LOG and ip6t_LOG, add ring bufffer support @ 2012-02-05 0:14 Richard Weinberger 2012-02-05 0:14 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 0 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-05 0:14 UTC (permalink / raw) To: netfilter-devel; +Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt This patch set merges ipt_LOG and ip6t_LOG and adds ring buffer support to xt_LOG. Using "--ring" an user can create LOG rules which log messages into one or more ring buffers. Each ring buffer is represented as pipe-like file in /proc/net/netfilter/xt_LOG_ring/. Ring buffer support can be enabled/disabled using CONFIG_NETFILTER_XT_TARGET_LOG_RING. Changes since v1: - Merged ipt_LOG and ip6t_LOG - Implemented ring buffer support as part of xt_LOG. Changes since v2: - Fixed struct xt_log_info_v1's data layout - Removed the sizeof(void *) hack - Replaced BUG() by WARN_ON_ONCE() git://git.kernel.org/pub/scm/linux/kernel/git/rw/misc.git xt_LOG [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() [PATCH 3/4] xt_log: Make printk() in sb_close() optional [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support git://git.kernel.org/pub/scm/linux/kernel/git/rw/iptables.git xt_LOG [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG [PATCH 2/2] iptables: xt_LOG: Add ring buffer support Thanks, //richard ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-05 0:14 [PATCH v3] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger @ 2012-02-05 0:14 ` Richard Weinberger 2012-02-08 10:29 ` Pablo Neira Ayuso 0 siblings, 1 reply; 21+ messages in thread From: Richard Weinberger @ 2012-02-05 0:14 UTC (permalink / raw) To: netfilter-devel Cc: netdev, linux-kernel, eric.dumazet, jengelh, rostedt, Richard Weinberger This patch introduces NETFILTER_XT_TARGET_LOG_RING. It allows logging into various ring buffers which are represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. Signed-off-by: Richard Weinberger <richard@nod.at> --- include/linux/netfilter/xt_LOG.h | 13 +- include/net/netfilter/nf_log.h | 1 + include/net/netfilter/xt_log_ring.h | 24 + net/netfilter/Kconfig | 13 + net/netfilter/Makefile | 2 + net/netfilter/xt_LOG.c | 921 ----------------------------- net/netfilter/xt_LOG_core.c | 1108 +++++++++++++++++++++++++++++++++++ net/netfilter/xt_LOG_ring.c | 430 ++++++++++++++ 8 files changed, 1590 insertions(+), 922 deletions(-) create mode 100644 include/net/netfilter/xt_log_ring.h delete mode 100644 net/netfilter/xt_LOG.c create mode 100644 net/netfilter/xt_LOG_core.c create mode 100644 net/netfilter/xt_LOG_ring.c diff --git a/include/linux/netfilter/xt_LOG.h b/include/linux/netfilter/xt_LOG.h index cac0790..d84710c 100644 --- a/include/linux/netfilter/xt_LOG.h +++ b/include/linux/netfilter/xt_LOG.h @@ -8,7 +8,8 @@ #define XT_LOG_UID 0x08 /* Log UID owning local socket */ #define XT_LOG_NFLOG 0x10 /* Unsupported, don't reuse */ #define XT_LOG_MACDECODE 0x20 /* Decode MAC header */ -#define XT_LOG_MASK 0x2f +#define XT_LOG_ADD_TIMESTAMP 0x40 /* Add a timestamp */ +#define XT_LOG_MASK 0x6f struct xt_log_info { unsigned char level; @@ -16,4 +17,14 @@ struct xt_log_info { char prefix[30]; }; +struct xt_log_info_v1 { + unsigned char level; + unsigned char logflags; + char prefix[30]; + + char ring_name[30]; + __aligned_u64 ring_size; + struct xt_LOG_ring_ctx *rctx __attribute__((aligned(8))); +}; + #endif /* _XT_LOG_H */ diff --git a/include/net/netfilter/nf_log.h b/include/net/netfilter/nf_log.h index e991bd0..18a94f9 100644 --- a/include/net/netfilter/nf_log.h +++ b/include/net/netfilter/nf_log.h @@ -14,6 +14,7 @@ #define NF_LOG_TYPE_LOG 0x01 #define NF_LOG_TYPE_ULOG 0x02 +#define NF_LOG_TYPE_RING 0x04 struct nf_loginfo { u_int8_t type; diff --git a/include/net/netfilter/xt_log_ring.h b/include/net/netfilter/xt_log_ring.h new file mode 100644 index 0000000..1c14ddf --- /dev/null +++ b/include/net/netfilter/xt_log_ring.h @@ -0,0 +1,24 @@ +#ifndef XT_LOG_RING_H +#define XT_LOG_RING_H + +struct xt_LOG_ring_ctx; +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size); +int xt_LOG_ring_add_record(const struct xt_LOG_ring_ctx *rctx, const char *buf, unsigned int len); +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx); +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx); +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name); + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING +void xt_LOG_ring_exit(void); +int xt_LOG_ring_init(void); +#else +static inline void xt_LOG_ring_exit(void) +{ +} + +static inline int xt_LOG_ring_init(void) +{ + return 0; +} +#endif +#endif diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index b895d8b..ac62599 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -533,6 +533,19 @@ config NETFILTER_XT_TARGET_LOG To compile it as a module, choose M here. If unsure, say N. +if NETFILTER_XT_TARGET_LOG + +config NETFILTER_XT_TARGET_LOG_RING + bool 'Ring buffer support' + default y + select RING_BUFFER + help + Using this it is possible to record packets into one or more ring buffers + instead of the kernel syslog. + Each ring buffer is represented as file in /proc/net/netfilter/xt_LOG_ring/. + +endif + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a28c2a6..e93de2a 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -58,6 +58,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o +xt_LOG-$(CONFIG_NETFILTER_XT_TARGET_LOG_RING) := xt_LOG_ring.o +xt_LOG-y += xt_LOG_core.o obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c deleted file mode 100644 index 1595608..0000000 --- a/net/netfilter/xt_LOG.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - * This is a module which is used for logging packets. - */ - -/* (C) 1999-2001 Paul `Rusty' Russell - * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> - * - * 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <net/ipv6.h> -#include <net/icmp.h> -#include <net/udp.h> -#include <net/tcp.h> -#include <net/route.h> - -#include <linux/netfilter.h> -#include <linux/netfilter/x_tables.h> -#include <linux/netfilter/xt_LOG.h> -#include <linux/netfilter_ipv6/ip6_tables.h> -#include <net/netfilter/nf_log.h> -#include <net/netfilter/xt_log.h> - -static struct nf_loginfo default_loginfo = { - .type = NF_LOG_TYPE_LOG, - .u = { - .log = { - .level = 5, - .logflags = NF_LOG_MASK, - }, - }, -}; - -static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, - u8 proto, int fragment, unsigned int offset) -{ - struct udphdr _udph; - const struct udphdr *uh; - - if (proto == IPPROTO_UDP) - /* Max length: 10 "PROTO=UDP " */ - sb_add(m, "PROTO=UDP "); - else /* Max length: 14 "PROTO=UDPLITE " */ - sb_add(m, "PROTO=UDPLITE "); - - if (fragment) - goto out; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); - if (uh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); - - return 1; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest), - ntohs(uh->len)); - -out: - return 0; -} - -static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, - u8 proto, int fragment, unsigned int offset, - unsigned int logflags) -{ - struct tcphdr _tcph; - const struct tcphdr *th; - - /* Max length: 10 "PROTO=TCP " */ - sb_add(m, "PROTO=TCP "); - - if (fragment) - return 0; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); - if (th == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); - return 1; - } - - /* Max length: 20 "SPT=65535 DPT=65535 " */ - sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest)); - /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ - if (logflags & XT_LOG_TCPSEQ) - sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq)); - - /* Max length: 13 "WINDOW=65535 " */ - sb_add(m, "WINDOW=%u ", ntohs(th->window)); - /* Max length: 9 "RES=0x3C " */ - sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & - TCP_RESERVED_BITS) >> 22)); - /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ - if (th->cwr) - sb_add(m, "CWR "); - if (th->ece) - sb_add(m, "ECE "); - if (th->urg) - sb_add(m, "URG "); - if (th->ack) - sb_add(m, "ACK "); - if (th->psh) - sb_add(m, "PSH "); - if (th->rst) - sb_add(m, "RST "); - if (th->syn) - sb_add(m, "SYN "); - if (th->fin) - sb_add(m, "FIN "); - /* Max length: 11 "URGP=65535 " */ - sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); - - if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { - u_int8_t _opt[60 - sizeof(struct tcphdr)]; - const u_int8_t *op; - unsigned int i; - unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); - - op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), - optsize, _opt); - if (op == NULL) { - sb_add(m, "OPT (TRUNCATED)"); - return 1; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - - sb_add(m, ") "); - } - - return 0; -} - -/* One level of recursion won't kill us */ -static void dump_ipv4_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, - unsigned int iphoff) -{ - struct iphdr _iph; - const struct iphdr *ih; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Important fields: - * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ - /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ - sb_add(m, "SRC=%pI4 DST=%pI4 ", - &ih->saddr, &ih->daddr); - - /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ - sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", - ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, - ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); - - /* Max length: 6 "CE DF MF " */ - if (ntohs(ih->frag_off) & IP_CE) - sb_add(m, "CE "); - if (ntohs(ih->frag_off) & IP_DF) - sb_add(m, "DF "); - if (ntohs(ih->frag_off) & IP_MF) - sb_add(m, "MF "); - - /* Max length: 11 "FRAG:65535 " */ - if (ntohs(ih->frag_off) & IP_OFFSET) - sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); - - if ((logflags & XT_LOG_IPOPT) && - ih->ihl * 4 > sizeof(struct iphdr)) { - const unsigned char *op; - unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; - unsigned int i, optsize; - - optsize = ih->ihl * 4 - sizeof(struct iphdr); - op = skb_header_pointer(skb, iphoff+sizeof(_iph), - optsize, _opt); - if (op == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 127 "OPT (" 15*4*2chars ") " */ - sb_add(m, "OPT ("); - for (i = 0; i < optsize; i++) - sb_add(m, "%02X", op[i]); - sb_add(m, ") "); - } - - switch (ih->protocol) { - case IPPROTO_TCP: - if (dump_tcp_header(m, skb, ih->protocol, - ntohs(ih->frag_off) & IP_OFFSET, - iphoff+ih->ihl*4, logflags)) - return; - case IPPROTO_UDP: - case IPPROTO_UDPLITE: - if (dump_udp_header(m, skb, ih->protocol, - ntohs(ih->frag_off) & IP_OFFSET, - iphoff+ih->ihl*4)) - return; - case IPPROTO_ICMP: { - struct icmphdr _icmph; - const struct icmphdr *ich; - static const size_t required_len[NR_ICMP_TYPES+1] - = { [ICMP_ECHOREPLY] = 4, - [ICMP_DEST_UNREACH] - = 8 + sizeof(struct iphdr), - [ICMP_SOURCE_QUENCH] - = 8 + sizeof(struct iphdr), - [ICMP_REDIRECT] - = 8 + sizeof(struct iphdr), - [ICMP_ECHO] = 4, - [ICMP_TIME_EXCEEDED] - = 8 + sizeof(struct iphdr), - [ICMP_PARAMETERPROB] - = 8 + sizeof(struct iphdr), - [ICMP_TIMESTAMP] = 20, - [ICMP_TIMESTAMPREPLY] = 20, - [ICMP_ADDRESS] = 12, - [ICMP_ADDRESSREPLY] = 12 }; - - /* Max length: 11 "PROTO=ICMP " */ - sb_add(m, "PROTO=ICMP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, - sizeof(_icmph), &_icmph); - if (ich == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - if (ich->type <= NR_ICMP_TYPES && - required_len[ich->type] && - skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - switch (ich->type) { - case ICMP_ECHOREPLY: - case ICMP_ECHO: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ich->un.echo.id), - ntohs(ich->un.echo.sequence)); - break; - - case ICMP_PARAMETERPROB: - /* Max length: 14 "PARAMETER=255 " */ - sb_add(m, "PARAMETER=%u ", - ntohl(ich->un.gateway) >> 24); - break; - case ICMP_REDIRECT: - /* Max length: 24 "GATEWAY=255.255.255.255 " */ - sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); - /* Fall through */ - case ICMP_DEST_UNREACH: - case ICMP_SOURCE_QUENCH: - case ICMP_TIME_EXCEEDED: - /* Max length: 3+maxlen */ - if (!iphoff) { /* Only recurse once. */ - sb_add(m, "["); - dump_ipv4_packet(m, info, skb, - iphoff + ih->ihl*4+sizeof(_icmph)); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ich->type == ICMP_DEST_UNREACH && - ich->code == ICMP_FRAG_NEEDED) - sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); - } - break; - } - /* Max Length */ - case IPPROTO_AH: { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 9 "PROTO=AH " */ - sb_add(m, "PROTO=AH "); - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ah = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_ahdr), &_ahdr); - if (ah == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - break; - } - case IPPROTO_ESP: { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 10 "PROTO=ESP " */ - sb_add(m, "PROTO=ESP "); - - if (ntohs(ih->frag_off) & IP_OFFSET) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - eh = skb_header_pointer(skb, iphoff+ih->ihl*4, - sizeof(_esph), &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", - skb->len - iphoff - ih->ihl*4); - break; - } - - /* Length: 15 "SPI=0xF1234567 " */ - sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); - break; - } - /* Max length: 10 "PROTO 255 " */ - default: - sb_add(m, "PROTO=%u ", ih->protocol); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!iphoff && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); - - /* Proto Max log string length */ - /* IP: 40+46+6+11+127 = 230 */ - /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ - /* UDP: 10+max(25,20) = 35 */ - /* UDPLITE: 14+max(25,20) = 39 */ - /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ - /* ESP: 10+max(25)+15 = 50 */ - /* AH: 9+max(25)+15 = 49 */ - /* unknown: 10 */ - - /* (ICMP allows recursion one level deep) */ - /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ - /* maxlen = 230+ 91 + 230 + 252 = 803 */ -} - -static void dump_ipv4_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & XT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int i; - - sb_add(m, "%02x", *p++); - for (i = 1; i < dev->hard_header_len; i++, p++) - sb_add(m, ":%02x", *p); - } - sb_add(m, " "); -} - -static void -log_packet_common(struct sbuff *m, - u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, - prefix, - in ? in->name : "", - out ? out->name : ""); -#ifdef CONFIG_BRIDGE_NETFILTER - if (skb->nf_bridge) { - const struct net_device *physindev; - const struct net_device *physoutdev; - - physindev = skb->nf_bridge->physindev; - if (physindev && in != physindev) - sb_add(m, "PHYSIN=%s ", physindev->name); - physoutdev = skb->nf_bridge->physoutdev; - if (physoutdev && out != physoutdev) - sb_add(m, "PHYSOUT=%s ", physoutdev->name); - } -#endif -} - - -static void -ipt_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); - - if (in != NULL) - dump_ipv4_mac_header(m, loginfo, skb); - - dump_ipv4_packet(m, loginfo, skb, 0); - - sb_close(m); -} - -#if IS_ENABLED(CONFIG_IPV6) -/* One level of recursion won't kill us */ -static void dump_ipv6_packet(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb, unsigned int ip6hoff, - int recurse) -{ - u_int8_t currenthdr; - int fragment; - struct ipv6hdr _ip6h; - const struct ipv6hdr *ih; - unsigned int ptr; - unsigned int hdrlen = 0; - unsigned int logflags; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - else - logflags = NF_LOG_MASK; - - ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); - if (ih == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ - sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); - - /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ - sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", - ntohs(ih->payload_len) + sizeof(struct ipv6hdr), - (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, - ih->hop_limit, - (ntohl(*(__be32 *)ih) & 0x000fffff)); - - fragment = 0; - ptr = ip6hoff + sizeof(struct ipv6hdr); - currenthdr = ih->nexthdr; - while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { - struct ipv6_opt_hdr _hdr; - const struct ipv6_opt_hdr *hp; - - hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); - if (hp == NULL) { - sb_add(m, "TRUNCATED"); - return; - } - - /* Max length: 48 "OPT (...) " */ - if (logflags & XT_LOG_IPOPT) - sb_add(m, "OPT ( "); - - switch (currenthdr) { - case IPPROTO_FRAGMENT: { - struct frag_hdr _fhdr; - const struct frag_hdr *fh; - - sb_add(m, "FRAG:"); - fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), - &_fhdr); - if (fh == NULL) { - sb_add(m, "TRUNCATED "); - return; - } - - /* Max length: 6 "65535 " */ - sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); - - /* Max length: 11 "INCOMPLETE " */ - if (fh->frag_off & htons(0x0001)) - sb_add(m, "INCOMPLETE "); - - sb_add(m, "ID:%08x ", ntohl(fh->identification)); - - if (ntohs(fh->frag_off) & 0xFFF8) - fragment = 1; - - hdrlen = 8; - - break; - } - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - case IPPROTO_HOPOPTS: - if (fragment) { - if (logflags & XT_LOG_IPOPT) - sb_add(m, ")"); - return; - } - hdrlen = ipv6_optlen(hp); - break; - /* Max Length */ - case IPPROTO_AH: - if (logflags & XT_LOG_IPOPT) { - struct ip_auth_hdr _ahdr; - const struct ip_auth_hdr *ah; - - /* Max length: 3 "AH " */ - sb_add(m, "AH "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), - &_ahdr); - if (ah == NULL) { - /* - * Max length: 26 "INCOMPLETE [65535 - * bytes] )" - */ - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 15 "SPI=0xF1234567 */ - sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); - - } - - hdrlen = (hp->hdrlen+2)<<2; - break; - case IPPROTO_ESP: - if (logflags & XT_LOG_IPOPT) { - struct ip_esp_hdr _esph; - const struct ip_esp_hdr *eh; - - /* Max length: 4 "ESP " */ - sb_add(m, "ESP "); - - if (fragment) { - sb_add(m, ")"); - return; - } - - /* - * Max length: 26 "INCOMPLETE [65535 bytes] )" - */ - eh = skb_header_pointer(skb, ptr, sizeof(_esph), - &_esph); - if (eh == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] )", - skb->len - ptr); - return; - } - - /* Length: 16 "SPI=0xF1234567 )" */ - sb_add(m, "SPI=0x%x )", ntohl(eh->spi)); - - } - return; - default: - /* Max length: 20 "Unknown Ext Hdr 255" */ - sb_add(m, "Unknown Ext Hdr %u", currenthdr); - return; - } - if (logflags & XT_LOG_IPOPT) - sb_add(m, ") "); - - currenthdr = hp->nexthdr; - ptr += hdrlen; - } - - switch (currenthdr) { - case IPPROTO_TCP: - if (dump_tcp_header(m, skb, currenthdr, fragment, ptr, - logflags)) - return; - case IPPROTO_UDP: - case IPPROTO_UDPLITE: - if (dump_udp_header(m, skb, currenthdr, fragment, ptr)) - return; - case IPPROTO_ICMPV6: { - struct icmp6hdr _icmp6h; - const struct icmp6hdr *ic; - - /* Max length: 13 "PROTO=ICMPv6 " */ - sb_add(m, "PROTO=ICMPv6 "); - - if (fragment) - break; - - /* Max length: 25 "INCOMPLETE [65535 bytes] " */ - ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); - if (ic == NULL) { - sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); - return; - } - - /* Max length: 18 "TYPE=255 CODE=255 " */ - sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); - - switch (ic->icmp6_type) { - case ICMPV6_ECHO_REQUEST: - case ICMPV6_ECHO_REPLY: - /* Max length: 19 "ID=65535 SEQ=65535 " */ - sb_add(m, "ID=%u SEQ=%u ", - ntohs(ic->icmp6_identifier), - ntohs(ic->icmp6_sequence)); - break; - case ICMPV6_MGM_QUERY: - case ICMPV6_MGM_REPORT: - case ICMPV6_MGM_REDUCTION: - break; - - case ICMPV6_PARAMPROB: - /* Max length: 17 "POINTER=ffffffff " */ - sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); - /* Fall through */ - case ICMPV6_DEST_UNREACH: - case ICMPV6_PKT_TOOBIG: - case ICMPV6_TIME_EXCEED: - /* Max length: 3+maxlen */ - if (recurse) { - sb_add(m, "["); - dump_ipv6_packet(m, info, skb, - ptr + sizeof(_icmp6h), 0); - sb_add(m, "] "); - } - - /* Max length: 10 "MTU=65535 " */ - if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) - sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); - } - break; - } - /* Max length: 10 "PROTO=255 " */ - default: - sb_add(m, "PROTO=%u ", currenthdr); - } - - /* Max length: 15 "UID=4294967295 " */ - if ((logflags & XT_LOG_UID) && recurse && skb->sk) { - read_lock_bh(&skb->sk->sk_callback_lock); - if (skb->sk->sk_socket && skb->sk->sk_socket->file) - sb_add(m, "UID=%u GID=%u ", - skb->sk->sk_socket->file->f_cred->fsuid, - skb->sk->sk_socket->file->f_cred->fsgid); - read_unlock_bh(&skb->sk->sk_callback_lock); - } - - /* Max length: 16 "MARK=0xFFFFFFFF " */ - if (!recurse && skb->mark) - sb_add(m, "MARK=0x%x ", skb->mark); -} - -static void dump_ipv6_mac_header(struct sbuff *m, - const struct nf_loginfo *info, - const struct sk_buff *skb) -{ - struct net_device *dev = skb->dev; - unsigned int logflags = 0; - - if (info->type == NF_LOG_TYPE_LOG) - logflags = info->u.log.logflags; - - if (!(logflags & XT_LOG_MACDECODE)) - goto fallback; - - switch (dev->type) { - case ARPHRD_ETHER: - sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", - eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, - ntohs(eth_hdr(skb)->h_proto)); - return; - default: - break; - } - -fallback: - sb_add(m, "MAC="); - if (dev->hard_header_len && - skb->mac_header != skb->network_header) { - const unsigned char *p = skb_mac_header(skb); - unsigned int len = dev->hard_header_len; - unsigned int i; - - if (dev->type == ARPHRD_SIT) { - p -= ETH_HLEN; - - if (p < skb->head) - p = NULL; - } - - if (p != NULL) { - sb_add(m, "%02x", *p++); - for (i = 1; i < len; i++) - sb_add(m, ":%02x", *p++); - } - sb_add(m, " "); - - if (dev->type == ARPHRD_SIT) { - const struct iphdr *iph = - (struct iphdr *)skb_mac_header(skb); - sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, - &iph->daddr); - } - } else - sb_add(m, " "); -} - -static void -ip6t_log_packet(u_int8_t pf, - unsigned int hooknum, - const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) -{ - struct sbuff *m = sb_open(); - - if (!loginfo) - loginfo = &default_loginfo; - - log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); - - if (in != NULL) - dump_ipv6_mac_header(m, loginfo, skb); - - dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); - - sb_close(m); -} -#endif - -static unsigned int -log_tg(struct sk_buff *skb, const struct xt_action_param *par) -{ - const struct xt_log_info *loginfo = par->targinfo; - struct nf_loginfo li; - - li.type = NF_LOG_TYPE_LOG; - li.u.log.level = loginfo->level; - li.u.log.logflags = loginfo->logflags; - - if (par->family == NFPROTO_IPV4) - ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); -#if IS_ENABLED(CONFIG_IPV6) - else if (par->family == NFPROTO_IPV6) - ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, - par->out, &li, loginfo->prefix); -#endif - else - WARN_ON_ONCE(1); - - return XT_CONTINUE; -} - -static int log_tg_check(const struct xt_tgchk_param *par) -{ - const struct xt_log_info *loginfo = par->targinfo; - - if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) - return -EINVAL; - - if (loginfo->level >= 8) { - pr_debug("level %u >= 8\n", loginfo->level); - return -EINVAL; - } - - if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { - pr_debug("prefix is not null-terminated\n"); - return -EINVAL; - } - - return 0; -} - -static struct xt_target log_tg_regs[] __read_mostly = { - { - .name = "LOG", - .family = NFPROTO_IPV4, - .target = log_tg, - .targetsize = sizeof(struct xt_log_info), - .checkentry = log_tg_check, - .me = THIS_MODULE, - }, -#if IS_ENABLED(CONFIG_IPV6) - { - .name = "LOG", - .family = NFPROTO_IPV6, - .target = log_tg, - .targetsize = sizeof(struct xt_log_info), - .checkentry = log_tg_check, - .me = THIS_MODULE, - }, -#endif -}; - -static struct nf_logger ipt_log_logger __read_mostly = { - .name = "ipt_LOG", - .logfn = &ipt_log_packet, - .me = THIS_MODULE, -}; - -#if IS_ENABLED(CONFIG_IPV6) -static struct nf_logger ip6t_log_logger __read_mostly = { - .name = "ip6t_LOG", - .logfn = &ip6t_log_packet, - .me = THIS_MODULE, -}; -#endif - -static int __init log_tg_init(void) -{ - int ret; - - ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); - if (ret < 0) - return ret; - - nf_log_register(NFPROTO_IPV4, &ipt_log_logger); -#if IS_ENABLED(CONFIG_IPV6) - nf_log_register(NFPROTO_IPV6, &ip6t_log_logger); -#endif - return 0; -} - -static void __exit log_tg_exit(void) -{ - nf_log_unregister(&ipt_log_logger); -#if IS_ENABLED(CONFIG_IPV6) - nf_log_unregister(&ip6t_log_logger); -#endif - xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); -} - -module_init(log_tg_init); -module_exit(log_tg_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); -MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); -MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); -MODULE_ALIAS("ipt_LOG"); -MODULE_ALIAS("ip6t_LOG"); diff --git a/net/netfilter/xt_LOG_core.c b/net/netfilter/xt_LOG_core.c new file mode 100644 index 0000000..d69e95b --- /dev/null +++ b/net/netfilter/xt_LOG_core.c @@ -0,0 +1,1108 @@ +/* + * This is a module which is used for logging packets. + */ + +/* (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/init.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/ip.h> +#include <linux/ctype.h> +#include <net/ipv6.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/tcp.h> +#include <net/route.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_LOG.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <net/netfilter/nf_log.h> +#include <net/netfilter/xt_log.h> +#include <net/netfilter/xt_log_ring.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); +MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); +MODULE_AUTHOR("Richard Weinberger <richard@nod.at>"); +MODULE_DESCRIPTION("Xtables: IPv4/IPv6 packet logging"); +MODULE_ALIAS("ipt_LOG"); +MODULE_ALIAS("ip6t_LOG"); + +static struct nf_loginfo default_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 5, + .logflags = NF_LOG_MASK, + }, + }, +}; + +static int dump_udp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset) +{ + struct udphdr _udph; + const struct udphdr *uh; + + if (proto == IPPROTO_UDP) + /* Max length: 10 "PROTO=UDP " */ + sb_add(m, "PROTO=UDP "); + else /* Max length: 14 "PROTO=UDPLITE " */ + sb_add(m, "PROTO=UDPLITE "); + + if (fragment) + goto out; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u LEN=%u ", ntohs(uh->source), ntohs(uh->dest), + ntohs(uh->len)); + +out: + return 0; +} + +static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb, + u8 proto, int fragment, unsigned int offset, + unsigned int logflags) +{ + struct tcphdr _tcph; + const struct tcphdr *th; + + /* Max length: 10 "PROTO=TCP " */ + sb_add(m, "PROTO=TCP "); + + if (fragment) + return 0; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - offset); + return 1; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + sb_add(m, "SPT=%u DPT=%u ", ntohs(th->source), ntohs(th->dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + if (logflags & XT_LOG_TCPSEQ) + sb_add(m, "SEQ=%u ACK=%u ", ntohl(th->seq), ntohl(th->ack_seq)); + + /* Max length: 13 "WINDOW=65535 " */ + sb_add(m, "WINDOW=%u ", ntohs(th->window)); + /* Max length: 9 "RES=0x3C " */ + sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & + TCP_RESERVED_BITS) >> 22)); + /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ + if (th->cwr) + sb_add(m, "CWR "); + if (th->ece) + sb_add(m, "ECE "); + if (th->urg) + sb_add(m, "URG "); + if (th->ack) + sb_add(m, "ACK "); + if (th->psh) + sb_add(m, "PSH "); + if (th->rst) + sb_add(m, "RST "); + if (th->syn) + sb_add(m, "SYN "); + if (th->fin) + sb_add(m, "FIN "); + /* Max length: 11 "URGP=65535 " */ + sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); + + if ((logflags & XT_LOG_TCPOPT) && th->doff*4 > sizeof(struct tcphdr)) { + u_int8_t _opt[60 - sizeof(struct tcphdr)]; + const u_int8_t *op; + unsigned int i; + unsigned int optsize = th->doff*4 - sizeof(struct tcphdr); + + op = skb_header_pointer(skb, offset + sizeof(struct tcphdr), + optsize, _opt); + if (op == NULL) { + sb_add(m, "OPT (TRUNCATED)"); + return 1; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + + sb_add(m, ") "); + } + + return 0; +} + +/* One level of recursion won't kill us */ +static void dump_ipv4_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, + unsigned int iphoff) +{ + struct iphdr _iph; + const struct iphdr *ih; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, iphoff, sizeof(_iph), &_iph); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + sb_add(m, "SRC=%pI4 DST=%pI4 ", + &ih->saddr, &ih->daddr); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + sb_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK, + ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(ih->frag_off) & IP_CE) + sb_add(m, "CE "); + if (ntohs(ih->frag_off) & IP_DF) + sb_add(m, "DF "); + if (ntohs(ih->frag_off) & IP_MF) + sb_add(m, "MF "); + + /* Max length: 11 "FRAG:65535 " */ + if (ntohs(ih->frag_off) & IP_OFFSET) + sb_add(m, "FRAG:%u ", ntohs(ih->frag_off) & IP_OFFSET); + + if ((logflags & XT_LOG_IPOPT) && + ih->ihl * 4 > sizeof(struct iphdr)) { + const unsigned char *op; + unsigned char _opt[4 * 15 - sizeof(struct iphdr)]; + unsigned int i, optsize; + + optsize = ih->ihl * 4 - sizeof(struct iphdr); + op = skb_header_pointer(skb, iphoff+sizeof(_iph), + optsize, _opt); + if (op == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + sb_add(m, "OPT ("); + for (i = 0; i < optsize; i++) + sb_add(m, "%02X", op[i]); + sb_add(m, ") "); + } + + switch (ih->protocol) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4, logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, ih->protocol, + ntohs(ih->frag_off) & IP_OFFSET, + iphoff+ih->ihl*4)) + return; + case IPPROTO_ICMP: { + struct icmphdr _icmph; + const struct icmphdr *ich; + static const size_t required_len[NR_ICMP_TYPES+1] + = { [ICMP_ECHOREPLY] = 4, + [ICMP_DEST_UNREACH] + = 8 + sizeof(struct iphdr), + [ICMP_SOURCE_QUENCH] + = 8 + sizeof(struct iphdr), + [ICMP_REDIRECT] + = 8 + sizeof(struct iphdr), + [ICMP_ECHO] = 4, + [ICMP_TIME_EXCEEDED] + = 8 + sizeof(struct iphdr), + [ICMP_PARAMETERPROB] + = 8 + sizeof(struct iphdr), + [ICMP_TIMESTAMP] = 20, + [ICMP_TIMESTAMPREPLY] = 20, + [ICMP_ADDRESS] = 12, + [ICMP_ADDRESSREPLY] = 12 }; + + /* Max length: 11 "PROTO=ICMP " */ + sb_add(m, "PROTO=ICMP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ich = skb_header_pointer(skb, iphoff + ih->ihl * 4, + sizeof(_icmph), &_icmph); + if (ich == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ich->type, ich->code); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (ich->type <= NR_ICMP_TYPES && + required_len[ich->type] && + skb->len-iphoff-ih->ihl*4 < required_len[ich->type]) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + switch (ich->type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ich->un.echo.id), + ntohs(ich->un.echo.sequence)); + break; + + case ICMP_PARAMETERPROB: + /* Max length: 14 "PARAMETER=255 " */ + sb_add(m, "PARAMETER=%u ", + ntohl(ich->un.gateway) >> 24); + break; + case ICMP_REDIRECT: + /* Max length: 24 "GATEWAY=255.255.255.255 " */ + sb_add(m, "GATEWAY=%pI4 ", &ich->un.gateway); + /* Fall through */ + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + /* Max length: 3+maxlen */ + if (!iphoff) { /* Only recurse once. */ + sb_add(m, "["); + dump_ipv4_packet(m, info, skb, + iphoff + ih->ihl*4+sizeof(_icmph)); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ich->type == ICMP_DEST_UNREACH && + ich->code == ICMP_FRAG_NEEDED) + sb_add(m, "MTU=%u ", ntohs(ich->un.frag.mtu)); + } + break; + } + /* Max Length */ + case IPPROTO_AH: { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 9 "PROTO=AH " */ + sb_add(m, "PROTO=AH "); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ah = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_ahdr), &_ahdr); + if (ah == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + break; + } + case IPPROTO_ESP: { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 10 "PROTO=ESP " */ + sb_add(m, "PROTO=ESP "); + + if (ntohs(ih->frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + eh = skb_header_pointer(skb, iphoff+ih->ihl*4, + sizeof(_esph), &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", + skb->len - iphoff - ih->ihl*4); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + sb_add(m, "SPI=0x%x ", ntohl(eh->spi)); + break; + } + /* Max length: 10 "PROTO 255 " */ + default: + sb_add(m, "PROTO=%u ", ih->protocol); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!iphoff && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ + /* UDP: 10+max(25,20) = 35 */ + /* UDPLITE: 14+max(25,20) = 39 */ + /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ + /* ESP: 10+max(25)+15 = 50 */ + /* AH: 9+max(25)+15 = 49 */ + /* unknown: 10 */ + + /* (ICMP allows recursion one level deep) */ + /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ + /* maxlen = 230+ 91 + 230 + 252 = 803 */ +} + +static void dump_ipv4_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int i; + + sb_add(m, "%02x", *p++); + for (i = 1; i < dev->hard_header_len; i++, p++) + sb_add(m, ":%02x", *p); + } + sb_add(m, " "); +} + +static void +log_packet_common(struct sbuff *m, + u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + if (loginfo->type == NF_LOG_TYPE_LOG) + sb_add(m, "<%d>", loginfo->u.log.level); + + if (loginfo->u.log.logflags & XT_LOG_ADD_TIMESTAMP) { + static struct timespec tv; + unsigned int msec; + + getnstimeofday(&tv); + msec = tv.tv_nsec; + do_div(msec, 1000000); + sb_add(m, "TIMESTAMP=%li.%03li ", tv.tv_sec, msec); + } + + sb_add(m, "%sIN=%s OUT=%s ", prefix, in ? in->name : "", + out ? out->name : ""); +#ifdef CONFIG_BRIDGE_NETFILTER + if (skb->nf_bridge) { + const struct net_device *physindev; + const struct net_device *physoutdev; + + physindev = skb->nf_bridge->physindev; + if (physindev && in != physindev) + sb_add(m, "PHYSIN=%s ", physindev->name); + physoutdev = skb->nf_bridge->physoutdev; + if (physoutdev && out != physoutdev) + sb_add(m, "PHYSOUT=%s ", physoutdev->name); + } +#endif +} + + +static struct sbuff *ipt_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv4_mac_header(m, loginfo, skb); + + dump_ipv4_packet(m, loginfo, skb, 0); + + return m; +} + +static void ipt_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ipt_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + + sb_close(m); +} + +#if IS_ENABLED(CONFIG_IPV6) +/* One level of recursion won't kill us */ +static void dump_ipv6_packet(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb, unsigned int ip6hoff, + int recurse) +{ + u_int8_t currenthdr; + int fragment; + struct ipv6hdr _ip6h; + const struct ipv6hdr *ih; + unsigned int ptr; + unsigned int hdrlen = 0; + unsigned int logflags; + + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) + logflags = info->u.log.logflags; + else + logflags = NF_LOG_MASK; + + ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); + if (ih == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ + sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); + + /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ + sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", + ntohs(ih->payload_len) + sizeof(struct ipv6hdr), + (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, + ih->hop_limit, + (ntohl(*(__be32 *)ih) & 0x000fffff)); + + fragment = 0; + ptr = ip6hoff + sizeof(struct ipv6hdr); + currenthdr = ih->nexthdr; + while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { + struct ipv6_opt_hdr _hdr; + const struct ipv6_opt_hdr *hp; + + hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); + if (hp == NULL) { + sb_add(m, "TRUNCATED"); + return; + } + + /* Max length: 48 "OPT (...) " */ + if (logflags & XT_LOG_IPOPT) + sb_add(m, "OPT ( "); + + switch (currenthdr) { + case IPPROTO_FRAGMENT: { + struct frag_hdr _fhdr; + const struct frag_hdr *fh; + + sb_add(m, "FRAG:"); + fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), + &_fhdr); + if (fh == NULL) { + sb_add(m, "TRUNCATED "); + return; + } + + /* Max length: 6 "65535 " */ + sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); + + /* Max length: 11 "INCOMPLETE " */ + if (fh->frag_off & htons(0x0001)) + sb_add(m, "INCOMPLETE "); + + sb_add(m, "ID:%08x ", ntohl(fh->identification)); + + if (ntohs(fh->frag_off) & 0xFFF8) + fragment = 1; + + hdrlen = 8; + + break; + } + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_HOPOPTS: + if (fragment) { + if (logflags & XT_LOG_IPOPT) + sb_add(m, ")"); + return; + } + hdrlen = ipv6_optlen(hp); + break; + /* Max Length */ + case IPPROTO_AH: + if (logflags & XT_LOG_IPOPT) { + struct ip_auth_hdr _ahdr; + const struct ip_auth_hdr *ah; + + /* Max length: 3 "AH " */ + sb_add(m, "AH "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), + &_ahdr); + if (ah == NULL) { + /* + * Max length: 26 "INCOMPLETE [65535 + * bytes] )" + */ + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 15 "SPI=0xF1234567 */ + sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); + + } + + hdrlen = (hp->hdrlen+2)<<2; + break; + case IPPROTO_ESP: + if (logflags & XT_LOG_IPOPT) { + struct ip_esp_hdr _esph; + const struct ip_esp_hdr *eh; + + /* Max length: 4 "ESP " */ + sb_add(m, "ESP "); + + if (fragment) { + sb_add(m, ")"); + return; + } + + /* + * Max length: 26 "INCOMPLETE [65535 bytes] )" + */ + eh = skb_header_pointer(skb, ptr, sizeof(_esph), + &_esph); + if (eh == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] )", + skb->len - ptr); + return; + } + + /* Length: 16 "SPI=0xF1234567 )" */ + sb_add(m, "SPI=0x%x )", ntohl(eh->spi)); + + } + return; + default: + /* Max length: 20 "Unknown Ext Hdr 255" */ + sb_add(m, "Unknown Ext Hdr %u", currenthdr); + return; + } + if (logflags & XT_LOG_IPOPT) + sb_add(m, ") "); + + currenthdr = hp->nexthdr; + ptr += hdrlen; + } + + switch (currenthdr) { + case IPPROTO_TCP: + if (dump_tcp_header(m, skb, currenthdr, fragment, ptr, + logflags)) + return; + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + if (dump_udp_header(m, skb, currenthdr, fragment, ptr)) + return; + case IPPROTO_ICMPV6: { + struct icmp6hdr _icmp6h; + const struct icmp6hdr *ic; + + /* Max length: 13 "PROTO=ICMPv6 " */ + sb_add(m, "PROTO=ICMPv6 "); + + if (fragment) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); + if (ic == NULL) { + sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); + return; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); + + switch (ic->icmp6_type) { + case ICMPV6_ECHO_REQUEST: + case ICMPV6_ECHO_REPLY: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + sb_add(m, "ID=%u SEQ=%u ", + ntohs(ic->icmp6_identifier), + ntohs(ic->icmp6_sequence)); + break; + case ICMPV6_MGM_QUERY: + case ICMPV6_MGM_REPORT: + case ICMPV6_MGM_REDUCTION: + break; + + case ICMPV6_PARAMPROB: + /* Max length: 17 "POINTER=ffffffff " */ + sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); + /* Fall through */ + case ICMPV6_DEST_UNREACH: + case ICMPV6_PKT_TOOBIG: + case ICMPV6_TIME_EXCEED: + /* Max length: 3+maxlen */ + if (recurse) { + sb_add(m, "["); + dump_ipv6_packet(m, info, skb, + ptr + sizeof(_icmp6h), 0); + sb_add(m, "] "); + } + + /* Max length: 10 "MTU=65535 " */ + if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) + sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); + } + break; + } + /* Max length: 10 "PROTO=255 " */ + default: + sb_add(m, "PROTO=%u ", currenthdr); + } + + /* Max length: 15 "UID=4294967295 " */ + if ((logflags & XT_LOG_UID) && recurse && skb->sk) { + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket && skb->sk->sk_socket->file) + sb_add(m, "UID=%u GID=%u ", + skb->sk->sk_socket->file->f_cred->fsuid, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + } + + /* Max length: 16 "MARK=0xFFFFFFFF " */ + if (!recurse && skb->mark) + sb_add(m, "MARK=0x%x ", skb->mark); +} + +static void dump_ipv6_mac_header(struct sbuff *m, + const struct nf_loginfo *info, + const struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + unsigned int logflags = 0; + + if (info->type == NF_LOG_TYPE_LOG || info->type == NF_LOG_TYPE_RING) + logflags = info->u.log.logflags; + + if (!(logflags & XT_LOG_MACDECODE)) + goto fallback; + + switch (dev->type) { + case ARPHRD_ETHER: + sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", + eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, + ntohs(eth_hdr(skb)->h_proto)); + return; + default: + break; + } + +fallback: + sb_add(m, "MAC="); + if (dev->hard_header_len && + skb->mac_header != skb->network_header) { + const unsigned char *p = skb_mac_header(skb); + unsigned int len = dev->hard_header_len; + unsigned int i; + + if (dev->type == ARPHRD_SIT) { + p -= ETH_HLEN; + + if (p < skb->head) + p = NULL; + } + + if (p != NULL) { + sb_add(m, "%02x", *p++); + for (i = 1; i < len; i++) + sb_add(m, ":%02x", *p++); + } + sb_add(m, " "); + + if (dev->type == ARPHRD_SIT) { + const struct iphdr *iph = + (struct iphdr *)skb_mac_header(skb); + sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, + &iph->daddr); + } + } else + sb_add(m, " "); +} + +static struct sbuff *ip6t_log_packet(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = sb_open(); + + if (!loginfo) + loginfo = &default_loginfo; + + log_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); + + if (in != NULL) + dump_ipv6_mac_header(m, loginfo, skb); + + dump_ipv6_packet(m, loginfo, skb, skb_network_offset(skb), 1); + + return m; +} + +static void ip6t_log_packet_logger(u_int8_t pf, + unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) +{ + struct sbuff *m = ip6t_log_packet(pf, hooknum, skb, in, out, loginfo, prefix); + + sb_close(m); +} +#endif + +static unsigned int +log_tg(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + struct nf_loginfo li; + struct sbuff *m; + + li.type = NF_LOG_TYPE_LOG; + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else { + WARN_ON_ONCE(1); + goto out; + } + + sb_close(m); + +out: + return XT_CONTINUE; +} + +static int log_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_log_info *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + return 0; +} + +static unsigned int +log_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct nf_loginfo li; + struct sbuff *m; + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) + li.type = NF_LOG_TYPE_RING; + else +#endif + li.type = NF_LOG_TYPE_LOG; + + li.u.log.level = loginfo->level; + li.u.log.logflags = loginfo->logflags; + + if (par->family == NFPROTO_IPV4) + m = ipt_log_packet(NFPROTO_IPV4, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#if IS_ENABLED(CONFIG_IPV6) + else if (par->family == NFPROTO_IPV6) + m = ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, + par->out, &li, loginfo->prefix); +#endif + else { + WARN_ON_ONCE(1); + goto out; + } + +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + if (loginfo->ring_size) { + sb_add(m, "\n"); + xt_LOG_ring_add_record(loginfo->rctx, m->buf, m->count); + __sb_close(m, 0); + } + else +#endif + sb_close(m); + +out: + return XT_CONTINUE; +} + +static int log_tg_check_v1(const struct xt_tgchk_param *par) +{ + struct xt_log_info_v1 *loginfo = par->targinfo; + + if (par->family != NFPROTO_IPV4 && par->family != NFPROTO_IPV6) + return -EINVAL; + + if (loginfo->level >= 8) { + pr_debug("level %u >= 8\n", loginfo->level); + return -EINVAL; + } + + if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { + pr_debug("prefix is not null-terminated\n"); + return -EINVAL; + } + + /* a non-empty ring_size indicates that we're using ring_buffer */ + if (loginfo->ring_size) { +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + int i; + struct xt_LOG_ring_ctx *rctx; + + if (loginfo->ring_name[sizeof(loginfo->ring_name)-1] != '\0') { + pr_debug("ring_name is not null-terminated\n"); + return -EINVAL; + } + + if (!loginfo->ring_name[0]) { + pr_debug("ring_name is empty\n"); + return -EINVAL; + } + + for (i = 0; i < strlen(loginfo->ring_name); i++) { + if (!isalnum(loginfo->ring_name[i])) { + pr_debug("ring_name contains " + "a non-alphanumeric character\n"); + return -EINVAL; + } + } + + rctx = xt_LOG_ring_find_ctx(loginfo->ring_name); + if (!rctx) { + rctx = xt_LOG_ring_new_ctx(loginfo->ring_name, + loginfo->ring_size); + if (IS_ERR(rctx)) + return PTR_ERR(rctx); + } + + xt_LOG_ring_get(rctx); + loginfo->rctx = rctx; +#else + pr_debug("Sorry, this kernel was built without CONFIG_NETFILTER_XT_TARGET_LOG_RING\n"); + return -EINVAL; +#endif + } + + return 0; +} + +static void log_tg_destroy_v1(const struct xt_tgdtor_param *par) +{ +#ifdef CONFIG_NETFILTER_XT_TARGET_LOG_RING + const struct xt_log_info_v1 *loginfo = par->targinfo; + struct xt_LOG_ring_ctx *rctx = loginfo->rctx; + + if (loginfo->ring_size) + xt_LOG_ring_put(rctx); +#endif +} + +static struct xt_target log_tg_regs[] __read_mostly = { + { + .name = "LOG", + .family = NFPROTO_IPV4, + .revision = 0, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, + { + .name = "LOG", + .family = NFPROTO_IPV4, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, +#if IS_ENABLED(CONFIG_IPV6) + { + .name = "LOG", + .family = NFPROTO_IPV6, + .revision = 0, + .target = log_tg, + .targetsize = sizeof(struct xt_log_info), + .checkentry = log_tg_check, + .me = THIS_MODULE, + }, + { + .name = "LOG", + .family = NFPROTO_IPV6, + .revision = 1, + .target = log_tg_v1, + .targetsize = sizeof(struct xt_log_info_v1), + .checkentry = log_tg_check_v1, + .destroy = log_tg_destroy_v1, + .me = THIS_MODULE, + }, +#endif +}; + +static struct nf_logger ipt_log_logger __read_mostly = { + .name = "ipt_LOG", + .logfn = &ipt_log_packet_logger, + .me = THIS_MODULE, +}; + +#if IS_ENABLED(CONFIG_IPV6) +static struct nf_logger ip6t_log_logger __read_mostly = { + .name = "ip6t_LOG", + .logfn = &ip6t_log_packet_logger, + .me = THIS_MODULE, +}; +#endif + +static int __init log_tg_init(void) +{ + int ret; + + ret = xt_LOG_ring_init(); + if (ret < 0) + return ret; + + ret = xt_register_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + if (ret < 0) + return ret; + + nf_log_register(NFPROTO_IPV4, &ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_register(NFPROTO_IPV6, &ip6t_log_logger); +#endif + return 0; +} + +static void __exit log_tg_exit(void) +{ + nf_log_unregister(&ipt_log_logger); +#if IS_ENABLED(CONFIG_IPV6) + nf_log_unregister(&ip6t_log_logger); +#endif + xt_unregister_targets(log_tg_regs, ARRAY_SIZE(log_tg_regs)); + xt_LOG_ring_exit(); +} + +module_init(log_tg_init); +module_exit(log_tg_exit); diff --git a/net/netfilter/xt_LOG_ring.c b/net/netfilter/xt_LOG_ring.c new file mode 100644 index 0000000..b06081c --- /dev/null +++ b/net/netfilter/xt_LOG_ring.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2012 Richard Weinberger <richard@nod.at> + * + * 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 <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/ring_buffer.h> +#include <linux/cpu.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/atomic.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/netfilter.h> + +#define TARGETNAME_LEN 32 +#define ME "xt_LOG_ring" + +struct xt_LOG_ring_ctx { + char name[TARGETNAME_LEN]; + struct ring_buffer *buffer; + atomic_t pipe_in_use; + atomic_t refcnt; + + struct list_head list; +}; + +struct rlog_entry { + size_t count; + char msg[0]; +}; + +struct rlog_iter { + struct ring_buffer *buffer; + const char *buffer_name; + struct rlog_entry *ent; + + char print_buf[PAGE_SIZE]; + size_t print_buf_len; + size_t print_buf_pos; + + unsigned long lost_events; + int cpu; + + struct mutex lock; +}; + +static DEFINE_SPINLOCK(target_list_lock); +static LIST_HEAD(target_list); +static DECLARE_WAIT_QUEUE_HEAD(rlog_wait); +static struct proc_dir_entry *prlog; + +static void wakeup_work_handler(struct work_struct *work) +{ + wake_up(&rlog_wait); +} + +static DECLARE_WORK(wakeup_work, wakeup_work_handler); + +static void rlog_wake_up(void) +{ + schedule_work(&wakeup_work); +} + +int xt_LOG_ring_add_record(struct xt_LOG_ring_ctx *rctx, const char *buf, unsigned int len) +{ + struct rlog_entry *entry; + struct ring_buffer_event *event; + + event = ring_buffer_lock_reserve(rctx->buffer, sizeof(*entry) + len); + if (!event) + return 1; + + entry = ring_buffer_event_data(event); + memcpy(entry->msg, buf, len); + entry->count = len; + + ring_buffer_unlock_commit(rctx->buffer, event); + rlog_wake_up(); + + return 0; +} + +static struct rlog_entry *peek_next_entry(struct rlog_iter *iter, int cpu, + unsigned long long *ts) +{ + struct ring_buffer_event *event; + + event = ring_buffer_peek(iter->buffer, cpu, ts, &iter->lost_events); + + if (event) + return ring_buffer_event_data(event); + + return NULL; +} + +static struct rlog_entry *find_next_entry(struct rlog_iter *iter) +{ + struct rlog_entry *ent, *next = NULL; + unsigned long long next_ts = 0, ts; + int cpu, next_cpu = -1; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (ring_buffer_empty_cpu(iter->buffer, cpu)) + continue; + + ent = peek_next_entry(iter, cpu, &ts); + + if (ent && (!next || ts < next_ts)) { + next = ent; + next_cpu = cpu; + next_ts = ts; + } + } + + iter->cpu = next_cpu; + + return next; +} + +static struct rlog_iter *find_next_entry_inc(struct rlog_iter *iter) +{ + iter->ent = find_next_entry(iter); + + if (iter->ent) + return iter; + + return NULL; +} + +static int buffer_empty(struct rlog_iter *iter) +{ + int cpu; + + for_each_buffer_cpu (iter->buffer, cpu) { + if (!ring_buffer_empty_cpu(iter->buffer, cpu)) + return 0; + } + + return 1; +} + +static ssize_t rlog_to_user(struct rlog_iter *iter, char __user *ubuf, + size_t cnt) +{ + int ret; + int len; + + if (!cnt) + goto out; + + len = iter->print_buf_len - iter->print_buf_pos; + if (len < 1) + return -EBUSY; + + if (cnt > len) + cnt = len; + + ret = copy_to_user(ubuf, iter->print_buf + iter->print_buf_pos, cnt); + if (ret == cnt) + return -EFAULT; + + cnt -= ret; + iter->print_buf_pos += cnt; + +out: + return cnt; +} + +static int rlog_open_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + int ret = 0; + + /* only one consuming reader is allowed */ + if (atomic_cmpxchg(&tgt->pipe_in_use, 0, 1)) { + ret = -EBUSY; + goto out; + } + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) { + ret = -ENOMEM; + goto out; + } + + mutex_init(&iter->lock); + iter->buffer = tgt->buffer; + iter->buffer_name = tgt->name; + + file->private_data = iter; +out: + return ret; +} + +static unsigned int rlog_poll_pipe(struct file *file, poll_table *poll_table) +{ + struct rlog_iter *iter = file->private_data; + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + poll_wait(file, &rlog_wait, poll_table); + + if (!buffer_empty(iter)) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int rlog_release_pipe(struct inode *inode, struct file *file) +{ + struct rlog_iter *iter = file->private_data; + struct xt_LOG_ring_ctx *tgt = PDE(inode)->data; + + mutex_destroy(&iter->lock); + kfree(iter); + atomic_set(&tgt->pipe_in_use, 0); + + return 0; +} + +static void wait_pipe(struct rlog_iter *iter) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(&rlog_wait, &wait, TASK_INTERRUPTIBLE); + + if (buffer_empty(iter)) + schedule(); + + finish_wait(&rlog_wait, &wait); +} + +static int rlog_wait_pipe(struct file *file) +{ + struct rlog_iter *iter = file->private_data; + + while (buffer_empty(iter)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + mutex_unlock(&iter->lock); + + wait_pipe(iter); + + mutex_lock(&iter->lock); + + if (signal_pending(current)) + return -EINTR; + } + + return 1; +} + +static ssize_t rlog_read_pipe(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct rlog_iter *iter = file->private_data; + ssize_t ret; + + ret = rlog_to_user(iter, ubuf, cnt); + if (ret != -EBUSY) + goto out; + + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + mutex_lock(&iter->lock); +again: + ret = rlog_wait_pipe(file); + if (ret <= 0) + goto out_unlock; + + while (find_next_entry_inc(iter) != NULL) { + struct rlog_entry *ent; + ent = iter->ent; + + if (ent->count >= PAGE_SIZE - iter->print_buf_len) + break; + + memcpy(iter->print_buf + iter->print_buf_len, ent->msg, + ent->count); + iter->print_buf_len += ent->count; + + ring_buffer_consume(iter->buffer, iter->cpu, NULL, + &iter->lost_events); + if (iter->lost_events) + printk(KERN_WARNING ME ": Ring %s " + "lost %lu events\n", iter->buffer_name, + iter->lost_events); + + if (iter->print_buf_len >= cnt) + break; + } + + ret = rlog_to_user(iter, ubuf, cnt); + + if (iter->print_buf_pos >= iter->print_buf_len) { + iter->print_buf_pos = 0; + iter->print_buf_len = 0; + } + + if (ret == -EBUSY) + goto again; +out_unlock: + mutex_unlock(&iter->lock); +out: + return ret; +} + +static const struct file_operations rlog_pipe_fops = { + .open = rlog_open_pipe, + .poll = rlog_poll_pipe, + .read = rlog_read_pipe, + .release = rlog_release_pipe, + .llseek = no_llseek, +}; + +struct xt_LOG_ring_ctx *xt_LOG_ring_new_ctx(const char *name, size_t rb_size) +{ + struct xt_LOG_ring_ctx *new; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) { + new = ERR_PTR(-ENOMEM); + + goto out; + } + + new->buffer = ring_buffer_alloc(rb_size << 10, RB_FL_OVERWRITE); + if (!new->buffer) { + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + strlcpy(new->name, name, TARGETNAME_LEN); + + if (!proc_create_data(name, 0400, prlog, &rlog_pipe_fops, new)) { + ring_buffer_free(new->buffer); + kfree(new); + new = ERR_PTR(-ENOMEM); + + goto out; + } + + atomic_set(&new->pipe_in_use, 0); + atomic_set(&new->refcnt, 0); + + spin_lock(&target_list_lock); + list_add(&new->list, &target_list); + spin_unlock(&target_list_lock); +out: + return new; +} + +static void free_xt_LOG_ring_ctx(struct xt_LOG_ring_ctx *target) +{ + remove_proc_entry(target->name, prlog); + ring_buffer_free(target->buffer); + list_del(&target->list); + kfree(target); +} + +void xt_LOG_ring_get(struct xt_LOG_ring_ctx *ctx) +{ + atomic_inc(&ctx->refcnt); +} + +void xt_LOG_ring_put(struct xt_LOG_ring_ctx *ctx) +{ + if (atomic_dec_and_test(&ctx->refcnt)) + free_xt_LOG_ring_ctx(ctx); +} + +struct xt_LOG_ring_ctx *xt_LOG_ring_find_ctx(const char *name) +{ + struct list_head *e; + struct xt_LOG_ring_ctx *tmp, *victim = NULL; + + spin_lock(&target_list_lock); + + list_for_each(e, &target_list) { + tmp = list_entry(e, struct xt_LOG_ring_ctx, list); + if (strcmp(tmp->name, name) == 0) { + victim = tmp; + + goto out; + } + } + +out: + spin_unlock(&target_list_lock); + + return victim; +} + +void __exit xt_LOG_ring_exit(void) +{ + WARN_ON(!list_empty(&target_list)); + remove_proc_entry(ME, proc_net_netfilter); +} + +int __init xt_LOG_ring_init(void) +{ + prlog = proc_mkdir(ME, proc_net_netfilter); + if (!prlog) + return -ENOMEM; + + return 0; +} -- 1.7.7.3 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-05 0:14 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger @ 2012-02-08 10:29 ` Pablo Neira Ayuso 2012-02-08 10:40 ` Richard Weinberger 0 siblings, 1 reply; 21+ messages in thread From: Pablo Neira Ayuso @ 2012-02-08 10:29 UTC (permalink / raw) To: Richard Weinberger Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt On Sun, Feb 05, 2012 at 01:14:52AM +0100, Richard Weinberger wrote: > This patch introduces NETFILTER_XT_TARGET_LOG_RING. > It allows logging into various ring buffers which are > represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. > > Signed-off-by: Richard Weinberger <richard@nod.at> > --- > include/linux/netfilter/xt_LOG.h | 13 +- > include/net/netfilter/nf_log.h | 1 + > include/net/netfilter/xt_log_ring.h | 24 + > net/netfilter/Kconfig | 13 + > net/netfilter/Makefile | 2 + > net/netfilter/xt_LOG.c | 921 ----------------------------- > net/netfilter/xt_LOG_core.c | 1108 +++++++++++++++++++++++++++++++++++ > net/netfilter/xt_LOG_ring.c | 430 ++++++++++++++ Can you avoid this rename and splitting LOG into two files? I prefer having everything into the same file xt_LOG.c. Apart from that, this looks fine to me. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support 2012-02-08 10:29 ` Pablo Neira Ayuso @ 2012-02-08 10:40 ` Richard Weinberger 0 siblings, 0 replies; 21+ messages in thread From: Richard Weinberger @ 2012-02-08 10:40 UTC (permalink / raw) To: Pablo Neira Ayuso Cc: netfilter-devel, netdev, linux-kernel, eric.dumazet, jengelh, rostedt On 08.02.2012 11:29, Pablo Neira Ayuso wrote: > On Sun, Feb 05, 2012 at 01:14:52AM +0100, Richard Weinberger wrote: >> This patch introduces NETFILTER_XT_TARGET_LOG_RING. >> It allows logging into various ring buffers which are >> represented as pipe-like files in /proc/net/netfilter/xt_LOG_ring/. >> >> Signed-off-by: Richard Weinberger<richard@nod.at> >> --- >> include/linux/netfilter/xt_LOG.h | 13 +- >> include/net/netfilter/nf_log.h | 1 + >> include/net/netfilter/xt_log_ring.h | 24 + >> net/netfilter/Kconfig | 13 + >> net/netfilter/Makefile | 2 + >> net/netfilter/xt_LOG.c | 921 ----------------------------- >> net/netfilter/xt_LOG_core.c | 1108 +++++++++++++++++++++++++++++++++++ >> net/netfilter/xt_LOG_ring.c | 430 ++++++++++++++ > > Can you avoid this rename and splitting LOG into two files? I prefer > having everything into the same file xt_LOG.c. Sure, I'll merge it into one file. I made the split to keep xt_LOG_core.c clean. 99% of xt_LOG_ring.c has nothing to do with netfilter and is just ring_buffer and proc stuff. > Apart from that, this looks fine to me. :-) Thanks, //richard ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2012-02-28 22:55 UTC | newest] Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-02-10 22:10 [PATCH v5] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger 2012-02-10 22:10 ` [PATCH 1/2] iptables: Merge libip6t_LOG and libipt_LOG into libxt_LOG Richard Weinberger 2012-02-25 13:38 ` Pablo Neira Ayuso 2012-02-28 16:06 ` Richard Weinberger 2012-02-28 16:15 ` Dave Taht 2012-02-28 16:15 ` Dave Taht 2012-02-28 22:54 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 1/4] Netfilter: Merge ipt_LOG and ip6_LOG into xt_LOG Richard Weinberger 2012-02-13 23:56 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 2/2] iptables: xt_LOG: Add ring buffer support Richard Weinberger 2012-02-10 22:10 ` [PATCH 2/4] ring_buffer: Export for_each_buffer_cpu() Richard Weinberger 2012-02-14 0:05 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 3/4] xt_log: Make printk() in sb_close() optional Richard Weinberger 2012-02-14 0:12 ` Pablo Neira Ayuso 2012-02-10 22:10 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 2012-02-14 0:11 ` Pablo Neira Ayuso 2012-02-14 0:12 ` Richard Weinberger -- strict thread matches above, loose matches on Subject: below -- 2012-02-09 21:54 [PATCH v4] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger 2012-02-09 21:54 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 2012-02-05 0:14 [PATCH v3] Merge ipt_LOG and ip6t_LOG, add ring bufffer support Richard Weinberger 2012-02-05 0:14 ` [PATCH 4/4] Netfilter: xt_LOG: Implement ring buffer support Richard Weinberger 2012-02-08 10:29 ` Pablo Neira Ayuso 2012-02-08 10:40 ` Richard Weinberger
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.