physdev匹配帮助信息如下。
# iptables -m physdev -h
physdev match options:
[!] --physdev-in inputname[+] bridge port name ([+] for wildcard)
[!] --physdev-out outputname[+] bridge port name ([+] for wildcard)
[!] --physdev-is-in arrived on a bridge device
[!] --physdev-is-out will leave on a bridge device
[!] --physdev-is-bridged it's a bridged packet
–physdev-in 匹配报文入方向虚拟网桥的物理接口名称,最后的字符+匹配随后的任何字符。
–physdev-out 匹配报文出方向虚拟网桥的物理接口名称。
–physdev-is-in 匹配由网桥接收到的报文,
–physdev-is-out 匹配由网桥发送的报文。
–physdev-is-bridged 匹配在网桥内部中转发的报文,仅在FORWARD和POSTROUTING点有效。
如下配置策略,匹配由网桥发出,并且由网桥子接口ens39发出的报文。
# ip link add br0 type bridge
# ip link set ens39 master br0
#
# iptables -A FORWARD -m physdev --physdev-is-out --physdev-out ens39 -j ACCEPT
#
# iptables -L -n -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 PHYSDEV match --physdev-is-out --physdev-out ens39
physdev匹配
函数xt_register_matche注册匹配结构physdev_mt_reg。
static struct xt_match physdev_mt_reg __read_mostly = {
.name = "physdev",
.revision = 0,
.family = NFPROTO_UNSPEC,
.checkentry = physdev_mt_check,
.match = physdev_mt,
.matchsize = sizeof(struct xt_physdev_info),
.me = THIS_MODULE,
};
static int __init physdev_mt_init(void)
{
return xt_register_match(&physdev_mt_reg);
配置项检查函数,首先判断是否为以上定义的5种配置选项(XT_PHYSDEV_OP_MASK);其次,对于配置项–physdev-out和–physdev-is-out,仅支持在FORWARD和POSTROUTING点配置,但是,只有在配置OUTPUT链时,出错并提示以下错误,对于INPUT和PREROUTING可配置成功,没有告警。
# iptables -t mangle -A OUTPUT -m physdev --physdev-out ens39 -j ACCEPT
#
# dmesg | tail
[2712782.456829] Bridge firewalling registered
[2712850.494235] xt_physdev: --physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic
另外,此功能需要br_netfilter内核模块。
static int physdev_mt_check(const struct xt_mtchk_param *par)
{
const struct xt_physdev_info *info = par->matchinfo;
static bool brnf_probed __read_mostly;
if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
info->bitmask & ~XT_PHYSDEV_OP_MASK)
return -EINVAL;
if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
(!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) || info->invert & XT_PHYSDEV_OP_BRIDGED) &&
par->hook_mask & (1 << NF_INET_LOCAL_OUT)) {
pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n");
return -EINVAL;
}
if (!brnf_probed) {
brnf_probed = true;
request_module("br_netfilter");
}
匹配函数physdev_mt如下,首先,判断网桥信息是否存在,在报文经过NF_BR_PRE_ROUTING时将创建此结构(br_nf_pre_routing)。如果网桥信息不存在,并且没有配置取反操作,返回不匹配。否则,匹配。
static bool
physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_physdev_info *info = par->matchinfo;
const struct net_device *physdev;
unsigned long ret;
const char *indev, *outdev;
/* Not a bridged IP packet or no info available yet:
* LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
* the destination device will be a bridge. */
if (!nf_bridge_info_exists(skb)) {
/* Return MATCH if the invert flags of the used options are on */
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
!(info->invert & XT_PHYSDEV_OP_BRIDGED))
return false;
if ((info->bitmask & XT_PHYSDEV_OP_ISIN) &&
!(info->invert & XT_PHYSDEV_OP_ISIN))
return false;
if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) &&
!(info->invert & XT_PHYSDEV_OP_ISOUT))
return false;
if ((info->bitmask & XT_PHYSDEV_OP_IN) &&
!(info->invert & XT_PHYSDEV_OP_IN))
return false;
if ((info->bitmask & XT_PHYSDEV_OP_OUT) &&
!(info->invert & XT_PHYSDEV_OP_OUT))
return false;
return true;
}
这里网桥信息存在,表明报文由网桥子接口进入,如果出接口不是网桥的子接口,对于physdev-is-bridged网桥转发选项,不匹配。
physdev = nf_bridge_get_physoutdev(skb);
outdev = physdev ? physdev->name : NULL;
/* This only makes sense in the FORWARD and POSTROUTING chains */
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
(!!outdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
return false;
对于physdev-is-in配置项,如果indev不存在,或者对于physdev-is-out配置项,如果outdev不存在,表明不匹配。
physdev = nf_bridge_get_physindev(skb);
indev = physdev ? physdev->name : NULL;
if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
(!indev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
(info->bitmask & XT_PHYSDEV_OP_ISOUT &&
(!outdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
return false;
对于physdev-in配置项,如果没有开启,跳转到下一个判断(match_outdev)。否则,在入接口存在的情况下,比较入接口名称是否相符。入接口不存在的情况下,开始比较下一个physdev-out出接口。
if (!(info->bitmask & XT_PHYSDEV_OP_IN))
goto match_outdev;
if (indev) {
ret = ifname_compare_aligned(indev, info->physindev, info->in_mask);
if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
return false;
}
对于physdev-out配置项,比较出接口名称是否相符。
match_outdev:
if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
return true;
if (!outdev)
return false;
ret = ifname_compare_aligned(outdev, info->physoutdev, info->out_mask);
return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
内核版本 5.10