我有一个 ubuntu 22.04 系统作为我的家庭路由器/防火墙,我使用 systemd-networkd 作为 LAN 的 DHCP 服务器。我在提供 DNS 的容器中运行 pihole,上游 DNS 是在同一主机上运行的 dnscrypt-proxy 守护程序。我使用 shorewall 进行防火墙/NAT。
我希望 LAN 计算机的主机名映射到 DNS 中的 DHCP 租约,我认为最好的方法是解析 DHCP 租约数据库并附加 /etc/hosts。我不希望 pihole 成为 DHCP 服务器,也不希望 systemd-resolved 成为 DNS 服务器。
当充当 DHCP 服务器时,systemd-networkd 将 DHCP 租约数据库保存在哪里?或者有没有更好的方法来获取 IP 地址 <-> 主机名映射?
LAN接口的配置:
[Match]
MACAddress=aa:bb:cc:dd:ee:ff
[Network]
IPv6PrefixDelegation=dhcpv6
IPv6DuplicateAddressDetection=1
IPv6PrivacyExtensions=yes
LinkLocalAddressing=ipv6
Address=10.100.10.1/24
DNS=10.100.10.1
DHCPServer=yes
IPv6Token=::1
IPv6SendRA=yes
DHCPv6PrefixDelegation=yes
[DHCPv6PrefixDelegation]
SubnetId=2
Assign=yes
[IPv6PrefixDelegation]
RouterLifetimeSec=900
EmitDNS=yes
DNS=_link_local
[DHCPServer]
EmitDNS=yes
DNS=10.100.10.1
EmitNTP=yes
NTP=10.100.10.1
答案1
systemd-networkd 应将租约信息存储在 /run/systemd/netif/leases/ 下
答案2
总结:需要使用DBus来获取属性。
此处接受的答案是不正确的;给出的答案是针对 DHCP客户租约(从另一个 DHCP 服务器接收)。
networkctl
配置报告Offered DHCP leases
时的DHCPServer=yes
源代码链接https://github.com/systemd/systemd/blob/fafded0ce0902e948cdeeaa6a12609a28d9c3ded/src/network/networkctl.c#L2339
要得到全部租约并仅隔离 IP 地址:
$ if=manage; \
link_id="$(ip --oneline link show dev "$if" | cut -f 1 -d:)"; \
busctl -j get-property org.freedesktop.network1 \
"/org/freedesktop/network1/link/${link_id}" \
org.freedesktop.network1.DHCPServer \
Leases \
| jq -r '"\(.data[][2][0]).\(.data[][2][1]).\(.data[][2][2]).\(.data[][2][3])"'
10.254.99.141
如果有多个租约,那么将返回多个结果;在这种情况下,需要进行一些更复杂的过滤,以报告客户端的 MAC 地址,从而识别正确的租约:
$ if=manage; \
link_id="$(ip --oneline link show dev "$if" | cut -f 1 -d:)"; \
eval "$( \
busctl -j get-property org.freedesktop.network1 \
"/org/freedesktop/network1/link/${link_id}" \
org.freedesktop.network1.DHCPServer \
Leases \
| jq -r '[ "printf", "%d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X\\n", \
.data[][2][0], .data[][2][1], .data[][2][2], .data[][2][3], \
.data[][1][1], .data[][1][2], .data[][1][3], .data[][1][4], .data[][1][5], .data[][1][6] \
] | @sh' \
)"
10.254.99.141 98:DA:C4:C2:EE:80
这可以通过几项更改扩展到远程工作:使用 ssh 来“获取”链接 ID,并添加busctl --host
:
$ if=manage; host="apu2.local"; \
link_id="$(ssh "$host" ip --oneline link show dev "$if" | cut -f 1 -d:)"; \
eval "$( \
busctl --host "$host" -j get-property org.freedesktop.network1 \
"/org/freedesktop/network1/link/${link_id}" \
org.freedesktop.network1.DHCPServer \
Leases \
| jq -r '[ "printf", "%d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X\\n", \
.data[][2][0], .data[][2][1], .data[][2][2], .data[][2][3], \
.data[][1][1], .data[][1][2], .data[][1][3], .data[][1][4], .data[][1][5], .data[][1][6] \
] | @sh' \
)"
10.254.99.141 98:DA:C4:C2:EE:80
答案3
此答案基于@ImproviseAdaptOvercome 的回答但使用较低的嵌套级别,因此希望更容易阅读和粘贴而没有引用问题。
link_id="$(ip --oneline link show dev "$iface" | cut -f 1 -d:)"
busctl -j get-property org.freedesktop.network1 "/org/freedesktop/network1/link/$link_id" org.freedesktop.network1.DHCPServer Leases \
| jq -r 'def bytehex:
[(./16|floor), .%16] | map(if . < 10 then 48 + . else . + 87 end) | implode;
def formatentry:
(.[2]|map(tostring)|join(".")) as $ip" | (.[1][1:]|map(bytehex)|join(":")) as $mac | \($ip) \($mac)";
.data[] | formatentry'
它使用 purejq
来格式化结果:bytehex
将整数转换为十六进制,并将formatentry
每条记录转换为字符串。可以轻松更改输出格式以供后续处理。