Skip to content

Commit 7af3ea4

Browse files
authored
Merge pull request #1739 from gwenya/dhcp-dns-option
Add option to configure DNS server for bridge and OVN networks
2 parents f0f9a23 + 81d243b commit 7af3ea4

File tree

8 files changed

+92
-30
lines changed

8 files changed

+92
-30
lines changed

doc/.wordlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ qgroups
226226
RADOS
227227
RBAC
228228
RBD
229+
RDNSS
229230
README
230231
reconfiguring
231232
requestor

doc/api-extensions.md

+5
Original file line numberDiff line numberDiff line change
@@ -2735,3 +2735,8 @@ This allows specifying pairs of CIDR networks and gateway address to be announce
27352735
Adds a new `LogicalSwitch` field to the `NetworkStateOVN` struct which is part of the `GET /1.0/networks/NAME/state` API.
27362736

27372737
This is used to get the OVN logical switch name.
2738+
2739+
## `network_dns_nameservers`
2740+
2741+
Introduces the `dns.nameservers` configuration option on bridged and OVN networks.
2742+
This allows specifying IPv4 and IPv6 DNS server addresses to be announced by the DHCP server and via Router Advertisements.

doc/reference/network_bridge.md

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Key | Type | Condition | Defau
6464
`bridge.external_interfaces` | string | - | - | Comma-separated list of unconfigured network interfaces to include in the bridge
6565
`bridge.hwaddr` | string | - | - | MAC address for the bridge
6666
`bridge.mtu` | integer | - | `1500` | Bridge MTU (default varies if tunnel in use)
67+
`dns.nameservers` | string | - | IPv4 and IPv6 address | DNS server IPs to advertise to DHCP clients and via Router Advertisements. Both IPv4 and IPv6 addresses get pushed via DHCP, and IPv6 addresses are also advertised as RDNSS via RA.
6768
`dns.domain` | string | - | `incus` | Domain to advertise to DHCP clients and use for DNS resolution
6869
`dns.mode` | string | - | `managed` | DNS registration mode: `none` for no DNS record, `managed` for Incus-generated static records or `dynamic` for client-generated records
6970
`dns.search` | string | - | - | Full comma-separated domain search list, defaulting to `dns.domain` value

doc/reference/network_ovn.md

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Key | Type | Condition | Defau
4545
`bridge.external_interfaces` | string | - | - | Comma-separated list of unconfigured network interfaces to include in the bridge
4646
`bridge.hwaddr` | string | - | - | MAC address for the bridge
4747
`bridge.mtu` | integer | - | `1442` | Bridge MTU (default allows host to host Geneve tunnels)
48+
`dns.nameservers` | string | - | Uplink DNS servers (IPv4 and IPv6 address if no uplink is configured) | DNS server IPs to advertise to DHCP clients and via Router Advertisements. Both IPv4 and IPv6 addresses get pushed via DHCP, and the first IPv6 address is also advertised as RDNSS via RA.
4849
`dns.domain` | string | - | `incus` | Domain to advertise to DHCP clients and use for DNS resolution
4950
`dns.search` | string | - | - | Full comma-separated domain search list, defaulting to `dns.domain` value
5051
`dns.zone.forward` | string | - | - | Comma-separated list of DNS zone names for forward DNS records

internal/server/network/driver_bridge.go

+27
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ func (n *bridge) Validate(config map[string]string) error {
206206
"ipv6.routes": validate.Optional(validate.IsListOf(validate.IsNetworkV6)),
207207
"ipv6.routing": validate.Optional(validate.IsBool),
208208
"ipv6.ovn.ranges": validate.Optional(validate.IsListOf(validate.IsNetworkRangeV6)),
209+
"dns.nameservers": validate.Optional(validate.IsListOf(validate.IsNetworkAddress)),
209210
"dns.domain": validate.IsAny,
210211
"dns.mode": validate.Optional(validate.IsOneOf("dynamic", "managed", "none")),
211212
"dns.search": validate.IsAny,
@@ -901,6 +902,16 @@ func (n *bridge) setup(oldConfig map[string]string) error {
901902
}
902903
}
903904

