mirror of
https://github.com/lucaspalomodevelop/opnsense-core.git
synced 2026-03-13 00:07:27 +00:00
dnsmasq: Add full dhcp-host support for IPv4 and IPv6 (#8497)
* dnsmasq: Add full dhcp-host support for IPv4 and IPv6 * dnsmasq: Cleanup previous in dnsmasq.inc * dnsmasq: Change comma placement in template to reduce one condition * dnsmasq: Add validation to client_id * dnsmasq: There can be multiple hardware addresses so change label accordingly * dnsmasq: Change hostname validation so that client_id is also a valid choice without hostname defined. * dnsmasq: Add validation that prevents duplicate IP addresses in dhcp-host set * remove one stray newline * Services: Dnsmasq DNS & DHCP - minor cleanups in https://github.com/opnsense/core/pull/8497 o fix possible race condition in validations o simplify jinja template --------- Co-authored-by: Ad Schellevis <ad@opnsense.org>
This commit is contained in:
parent
ad09e7aa6c
commit
8d6ca1fa98
@ -180,11 +180,13 @@ function _dnsmasq_add_host_entries()
|
||||
}
|
||||
|
||||
foreach ($dnsmasqcfg['hosts'] as $host) {
|
||||
/* The host.ip field supports multiple IPv4 and IPv6 addresses */
|
||||
foreach (explode(',', $host['ip']) as $ip) {
|
||||
if (!empty($host['host']) && empty($host['domain'])) {
|
||||
$lhosts .= "{$host['ip']}\t{$host['host']}\n";
|
||||
$lhosts .= "{$ip}\t{$host['host']}\n";
|
||||
} elseif (!empty($host['host'])) {
|
||||
/* XXX: The question is if we do want "host" as a global alias */
|
||||
$lhosts .= "{$host['ip']}\t{$host['host']}.{$host['domain']} {$host['host']}\n";
|
||||
$lhosts .= "{$ip}\t{$host['host']}.{$host['domain']} {$host['host']}\n";
|
||||
}
|
||||
if (!empty($host['aliases'])) {
|
||||
foreach (explode(",", $host['aliases']) as $alias) {
|
||||
@ -192,7 +194,8 @@ function _dnsmasq_add_host_entries()
|
||||
* XXX: pre migration all hosts where added here as alias, when we combine host.domain we
|
||||
* miss some information, which is likely not a very good idea anyway.
|
||||
*/
|
||||
$lhosts .= "{$host['ip']}\t{$alias}\n";
|
||||
$lhosts .= "{$ip}\t{$alias}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,15 +17,18 @@
|
||||
</field>
|
||||
<field>
|
||||
<id>host.ip</id>
|
||||
<label>IP address</label>
|
||||
<type>text</type>
|
||||
<help>IP address of the host, e.g. 192.168.100.100 or fd00:abcd::1</help>
|
||||
<label>IP addresses</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help>IP addresses of the host, e.g. 192.168.100.100 or fd00:abcd::1. Can be multiple IPv4 and IPv6 addresses for dual stack configurations. Setting multiple addresses will automatically assign the best match based on the subnet of the interface receiving the DHCP Discover.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.aliases</id>
|
||||
<label>Aliases</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help>list of aliases (fqdn)</help>
|
||||
<grid_view>
|
||||
<visible>false</visible>
|
||||
@ -36,16 +39,48 @@
|
||||
<label>DHCP</label>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.hwaddr</id>
|
||||
<label>Hardware address</label>
|
||||
<id>host.client_id</id>
|
||||
<label>Client identifier</label>
|
||||
<type>text</type>
|
||||
<help>When offered and the client requests an address via dhcp, assign the address provided here</help>
|
||||
<help>Match the identifier of the client, e.g., DUID for DHCPv6. Setting the special character "*" will ignore the client identifier for DHCPv4 leases if a client offers both as choice.</help>
|
||||
<grid_view>
|
||||
<visible>false</visible>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.hwaddr</id>
|
||||
<label>Hardware addresses</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<allownew>true</allownew>
|
||||
<help>Match the hardware address of the client. Can be multiple addresses, e.g., if the client has multiple network cards. Though keep in mind that DNSmasq cannot assume which address is the correct one when multiple send DHCP Discover at the same time.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.lease_time</id>
|
||||
<label>Lease time</label>
|
||||
<type>text</type>
|
||||
<hint>86400</hint>
|
||||
<help>Defines how long the addresses (leases) given out by the server are valid (in seconds)</help>
|
||||
<grid_view>
|
||||
<visible>false</visible>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.set_tag</id>
|
||||
<label>Tag [set]</label>
|
||||
<type>dropdown</type>
|
||||
<help>Optional tag to set for requests matching this range which can be used to selectively match dhcp options</help>
|
||||
<help>Optional tag to set for requests matching this range which can be used to selectively match dhcp options. Can be left empty if options with an interface tag exist, since the client automatically receives this tag based on the interface receiving the DHCP Discover.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.ignore</id>
|
||||
<label>Ignore</label>
|
||||
<type>checkbox</type>
|
||||
<help>Ignore any DHCP packets of this host. Useful if it should get served by a different DHCP server.</help>
|
||||
<grid_view>
|
||||
<visible>false</visible>
|
||||
<type>boolean</type>
|
||||
<formatter>boolean</formatter>
|
||||
</grid_view>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
|
||||
@ -55,24 +55,46 @@ class Dnsmasq extends BaseModel
|
||||
|
||||
$messages = parent::performValidation($validateFullModel);
|
||||
|
||||
$usedDhcpIpAddresses = [];
|
||||
foreach ($this->hosts->iterateItems() as $host) {
|
||||
if (!$host->hwaddr->isEmpty() || !$host->client_id->isEmpty()) {
|
||||
foreach (explode(',', (string)$host->ip) as $ip) {
|
||||
$usedDhcpIpAddresses[$ip] = isset($usedDhcpIpAddresses[$ip]) ? $usedDhcpIpAddresses[$ip] + 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->hosts->iterateItems() as $host) {
|
||||
if (!$validateFullModel && !$host->isFieldChanged()) {
|
||||
continue;
|
||||
}
|
||||
$key = $host->__reference;
|
||||
if (!$host->hwaddr->isEmpty() && strpos($host->ip->getCurrentValue(), ':') !== false) {
|
||||
|
||||
// all dhcp-host IP addresses must be unique, host overrides can have duplicate IP addresses
|
||||
if (!$host->hwaddr->isEmpty() || !$host->client_id->isEmpty()) {
|
||||
foreach (explode(',', (string)$host->ip) as $ip) {
|
||||
if ($usedDhcpIpAddresses[$ip] > 1) {
|
||||
$messages->appendMessage(
|
||||
new Message(
|
||||
gettext("Only IPv4 reservations are currently supported"),
|
||||
sprintf(gettext("'%s' is already used in another DHCP host entry."), $ip),
|
||||
$key . ".ip"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($host->host->isEmpty() && $host->hwaddr->isEmpty()) {
|
||||
if (
|
||||
$host->host->isEmpty() &&
|
||||
$host->hwaddr->isEmpty() &&
|
||||
$host->client_id->isEmpty()
|
||||
) {
|
||||
$messages->appendMessage(
|
||||
new Message(
|
||||
gettext("Hostnames my only be omitted when a hardware address is offered."),
|
||||
gettext(
|
||||
"Hostnames may only be omitted when either a hardware address " .
|
||||
"or a client identifier is provided."
|
||||
),
|
||||
$key . ".host"
|
||||
)
|
||||
);
|
||||
|
||||
@ -82,8 +82,21 @@
|
||||
<ip type="NetworkField">
|
||||
<Required>Y</Required>
|
||||
<NetMaskAllowed>N</NetMaskAllowed>
|
||||
<FieldSeparator>,</FieldSeparator>
|
||||
<AsList>Y</AsList>
|
||||
</ip>
|
||||
<hwaddr type="MacAddressField"/>
|
||||
<client_id type="TextField">
|
||||
<Mask>/^(?:\*|(?:[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2})+))$/</Mask>
|
||||
<ValidationMessage>Value must be a colon-separated hexadecimal sequence (e.g., 01:02:f3) or "*".</ValidationMessage>
|
||||
</client_id>
|
||||
<hwaddr type="MacAddressField">
|
||||
<FieldSeparator>,</FieldSeparator>
|
||||
<AsList>Y</AsList>
|
||||
</hwaddr>
|
||||
<lease_time type="IntegerField">
|
||||
<MinimumValue>1</MinimumValue>
|
||||
</lease_time>
|
||||
<ignore type="BooleanField"/>
|
||||
<set_tag type="ModelRelationField">
|
||||
<Model>
|
||||
<tag>
|
||||
|
||||
@ -186,9 +186,32 @@ ra-param={{ helpers.physical_interface(dhcp_range.interface) }}
|
||||
{% endfor %}
|
||||
|
||||
{% for host in helpers.toList('dnsmasq.hosts') %}
|
||||
{% if host.hwaddr and host.hwaddr.find(':') == -1%}
|
||||
dhcp-host={{host.hwaddr}}{% if host.set_tag%},set:{{host.set_tag|replace('-','')}}{% endif %},{{host.ip}}
|
||||
{# Skip if MAC is missing or invalid (no colon), unless client_id is present #}
|
||||
{% if not host.client_id and (not host.hwaddr or host.hwaddr.find(':') == -1) %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
dhcp-host=
|
||||
{%- if host.client_id -%}
|
||||
id:{{ host.client_id }},
|
||||
{%- endif -%}
|
||||
{%- if host.hwaddr -%}
|
||||
{{ host.hwaddr }},
|
||||
{%- endif -%}
|
||||
{%- if host.set_tag -%}
|
||||
set:{{ host.set_tag|replace('-', '') }},
|
||||
{%- endif -%}
|
||||
{%- for ip in host.ip.split(',')|map('trim') if host.ip -%}
|
||||
{{ '[' ~ ip ~ ']' if ':' in ip else ip }}{%- if not loop.last %},{% endif %}
|
||||
{%- endfor -%}
|
||||
{%- if host.host -%}
|
||||
,{{ host.host }}
|
||||
{%- endif -%}
|
||||
{%- if host.lease_time -%}
|
||||
,{{ host.lease_time }}{% else %},86400
|
||||
{%- endif -%}
|
||||
{%- if host.ignore|default('0') == '1' -%}
|
||||
,ignore
|
||||
{%- endif +%}
|
||||
{% endfor %}
|
||||
|
||||
{% set has_default=[] %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user