nf_log日志记录

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: [email protected]
来源:http://yfydz.cublog.cn

1. 前言
netfilter中和queue机制类似,对每种协议也可进行单独日志记录处理,每个协议可挂接一种日志记录处理结构,然后对外提供一个统一接口的日志记录函数,只要调用该函数就能找到对应协议的日志记录处理函数而记录日志信息。

以下内核代码版本2.6.17.11。

2. 登记/输出日志函数

/* include/linux/netfilter.h */
typedef void nf_logfn(unsigned int pf,
        unsigned int hooknum,
        const struct sk_buff *skb,
        const struct net_device *in,
        const struct net_device *out,
        const struct nf_loginfo *li,
        const char *prefix);
// 日志记录结构
struct nf_logger {
// 对应模块
 struct module *me;
// 日志记录函数
 nf_logfn  *logfn;
// 名称
 char  *name;
};

/* net/netfilter/nf_log.c */
// 协议相关的日志记录结构指针数组,每个协议一个结构指针
static struct nf_logger *nf_logging[NPROTO]; /* = NULL */

/* return EBUSY if somebody else is registered, EEXIST if the same logger
 * is registred, 0 on success. */
int nf_log_register(int pf, struct nf_logger *logger)
{
// 缺省认为已经被登记过了
 int ret = -EBUSY;
 if (pf >= NPROTO)
  return -EINVAL;
 /* Any setup of logging members must be done before
  * substituting pointer. */
 spin_lock(&nf_log_lock);
// 检查数组中协议对应的位置是否已经被占了
 if (!nf_logging[pf]) {
// 还没被占, 可成功登记
  rcu_assign_pointer(nf_logging[pf], logger);
  ret = 0;
 } else if (nf_logging[pf] == logger)
// 重复登记
  ret = -EEXIST;
 spin_unlock(&nf_log_lock);
 return ret;
}  
EXPORT_SYMBOL(nf_log_register);
有两种取消登记的方法, 分别是根据协议号和根据日志记录结构:
// 根据协议号取消登记
int nf_log_unregister_pf(int pf)
{
 if (pf >= NPROTO)
  return -EINVAL;
 spin_lock(&nf_log_lock);
 nf_logging[pf] = NULL;
 spin_unlock(&nf_log_lock);
 /* Give time to concurrent readers. */
 synchronize_net();
 return 0;
}
EXPORT_SYMBOL(nf_log_unregister_pf);
// 根据日志结构取消登记
void nf_log_unregister_logger(struct nf_logger *logger)
{
 int i;
 spin_lock(&nf_log_lock);
 for (i = 0; i < NPROTO; i++) {
// 循环查找整个数组检查是否和提供的日志结构匹配
  if (nf_logging[i] == logger)
// 找到后将数组元素指针置空
   nf_logging[i] = NULL;
 }
 spin_unlock(&nf_log_lock);
 synchronize_net();
}
EXPORT_SYMBOL(nf_log_unregister_logger);

// 标准日志记录函数
void nf_log_packet(int pf,
     unsigned int hooknum,
     const struct sk_buff *skb,
     const struct net_device *in,
     const struct net_device *out,
     struct nf_loginfo *loginfo,
     const char *fmt, ...)
{
 va_list args;
 char prefix[NF_LOG_PREFIXLEN];
 struct nf_logger *logger;
 
 rcu_read_lock();
// 获取记录日志结构指针
 logger = rcu_dereference(nf_logging[pf]);
 if (logger) {
  va_start(args, fmt);
  vsnprintf(prefix, sizeof(prefix), fmt, args);
  va_end(args);
  /* We must read logging before nf_logfn[pf] */
// 调用结构中的日志记录函数
  logger->logfn(pf, hooknum, skb, in, out, loginfo, prefix);
 } else if (net_ratelimit()) {
  printk(KERN_WARNING "nf_log_packet: can\'t log since "
         "no backend logging module loaded in! Please either "
         "load one, or disable logging explicitly\n");
 }
 rcu_read_unlock();
}

3. 应用

在ipt_LOG.c中定义LOG目的时使用了日志记录结构的处理方法:
/* net/ipv4/netfilter/ipt_LOG.c */
结构定义:
static struct nf_logger ipt_log_logger ={
 .name  = "ipt_LOG",
// 日志记录函数就是ipt_log_packet
 .logfn  = &ipt_log_packet,
 .me  = THIS_MODULE,
};
结构登记:
static int __init ipt_log_init(void)
{
......
// 注册到PF_INET协议
 if (nf_log_register(PF_INET, &ipt_log_logger) < 0) {
  printk(KERN_WARNING "ipt_LOG: not logging via system console "
         "since somebody else already registered for PF_INET\n");
  /* we cannot make module load fail here, since otherwise
   * iptables userspace would abort */
 }
......
}

log通用函数调用:
static unsigned int
ipt_log_target(struct sk_buff **pskb,
        const struct net_device *in,
        const struct net_device *out,
        unsigned int hooknum,
        const struct xt_target *target,
        const void *targinfo,
        void *userinfo)
{
 const struct ipt_log_info *loginfo = 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_NFLOG标识时调用通用日志函数
// 本质还是调用ipt_log_packet
 if (loginfo->logflags & IPT_LOG_NFLOG)
  nf_log_packet(PF_INET, hooknum, *pskb, in, out, &li,
                "%s", loginfo->prefix);
 else
  ipt_log_packet(PF_INET, hooknum, *pskb, in, out, &li,
                 loginfo->prefix);
 return IPT_CONTINUE;
}

在ipt_ULOG.c函数中,同样可能会在PF_INET上注册日志记录函数,因此ipt_LOG.c和ipt_ULOG.c最好不要同时使用。

4. 结论

nf_log本身倒也没什么,只是想把不同协议的日志记录处理用一种统一的方式进行处理而已,不过感觉意思不大。

发表于: 2007-01-16,修改于: 2007-01-16 08:48,已浏览2346次,有评论3条 推荐 投诉
	网友: 本站网友 	时间:2007-12-26 15:34:24 IP地址:222.66.22.★
	

ipt_log和ipt_ulog机制不一样吧,后者是用了netlink的方法输出日志信息的,前者直接输出到dmesg中


	网友: yfydz 	时间:2008-01-02 12:36:45 IP地址:218.247.216.★
	

以前不一样,现在一样了


猜你喜欢

转载自cxw06023273.iteye.com/blog/867202
今日推荐