Skip to content

Commit e7901ab

Browse files
authored
Merge pull request systemd#19611 from yuwata/network-dhcp-server-introduce-server-address
network: dhcp-server: introduce ServerAddress= setting
2 parents 734b311 + 72ffb91 commit e7901ab

18 files changed

+280
-136
lines changed

man/systemd.network.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,6 +2344,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
23442344

23452345
<variablelist class='network-directives'>
23462346

2347+
<varlistentry>
2348+
<term><varname>ServerAddress=</varname></term>
2349+
<listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
2350+
length, e.g., <literal>192.168.0.1/24</literal>. This setting may be useful when the link which
2351+
DHCP server running on has multiple static addresses. When unset, one of static addresses in
2352+
the link will be automatically selected. Defaults to unset.</para></listitem>
2353+
</varlistentry>
2354+
23472355
<varlistentry>
23482356
<term><varname>PoolOffset=</varname></term>
23492357
<term><varname>PoolSize=</varname></term>

src/network/networkd-address-label.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ int config_parse_address_label(
225225
return 0;
226226
}
227227

228-
if (k == 0xffffffffUL) {
228+
if (k == UINT32_C(0xffffffff)) {
229229
log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue);
230230
return 0;
231231
}

src/network/networkd-address.c

Lines changed: 97 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,38 @@ Address *address_free(Address *address) {
146146
static bool address_may_have_broadcast(const Address *a) {
147147
assert(a);
148148

149+
if (a->family != AF_INET)
150+
return false;
151+
152+
if (in4_addr_is_set(&a->in_addr_peer.in))
153+
return false;
154+
149155
/* A /31 or /32 IPv4 address does not have a broadcast address.
150156
* See https://tools.ietf.org/html/rfc3021 */
157+
if (a->prefixlen > 30)
158+
return false;
159+
160+
if (a->set_broadcast >= 0)
161+
return a->set_broadcast;
162+
163+
return true; /* Defaults to true. */
164+
}
165+
166+
void address_set_broadcast(Address *a) {
167+
assert(a);
168+
169+
if (!address_may_have_broadcast(a))
170+
return;
151171

152-
return a->family == AF_INET &&
153-
in4_addr_is_null(&a->in_addr_peer.in) &&
154-
a->prefixlen <= 30;
172+
/* If explicitly configured, do not update the address. */
173+
if (in4_addr_is_set(&a->broadcast))
174+
return;
175+
176+
/* If Address= is 0.0.0.0, then the broadcast address will be set later in address_acquire(). */
177+
if (in4_addr_is_null(&a->in_addr.in))
178+
return;
179+
180+
a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen);
155181
}
156182

157183
static bool address_may_set_broadcast(const Address *a, const Link *link) {
@@ -161,9 +187,6 @@ static bool address_may_set_broadcast(const Address *a, const Link *link) {
161187
if (!address_may_have_broadcast(a))
162188
return false;
163189

164-
if (a->set_broadcast >= 0)
165-
return a->set_broadcast;
166-
167190
/* Typical configuration for wireguard does not set broadcast. */
168191
return !streq_ptr(link->kind, "wireguard");
169192
}
@@ -466,7 +489,7 @@ int address_get(Link *link, const Address *in, Address **ret) {
466489
return -ENOENT;
467490
}
468491

469-
int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
492+
int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) {
470493
_cleanup_(address_freep) Address *a = NULL;
471494
int r;
472495

@@ -482,10 +505,10 @@ int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
482505
a->family = AF_INET6;
483506
a->in_addr.in6 = *address;
484507

485-
return address_get(link, a, NULL) >= 0;
508+
return address_get(link, a, ret);
486509
}
487510

488-
static int link_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
511+
static int addresses_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
489512
Address *a;
490513

491514
assert(address);
@@ -506,26 +529,48 @@ static int link_get_ipv4_address(Set *addresses, const struct in_addr *address,
506529
return -ENOENT;
507530
}
508531

532+
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) {
533+
int r;
534+
535+
assert(link);
536+
assert(address);
537+
538+
if (prefixlen != 0) {
539+
_cleanup_(address_freep) Address *a = NULL;
540+
541+
/* If prefixlen is set, then we can use address_get(). */
542+
543+
r = address_new(&a);
544+
if (r < 0)
545+
return r;
546+
547+
a->family = AF_INET;
548+
a->in_addr.in = *address;
549+
a->prefixlen = prefixlen;
550+
551+
return address_get(link, a, ret);
552+
}
553+
554+
if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0)
555+
return 0;
556+
return addresses_get_ipv4_address(link->addresses_foreign, address, ret);
557+
}
558+
509559
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
560+
Address *a;
510561
Link *link;
511562
int r;
512563

513564
assert(manager);
514565
assert(IN_SET(family, AF_INET, AF_INET6));
515566
assert(address);
516567

