X Tutup
Skip to content

Commit c95df58

Browse files
yegor-alexeyevkeszybz
authored andcommitted
relay role implementation
1 parent d5bfddf commit c95df58

17 files changed

+222
-21
lines changed

man/systemd.network.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2294,6 +2294,17 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
22942294

22952295
<variablelist class='network-directives'>
22962296

2297+
<varlistentry>
2298+
<term><varname>RelayTarget=</varname></term>
2299+
<listitem>
2300+
<para>Takes an IPv4 address, which must be in the format
2301+
described in
2302+
<citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
2303+
Turns this DHCP server into a DHCP relay agent. See <ulink url="https://tools.ietf.org/html/rfc1542">RFC 1542</ulink>.
2304+
The address is the address of DHCP server or another relay agent to forward DHCP messages to and from.</para>
2305+
Check also BindToInterface= option. Turning it off is required for relaying messages outside.
2306+
</listitem>
2307+
</varlistentry>
22972308
<varlistentry>
22982309
<term><varname>PoolOffset=</varname></term>
22992310
<term><varname>PoolSize=</varname></term>

src/libsystemd-network/dhcp-server-internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef struct DHCPLease {
3939
} DHCPLease;
4040

4141
struct sd_dhcp_server {
42+
struct in_addr relay_target;
4243
unsigned n_ref;
4344

4445
sd_event *event;

src/libsystemd-network/sd-dhcp-server.c

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ int sd_dhcp_server_is_running(sd_dhcp_server *server) {
114114
return !!server->receive_message;
115115
}
116116

117+
int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
118+
assert_return(server, -EINVAL);
119+
120+
return in4_addr_is_set(&server->relay_target);
121+
}
122+
117123
void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
118124
assert(id);
119125
assert(id->length);
@@ -343,10 +349,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
343349
return 0;
344350
}
345351

346-
static bool requested_broadcast(DHCPRequest *req) {
347-
assert(req);
352+
static bool requested_broadcast(DHCPMessage *message) {
353+
assert(message);
354+
return message->flags & htobe16(0x8000);
355+
}
348356

349-
return req->message->flags & htobe16(0x8000);
357+
static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
358+
DHCPPacket *packet, size_t optoffset, bool l2_broadcast) {
359+
if (destination != INADDR_ANY)
360+
return dhcp_server_send_udp(server, destination,
361+
destination_port, &packet->dhcp,
362+
sizeof(DHCPMessage) + optoffset);
363+
else if (l2_broadcast)
364+
return dhcp_server_send_udp(server, INADDR_BROADCAST,
365+
destination_port, &packet->dhcp,
366+
sizeof(DHCPMessage) + optoffset);
367+
else
368+
/* we cannot send UDP packet to specific MAC address when the
369+
address is not yet configured, so must fall back to raw
370+
packets */
371+
return dhcp_server_send_unicast_raw(server, packet,
372+
sizeof(DHCPPacket) + optoffset);
350373
}
351374

352375
int dhcp_server_send_packet(sd_dhcp_server *server,
@@ -404,20 +427,8 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
404427
} else if (req->message->ciaddr && type != DHCP_NAK)
405428
destination = req->message->ciaddr;
406429

407-
if (destination != INADDR_ANY)
408-
return dhcp_server_send_udp(server, destination,
409-
destination_port, &packet->dhcp,
410-
sizeof(DHCPMessage) + optoffset);
411-
else if (requested_broadcast(req) || type == DHCP_NAK)
412-
return dhcp_server_send_udp(server, INADDR_BROADCAST,
413-
destination_port, &packet->dhcp,
414-
sizeof(DHCPMessage) + optoffset);
415-
else
416-
/* we cannot send UDP packet to specific MAC address when the
417-
address is not yet configured, so must fall back to raw
418-
packets */
419-
return dhcp_server_send_unicast_raw(server, packet,
420-
sizeof(DHCPPacket) + optoffset);
430+
bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
431+
return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
421432
}
422433

423434
static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
@@ -701,6 +712,47 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
701712
return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
702713
}
703714

715+
static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length) {
716+
_cleanup_free_ DHCPPacket *packet = NULL;
717+
718+
assert(server);
719+
assert(message);
720+
assert(sd_dhcp_server_is_in_relay_mode(server));
721+
722+
if (message->op == BOOTREQUEST) {
723+
log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
724+
if (message->hops >= 16)
725+
return -ETIME;
726+
message->hops++;
727+
728+
/* https://tools.ietf.org/html/rfc1542#section-4.1.1 */
729+
if (message->giaddr == 0)
730+
message->giaddr = server->address;
731+
732+
return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length);
733+
} else if (message->op == BOOTREPLY) {
734+
log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid));
735+
if (message->giaddr != server->address) {
736+
return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
737+
"(relay agent) BOOTREPLY giaddr mismatch, discarding");
738+
}
739+
740+
int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL);
741+
if (message_type < 0)
742+
return message_type;
743+
744+
packet = malloc0(sizeof(DHCPPacket) + opt_length);
745+
if (!packet)
746+
return -ENOMEM;
747+
memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length);
748+
749+
bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
750+
const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
751+
return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
752+
}
753+
return -EBADMSG;
754+
}
755+
704756
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
705757

