内核由函数ebt_log_init注册log目标,即结构ebt_log_tg_reg。
static struct xt_target ebt_log_tg_reg __read_mostly = {
.name = "log",
.revision = 0,
.family = NFPROTO_BRIDGE,
.target = ebt_log_tg,
.checkentry = ebt_log_tg_check,
.targetsize = sizeof(struct ebt_log_info),
.me = THIS_MODULE,
};
static int __init ebt_log_init(void)
{
return xt_register_target(&ebt_log_tg_reg);
如下ebtables的日志命令:
# ebtables -A INPUT --log-level info --log-ip --log-ip6 --log-arp --log-prefix BRIDGE-FW-LOG
函数ebt_log_tg_check对于配置参数进行必要的合法性检查,确保日志前缀字符串的结束符,最长30字节(EBT_LOG_PREFIX_SIZE)。
static int ebt_log_tg_check(const struct xt_tgchk_param *par)
{
struct ebt_log_info *info = par->targinfo;
if (info->bitmask & ~EBT_LOG_MASK)
return -EINVAL;
if (info->loglevel >= 8)
return -EINVAL;
info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
return 0;
对于匹配的报文,由ebt_log_packet函数输出报文信息。
static unsigned int
ebt_log_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct ebt_log_info *info = par->targinfo;
struct nf_loginfo li;
struct net *net = xt_net(par);
li.type = NF_LOG_TYPE_LOG;
li.u.log.level = info->loglevel;
li.u.log.logflags = info->bitmask;
/* Remember that we have to use ebt_log_packet() not to break backward
* compatibility. We cannot use the default bridge packet logger via
* nf_log_packet() with NFT_LOG_TYPE_LOG here. --Pablo
*/
if (info->bitmask & EBT_LOG_NFLOG)
nf_log_packet(net, NFPROTO_BRIDGE, xt_hooknum(par), skb,
xt_in(par), xt_out(par), &li, "%s", info->prefix);
else
ebt_log_packet(net, NFPROTO_BRIDGE, xt_hooknum(par), skb,
xt_in(par), xt_out(par), &li, info->prefix);
return EBT_CONTINUE;
首先,显示出入接口信息,以及报文二层信息。
static void
ebt_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 *prefix)
{
unsigned int bitmask;
/* FIXME: Disabled from containers until syslog ns is supported */
if (!net_eq(net, &init_net) && !sysctl_nf_log_all_netns)
return;
spin_lock_bh(&ebt_log_lock);
printk(KERN_SOH "%c%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
'0' + loginfo->u.log.level, prefix,
in ? in->name : "", out ? out->name : "",
eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
ntohs(eth_hdr(skb)->h_proto));
if (loginfo->type == NF_LOG_TYPE_LOG)
bitmask = loginfo->u.log.logflags;
else
bitmask = NF_LOG_DEFAULT_MASK;
如果设置了控制选项(–log-ip),并且报文为IP报文,显示IP头部信息,以及尝试显示四层报文信息(print_ports)。
if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto ==
htons(ETH_P_IP)) {
const struct iphdr *ih;
struct iphdr _iph;
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
if (ih == NULL) {
pr_cont(" INCOMPLETE IP header");
goto out;
}
pr_cont(" IP SRC=%pI4 IP DST=%pI4, IP tos=0x%02X, IP proto=%d",
&ih->saddr, &ih->daddr, ih->tos, ih->protocol);
print_ports(skb, ih->protocol, ih->ihl*4);
goto out;
}
对于IPv6协议报文,如果设置了选项(–log-ip6),显示IPv6基础头部的信息,之后跳过其于头部,显示四层报文信息。
#if IS_ENABLED(CONFIG_BRIDGE_EBT_IP6)
if ((bitmask & EBT_LOG_IP6) && eth_hdr(skb)->h_proto ==
htons(ETH_P_IPV6)) {
const struct ipv6hdr *ih;
struct ipv6hdr _iph;
uint8_t nexthdr;
__be16 frag_off;
int offset_ph;
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
if (ih == NULL) {
pr_cont(" INCOMPLETE IPv6 header");
goto out;
}
pr_cont(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d",
&ih->saddr, &ih->daddr, ih->priority, ih->nexthdr);
nexthdr = ih->nexthdr;
offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr, &frag_off);
if (offset_ph == -1)
goto out;
print_ports(skb, nexthdr, offset_ph);
goto out;
}
#endif
对于ARP协议报文,如果设置了选项(–log-arp),显示ARP头部信息。对于Ethernet,另外显示报文信息,包括发送者(Sender)的MAC和IP地址,目标(Target)的MAC和IP地址。
if ((bitmask & EBT_LOG_ARP) &&
((eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) ||
(eth_hdr(skb)->h_proto == htons(ETH_P_RARP)))) {
const struct arphdr *ah;
struct arphdr _arph;
ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph);
pr_cont(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
ntohs(ah->ar_hrd), ntohs(ah->ar_pro),
ntohs(ah->ar_op));
/* If it's for Ethernet and the lengths are OK,
* then log the ARP payload
*/
if (ah->ar_hrd == htons(1) &&
ah->ar_hln == ETH_ALEN &&
ah->ar_pln == sizeof(__be32)) {
const struct arppayload *ap;
struct arppayload _arpp;
ap = skb_header_pointer(skb, sizeof(_arph), sizeof(_arpp), &_arpp);
pr_cont(" ARP MAC SRC=%pM ARP IP SRC=%pI4 ARP MAC DST=%pM ARP IP DST=%pI4",
ap->mac_src, ap->ip_src,
ap->mac_dst, ap->ip_dst);
}
}
对于协议TCP、UDP、UDPLITE、SCTP和DCCP显示四层的端口信息。
static void
print_ports(const struct sk_buff *skb, uint8_t protocol, int offset)
{
if (protocol == IPPROTO_TCP ||
protocol == IPPROTO_UDP ||
protocol == IPPROTO_UDPLITE ||
protocol == IPPROTO_SCTP ||
protocol == IPPROTO_DCCP) {
const struct tcpudphdr *pptr;
struct tcpudphdr _ports;
pptr = skb_header_pointer(skb, offset, sizeof(_ports), &_ports);
if (pptr == NULL) {
pr_cont(" INCOMPLETE TCP/UDP header");
return;
}
pr_cont(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst));
如下ebtables命令,配置日志规则。
# ebtables -A INPUT --log-level info --log-ip --log-ip6 --log-arp --log-prefix BRIDGE-FW-LOG
#
# ebtables -L
Bridge table: filter
Bridge chain: INPUT, entries: 1, policy: ACCEPT
--log-level info --log-prefix "BRIDGE-FW-LOG" --log-ip --log-arp --log-ip6 -j CONTINUE
#
#
# lsmod | grep log
ebt_log 16384 1
nfnetlink_log 20480 1
nfnetlink 20480 4 nft_compat,nf_tables,nfnetlink_log
x_tables 49152 3 nft_compat,ebt_log,ip_tables
如下配置网桥接口br0,将物理口ens33添加到网桥下。
# ip link add br0 type bridge
#
# ip link set dev br0 up
#
# ip link set dev ens33 master br0
#
# ip addr add dev br0 192.168.3.143/24
查看日志显示(或者使用dmesg查看),如下有IPv4协议、ARP协议和IPv6协议的日志信息。
# tail -f /var/log/kern.log
Feb 19 09:35:15 h323 kernel: [22126.421854] BRIDGE-FW-LOG IN=ens33 OUT= MAC source = 54:a7:03:16:55:c2 MAC dest = ff:ff:ff:ff:ff:ff proto = 0x0800 IP SRC=192.168.3.123 IP DST=192.168.3.255, IP tos=0x00, IP proto=17 SPT=1024 DPT=5001
Feb 19 09:35:15 h323 kernel: [22127.132133] BRIDGE-FW-LOG IN=ens33 OUT= MAC source = 94:d9:b3:7f:a1:eb MAC dest = ff:ff:ff:ff:ff:ff proto = 0x0806 ARP HTYPE=1, PTYPE=0x0800, OPCODE=1 ARP MAC SRC=94:d9:b3:7f:a1:eb ARP IP SRC=192.168.3.44 ARP MAC DST=00:00:00:00:00:00 ARP IP DST=192.168.3.36
Feb 19 09:36:46 h323 kernel: [22217.654690] BRIDGE-FW-LOG IN=ens33 OUT= MAC source = 80:30:49:aa:52:61 MAC dest = 33:33:ff:fb:8a:d4 proto = 0x86dd IPv6 SRC=fe80:0000:0000:0000:a8fa:63e4:b573:7c08 IPv6 DST=ff02:0000:0000:0000:0000:0001:fffb:8ad4, IPv6 priority=0x0, Next Header=58
内核版本 5.10