diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 7f137db223..5c1c6e118e 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2325,17 +2325,6 @@ IPv6Token=prefixstable:2002:da8:1:: - - RelayTarget= - - Takes an IPv4 address, which must be in the format - described in - inet_pton3. - Turns this DHCP server into a DHCP relay agent. See RFC 1542. - The address is the address of DHCP server or another relay agent to forward DHCP messages to and from. - Check also BindToInterface= option. Turning it off is required for relaying messages outside. - - PoolOffset= PoolSize= @@ -2461,9 +2450,40 @@ IPv6Token=prefixstable:2002:da8:1:: BindToInterface= - Takes a boolean value. When yes, DHCP server socket will be bound - to its network interface and all socket communication will be restricted to this interface. - Defaults to yes. + + Takes a boolean value. When yes, DHCP server socket will be bound + to its network interface and all socket communication will be restricted to this interface. + Defaults to yes, except if RelayTarget= is used (see below), + in which case it defaults defaults to no. + + + + RelayTarget= + + Takes an IPv4 address, which must be in the format described in + inet_pton3. + Turns this DHCP server into a DHCP relay agent. See RFC 1542. + The address is the address of DHCP server or another relay agent to forward DHCP messages to and from. + + + + RelayAgentCircuitId= + + Specifies value for Agent Circuit ID suboption of Relay Agent Information option. + Takes a string, which must be in the format string:value, + where value should be replaced with the value of the suboption. + Defaults to unset (means no Agent Circuit ID suboption is generated). + Ignored if RelayTarget= is not specified. + + + + RelayAgentRemoteId= + + Specifies value for Agent Remote ID suboption of Relay Agent Information option. + Takes a string, which must be in the format string:value, + where value should be replaced with the value of the suboption. + Defaults to unset (means no Agent Remote ID suboption is generated). + Ignored if RelayTarget= is not specified. diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index e5be7c5a63..d8c42757aa 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -42,6 +42,8 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, uint8_t code, size_t optlen, const void *optval); +int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t wanted_code, size_t *ret_offset); +int dhcp_option_remove_option(uint8_t *options, size_t buflen, uint8_t option_code); typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, const void *option, void *userdata); diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 8899e8a552..ebe8eecc9d 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -14,12 +14,33 @@ #include "strv.h" #include "utf8.h" +/* Append type-length value structure to the options buffer */ +static int dhcp_option_append_tlv(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { + assert(options); + assert(size > 0); + assert(offset); + assert(optlen <= UINT8_MAX); + assert(*offset < size); + + if (*offset + 2 + optlen > size) + return -ENOBUFS; + + options[*offset] = code; + options[*offset + 1] = optlen; + + memcpy_safe(&options[*offset + 2], optval, optlen); + *offset += 2 + optlen; + return 0; +} + static int option_append(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { assert(options); assert(size > 0); assert(offset); + int r; + if (code != SD_DHCP_OPTION_END) /* always make sure there is space for an END option */ size--; @@ -93,34 +114,93 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, options[*offset] = code; options[*offset + 1] = l; - *offset += 2; ORDERED_SET_FOREACH(p, s) { - options[*offset] = p->option; - options[*offset + 1] = p->length; - memcpy(&options[*offset + 2], p->data, p->length); - *offset += 2 + p->length; + r = dhcp_option_append_tlv(options, size, offset, p->option, p->length, p->data); + if (r < 0) + return r; + } + break; + } + case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: { + sd_dhcp_server *server = (sd_dhcp_server *) optval; + size_t current_offset = *offset + 2; + + if (server->agent_circuit_id) { + r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_CIRCUIT_ID, + strlen(server->agent_circuit_id), server->agent_circuit_id); + if (r < 0) + return r; + } + if (server->agent_remote_id) { + r = dhcp_option_append_tlv(options, size, ¤t_offset, SD_DHCP_RELAY_AGENT_REMOTE_ID, + strlen(server->agent_remote_id), server->agent_remote_id); + if (r < 0) + return r; } + options[*offset] = code; + options[*offset + 1] = current_offset - *offset - 2; + assert(current_offset - *offset - 2 <= UINT8_MAX); + *offset = current_offset; break; } default: - if (*offset + 2 + optlen > size) - return -ENOBUFS; - - options[*offset] = code; - options[*offset + 1] = optlen; - - memcpy_safe(&options[*offset + 2], optval, optlen); - *offset += 2 + optlen; - - break; + return dhcp_option_append_tlv(options, size, offset, code, optlen, optval); } - return 0; } +static int option_length(uint8_t *options, size_t length, size_t offset) { + assert(options); + assert(offset < length); + + if (IN_SET(options[offset], SD_DHCP_OPTION_PAD, SD_DHCP_OPTION_END)) + return 1; + if (length < offset + 2) + return -ENOBUFS; + + /* validating that buffer is long enough */ + if (length < offset + 2 + options[offset + 1]) + return -ENOBUFS; + + return options[offset + 1] + 2; +} + +int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t code, size_t *ret_offset) { + int r; + + assert(options); + assert(ret_offset); + + for (size_t offset = 0; offset < length; offset += r) { + r = option_length(options, length, offset); + if (r < 0) + return r; + + if (code == options[offset]) { + *ret_offset = offset; + return r; + } + } + return -ENOENT; +} + +int dhcp_option_remove_option(uint8_t *options, size_t length, uint8_t option_code) { + int r; + size_t offset; + + assert(options); + + r = dhcp_option_find_option(options, length, option_code, &offset); + if (r < 0) + return r; + + memmove(options + offset, options + offset + r, length - offset - r); + return length - r; +} + int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, uint8_t code, size_t optlen, const void *optval) { diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 3628223ae9..088155d6f1 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -39,7 +39,6 @@ typedef struct DHCPLease { } DHCPLease; struct sd_dhcp_server { - struct in_addr relay_target; unsigned n_ref; sd_event *event; @@ -76,6 +75,11 @@ struct sd_dhcp_server { sd_dhcp_server_callback_t callback; void *callback_userdata; + + struct in_addr relay_target; + + char *agent_circuit_id; + char *agent_remote_id; }; typedef struct DHCPRequest { @@ -88,6 +92,7 @@ typedef struct DHCPRequest { be32_t server_id; be32_t requested_ip; uint32_t lifetime; + const uint8_t *agent_info_option; } DHCPRequest; int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index e2ad99ebcb..05c8a0eae4 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -164,6 +164,9 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { ordered_set_free(server->extra_options); ordered_set_free(server->vendor_options); + free(server->agent_circuit_id); + free(server->agent_remote_id); + free(server->bound_leases); free(server->ifname); @@ -382,6 +385,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server, assert(server); assert(req); assert(req->max_optlen); + assert(req->message); assert(optoffset <= req->max_optlen); assert(packet); @@ -391,6 +395,15 @@ int dhcp_server_send_packet(sd_dhcp_server *server, if (r < 0) return r; + if (req->agent_info_option) { + size_t opt_full_length = *(req->agent_info_option + 1) + 2; + /* there must be space left for SD_DHCP_OPTION_END */ + if (optoffset + opt_full_length < req->max_optlen) { + memcpy(packet->dhcp.options + optoffset, req->agent_info_option, opt_full_length); + optoffset += opt_full_length; + } + } + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) @@ -649,6 +662,10 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket)) req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket); + break; + case SD_DHCP_OPTION_RELAY_AGENT_INFORMATION: + req->agent_info_option = (uint8_t*)option - 2; + break; } @@ -712,8 +729,30 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { return be32toh(requested_ip & ~server->netmask) - server->pool_offset; } -static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length) { +static int append_agent_information_option(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t size) { + int r; + size_t offset; + + assert(server); + assert(message); + + r = dhcp_option_find_option(message->options, opt_length, SD_DHCP_OPTION_END, &offset); + if (r < 0) + return r; + + r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION, 0, server); + if (r < 0) + return r; + + r = dhcp_option_append(message, size, &offset, 0, SD_DHCP_OPTION_END, 0, NULL); + if (r < 0) + return r; + return offset; +} + +static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *message, size_t opt_length, size_t buflen) { _cleanup_free_ DHCPPacket *packet = NULL; + int r; assert(server); assert(message); @@ -729,13 +768,19 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag if (message->giaddr == 0) message->giaddr = server->address; + if (server->agent_circuit_id || server->agent_remote_id) { + r = append_agent_information_option(server, message, opt_length, buflen - sizeof(DHCPMessage)); + if (r < 0) + return log_dhcp_server_errno(server, r, "could not append relay option: %m"); + opt_length = r; + } + return dhcp_server_send_udp(server, server->relay_target.s_addr, DHCP_PORT_SERVER, message, sizeof(DHCPMessage) + opt_length); } else if (message->op == BOOTREPLY) { log_dhcp_server(server, "(relay agent) BOOTREPLY (0x%x)", be32toh(message->xid)); - if (message->giaddr != server->address) { + if (message->giaddr != server->address) return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG), - "(relay agent) BOOTREPLY giaddr mismatch, discarding"); - } + "(relay agent) BOOTREPLY giaddr mismatch, discarding"); int message_type = dhcp_option_parse(message, sizeof(DHCPMessage) + opt_length, NULL, NULL, NULL); if (message_type < 0) @@ -746,6 +791,10 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag return -ENOMEM; memcpy(&packet->dhcp, message, sizeof(DHCPMessage) + opt_length); + r = dhcp_option_remove_option(packet->dhcp.options, opt_length, SD_DHCP_OPTION_RELAY_AGENT_INFORMATION); + if (r > 0) + opt_length = r; + bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK; const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr; return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast); @@ -1000,6 +1049,15 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, return 0; } +static size_t relay_agent_information_length(const char* agent_circuit_id, const char* agent_remote_id) { + size_t sum = 0; + if (agent_circuit_id) + sum += 2 + strlen(agent_circuit_id); + if (agent_remote_id) + sum += 2 + strlen(agent_remote_id); + return sum; +} + static int server_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { _cleanup_free_ DHCPMessage *message = NULL; @@ -1013,20 +1071,25 @@ static int server_receive_message(sd_event_source *s, int fd, .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; - ssize_t buflen, len; + ssize_t datagram_size, len; int r; assert(server); - buflen = next_datagram_size_fd(fd); - if (buflen < 0) - return buflen; + datagram_size = next_datagram_size_fd(fd); + if (datagram_size < 0) + return datagram_size; + + size_t buflen = datagram_size; + if (sd_dhcp_server_is_in_relay_mode(server)) + /* Preallocate the additional size for DHCP Relay Agent Information Option if neeeded */ + buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2; message = malloc(buflen); if (!message) return -ENOMEM; - iov = IOVEC_MAKE(message, buflen); + iov = IOVEC_MAKE(message, datagram_size); len = recvmsg_safe(fd, &msg, 0); if (IN_SET(len, -EAGAIN, -EINTR)) @@ -1052,7 +1115,7 @@ static int server_receive_message(sd_event_source *s, int fd, } if (sd_dhcp_server_is_in_relay_mode(server)) { - r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage)); + r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen); if (r < 0) log_dhcp_server_errno(server, r, "Couldn't relay message: %m"); } else { @@ -1296,7 +1359,7 @@ int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_ return 0; } -int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address) { +int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr *address) { assert_return(server, -EINVAL); assert_return(!sd_dhcp_server_is_running(server), -EBUSY); @@ -1306,3 +1369,31 @@ int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr server->relay_target = *address; return 1; } + +int sd_dhcp_server_set_relay_agent_information( + sd_dhcp_server *server, + const char *agent_circuit_id, + const char *agent_remote_id) { + _cleanup_free_ char *circuit_id_dup = NULL, *remote_id_dup = NULL; + + assert_return(server, -EINVAL); + + if (relay_agent_information_length(agent_circuit_id, agent_remote_id) > UINT8_MAX) + return -ENOBUFS; + + if (agent_circuit_id) { + circuit_id_dup = strdup(agent_circuit_id); + if (!circuit_id_dup) + return -ENOMEM; + } + + if (agent_remote_id) { + remote_id_dup = strdup(agent_remote_id); + if (!remote_id_dup) + return -ENOMEM; + } + + free_and_replace(server->agent_circuit_id, circuit_id_dup); + free_and_replace(server->agent_remote_id, remote_id_dup); + return 0; +} diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 1eebe3a10f..d152e7925d 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -270,6 +270,14 @@ static void test_options(struct option_desc *desc) { printf("DHCP type %s\n", dhcp_type(res)); } +static void test_option_removal(struct option_desc *desc) { + _cleanup_free_ DHCPMessage *message = create_message(&desc->options[0], desc->len, NULL, 0, NULL, 0); + + assert_se(dhcp_option_parse(message, sizeof(DHCPMessage) + desc->len, NULL, NULL, NULL) >= 0); + assert_se((desc->len = dhcp_option_remove_option(message->options, desc->len, SD_DHCP_OPTION_MESSAGE_TYPE)) >= 0); + assert_se(dhcp_option_parse(message, sizeof(DHCPMessage) + desc->len, NULL, NULL, NULL) < 0); +} + static uint8_t options[64] = { 'A', 'B', 'C', 'D', 160, 2, 0x11, 0x12, @@ -366,5 +374,12 @@ int main(int argc, char *argv[]) { test_option_set(); + for (i = 0; i < ELEMENTSOF(option_tests); i++) { + struct option_desc *desc = &option_tests[i]; + if (!desc->success || desc->snamelen > 0 || desc->filelen > 0) + continue; + test_option_removal(desc); + } + return 0; } diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index bd368672da..f1216183bf 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -252,6 +252,7 @@ int dhcp4_server_configure(Link *link) { sd_dhcp_option *p; Link *uplink = NULL; Address *address; + bool bind_to_interface; int r; assert(link); @@ -344,10 +345,6 @@ int dhcp4_server_configure(Link *link) { dhcp_lease_server_type_to_string(type)); } - r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, link->network->dhcp_server_bind_to_interface); - if (r < 0) - return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m"); - r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); if (r < 0) return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m"); @@ -356,6 +353,15 @@ int dhcp4_server_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m"); + bind_to_interface = sd_dhcp_server_is_in_relay_mode(link->dhcp_server) ? false : link->network->dhcp_server_bind_to_interface; + r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, bind_to_interface); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m"); + + r = sd_dhcp_server_set_relay_agent_information(link->dhcp_server, link->network->dhcp_server_relay_agent_circuit_id, link->network->dhcp_server_relay_agent_remote_id); + if (r < 0) + return log_link_error_errno(link, r, "Failed to set agent circuit/remote id for DHCP server: %m"); + if (link->network->dhcp_server_emit_timezone) { _cleanup_free_ char *buffer = NULL; const char *tz; @@ -402,6 +408,40 @@ int dhcp4_server_configure(Link *link) { return 0; } +int config_parse_dhcp_server_relay_agent_suboption( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **suboption_value = data; + char* p; + + assert(filename); + assert(lvalue); + assert(rvalue); + + + if (isempty(rvalue)) { + *suboption_value = mfree(*suboption_value); + return 0; + } + + p = startswith(rvalue, "string:"); + if (!p) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse %s=%s'. Invalid format, ignoring.", lvalue, rvalue); + return 0; + } + return free_and_strdup(suboption_value, empty_to_null(p)); +} + int config_parse_dhcp_server_relay_target( const char *unit, const char *filename, diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index 9e5d24fbe8..7a1e3a5173 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -9,5 +9,6 @@ typedef struct Link Link; int dhcp4_server_configure(Link *link); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_target); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 902953a629..732b53f71d 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -262,6 +262,8 @@ IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter, IPv6AcceptRA.RouteAllowList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_allow_listed_route_prefix) IPv6AcceptRA.RouteDenyList, config_parse_ndisc_address_filter, 0, offsetof(Network, ndisc_deny_listed_route_prefix) DHCPServer.RelayTarget, config_parse_dhcp_server_relay_target, 0, 0 +DHCPServer.RelayAgentCircuitId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_circuit_id) +DHCPServer.RelayAgentRemoteId, config_parse_dhcp_server_relay_agent_suboption, 0, offsetof(Network, dhcp_server_relay_agent_remote_id) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS].emit) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 78a4026258..8b236342b5 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -548,6 +548,9 @@ static Network *network_free(Network *network) { net_match_clear(&network->match); condition_free_list(network->conditions); + free(network->dhcp_server_relay_agent_circuit_id); + free(network->dhcp_server_relay_agent_remote_id); + free(network->description); free(network->dhcp_vendor_class_identifier); free(network->dhcp_mudurl); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index e8c2428bec..d7c216af6e 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -190,6 +190,9 @@ struct Network { bool dhcp_server; bool dhcp_server_bind_to_interface; struct in_addr dhcp_server_relay_target; + char *dhcp_server_relay_agent_circuit_id; + char *dhcp_server_relay_agent_remote_id; + NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; bool dhcp_server_emit_router; bool dhcp_server_emit_timezone; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index da0a5a7ac4..0877a6b9a9 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -90,6 +90,7 @@ enum { SD_DHCP_OPTION_POP3_SERVER = 70, SD_DHCP_OPTION_USER_CLASS = 77, SD_DHCP_OPTION_FQDN = 81, + SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, @@ -105,6 +106,12 @@ enum { SD_DHCP_OPTION_END = 255, }; +/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ +enum { + SD_DHCP_RELAY_AGENT_CIRCUIT_ID = 1, + SD_DHCP_RELAY_AGENT_REMOTE_ID = 2, +}; + typedef struct sd_dhcp_client sd_dhcp_client; typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 3cee2c2b06..012d147ec1 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -85,6 +85,8 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server); int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server); int sd_dhcp_server_set_relay_target(sd_dhcp_server *server, const struct in_addr* address); +int sd_dhcp_server_set_relay_agent_information(sd_dhcp_server *server, const char* circuit_id, const char* remote_id); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server, sd_dhcp_server_unref); _SD_END_DECLARATIONS; diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 12d3e3cfcd..127f26461e 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -362,6 +362,8 @@ SendOption= SendVendorOption= BindToInterface= RelayTarget= +RelayAgentCircuitId= +RelayAgentRemoteId= [NextHop] Id= Gateway= diff --git a/test/test-network/conf/agent-client-peer.network b/test/test-network/conf/agent-client-peer.network index 0db83dc9d2..4142938056 100644 --- a/test/test-network/conf/agent-client-peer.network +++ b/test/test-network/conf/agent-client-peer.network @@ -7,3 +7,5 @@ IPForward=ipv4 [DHCPServer] RelayTarget=192.168.5.1 BindToInterface=no +RelayAgentCircuitId=string:sample_circuit_id +RelayAgentRemoteId=string:sample_remote_id diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index bf8add778b..1e50a75980 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -3719,7 +3719,6 @@ class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities): copy_unit_to_networkd_unit_path(*self.units) start_networkd() - #Test is disabled until BindToInterface DHCP server configuration option is supported self.wait_online(['client:routable']) output = check_output(*networkctl_cmd, '-n', '0', 'status', 'client', env=env)