706758
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
@@ -999,10 +1051,15 @@ static int server_receive_message(sd_event_source *s, int fd,
9991051
}
10001052
}
10011053

1002-
r = dhcp_server_handle_message(server, message, (size_t) len);
1003-
if (r < 0)
1004-
log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
1005-
1054+
if (sd_dhcp_server_is_in_relay_mode(server)) {
1055+
r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage));
1056+
if (r < 0)
1057+
log_dhcp_server_errno(server, r, "Couldn't relay message: %m");
1058+
} else {
1059+
r = dhcp_server_handle_message(server, message, (size_t) len);
1060+
if (r < 0)
1061+
log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m");
1062+
}
10061063
return 0;
10071064
}
10081065

@@ -1238,3 +1295,14 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_
12381295

12391296
return 0;
12401297
}
1298+
1299+
int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address) {
1300+
assert_return(server, -EINVAL);
1301+
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
1302+
1303+
if (memcmp(address, &server->relay_target, sizeof(struct in_addr)) == 0)
1304+
return 0;
1305+
1306+
server->relay_target = *address;
1307+
return 1;
1308+
}

src/network/networkd-dhcp-server-bus.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ static int property_get_leases(
3131
if (!s)
3232
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname);
3333

34+
if (sd_dhcp_server_is_in_relay_mode(s))
35+
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has DHCP relay agent active.", l->ifname);
36+
3437
r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)");
3538
if (r < 0)
3639
return r;

src/network/networkd-dhcp-server.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ int dhcp4_server_configure(Link *link) {
352352
if (r < 0)
353353
return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
354354

355+
r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target);
356+
if (r < 0)
357+
return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m");
358+
355359
if (link->network->dhcp_server_emit_timezone) {
356360
_cleanup_free_ char *buffer = NULL;
357361
const char *tz;
@@ -398,6 +402,32 @@ int dhcp4_server_configure(Link *link) {
398402
return 0;
399403
}
400404

405+
int config_parse_dhcp_server_relay_target(
406+
const char *unit,
407+
const char *filename,
408+
unsigned line,
409+
const char *section,
410+
unsigned section_line,
411+
const char *lvalue,
412+
int ltype,
413+
const char *rvalue,
414+
void *data,
415+
void *userdata) {
416+
417+
Network *network = userdata;
418+
union in_addr_union a;
419+
int r;
420+
421+
r = in_addr_from_string(AF_INET, rvalue, &a);
422+
if (r < 0) {
423+
log_syntax(unit, LOG_WARNING, filename, line, r,
424+
"Failed to parse %s= address '%s', ignoring: %m", lvalue, rvalue);
425+
return 0;
426+
}
427+
network->dhcp_server_relay_target = a.in;
428+
return r;
429+
}
430+
401431
int config_parse_dhcp_server_emit(
402432
const char *unit,
403433
const char *filename,

src/network/networkd-dhcp-server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ typedef struct Link Link;
99

1010
int dhcp4_server_configure(Link *link);
1111

12+
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_target);
1213
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);

src/network/networkd-network-gperf.gperf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ IPv6AcceptRA.PrefixAllowList, config_parse_ndisc_address_filter,
256256
IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_prefix)
257257
IPv6AcceptRA.RouteAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_route_prefix)
258258
IPv6AcceptRA.RouteDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_route_prefix)
259+
DHCPServer.RelayTarget, config_parse_dhcp_server_relay_target, 0, 0
259260
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
260261
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
261262
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS].emit)

src/network/networkd-network.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ struct Network {
186186
/* DHCP Server Support */
187187
bool dhcp_server;
188188
bool dhcp_server_bind_to_interface;
189+
struct in_addr dhcp_server_relay_target;
189190
NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
190191
bool dhcp_server_emit_router;
191192
bool dhcp_server_emit_timezone;

src/systemd/sd-dhcp-server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);
8383

8484
int sd_dhcp_server_forcerenew(sd_dhcp_server *server);
8585

86+
int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server);
87+
int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address);
8688
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref);
8789

8890
_SD_END_DECLARATIONS;

test/fuzz/fuzz-network-parser/directives.network

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ DNS=
352352
SendOption=
353353
SendVendorOption=
354354
BindToInterface=
355+
RelayTarget=
355356
[NextHop]
356357
Id=
357358
Gateway=

0 commit comments

Comments
 (0)
X Tutup