1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <netinet/in.h>
4 #include <linux/if_arp.h>
7 #include "sd-dhcp-server.h"
11 #include "networkd-address.h"
12 #include "networkd-dhcp-server.h"
13 #include "networkd-dhcp-server-bus.h"
14 #include "networkd-link.h"
15 #include "networkd-manager.h"
16 #include "networkd-network.h"
17 #include "parse-util.h"
18 #include "socket-netlink.h"
19 #include "string-table.h"
20 #include "string-util.h"
23 static bool link_dhcp4_server_enabled(Link
*link
) {
26 if (link
->flags
& IFF_LOOPBACK
)
32 if (link
->network
->bond
)
35 if (link
->iftype
== ARPHRD_CAN
)
38 return link
->network
->dhcp_server
;
41 static Address
* link_find_dhcp_server_address(Link
*link
) {
45 assert(link
->network
);
47 /* The first statically configured address if there is any */
48 ORDERED_HASHMAP_FOREACH(address
, link
->network
->addresses_by_section
)
49 if (address
->family
== AF_INET
&&
50 in_addr_is_set(address
->family
, &address
->in_addr
))
53 /* If that didn't work, find a suitable address we got from the pool */
54 SET_FOREACH(address
, link
->pool_addresses
)
55 if (address
->family
== AF_INET
)
61 static int link_push_uplink_to_dhcp_server(
63 sd_dhcp_lease_server_type_t what
,
66 _cleanup_free_
struct in_addr
*addresses
= NULL
;
67 size_t n_addresses
= 0, n_allocated
= 0;
68 bool use_dhcp_lease_data
= true;
74 assert(link
->network
);
76 log_link_debug(link
, "Copying %s from link", dhcp_lease_server_type_to_string(what
));
80 case SD_DHCP_LEASE_DNS
:
81 /* For DNS we have a special case. We the data configured explicitly locally along with the
82 * data from the DHCP lease. */
84 for (unsigned i
= 0; i
< link
->network
->n_dns
; i
++) {
87 /* Only look for IPv4 addresses */
88 if (link
->network
->dns
[i
]->family
!= AF_INET
)
91 ia
= link
->network
->dns
[i
]->address
.in
;
93 /* Never propagate obviously borked data */
94 if (in4_addr_is_null(&ia
) || in4_addr_is_localhost(&ia
))
97 if (!GREEDY_REALLOC(addresses
, n_allocated
, n_addresses
+ 1))
100 addresses
[n_addresses
++] = ia
;
103 use_dhcp_lease_data
= link
->network
->dhcp_use_dns
;
106 case SD_DHCP_LEASE_NTP
: {
109 /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot
110 * propagate via DHCP. Hence let's only propagate those which are IP addresses. */
112 STRV_FOREACH(i
, link
->network
->ntp
) {
113 union in_addr_union ia
;
115 if (in_addr_from_string(AF_INET
, *i
, &ia
) < 0)
118 /* Never propagate obviously borked data */
119 if (in4_addr_is_null(&ia
.in
) || in4_addr_is_localhost(&ia
.in
))
122 if (!GREEDY_REALLOC(addresses
, n_allocated
, n_addresses
+ 1))
125 addresses
[n_addresses
++] = ia
.in
;
128 use_dhcp_lease_data
= link
->network
->dhcp_use_ntp
;
132 case SD_DHCP_LEASE_SIP
:
134 /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */
135 use_dhcp_lease_data
= link
->network
->dhcp_use_sip
;
138 case SD_DHCP_LEASE_POP3
:
139 case SD_DHCP_LEASE_SMTP
:
140 case SD_DHCP_LEASE_LPR
:
141 /* For the other server types we currently do not allow local configuration of server data,
142 * since there are typically no local consumers of the data. */
146 assert_not_reached("Unexpected server type");
149 if (use_dhcp_lease_data
&& link
->dhcp_lease
) {
150 const struct in_addr
*da
;
152 int n
= sd_dhcp_lease_get_servers(link
->dhcp_lease
, what
, &da
);
154 if (!GREEDY_REALLOC(addresses
, n_allocated
, n_addresses
+ n
))
157 for (int j
= 0; j
< n
; j
++)
158 if (in4_addr_is_non_local(&da
[j
]))
159 addresses
[n_addresses
++] = da
[j
];
163 if (n_addresses
<= 0)
166 return sd_dhcp_server_set_servers(s
, what
, addresses
, n_addresses
);
169 static int dhcp4_server_parse_dns_server_string_and_warn(Link
*l
, const char *string
, struct in_addr
**addresses
, size_t *n_allocated
, size_t *n_addresses
) {
171 _cleanup_free_
char *word
= NULL
, *server_name
= NULL
;
172 union in_addr_union address
;
173 int family
, r
, ifindex
= 0;
175 r
= extract_first_word(&string
, &word
, NULL
, 0);
181 r
= in_addr_ifindex_name_from_string_auto(word
, &family
, &address
, &ifindex
, &server_name
);
183 log_warning_errno(r
, "Failed to parse DNS server address '%s', ignoring: %m", word
);
187 /* Only look for IPv4 addresses */
188 if (family
!= AF_INET
)
191 /* Never propagate obviously borked data */
192 if (in4_addr_is_null(&address
.in
) || in4_addr_is_localhost(&address
.in
))
195 if (!GREEDY_REALLOC(*addresses
, *n_allocated
, *n_addresses
+ 1))
198 (*addresses
)[(*n_addresses
)++] = address
.in
;
204 static int dhcp4_server_set_dns_from_resolve_conf(Link
*link
) {
205 _cleanup_free_
struct in_addr
*addresses
= NULL
;
206 size_t n_addresses
= 0, n_allocated
= 0;
207 _cleanup_fclose_
FILE *f
= NULL
;
210 f
= fopen(PRIVATE_UPLINK_RESOLV_CONF
, "re");
215 return log_warning_errno(errno
, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF
": %m");
219 _cleanup_free_
char *line
= NULL
;
223 r
= read_line(f
, LONG_LINE_MAX
, &line
);
225 return log_error_errno(r
, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF
": %m");
232 if (IN_SET(*l
, '#', ';', 0))
235 a
= first_word(l
, "nameserver");
239 r
= dhcp4_server_parse_dns_server_string_and_warn(link
, a
, &addresses
, &n_allocated
, &n_addresses
);
241 log_warning_errno(r
, "Failed to parse DNS server address '%s', ignoring.", a
);
244 if (n_addresses
<= 0)
247 return sd_dhcp_server_set_dns(link
->dhcp_server
, addresses
, n_addresses
);
250 int dhcp4_server_configure(Link
*link
) {
251 bool acquired_uplink
= false;
259 if (!link_dhcp4_server_enabled(link
))
262 if (!(link
->flags
& IFF_UP
))
265 if (!link
->dhcp_server
) {
266 r
= sd_dhcp_server_new(&link
->dhcp_server
, link
->ifindex
);
270 r
= sd_dhcp_server_attach_event(link
->dhcp_server
, link
->manager
->event
, 0);
275 r
= sd_dhcp_server_set_callback(link
->dhcp_server
, dhcp_server_callback
, link
);
277 return log_link_warning_errno(link
, r
, "Failed to set callback for DHCPv4 server instance: %m");
279 address
= link_find_dhcp_server_address(link
);
281 return log_link_error_errno(link
, SYNTHETIC_ERRNO(EBUSY
),
282 "Failed to find suitable address for DHCPv4 server instance.");
284 /* use the server address' subnet as the pool */
285 r
= sd_dhcp_server_configure_pool(link
->dhcp_server
, &address
->in_addr
.in
, address
->prefixlen
,
286 link
->network
->dhcp_server_pool_offset
, link
->network
->dhcp_server_pool_size
);
288 return log_link_error_errno(link
, r
, "Failed to configure address pool for DHCPv4 server instance: %m");
291 r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
296 if (link
->network
->dhcp_server_max_lease_time_usec
> 0) {
297 r
= sd_dhcp_server_set_max_lease_time(link
->dhcp_server
,
298 DIV_ROUND_UP(link
->network
->dhcp_server_max_lease_time_usec
, USEC_PER_SEC
));
300 return log_link_error_errno(link
, r
, "Failed to set maximum lease time for DHCPv4 server instance: %m");
303 if (link
->network
->dhcp_server_default_lease_time_usec
> 0) {
304 r
= sd_dhcp_server_set_default_lease_time(link
->dhcp_server
,
305 DIV_ROUND_UP(link
->network
->dhcp_server_default_lease_time_usec
, USEC_PER_SEC
));
307 return log_link_error_errno(link
, r
, "Failed to set default lease time for DHCPv4 server instance: %m");
310 for (sd_dhcp_lease_server_type_t type
= 0; type
< _SD_DHCP_LEASE_SERVER_TYPE_MAX
; type
++) {
312 if (!link
->network
->dhcp_server_emit
[type
].emit
)
315 if (link
->network
->dhcp_server_emit
[type
].n_addresses
> 0)
316 /* Explicitly specified servers to emit */
317 r
= sd_dhcp_server_set_servers(
320 link
->network
->dhcp_server_emit
[type
].addresses
,
321 link
->network
->dhcp_server_emit
[type
].n_addresses
);
323 /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */
324 if (!acquired_uplink
) {
325 uplink
= manager_find_uplink(link
->manager
, link
);
326 acquired_uplink
= true;
329 if (uplink
&& uplink
->network
)
330 r
= link_push_uplink_to_dhcp_server(uplink
, type
, link
->dhcp_server
);
331 else if (type
== SD_DHCP_LEASE_DNS
)
332 r
= dhcp4_server_set_dns_from_resolve_conf(link
);
335 "Not emitting %s on link, couldn't find suitable uplink.",
336 dhcp_lease_server_type_to_string(type
));
342 log_link_warning_errno(link
, r
,
343 "Failed to set %s for DHCP server, ignoring: %m",
344 dhcp_lease_server_type_to_string(type
));
347 r
= sd_dhcp_server_set_bind_to_interface(link
->dhcp_server
, link
->network
->dhcp_server_bind_to_interface
);
349 return log_link_error_errno(link
, r
, "Failed to set interface binding for DHCP server: %m");
351 r
= sd_dhcp_server_set_emit_router(link
->dhcp_server
, link
->network
->dhcp_server_emit_router
);
353 return log_link_error_errno(link
, r
, "Failed to set router emission for DHCP server: %m");
355 if (link
->network
->dhcp_server_emit_timezone
) {
356 _cleanup_free_
char *buffer
= NULL
;
359 if (link
->network
->dhcp_server_timezone
)
360 tz
= link
->network
->dhcp_server_timezone
;
362 r
= get_timezone(&buffer
);
364 return log_link_error_errno(link
, r
, "Failed to determine timezone: %m");
369 r
= sd_dhcp_server_set_timezone(link
->dhcp_server
, tz
);
371 return log_link_error_errno(link
, r
, "Failed to set timezone for DHCP server: %m");
374 ORDERED_HASHMAP_FOREACH(p
, link
->network
->dhcp_server_send_options
) {
375 r
= sd_dhcp_server_add_option(link
->dhcp_server
, p
);
379 return log_link_error_errno(link
, r
, "Failed to set DHCPv4 option: %m");
382 ORDERED_HASHMAP_FOREACH(p
, link
->network
->dhcp_server_send_vendor_options
) {
383 r
= sd_dhcp_server_add_vendor_option(link
->dhcp_server
, p
);
387 return log_link_error_errno(link
, r
, "Failed to set DHCPv4 option: %m");
390 if (!sd_dhcp_server_is_running(link
->dhcp_server
)) {
391 r
= sd_dhcp_server_start(link
->dhcp_server
);
393 return log_link_error_errno(link
, r
, "Could not start DHCPv4 server instance: %m");
395 log_link_debug(link
, "Offering DHCPv4 leases");
401 int config_parse_dhcp_server_emit(
403 const char *filename
,
406 unsigned section_line
,
413 NetworkDHCPServerEmitAddress
*emit
= data
;
418 for (const char *p
= rvalue
;;) {
419 _cleanup_free_
char *w
= NULL
;
420 union in_addr_union a
;
423 r
= extract_first_word(&p
, &w
, NULL
, 0);
427 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
428 "Failed to extract word, ignoring: %s", rvalue
);
434 r
= in_addr_from_string(AF_INET
, w
, &a
);
436 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
437 "Failed to parse %s= address '%s', ignoring: %m", lvalue
, w
);
441 struct in_addr
*m
= reallocarray(emit
->addresses
, emit
->n_addresses
+ 1, sizeof(struct in_addr
));
446 emit
->addresses
[emit
->n_addresses
++] = a
.in
;