dnsmasq: Add router advertisement options (#8449)

* dnsmasq: Add router advertisement options

* dnsmasq: Add router advertisement options

* dnsmasq: Add router advertisement options, fix small issue with spurious newline

* dnsmasq: Selectively show and hide dhcpv4 and dhcpv6 related options without introducing a new selectpicker. Since the validation in dnsmasq.php uses : to determine IPv6, the front end behaves the same.

* dnsmasq: Fix small style issue in previous

* dnsmasq: Since router advertisements are not opt-out per dhcpv6 range, it makes more sense not to enable it implicitely, but explicitely as global option.

* dnsmasq: Add validation for ra_mode, fix template spot of ra_mode, improve help text

* dnsmasq: Clear helptext up regarding SLAAC and prefix length. Template ignores this by default when ra is enabled.

* dnsmasq: Fix terminology of ra-param and add MTU option for feature completeness

* Update src/opnsense/mvc/app/controllers/OPNsense/Dnsmasq/forms/dialogDHCPrange.xml

Co-authored-by: Franco Fichtner <franco@opnsense.org>

* Update src/opnsense/mvc/app/controllers/OPNsense/Dnsmasq/forms/dialogDHCPrange.xml

Co-authored-by: Franco Fichtner <franco@opnsense.org>

---------

Co-authored-by: Franco Fichtner <franco@opnsense.org>
This commit is contained in:
Monviech 2025-03-21 08:52:16 +01:00 committed by GitHub
parent fcd49fc603
commit 01f364e1bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 160 additions and 10 deletions

View File

@ -15,7 +15,7 @@
<id>range.start_addr</id>
<label>Start address</label>
<type>text</type>
<help>Start of the range, e.g. 192.168.1.100, 2000::1 or when constructor is used a partial like ::1.</help>
<help>Start of the range, e.g. 192.168.1.100 for DHCPv4, 2000::1 for DHCPv6 or when a constructor is using a suffix like ::1. To reveal IPv6 related options, enter a IPv6 address. When using router advertisements, it is possible to use a constructor with :: as the start address and no end address.</help>
</field>
<field>
<id>range.end_addr</id>
@ -27,19 +27,81 @@
<id>range.constructor</id>
<label>Constructor</label>
<type>dropdown</type>
<help>Interface to use to calculate the proper range, when selected, a range maybe specified as partial (e.g. ::1, ::400)</help>
<style>selectpicker style_dhcpv6</style>
<help>Interface to use to calculate the proper range, when selected, a range may be specified as a suffix (e.g. ::1, ::400)</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.prefix_len</id>
<label>Prefix length (ipv6)</label>
<type>text</type>
<hint>64</hint>
<help>Prefix length offered to the client.</help>
<style>style_dhcpv6</style>
<help>Prefix length offered to the client. Custom values in this field will be ignored if Router Advertisements are enabled, as SLAAC will only work with a prefix length of 64.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.ra_mode</id>
<label>RA Mode</label>
<type>select_multiple</type>
<hint>Default</hint>
<style>selectpicker style_dhcpv6</style>
<help>Control how IPv6 clients receive their addresses. Enabling Router Advertisements in general settings will enable it for all configured DHCPv6 ranges with the managed address bits set, and the use SLAAC bit reset. To change this default, select a combination of the possible options here. "slaac", "ra-stateless" and "ra-names" can be freely combined, all other options shall remain single selections.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.ra_priority</id>
<label>RA Priority</label>
<type>dropdown</type>
<help>Priority of the RA announcements.</help>
<style>selectpicker style_dhcpv6</style>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.ra_mtu</id>
<label>RA MTU</label>
<type>text</type>
<style>style_dhcpv6</style>
<help>Optional MTU to send to clients via Router Advertisements. If unsure leave empty.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.ra_interval</id>
<label>RA Interval</label>
<type>text</type>
<hint>60</hint>
<style>style_dhcpv6</style>
<help>Time (seconds) between Router Advertisements.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.ra_router_lifetime</id>
<label>RA Router Lifetime</label>
<type>text</type>
<hint>1200</hint>
<style>style_dhcpv6</style>
<help>The lifetime of the route may be changed or set to zero, which allows a router to advertise prefixes but not a route via itself. When using HA, setting a short timespan here is adviced for faster IPv6 failover. A good combination could be 10 seconds RA interval and 30 seconds RA router lifetime. Going lower than that can pose issues in busy networks.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>range.mode</id>
<label>Mode</label>
<type>select_multiple</type>
<style>selectpicker style_dhcpv4</style>
<help>Mode flags to set for this range, 'static' means no addresses will be automatically assigned. </help>
</field>
<field>

View File

@ -178,6 +178,12 @@
<type>checkbox</type>
<help>Automatically register firewall rules to allow dhcp traffic for all explicitly selected interfaces, can be disabled for more fine grained control if needed. Changes are only effective after a firewall service restart (see system diagnostics).</help>
</field>
<field>
<id>dnsmasq.dhcp.enable_ra</id>
<label>Router Advertisements</label>
<type>checkbox</type>
<help>Setting this will enable Router Advertisements for all configured DHCPv6 ranges with the managed address bits set, and the use SLAAC bit reset. To change this default, select a combination of the possible options in the individual DHCPv6 ranges. Keep in mind that this is a global option; if there are configured DHCPv6 ranges, RAs will be sent unconditionally and cannot be deactivated selectively. Setting Router Advertisement modes in DHCPv6 ranges will have no effect without this global option enabled.</help>
</field>
<field>
<id>dnsmasq.dhcp.nosync</id>
<label>Disable HA sync</label>

View File

@ -160,6 +160,39 @@ class Dnsmasq extends BaseModel
)
);
}
// Validate RA mode combinations
$valid_ra_mode_combinations = [
['ra-names', 'slaac'],
['ra-names', 'ra-stateless'],
['slaac', 'ra-stateless'],
['ra-names', 'slaac', 'ra-stateless']
];
$selected_ra_modes = explode(',', $range->ra_mode);
// If only one mode is selected, it is always valid
if (count($selected_ra_modes) > 1) {
$is_ra_mode_valid = false;
foreach ($valid_ra_mode_combinations as $ra_mode_combination) {
// Ensure order independant comparing
if (empty(array_diff($selected_ra_modes, $ra_mode_combination)) &&
empty(array_diff($ra_mode_combination, $selected_ra_modes))) {
$is_ra_mode_valid = true;
break;
}
}
if (!$is_ra_mode_valid) {
$messages->appendMessage(
new Message(
gettext("Invalid RA mode combination."),
$key . ".ra_mode"
)
);
}
}
}
foreach ($this->dhcp_options->iterateItems() as $option) {

View File

@ -1,6 +1,6 @@
<model>
<mount>/dnsmasq</mount>
<version>1.0.3</version>
<version>1.0.4</version>
<items>
<enable type="BooleanField"/>
<regdhcp type="BooleanField"/>
@ -63,6 +63,7 @@
<MinimumValue>0</MinimumValue>
<MaximumValue>60</MaximumValue>
</reply_delay>
<enable_ra type="BooleanField"/>
<nosync type="BooleanField"/>
</dhcp>
<no_ident type="BooleanField">
@ -180,6 +181,27 @@
<IpAllowed>N</IpAllowed>
</domain>
<nosync type="BooleanField"/>
<ra_mode type="OptionField">
<OptionValues>
<ra-only>ra-only</ra-only>
<slaac>slaac</slaac>
<ra-names>ra-names</ra-names>
<ra-stateless>ra-stateless</ra-stateless>
<ra-advrouter>ra-advrouter</ra-advrouter>
<off-link>off-link</off-link>
</OptionValues>
<Multiple>Y</Multiple>
</ra_mode>
<ra_priority type="OptionField">
<BlankDesc>Normal</BlankDesc>
<OptionValues>
<high>High</high>
<low>Low</low>
</OptionValues>
</ra_priority>
<ra_mtu type="IntegerField"/>
<ra_interval type="IntegerField"/>
<ra_router_lifetime type="IntegerField"/>
<description type="DescriptionField"/>
</dhcp_ranges>
<dhcp_options type="ArrayField">

View File

@ -124,6 +124,18 @@
});
let selected_tab = window.location.hash != "" ? window.location.hash : "#general";
$('a[href="' +selected_tab + '"]').click();
$("#range\\.start_addr").on("keyup change", function() {
let value = $(this).val() || "";
if (value.includes(":")) {
$(".style_dhcpv6").closest('tr').show();
$(".style_dhcpv4").closest('tr').hide();
} else {
$(".style_dhcpv6").closest('tr').hide();
$(".style_dhcpv4").closest('tr').show();
}
});
});
</script>

