我想知道是否可以不使用 DHCP 中继 netfilter(无论是那个tc
还是nftables
)来将 DHCP 广播数据包路由到连接到网桥的 Docker 容器。
这样做的原因是,我不想使用macvlan
DHCP 容器,这样看起来就好像一个 IP(即路由器 IP)正在处理所有网络操作。 DHCP 容器通常需要CAP_NET_ADMIN
(由于 DHCP 需要混杂模式),并且我知道如果没有macvlan
这将控制主机的网络堆栈(我也是userns-remap
我的容器)。
如果可以修改 DHCP 数据包并转发它们,那就太好了。中继在这里不起作用,因为它仍然需要macvlan
与 DHCP 容器已有的方法相同的方法。
这有可能吗?谢谢
答案1
看来是这样,而且我有一个不完美的解决方案。请注意,该解决方案的有效性可能会因网络配置而异。
DHCP 服务器位于 Docker 桥接网络内,并通过 、 和 运行CAP_NET_BIND
,CAP_NET_RAW
使其CAP_NET_ADMIN
能够控制其运行的网络命名空间。
为了DHCPDISCOVER
数据包到达 DHCP 服务器时,使用以下规则:
table netdev filterearly_lan {
chain ingress {
type filter hook ingress device eth0 priority -500; policy drop;
ip saddr 0.0.0.0 ether daddr ff:ff:ff:ff:ff:ff ip daddr 255.255.255.255 udp sport 68 udp dport 67 ip saddr set $LOCAL_NETWORK ip daddr set $DHCP meta pkttype set host accept;
}
}
这避免了中列出的麻烦这问题并意味着数据包正常穿过 netfilter,从而能够发送到NFQUEUE
:
table inet filter {
chain forward {
type filter hook forward priority 0; policy drop;
udp sport 68 udp dport 67 queue num 0;
udp sport 67 udp dport 68 queue num 0;
}
}
$LOCAL_NETWORK
可以是子网内的任何IP,但由于某种原因不能是本地设备的IP。似乎可以将其设置为网络地址(例如192.168.1.0)。看来不需要更改目标 MAC 地址。
要处理使用广播请求响应的客户端,必须将数据包类型更改为主机才能进行路由,并且可以从DHCPOFFER
数据包(ingress
此处不使用是为了保持接口不可知):
table inet filter {
chain raw {
type filter hook prerouting priority -400; policy accept;
ip daddr 255.255.255.255 udp sport 67 udp dport 68 @nh,128,32 set @th,192,32 meta pkttype set host;
}
}
当DHCPOFFER
数据包作为单播发送时,我有问题postrouting
在客户端获得 IP 之前,(在该阶段)向客户端发出 ARP 请求。要绕过此问题:
table inet mangle {
chain postrouting {
type filter hook postrouting priority 50;
udp sport 67 ether daddr set ff:ff:ff:ff:ff:ff ip daddr set 255.255.255.255;
}
}
一旦发出 ARP 请求,就可以从 ARP 请求的内容中恢复目标 MAC 和 IP。DHCPOFFER
并将它们发送到正确的目的地。还应该恢复 TTL,以免在数据包分析时引起怀疑:
table netdev filterearly_lan {
chain egress {
type filter hook egress device eth0 priority 500; policy accept;
#Restore the TTL that is changed during the routing process
udp sport 67 ip ttl set 64 continue;
#Client asked for broadcast
udp sport 67 @th,256,48 0x800000000000 ip daddr set 255.255.255.255 ether daddr set ff:ff:ff:ff:ff:ff;
#Set the ether addr to the CHADDR. Only if unicast flag
udp sport 67 @th,256,48 0x000000000000 @ll,0,48 set @th,400,48;
#Set the ip daddr from the YIADDR. Only if unicast flag
udp sport 67 @th,256,48 0x000000000000 @nh,128,32 set @th,304,32;
}
}
回复必须是SNAT
编辑正确,因为它们来自不同的网络:
table inet nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "eth0" masquerade;
}
}
DHCP 服务器的配置以及对数据包的任何所需修改超出了本问题和答案的范围。理想情况下,DHCP 服务器将支持向不同网络提供 IP 地址。如果服务器无法欺骗其 IP,则需要修改数据包来更改SIADDR
DHCP选项54(服务器标识符)。
通过此配置,可以在 Docker 桥后面使用 DHCP 服务器,从而满足原始问题中提出的要求。
答案2
是的,这听起来可行:DHCP 是一个完美的 UDP/IP 协议,nftables
应该能够将 IP 源地址为 0.0.0.0 且广播目标为 255.255.255.255 的 FF:…:FF 的所有以太网帧转发到网络接口,并且您的容器的 IP 地址。
但是:您的 DHCP 服务器软件可能不希望这样;它可能需要绑定到第 2 层(AF_PACKET
通常具有套接字类型SOCK_RAW
、协议ETHER_TYPE
)套接字(请参阅man 7 packet
),而不是传输层套接字(即 UDP 套接字AF_INET
)SOCK_DGRAM
。这取决于情况,但我敢打赌大多数 DHCP 服务器都可以配置。