创建网桥br0,将物理接口ens35加入到br0。动态获得地址192.168.1.130,注意br0的MAC地址为00:0c:29:e6:3f:76。
# ip link add br0 type bridge
# ip link set ens35 master br0
#
# dhclient br0
#
# ip addr
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:0c:29:e6:3f:76 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.130/24 brd 192.168.1.255 scope global dynamic br0
valid_lft 604798sec preferred_lft 604798sec
inet6 fe80::20c:29ff:fee6:3f76/64 scope link
valid_lft forever preferred_lft forever
增加ebtables规则arpreply,当网桥接收到arp请求时,回复MAC地址10:11:12:13:14:15。
# ebtables -t nat -A PREROUTING -p arp --arp-opcode Request -j arpreply --arpreply-mac 10:11:12:13:14:15
#
#
# ebtables -t nat -L
Bridge table: nat
Bridge chain: PREROUTING, entries: 1, policy: ACCEPT
-p ARP --arp-op Request -j arpreply --arpreply-mac 10:11:12:13:14:15
登录另外一台主机(192.168.1.201),使用arping测试,如下显示得到的MAC地址为arpreply规则中配置的值。
# arping 192.168.1.130
ARPING 192.168.1.130 from 192.168.1.201 eth0
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 1.342ms
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 2.104ms
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 1.965ms
^CSent 3 probes (1 broadcast(s))
Received 3 response(s)
查看本机的邻居表,并没有缓存主机192.168.1.201的硬件地址。原因是arpreply默认的动作是DROP,报文没有进入ARP子系统进行处理。
# ip neigh
192.168.1.1 dev br0 lladdr 40:62:31:08:e1:fa STALE
以下,将arpreply的动作设置为ACCEPT。
# ebtables -t nat -D PREROUTING 1
# ebtables -t nat -A PREROUTING -p arp --arp-opcode Request -j arpreply --arpreply-mac 10:11:12:13:14:15 --arpreply-target ACCEPT
#
# ebtables -t nat -L
Bridge table: nat
Bridge chain: PREROUTING, entries: 1, policy: ACCEPT
-p ARP --arp-op Request -j arpreply --arpreply-mac 10:11:12:13:14:15 --arpreply-target ACCEPT
再次登录测试主机(192.168.1.201),arping结果如下,每个ARP请求,回到了两个回复报文,一个为arpreply规则中配置的MAC地址,另外一个为ARP子系统回复的br0真实MAC地址。
# arping 192.168.1.130
ARPING 192.168.1.130 from 192.168.1.201 eth0
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 1.391ms
Unicast reply from 192.168.1.130 [00:0C:29:E6:3F:76] 1.419ms
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 2.160ms
Unicast reply from 192.168.1.130 [00:0C:29:E6:3F:76] 2.231ms
Unicast reply from 192.168.1.130 [10:11:12:13:14:15] 1.755ms
Unicast reply from 192.168.1.130 [00:0C:29:E6:3F:76] 2.064ms
^CSent 3 probes (1 broadcast(s))
Received 6 response(s)
本机中缓存了测试主机(192.168.1.201)的硬件地址。
# ip neigh
192.168.1.1 dev br0 lladdr 40:62:31:08:e1:fa STALE
192.168.1.201 dev br0 lladdr 00:0c:29:38:40:6b STALE
以上arpreply规则配置,会影响局域网中的所有ARP请求,导致ARP地址欺骗。安全起见限定源MAC地址00:0c:29:38:40:6b(测试主机192.168.1.130的MAC),这样只有测试主机的ARP请求,才回复arpreply规则指定的MAC地址。
# ebtables -t nat -A PREROUTING -p arp --arp-opcode Request --src 00:0c:29:38:40:6b -j arpreply --arpreply-mac 10:11:12:13:14:15
#
# ebtables -t nat -L --Lc --Ln
Bridge table: nat
Bridge chain: PREROUTING, entries: 1, policy: ACCEPT
1 -p ARP -s 00:0c:29:38:40:6b --arp-op Request -j arpreply --arpreply-mac 10:11:12:13:14:15 , pcnt = 4 -- bcnt = 184
或者,限定ARP报文的源地址。
# ebtables -t nat -A PREROUTING -p arp --arp-opcode Request --arp-ip-src 192.168.1.201 -j arpreply --arpreply-mac 10:11:12:13:14:15
#
# ebtables -L -t nat
Bridge table: nat
Bridge chain: PREROUTING, entries: 1, policy: ACCEPT
-p ARP --arp-op Request --arp-ip-src 192.168.1.201 -j arpreply --arpreply-mac 10:11:12:13:14:15
注册ebt_arpreply_tg_reg目标target,hook点仅支持NAT表的NF_BR_PRE_ROUTING。
static struct xt_target ebt_arpreply_tg_reg __read_mostly = {
.name = "arpreply",
.revision = 0,
.family = NFPROTO_BRIDGE,
.table = "nat",
.hooks = (1 << NF_BR_NUMHOOKS) | (1 << NF_BR_PRE_ROUTING),
.target = ebt_arpreply_tg,
.checkentry = ebt_arpreply_tg_check,
.targetsize = sizeof(struct ebt_arpreply_info),
.me = THIS_MODULE,
};
static int __init ebt_arpreply_init(void)
{
return xt_register_target(&ebt_arpreply_tg_reg);
}
配置合法性检查函数ebt_arpreply_tg_check,如果使用的为基础chain链,target不能为EBT_RETURN,可以使用EBT_ACCEPT、EBT_DROP和EBT_CONTINUE。但是,对于用户自创建的chain,不受此限制。另外,协议必须指定为ARP,并且不能进行取反操作。
static int ebt_arpreply_tg_check(const struct xt_tgchk_param *par)
{
const struct ebt_arpreply_info *info = par->targinfo;
const struct ebt_entry *e = par->entryinfo;
if (BASE_CHAIN && info->target == EBT_RETURN)
return -EINVAL;
if (e->ethproto != htons(ETH_P_ARP) ||
e->invflags & EBT_IPROTO)
return -EINVAL;
if (ebt_invalid_target(info->target))
return -EINVAL;
arpreply规则的报文处理函数如下。首先确保报文为ARP请求,并且,硬件地址长度为以太网的地址长度,协议为IPv4协议,协议地址长度为4。
static unsigned int
ebt_arpreply_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct ebt_arpreply_info *info = par->targinfo;
const __be32 *siptr, *diptr;
__be32 _sip, _dip;
const struct arphdr *ap;
struct arphdr _ah;
const unsigned char *shp;
unsigned char _sha[ETH_ALEN];
ap = skb_header_pointer(skb, 0, sizeof(_ah), &_ah);
if (ap == NULL)
return EBT_DROP;
if (ap->ar_op != htons(ARPOP_REQUEST) ||
ap->ar_hln != ETH_ALEN ||
ap->ar_pro != htons(ETH_P_IP) ||
ap->ar_pln != 4)
return EBT_CONTINUE;
以下,由ARP请求报文中取得发送者硬件地址shp,发送者IP地址,以及目标的IP地址。
shp = skb_header_pointer(skb, sizeof(_ah), ETH_ALEN, &_sha);
if (shp == NULL)
return EBT_DROP;
siptr = skb_header_pointer(skb, sizeof(_ah) + ETH_ALEN,
sizeof(_sip), &_sip);
if (siptr == NULL)
return EBT_DROP;
diptr = skb_header_pointer(skb,
sizeof(_ah) + 2 * ETH_ALEN + sizeof(_sip),
sizeof(_dip), &_dip);
if (diptr == NULL)
return EBT_DROP;
对应于ARP报文中的ar_sha、ar_sip和ar_tip。
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
unsigned char ar_sip[4]; /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
unsigned char ar_tip[4]; /* target IP address */
发送ARP响应报文,其中目标硬件地址使用arpreply规则中配置的MAC地址,对应于以上的报文字段ar_tha。返回值为规则中指定值。
arp_send(ARPOP_REPLY, ETH_P_ARP, *siptr,
(struct net_device *)xt_in(par),
*diptr, shp, info->mac, shp);
return info->target;
内核版本 5.10