517-
if (family == AF_INET)
518-
HASHMAP_FOREACH(link, manager->links) {
519-
Address *a;
520-
521-
if (link_get_ipv4_address(link->addresses, &address->in, &a) >= 0)
522-
return !check_ready || address_is_ready(a);
523-
if (link_get_ipv4_address(link->addresses_foreign, &address->in, &a) >= 0)
568+
if (family == AF_INET) {
569+
HASHMAP_FOREACH(link, manager->links)
570+
if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0)
524571
return !check_ready || address_is_ready(a);
525-
}
526-
else {
572+
} else {
527573
_cleanup_(address_freep) Address *tmp = NULL;
528-
Address *a;
529574

530575
r = address_new(&tmp);
531576
if (r < 0)
@@ -825,7 +870,6 @@ int link_drop_addresses(Link *link) {
825870

826871
static int address_acquire(Link *link, const Address *original, Address **ret) {
827872
union in_addr_union in_addr = IN_ADDR_NULL;
828-
struct in_addr broadcast = {};
829873
_cleanup_(address_freep) Address *na = NULL;
830874
int r;
831875

@@ -847,16 +891,10 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
847891
if (r == 0)
848892
return -EBUSY;
849893

850-
if (original->family == AF_INET) {
851-
/* Pick first address in range for ourselves ... */
894+
/* Pick first address in range for ourselves. */
895+
if (original->family == AF_INET)
852896
in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
853-
854-
/* .. and use last as broadcast address */
855-
if (original->prefixlen > 30)
856-
broadcast.s_addr = 0;
857-
else
858-
broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
859-
} else if (original->family == AF_INET6)
897+
else if (original->family == AF_INET6)
860898
in_addr.in6.s6_addr[15] |= 1;
861899

862900
r = address_new(&na);
@@ -867,8 +905,8 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
867905
if (r < 0)
868906
return r;
869907

870-
na->broadcast = broadcast;
871908
na->in_addr = in_addr;
909+
address_set_broadcast(na);
872910

873911
r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na);
874912
if (r < 0)
@@ -1119,6 +1157,32 @@ int link_request_static_addresses(Link *link) {
11191157
req->after_configure = static_address_after_configure;
11201158
}
11211159

1160+
if (in4_addr_is_set(&link->network->dhcp_server_address)) {
1161+
_cleanup_(address_freep) Address *address = NULL;
1162+
1163+
r = address_new(&address);
1164+
if (r < 0)
1165+
return log_oom();
1166+
1167+
address->family = AF_INET;
1168+
address->in_addr.in = link->network->dhcp_server_address;
1169+
address->prefixlen = link->network->dhcp_server_address_prefixlen;
1170+
address_set_broadcast(address);
1171+
1172+
/* The same address may be explicitly configured in [Address] or [Network] section.
1173+
* Configure the DHCP server address only when it is not. */
1174+
if (!link_is_static_address_configured(link, address)) {
1175+
Request *req;
1176+
1177+
r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
1178+
static_address_handler, &req);
1179+
if (r < 0)
1180+
return r;
1181+
1182+
req->after_configure = static_address_after_configure;
1183+
}
1184+
}
1185+
11221186
if (link->static_address_messages == 0) {
11231187
link->static_addresses_configured = true;
11241188
link_check_ready(link);
@@ -1960,10 +2024,9 @@ static int address_section_verify(Address *address) {
19602024
address->section->filename, address->section->line);
19612025
}
19622026

1963-
if (address_may_have_broadcast(address)) {
1964-
if (address->broadcast.s_addr == 0 && address->set_broadcast != 0)
1965-
address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(0xfffffffflu >> address->prefixlen);
1966-
} else if (address->broadcast.s_addr != 0) {
2027+
if (address_may_have_broadcast(address))
2028+
address_set_broadcast(address);
2029+
else if (address->broadcast.s_addr != 0) {
19672030
log_warning("%s: broadcast address is set for IPv6 address or IPv4 address with prefixlength larger than 30. "
19682031
"Ignoring Broadcast= setting in the [Address] section from line %u.",
19692032
address->section->filename, address->section->line);

src/network/networkd-address.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Lin
5757
int address_remove(const Address *address, Link *link, link_netlink_message_handler_t callback);
5858
bool address_equal(const Address *a1, const Address *a2);
5959
bool address_is_ready(const Address *a);
60+
void address_set_broadcast(Address *a);
6061

6162
int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret);
6263

@@ -65,7 +66,8 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
6566
int link_drop_addresses(Link *link);
6667
int link_drop_foreign_addresses(Link *link);
6768
bool link_address_is_dynamic(const Link *link, const Address *address);
68-
int link_has_ipv6_address(Link *link, const struct in6_addr *address);
69+
int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret);
70+
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
6971
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
7072

7173
void ipv4_dad_unref(Link *link);

0 commit comments

Comments
 (0)