View File

@ -42,6 +42,10 @@ dhcp-authoritative
dhcp-reply-delay={{dnsmasq.dhcp.reply_delay}}
{% endif %}
{# Globally enable RA #}
{% if dnsmasq.dhcp.enable_ra|default('0') == '1' %}
enable-ra
{% endif %}
{% if dnsmasq.strictbind == '1' %}
# On systems which support it, dnsmasq binds the wildcard address,
@ -123,7 +127,6 @@ local-ttl={{dnsmasq.local_ttl|default('1')}}
conf-dir=/usr/local/etc/dnsmasq.conf.d,\*.conf
{% for dhcp_range in helpers.toList('dnsmasq.dhcp_ranges') %}
{% if dhcp_range.start_addr and ':' not in dhcp_range.start_addr %}
{# IPv4 range #}
@ -142,7 +145,7 @@ set:{{dhcp_range.set_tag|replace('-','')}},
{%- endif -%}
{{dhcp_range.lease_time|default('86400')}}
{% else %}
{# IPv6 range #}
{# IPv6 range and RA #}
dhcp-range={%if dhcp_range.interface -%}
tag:{{helpers.physical_interface(dhcp_range.interface)}},
{%- endif -%}
@ -159,13 +162,25 @@ constructor:{{helpers.physical_interface(dhcp_range.constructor)}},
{%- if dhcp_range.mode -%}
{{dhcp_range.mode}},
{%- endif -%}
{%- if dhcp_range.prefix_len -%}
{{ dhcp_range.prefix_len }},
{%- endif -%}
{{dhcp_range.lease_time|default('86400')}}
{#- Using a prefix other than 64 is illegal when slaac is used. -#}
{%- if not dhcp_range.ra_mode and dnsmasq.dhcp.enable_ra|default('0') == '0' -%}
{{ dhcp_range.prefix_len|default('64') }},
{%- endif %}
{%- if dhcp_range.ra_mode %}{{ dhcp_range.ra_mode }},{% endif %}
{{ dhcp_range.lease_time|default('86400') }}
{% endif %}
{% if dhcp_range.domain %}
domain={{ dhcp_range.domain }},{{dhcp_range.start_addr}},{{dhcp_range.end_addr}}
{% endif %}
{% if dhcp_range.ra_mode %}
ra-param={{ helpers.physical_interface(dhcp_range.constructor) }}
{%- if dhcp_range.ra_mtu %},mtu:{{ dhcp_range.ra_mtu }}{% endif -%}
{%- if dhcp_range.ra_priority %},{{ dhcp_range.ra_priority }}{% endif -%},
{%- if dhcp_range.ra_interval %}{{ dhcp_range.ra_interval }}{% else %}60{% endif -%},
{%- if dhcp_range.ra_router_lifetime %}{{ dhcp_range.ra_router_lifetime }}{% else %}1200{% endif +%}
{% endif %}
{% endfor %}