dnsmasq: Add DHCPv6 options (#8456)

This commit is contained in:
Monviech 2025-03-21 07:52:18 +01:00 committed by GitHub
parent d4a8efd1b4
commit fcd49fc603
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 98 additions and 20 deletions

View File

@ -3,7 +3,13 @@
<id>match.option</id>
<label>Option</label>
<type>dropdown</type>
<help>Option to offer to the client.</help>
<help>DHCPv4 option to offer to the client.</help>
</field>
<field>
<id>match.option6</id>
<label>Option6</label>
<type>dropdown</type>
<help>DHCPv6 option to offer to the client.</help>
</field>
<field>
<id>match.set_tag</id>

View File

@ -3,13 +3,13 @@
<id>option.option</id>
<label>Option</label>
<type>dropdown</type>
<help>Option to offer to the client.</help>
<help>DHCPv4 option to offer to the client.</help>
</field>
<field>
<id>option.protocol</id>
<label>Protocol</label>
<id>option.option6</id>
<label>Option6</label>
<type>dropdown</type>
<help>The protocol to use for this option. Options used for DHCPv6 require IPv6 to match.</help>
<help>DHCPv6 option to offer to the client.</help>
</field>
<field>
<id>option.interface</id>

View File

@ -162,6 +162,56 @@ class Dnsmasq extends BaseModel
}
}
foreach ($this->dhcp_options->iterateItems() as $option) {
if (!$validateFullModel && !$option->isFieldChanged()) {
continue;
}
$key = $option->__reference;
if (!$option->option->isEmpty() && !$option->option6->isEmpty()) {
$messages->appendMessage(
new Message(
gettext("'Option' and 'Option6' cannot be selected at the same time."),
$key . ".option"
)
);
}
if ($option->option->isEmpty() && $option->option6->isEmpty()) {
$messages->appendMessage(
new Message(
gettext("Either 'Option' or 'Option6' is required."),
$key . ".option"
)
);
}
}
foreach ($this->dhcp_options_match->iterateItems() as $match) {
if (!$validateFullModel && !$match->isFieldChanged()) {
continue;
}
$key = $match->__reference;
if (!$match->option->isEmpty() && !$match->option6->isEmpty()) {
$messages->appendMessage(
new Message(
gettext("'Option' and 'Option6' cannot be selected at the same time."),
$key . ".option"
)
);
}
if ($match->option->isEmpty() && $match->option6->isEmpty()) {
$messages->appendMessage(
new Message(
gettext("Either 'Option' or 'Option6' is required."),
$key . ".option"
)
);
}
}
if (
($validateFullModel || $this->enable->isFieldChanged() || $this->port->isFieldChanged()) &&
!empty((string)$this->enable)

View File

@ -185,14 +185,10 @@
<dhcp_options type="ArrayField">
<option type="JsonKeyValueStoreField">
<ConfigdPopulateAct>dnsmasq list dhcp_options</ConfigdPopulateAct>
<Required>Y</Required>
</option>
<protocol type="OptionField">
<BlankDesc>IPv4</BlankDesc>
<OptionValues>
<option6>IPv6</option6>
</OptionValues>
</protocol>
<option6 type="JsonKeyValueStoreField">
<ConfigdPopulateAct>dnsmasq list dhcp_options6</ConfigdPopulateAct>
</option6>
<interface type="InterfaceField">
<BlankDesc>Any</BlankDesc>
<Filters>
@ -217,8 +213,10 @@
<dhcp_options_match type="ArrayField">
<option type="JsonKeyValueStoreField">
<ConfigdPopulateAct>dnsmasq list dhcp_options</ConfigdPopulateAct>
<Required>Y</Required>
</option>
<option6 type="JsonKeyValueStoreField">
<ConfigdPopulateAct>dnsmasq list dhcp_options6</ConfigdPopulateAct>
</option6>
<set_tag type="ModelRelationField">
<Model>
<tag>

View File

@ -28,13 +28,20 @@
"""
import json
import subprocess
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("mode", nargs="?", default="dhcp", choices=["dhcp", "dhcp6"])
args = parser.parse_args()
result = {}
# not yet registered by name, but pratical to have
# https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
result = {
'114' : 'dhcp captive-portal [114]'
}
sp = subprocess.run(['/usr/local/sbin/dnsmasq', '--help','dhcp'], capture_output=True, text=True)
if args.mode == "dhcp":
result['114'] = 'dhcp captive-portal [114]'
sp = subprocess.run(['/usr/local/sbin/dnsmasq', '--help', args.mode], capture_output=True, text=True)
for line in sp.stdout.split("\n"):
parts = line.split(maxsplit=1)
if len(parts) == 2 and parts[0].isdigit():

View File

@ -20,11 +20,17 @@ type:script_output
message:Request Dnsmasq status
[list.dhcp_options]
command:/usr/local/opnsense/scripts/dns/dnsmasq_dhcp_options.py
command:/usr/local/opnsense/scripts/dns/dnsmasq_dhcp_options.py dhcp
type:script_output
message:request dhcp options
cache_ttl:86400
[list.dhcp_options6]
command:/usr/local/opnsense/scripts/dns/dnsmasq_dhcp_options.py dhcp6
type:script_output
message:request dhcp options6
cache_ttl:86400
[list.leases]
command:/usr/local/opnsense/scripts/dhcp/get_dnsmasq_leases.py
parameters:

View File

@ -186,7 +186,13 @@ dhcp-host={{host.hwaddr}}{% if host.set_tag%},set:{{host.set_tag|replace('-','')
{% if option.interface %}
{% do all_tags.append('tag:' + helpers.physical_interface(option.interface)) %}
{% endif %}
dhcp-option{% if option.force == '1' %}-force{% endif %}={% if all_tags %}{{ all_tags|join(',') }},{% endif %}{% if option.protocol %}{{ option.protocol }}:{% endif %}{{ option.option }},{{ option.value }}
dhcp-option{% if option.force == '1' %}-force{% endif -%}=
{%- if all_tags %}{{ all_tags|join(',') }},{% endif -%}
{%- if option.option6 -%}
option6:{{ option.option6 }},{{ option.value }}
{%- else -%}
{{ option.option }},{{ option.value }}
{%- endif +%}
{% if not option.tag and not option.interface and option.option == '6' %}
{% do has_default.append(1) %}
{% endif %}
@ -198,7 +204,12 @@ dhcp-option=6,0.0.0.0
{% endif %}
{% for match in helpers.toList('dnsmasq.dhcp_options_match') %}
dhcp-match=set:{{match.set_tag.replace('-','')}},{{match.option}}{%if match.value %},{{match.value}}{% endif +%}
dhcp-match=set:{{ match.set_tag.replace('-','') }},
{%- if match.option6 -%}
option6:{{ match.option6 }}{% if match.value %},{{ match.value }}{% endif %}
{%- else -%}
{{ match.option }}{% if match.value %},{{ match.value }}{% endif %}
{%- endif +%}
{% endfor %}
{% if dnsmasq.no_ident == '1' %}