905+
var dnsIPv4 []string
906+
var dnsIPv6 []string
907+
for _, s := range util.SplitNTrimSpace(n.config["dns.nameservers"], ",", -1, false) {
908+
if net.ParseIP(s).To4() != nil {
909+
dnsIPv4 = append(dnsIPv4, s)
910+
} else {
911+
dnsIPv6 = append(dnsIPv6, s)
912+
}
913+
}
914+
904915
// Configure IPv4.
905916
if !util.IsNoneOrEmpty(n.config["ipv4.address"]) {
906917
// Parse the subnet.
@@ -920,6 +931,14 @@ func (n *bridge) setup(oldConfig map[string]string) error {
920931
dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option-force=3,%s", n.config["ipv4.dhcp.gateway"]))
921932
}
922933

934+
if n.config["dns.nameservers"] != "" {
935+
if len(dnsIPv4) == 0 {
936+
dnsmasqCmd = append(dnsmasqCmd, "--dhcp-option-force=6")
937+
} else {
938+
dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option-force=6,%s", strings.Join(dnsIPv4, ",")))
939+
}
940+
}
941+
923942
if bridge.MTU != bridgeMTUDefault {
924943
dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option-force=26,%d", bridge.MTU))
925944
}
@@ -1095,6 +1114,14 @@ func (n *bridge) setup(oldConfig map[string]string) error {
10951114
dnsmasqCmd = append(dnsmasqCmd, []string{"--dhcp-range", fmt.Sprintf("::,constructor:%s,ra-only", n.name)}...)
10961115
}
10971116

1117+
if n.config["dns.nameservers"] != "" {
1118+
if len(dnsIPv6) == 0 {
1119+
dnsmasqCmd = append(dnsmasqCmd, "--dhcp-option-force=option6:dns-server")
1120+
} else {
1121+
dnsmasqCmd = append(dnsmasqCmd, fmt.Sprintf("--dhcp-option-force=option6:dns-server,[%s]", strings.Join(dnsIPv6, ",")))
1122+
}
1123+
}
1124+
10981125
// Allow forwarding.
10991126
if util.IsTrueOrEmpty(n.config["ipv6.routing"]) {
11001127
// Get a list of proc entries.

internal/server/network/driver_ovn.go

+39-29
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ func (n *ovn) Validate(config map[string]string) error {
416416
"ipv6.nat.address": validate.Optional(validate.IsNetworkAddressV6),
417417
"ipv4.l3only": validate.Optional(validate.IsBool),
418418
"ipv6.l3only": validate.Optional(validate.IsBool),
419+
"dns.nameservers": validate.Optional(validate.IsListOf(validate.IsNetworkAddress)),
419420
"dns.domain": validate.IsAny,
420421
"dns.search": validate.IsAny,
421422
"dns.zone.forward": validate.IsAny,
@@ -2646,6 +2647,26 @@ func (n *ovn) setup(update bool) error {
26462647
}
26472648
}
26482649

2650+
var dnsIPv4 []net.IP
2651+
var dnsIPv6 []net.IP
2652+
2653+
if n.config["dns.nameservers"] != "" {
2654+
for _, s := range util.SplitNTrimSpace(n.config["dns.nameservers"], ",", -1, false) {
2655+
nsIP := net.ParseIP(s)
2656+
if nsIP.To4() != nil {
2657+
dnsIPv4 = append(dnsIPv4, nsIP)
2658+
} else {
2659+
dnsIPv6 = append(dnsIPv6, nsIP)
2660+
}
2661+
}
2662+
} else if uplinkNet != nil {
2663+
dnsIPv4 = uplinkNet.dnsIPv6
2664+
dnsIPv6 = uplinkNet.dnsIPv6
2665+
} else {
2666+
dnsIPv4 = []net.IP{routerIntPortIPv4}
2667+
dnsIPv6 = []net.IP{routerIntPortIPv6}
2668+
}
2669+
26492670
// Create DHCPv4 options for internal switch.
26502671
if dhcpV4Subnet != nil {
26512672
// In l3only mode we configure the DHCPv4 server to request the instances use a /32 subnet mask.
@@ -2655,21 +2676,16 @@ func (n *ovn) setup(update bool) error {
26552676
}
26562677

26572678
opts := &networkOVN.OVNDHCPv4Opts{
2658-
ServerID: routerIntPortIPv4,
2659-
ServerMAC: routerMAC,
2660-
Router: routerIntPortIPv4,
2661-
DomainName: n.getDomainName(),
2662-
LeaseTime: time.Duration(time.Hour * 1),
2663-
MTU: bridgeMTU,
2664-
Netmask: dhcpV4Netmask,
2665-
DNSSearchList: n.getDNSSearchList(),
2666-
StaticRoutes: n.config["ipv4.dhcp.routes"],
2667-
}
2668-
2669-
if uplinkNet != nil {
2670-
opts.RecursiveDNSServer = uplinkNet.dnsIPv4
2671-
} else {
2672-
opts.RecursiveDNSServer = []net.IP{routerIntPortIPv4}
2679+
ServerID: routerIntPortIPv4,
2680+
ServerMAC: routerMAC,
2681+
Router: routerIntPortIPv4,
2682+
DomainName: n.getDomainName(),
2683+
LeaseTime: time.Duration(time.Hour * 1),
2684+
MTU: bridgeMTU,
2685+
Netmask: dhcpV4Netmask,
2686+
DNSSearchList: n.getDNSSearchList(),
2687+
StaticRoutes: n.config["ipv4.dhcp.routes"],
2688+
RecursiveDNSServer: dnsIPv4,
26732689
}
26742690

26752691
err = n.ovnnb.UpdateLogicalSwitchDHCPv4Options(context.TODO(), n.getIntSwitchName(), dhcpv4UUID, dhcpV4Subnet, opts)
@@ -2681,14 +2697,9 @@ func (n *ovn) setup(update bool) error {
26812697
// Create DHCPv6 options for internal switch.
26822698
if dhcpV6Subnet != nil {
26832699
opts := &networkOVN.OVNDHCPv6Opts{
2684-
ServerID: routerMAC,
2685-
DNSSearchList: n.getDNSSearchList(),
2686-
}
2687-
2688-
if uplinkNet != nil {
2689-
opts.RecursiveDNSServer = uplinkNet.dnsIPv6
2690-
} else {
2691-
opts.RecursiveDNSServer = []net.IP{routerIntPortIPv6}
2700+
ServerID: routerMAC,
2701+
DNSSearchList: n.getDNSSearchList(),
2702+
RecursiveDNSServer: dnsIPv6,
26922703
}
26932704

26942705
err = n.ovnnb.UpdateLogicalSwitchDHCPv6Options(context.TODO(), n.getIntSwitchName(), dhcpv6UUID, dhcpV6Subnet, opts)
@@ -2708,10 +2719,9 @@ func (n *ovn) setup(update bool) error {
27082719
}
27092720

27102721
var recursiveDNSServer net.IP
2711-
if uplinkNet != nil && len(uplinkNet.dnsIPv6) > 0 {
2712-
recursiveDNSServer = uplinkNet.dnsIPv6[0] // OVN only supports 1 RA DNS server.
2713-
} else {
2714-
recursiveDNSServer = routerIntPortIPv6
2722+
2723+
if len(dnsIPv6) > 0 {
2724+
recursiveDNSServer = dnsIPv6[0] // OVN only supports 1 RA DNS server.
27152725
}
27162726

27172727
err = n.ovnnb.UpdateLogicalRouterPort(context.TODO(), n.getRouterIntPortName(), &networkOVN.OVNIPv6RAOpts{
@@ -2763,8 +2773,8 @@ func (n *ovn) setup(update bool) error {
27632773
// Apply baseline ACL rules to internal logical switch.
27642774
dnsServers := []net.IP{}
27652775
if uplinkNet != nil {
2766-
dnsServers = append(dnsServers, uplinkNet.dnsIPv4...)
2767-
dnsServers = append(dnsServers, uplinkNet.dnsIPv6...)
2776+
dnsServers = append(dnsServers, dnsIPv4...)
2777+
dnsServers = append(dnsServers, dnsIPv6...)
27682778
}
27692779

27702780
err = acl.OVNApplyNetworkBaselineRules(n.ovnnb, n.getIntSwitchName(), n.getIntSwitchRouterPortName(), intRouterIPs, dnsServers)

internal/server/network/ovn/ovn_nb_actions.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -1262,14 +1262,18 @@ func (o *NB) UpdateLogicalSwitchDHCPv4Options(ctx context.Context, switchName OV
12621262

12631263
if opts.Router != nil {
12641264
dhcpOption.Options["router"] = opts.Router.String()
1265+
} else {
1266+
delete(dhcpOption.Options, "router")
12651267
}
12661268

12671269
if len(opts.DNSSearchList) > 0 {
12681270
// Special quoting to allow domain names.
12691271
dhcpOption.Options["domain_search_list"] = fmt.Sprintf(`"%s"`, strings.Join(opts.DNSSearchList, ","))
1272+
} else {
1273+
delete(dhcpOption.Options, "domain_search_list")
12701274
}
12711275

1272-
if opts.RecursiveDNSServer != nil {
1276+
if len(opts.RecursiveDNSServer) > 0 {
12731277
nsIPs := make([]string, 0, len(opts.RecursiveDNSServer))
12741278
for _, nsIP := range opts.RecursiveDNSServer {
12751279
if nsIP.To4() == nil {
@@ -1280,19 +1284,27 @@ func (o *NB) UpdateLogicalSwitchDHCPv4Options(ctx context.Context, switchName OV
12801284
}
12811285

12821286
dhcpOption.Options["dns_server"] = fmt.Sprintf("{%s}", strings.Join(nsIPs, ","))
1287+
} else {
1288+
delete(dhcpOption.Options, "dns_server")
12831289
}
12841290

12851291
if opts.DomainName != "" {
12861292
// Special quoting to allow domain names.
12871293
dhcpOption.Options["domain_name"] = fmt.Sprintf(`"%s"`, opts.DomainName)
1294+
} else {
1295+
delete(dhcpOption.Options, "domain_name")
12881296
}
12891297

12901298
if opts.MTU > 0 {
12911299
dhcpOption.Options["mtu"] = fmt.Sprintf("%d", opts.MTU)
1300+
} else {
1301+
delete(dhcpOption.Options, "mtu")
12921302
}
12931303

12941304
if opts.Netmask != "" {
12951305
dhcpOption.Options["netmask"] = opts.Netmask
1306+
} else {
1307+
delete(dhcpOption.Options, "netmask")
12961308
}
12971309

12981310
if opts.StaticRoutes != "" {
@@ -1364,6 +1376,8 @@ func (o *NB) UpdateLogicalSwitchDHCPv6Options(ctx context.Context, switchName OV
13641376
if len(opts.DNSSearchList) > 0 {
13651377
// Special quoting to allow domain names.
13661378
dhcpOption.Options["domain_search"] = fmt.Sprintf(`"%s"`, strings.Join(opts.DNSSearchList, ","))
1379+
} else {
1380+
delete(dhcpOption.Options, "domain_search")
13671381
}
13681382

13691383
if opts.RecursiveDNSServer != nil {
@@ -1377,6 +1391,8 @@ func (o *NB) UpdateLogicalSwitchDHCPv6Options(ctx context.Context, switchName OV
13771391
}
13781392

13791393
dhcpOption.Options["dns_server"] = fmt.Sprintf("{%s}", strings.Join(nsIPs, ","))
1394+
} else {
1395+
delete(dhcpOption.Options, "dns_server")
13801396
}
13811397

13821398
// Prepare the changes.

internal/version/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ var APIExtensions = []string{
468468
"security_iommu",
469469
"network_ipv4_dhcp_routes",
470470
"network_state_ovn_ls",
471+
"network_dns_nameservers",
471472
}
472473

473474
// APIExtensionsCount returns the number of available API extensions.

0 commit comments

Comments
 (0)