From: Dan Fuhry Date: Fri, 13 Sep 2024 01:04:26 +0000 (-0400) Subject: machines/agent: support dns_server_v[46] fields with interface name stand ins X-Git-Url: https://go.fuhry.dev/?a=commitdiff_plain;h=c906502f427c8ba30e3809e2c58c437bcc599056;p=runtime.git machines/agent: support dns_server_v[46] fields with interface name stand ins Add support for the `dns_server_v4` and `dns_server_v6` domain fields. If either is a unix network interface name, use the primary IP address of that interface as the address. --- diff --git a/machines/templates/dhcpd4.conf.j2 b/machines/templates/dhcpd4.conf.j2 index 49ef12c..67dbc09 100644 --- a/machines/templates/dhcpd4.conf.j2 +++ b/machines/templates/dhcpd4.conf.j2 @@ -24,9 +24,14 @@ on commit { {% if (sites[domain.Site.ID()].Name == siteName) and ('dhcp4' in domain.Features) and domain.IPv4Address and domain.DefaultRange.Defined() and domain.DefaultRange.Get().IPv4Start and domain.DefaultRange.Get().IPv4End %} # {{ domain.Name }} subnet {{ domain.IPv4Address }} netmask {{ domain.IPv4PrefixLength.Mask() }} { + {% if domain.IPv4DNSServer.AsIP() %} + option domain-name-servers {{ domain.IPv4DNSServer.AsIP().String() }}; + {% elif routeraddresses.IPv4[domain.IPv4Address] is defined %} + option domain-name-servers {{ routeraddresses.IPv4[domain.IPv4Address].Address }}; + {% endif %} + {% if routeraddresses.IPv4[domain.IPv4Address] is defined %} option routers {{ routeraddresses.IPv4[domain.IPv4Address].Address }}; - option domain-name-servers {{ routeraddresses.IPv4[domain.IPv4Address].Address }}; {% endif %} option domain-name "{{ domain.DNSSearch[0] }}"; diff --git a/machines/templates/dhcpd6.conf.j2 b/machines/templates/dhcpd6.conf.j2 index 4a669a4..683088e 100644 --- a/machines/templates/dhcpd6.conf.j2 +++ b/machines/templates/dhcpd6.conf.j2 @@ -35,11 +35,13 @@ on commit { {% if sites[domain.Site.ID()].Name == siteName and 'dhcp6' in domain.Features and domain.IPv6Address is defined and domain.DefaultRange.Defined() and domain.DefaultRange.Get().IPv6Start and domain.DefaultRange.Get().IPv6End %} # {{ domain.Name }} subnet6 {{ domain.IPv6Address }}/{{ domain.IPv6PrefixLength }} { - {% if routeraddresses.IPv6[domain.IPv6Address] -%} + {% if domain.IPv6DNSServer.AsIP() -%} + option dhcp6.name-servers {{ domain.IPv6DNSServer.AsIP().String() }}; + {% elif routeraddresses.IPv6[domain.IPv6Address] -%} option dhcp6.name-servers {{ routeraddresses.IPv6[domain.IPv6Address].Address }}; {%- endif %} - # option dhcp6.domain-name {{ domain.DNSSearch[0] }}; - # option dhcp6.domain-search {{ domain.DNSSearch[0] }}; + # option dhcp6.domain-name "{{ domain.DNSSearch[0] }}"; + # option dhcp6.domain-search "{{ domain.DNSSearch[0] }}"; option dhcp6.domain-search "{{ domain.DNSSearch | join("\", \"") }}"; {% for id, range in domain.Ranges -%} diff --git a/machines/types.go b/machines/types.go index 2ab2e63..b9e1923 100644 --- a/machines/types.go +++ b/machines/types.go @@ -8,6 +8,7 @@ import ( "net" "slices" "strings" + "sync" "time" "github.com/miekg/dns" @@ -19,12 +20,17 @@ const StaleHostTTL = 1440 * time.Hour type Timestamp uint64 type IPString string +type IPv4OrInterfaceString string +type IPv6OrInterfaceString string type HexEncoded string type IPv4PrefixLength uint8 type IPv6PrefixLength uint8 type ErrUnhandledRecordType string +var netIfaces []net.Interface +var netIfacesOnce sync.Once + func (e ErrUnhandledRecordType) Error() string { return string(e) } @@ -128,6 +134,18 @@ type DNSRecord struct { TTL uint `json:"ttl"` } +func getNetInterfaces() []net.Interface { + netIfacesOnce.Do(func() { + var err error + netIfaces, err = net.Interfaces() + if err != nil { + logger.Fatalf("failed to get network interfaces: %v") + } + }) + + return netIfaces +} + func (r *DNSRecord) ToRR() (dns.RR, error) { var msg dns.RR @@ -281,13 +299,15 @@ type Domain struct { Site *Sparse[Site] `json:"site"` VlanID uint `json:"vlan_id"` - IPv4Address IPString `json:"inet4_address"` - IPv4PrefixLength IPv4PrefixLength `json:"inet4_prefixlen"` - IPv4RouterAddress IPString `json:"inet4_routeraddr"` + IPv4Address IPString `json:"inet4_address"` + IPv4PrefixLength IPv4PrefixLength `json:"inet4_prefixlen"` + IPv4RouterAddress IPString `json:"inet4_routeraddr"` + IPv4DNSServer IPv4OrInterfaceString `json:"dns_server_v4"` - IPv6Address IPString `json:"inet6_address"` - IPv6PrefixLength IPv6PrefixLength `json:"inet6_prefixlen"` - IPv6RouterAddress IPString `json:"inet6_routeraddr"` + IPv6Address IPString `json:"inet6_address"` + IPv6PrefixLength IPv6PrefixLength `json:"inet6_prefixlen"` + IPv6RouterAddress IPString `json:"inet6_routeraddr"` + IPv6DNSServer IPv6OrInterfaceString `json:"dns_server_v6"` PXEServerIPv4 IPString `json:"pxe4_server"` PXEServerIPv6 IPString `json:"pxe6_server"` @@ -360,6 +380,80 @@ func (ip IPString) AsIP() net.IP { return net.ParseIP(string(ip)) } +func (s IPv4OrInterfaceString) AsIP() net.IP { + if string(s) == "" { + return nil + } + + if ip := net.ParseIP(string(s)); ip.To4() != nil { + return ip + } + + netifaces := getNetInterfaces() + for _, iface := range netifaces { + if iface.Name != string(s) { + continue + } + + if addrs, err := iface.Addrs(); err == nil { + for _, addr := range addrs { + if addr.Network() != "ip+net" { + continue + } + + ipaddr, _, err := net.ParseCIDR(addr.String()) + if err != nil { + continue + } + + if ipv4 := ipaddr.To4(); ipv4 != nil { + return ipv4 + } + } + } + } + + return nil +} + +func (s IPv6OrInterfaceString) AsIP() net.IP { + if ip := net.ParseIP(string(s)); ip.To4() == nil && ip.To16() != nil { + return ip + } + + netifaces := getNetInterfaces() + for _, iface := range netifaces { + if iface.Name != string(s) { + continue + } + + if addrs, err := iface.Addrs(); err == nil { + for _, addr := range addrs { + if addr.Network() != "ip+net" { + continue + } + + ipaddr, _, err := net.ParseCIDR(addr.String()) + if err != nil { + continue + } + + if bytes.Equal(ipaddr[0:2], []byte{0xfe, 0x80}) { + continue + } + + if ipaddr.To4() != nil { + continue + } + + return ipaddr + } + } + } + + return nil +} + func (ip IPString) AsInt32() int32 { netip := ip.AsIP().To4() if netip == nil {