当前位置:   article > 正文

Netfilter日志记录器_netfilter log

netfilter log

系统内的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;
  • 1
  • 2
  • 3
  • 4

参数nf_log_all_netns默认为零,仅记录init_net命名空间中的报文日志,参见以下函数nf_log_ip_packet。

# sysctl net.netfilter.nf_log_all_netns
net.netfilter.nf_log_all_netns = 0
  • 1
  • 2

支持的协议类型和日志类型定义如下:

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
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

日志缓存

如下函数分配日志缓存结构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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

紧急内存为以下定义的静态结构。

#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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

函数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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

函数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();          
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

日志记录器注册

如果协议类型等于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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

函数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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

日志打印

如果参数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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

IPv4日志记录器

协议类型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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

只有在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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

ARP日志记录器

协议类型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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

只有在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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

netdev类型

对于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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

日记记录函数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); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

内核版本 5.10

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/482201
推荐阅读
相关标签
  

闽ICP备14008679号