赞
踩
系统内的logger按照协议和类型保存在全局二维数组loggers中。
int sysctl_nf_log_all_netns __read_mostly;
EXPORT_SYMBOL(sysctl_nf_log_all_netns);
static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly;
参数nf_log_all_netns默认为零,仅记录init_net命名空间中的报文日志,参见以下函数nf_log_ip_packet。
# sysctl net.netfilter.nf_log_all_netns
net.netfilter.nf_log_all_netns = 0
支持的协议类型和日志类型定义如下:
enum { NFPROTO_UNSPEC = 0, NFPROTO_INET = 1, NFPROTO_IPV4 = 2, NFPROTO_ARP = 3, NFPROTO_NETDEV = 5, NFPROTO_BRIDGE = 7, NFPROTO_IPV6 = 10, NFPROTO_DECNET = 12, NFPROTO_NUMPROTO, }; enum nf_log_type { NF_LOG_TYPE_LOG = 0, NF_LOG_TYPE_ULOG, NF_LOG_TYPE_MAX };
如下函数分配日志缓存结构nf_log_buf,如果分配失败,使用紧急内存。
struct nf_log_buf *nf_log_buf_open(void)
{
struct nf_log_buf *m = kmalloc(sizeof(*m), GFP_ATOMIC);
if (unlikely(!m)) {
local_bh_disable();
do {
m = xchg(&emergency_ptr, NULL);
} while (!m);
}
m->count = 0;
return m;
紧急内存为以下定义的静态结构。
#define S_SIZE (1024 - (sizeof(unsigned int) + 1))
struct nf_log_buf {
unsigned int count;
char buf[S_SIZE + 1];
};
static struct nf_log_buf emergency, *emergency_ptr = &emergency;
函数nf_log_buf_add向日志缓存中添加内容,并且增加内容长度字段(count)。
__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...) { va_list args; int len; if (likely(m->count < S_SIZE)) { va_start(args, f); len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args); va_end(args); if (likely(m->count + len < S_SIZE)) { m->count += len; return 0; } } m->count = S_SIZE; printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n"); return -1;
函数nf_log_buf_close,打印日志缓存中的内容,之后,如果使用的不是紧急缓存,将其释放。
void nf_log_buf_close(struct nf_log_buf *m)
{
m->buf[m->count] = 0;
printk("%s\n", m->buf);
if (likely(m != &emergency))
kfree(m);
else {
emergency_ptr = m;
local_bh_enable();
}
如果协议类型等于NFPROTO_UNSPEC,将日志logger赋值给所有的协议类型。否则,将logger赋值给对应的协议。
int nf_log_register(u_int8_t pf, struct nf_logger *logger) { if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers)) return -EINVAL; mutex_lock(&nf_log_mutex); if (pf == NFPROTO_UNSPEC) { for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { if (rcu_access_pointer(loggers[i][logger->type])) { ret = -EEXIST; goto unlock; } } for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) rcu_assign_pointer(loggers[i][logger->type], logger); } else { if (rcu_access_pointer(loggers[pf][logger->type])) { ret = -EEXIST; goto unlock; } rcu_assign_pointer(loggers[pf][logger->type], logger);
函数nf_log_set设置每个命名空间的日志记录logger。协议类型不能等于NFPROTO_UNSPEC。
int nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
{
const struct nf_logger *log;
if (pf == NFPROTO_UNSPEC || pf >= ARRAY_SIZE(net->nf.nf_loggers))
return -EOPNOTSUPP;
mutex_lock(&nf_log_mutex);
log = nft_log_dereference(net->nf.nf_loggers[pf]);
if (log == NULL)
rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
如果参数loginfo不等于空,使用全局loggers中注册的日志记录器,否则,使用命名空间中设置日志记录器。
void nf_log_packet(struct net *net, 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 *fmt, ...) { va_list args; char prefix[NF_LOG_PREFIXLEN]; const struct nf_logger *logger; rcu_read_lock(); if (loginfo != NULL) logger = rcu_dereference(loggers[pf][loginfo->type]); else logger = rcu_dereference(net->nf.nf_loggers[pf]); if (logger) { va_start(args, fmt); vsnprintf(prefix, sizeof(prefix), fmt, args); va_end(args); logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix);
协议类型NFPROTO_IPV4注册了全局和命名空间日志记录器nf_log_ipv4,其输出函数为nf_log_ip_packet。
static struct nf_logger nf_ip_logger __read_mostly = { .name = "nf_log_ipv4", .type = NF_LOG_TYPE_LOG, .logfn = nf_log_ip_packet, .me = THIS_MODULE, }; static int __net_init nf_log_ipv4_net_init(struct net *net) { return nf_log_set(net, NFPROTO_IPV4, &nf_ip_logger); } static struct pernet_operations nf_log_ipv4_net_ops = { .init = nf_log_ipv4_net_init, .exit = nf_log_ipv4_net_exit, }; static int __init nf_log_ipv4_init(void) { ret = nf_log_register(NFPROTO_IPV4, &nf_ip_logger);
只有在sysctl_nf_log_all_netns为真的情况下,才会生成其它命名空间的报文日志。函数nf_log_buf_close输出日志缓存中的内容。
static void nf_log_ip_packet(struct net *net, 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 nf_log_buf *m; /* FIXME: Disabled from containers until syslog ns is supported */ if (!net_eq(net, &init_net) && !sysctl_nf_log_all_netns) return; m = nf_log_buf_open(); if (!loginfo) loginfo = &default_loginfo; nf_log_dump_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); if (in != NULL) dump_ipv4_mac_header(m, loginfo, skb); dump_ipv4_packet(net, m, loginfo, skb, 0); nf_log_buf_close(m);
协议类型NFPROTO_ARP注册了全局和命名空间的日志记录器nf_log_arp。
static struct nf_logger nf_arp_logger __read_mostly = { .name = "nf_log_arp", .type = NF_LOG_TYPE_LOG, .logfn = nf_log_arp_packet, .me = THIS_MODULE, }; static int __net_init nf_log_arp_net_init(struct net *net) { return nf_log_set(net, NFPROTO_ARP, &nf_arp_logger); } static struct pernet_operations nf_log_arp_net_ops = { .init = nf_log_arp_net_init, .exit = nf_log_arp_net_exit, }; static int __init nf_log_arp_init(void) { ret = nf_log_register(NFPROTO_ARP, &nf_arp_logger);
只有在sysctl_nf_log_all_netns为真的情况下,才会生成其它命名空间的报文日志。函数nf_log_buf_close输出日志缓存中的内容。
static void nf_log_arp_packet(struct net *net, 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 nf_log_buf *m; /* FIXME: Disabled from containers until syslog ns is supported */ if (!net_eq(net, &init_net) && !sysctl_nf_log_all_netns) return; m = nf_log_buf_open(); if (!loginfo) loginfo = &default_loginfo; nf_log_dump_packet_common(m, pf, hooknum, skb, in, out, loginfo, prefix); dump_arp_packet(m, loginfo, skb, 0); nf_log_buf_close(m);
对于NFPROTO_NETDEV类型,其依赖于nf_log_ipv4、nf_log_ipv6和nf_log_arp日志记录器
static struct nf_logger nf_netdev_logger __read_mostly = { .name = "nf_log_netdev", .type = NF_LOG_TYPE_LOG, .logfn = nf_log_netdev_packet, .me = THIS_MODULE, }; static int __net_init nf_log_netdev_net_init(struct net *net) { return nf_log_set(net, NFPROTO_NETDEV, &nf_netdev_logger); } static struct pernet_operations nf_log_netdev_net_ops = { .init = nf_log_netdev_net_init, .exit = nf_log_netdev_net_exit, }; static int __init nf_log_netdev_init(void) { /* Request to load the real packet loggers. */ nf_logger_request_module(NFPROTO_IPV4, NF_LOG_TYPE_LOG); nf_logger_request_module(NFPROTO_IPV6, NF_LOG_TYPE_LOG); nf_logger_request_module(NFPROTO_ARP, NF_LOG_TYPE_LOG); ret = register_pernet_subsys(&nf_log_netdev_net_ops); if (ret < 0) return ret; nf_log_register(NFPROTO_NETDEV, &nf_netdev_logger);
日记记录函数nf_log_netdev_packet实际上调用的为其它三个类型的日志记录函数。
static void nf_log_netdev_packet(struct net *net, 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) { nf_log_l2packet(net, pf, skb->protocol, hooknum, skb, in, out, loginfo, prefix); } void nf_log_l2packet(struct net *net, u_int8_t pf, __be16 protocol, 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) { switch (protocol) { case htons(ETH_P_IP): nf_log_packet(net, NFPROTO_IPV4, hooknum, skb, in, out, loginfo, "%s", prefix); break; case htons(ETH_P_IPV6): nf_log_packet(net, NFPROTO_IPV6, hooknum, skb, in, out, loginfo, "%s", prefix); break; case htons(ETH_P_ARP): case htons(ETH_P_RARP): nf_log_packet(net, NFPROTO_ARP, hooknum, skb, in, out, loginfo, "%s", prefix);
内核版本 5.10
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。