iptables -t raw -I PREROUTING -p tcp --dport 80 -j LOG
# iptables -t raw -I PREROUTING -p tcp --dport 80 -j LOG --log-level 3 --log-prefix "ipt-err:" 可以指定log级别
日志级别可通过syslog定义进行查看。另外LOG目标还可指定参数:–log-tcp-sequence,–log-tcp-options,–log-ip-options,–log-uid和–log-macdecode。
# man syslog
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
如果将IPv4协议(NFPROTO_IPV4=2)的logger设置为nf_log_ipv4。
sysctl net.netfilter.nf_log.2=nf_log_ipv4
#
# sysctl -a | grep nf_log
net.netfilter.nf_log.0 = NONE
net.netfilter.nf_log.1 = NONE
net.netfilter.nf_log.2 = nf_log_ipv4
内核由函数log_tg_init注册LOG目标,即结构log_tg_regs。
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,
.destroy = log_tg_destroy,
.me = THIS_MODULE,
},
};
函数log_tg_check进行必要的参数合法性判断,之后查找系统中注册的logger,以上示例为nf_log_ipv4
static int log_tg_check(const struct xt_tgchk_param *par)
{
const struct xt_log_info *loginfo = par->targinfo;
int ret;
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;
}
ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG);
if (ret != 0 && !par->nft_compat) {
request_module("%s", "nf_log_syslog");
ret = nf_logger_find_get(par->family, NF_LOG_TYPE_LOG);
}
return ret;
}
以下将匹配LOG规则的报文通过函数nf_log_packet进行日志输出,在使用日志nf_log_ipv4时,其日志输出函数为nf_log_ip_packet。函数返回值为XT_CONTINUE,继续下一跳规则匹配。
static unsigned int
log_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct xt_log_info *loginfo = par->targinfo;
struct net *net = xt_net(par);
struct nf_loginfo li;
li.type = NF_LOG_TYPE_LOG;
li.u.log.level = loginfo->level;
li.u.log.logflags = loginfo->logflags;
nf_log_packet(net, xt_family(par), xt_hooknum(par), skb, xt_in(par),
xt_out(par), &li, "%s", loginfo->prefix);
return XT_CONTINUE;
}
系统内的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
日志记录器注册
如果协议类型等于NFPROTO_UNSPEC,将日志logger赋值给所有的协议类型。否则,将logger赋值给对应的协议。
/* return EEXIST if the same logger is registered, 0 on success. */
int nf_log_register(u_int8_t pf, struct nf_logger *logger)
{
int i;
int ret = 0;
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);
}
unlock:
mutex_unlock(&nf_log_mutex);
return ret;
}
ret = nf_log_register(NFPROTO_IPV4, &nf_ip_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);
mutex_unlock(&nf_log_mutex);
return 0;
}
int ret = nf_log_set(net, NFPROTO_IPV4, &nf_ip_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);
}
rcu_read_unlock();
}
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);
只有在sysctl_nf_log_all_netns为真的情况下,才会生成其它命名空间的报文日志。函数nf_log_buf_close输出日志缓存中的内容。
/* Guard against containers flooding syslog. */
static bool nf_log_allowed(const struct net *net)
{
return net_eq(net, &init_net) || sysctl_nf_log_all_netns;
}
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;
if (!nf_log_allowed(net))
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)
dump_mac_header(m, loginfo, skb);
dump_ipv4_packet(net, m, loginfo, skb, skb_network_offset(skb));
nf_log_buf_close(m);
}
标签:logger,log,记录,nf,loginfo,日志,net,Netfilter,struct From: https://www.cnblogs.com/codestack/p/17718359.html