当前位置: 首页 > 工具软件 > ebtables > 使用案例 >

ebtables日志目标log

高化
2023-12-01

内核由函数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

 类似资料: