diff options
author | Yegor Alexeyev <yegor.alexeyev@gmail.com> | 2021-03-09 14:57:37 +0300 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-04-14 07:30:40 +0900 |
commit | 21b6b87eb326a447ca5ed9e566e1ec7a2f227cfe (patch) | |
tree | a08c195b4851769a3d9f50611d5c6ccb5db2562d /src | |
parent | bcaf24cd77d80529f70643e9b96c45a437dbed54 (diff) | |
download | systemd-21b6b87eb326a447ca5ed9e566e1ec7a2f227cfe.tar.gz |
dhcp: Implemented BindToInterface= configuration option
Diffstat (limited to 'src')
-rw-r--r-- | src/libsystemd-network/dhcp-network.c | 15 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-server-internal.h | 3 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-server.c | 99 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-server.c | 11 | ||||
-rw-r--r-- | src/network/networkd-dhcp-server.c | 4 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-network.c | 2 | ||||
-rw-r--r-- | src/network/networkd-network.h | 1 | ||||
-rw-r--r-- | src/systemd/sd-dhcp-server.h | 1 |
9 files changed, 96 insertions, 41 deletions
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index 656482bf83..85059102b1 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -186,15 +186,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int return r; } - if (address == INADDR_ANY) { - r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); - if (r < 0) - return r; - + if (port == DHCP_PORT_SERVER) { r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) return r; - + if (address == INADDR_ANY) { + /* IP_PKTINFO filter should not be applied when packets are + allowed to enter/leave through the interface other than + DHCP server sits on(BindToInterface option). */ + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); + if (r < 0) + return r; + } } else { r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 33e236627f..d30d91082f 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -44,11 +44,14 @@ struct sd_dhcp_server { sd_event *event; int event_priority; sd_event_source *receive_message; + sd_event_source *receive_broadcast; int fd; int fd_raw; + int fd_broadcast; int ifindex; char *ifname; + bool bind_to_interface; be32_t address; be32_t netmask; be32_t subnet; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 0036cddbf9..be61474758 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -180,9 +180,11 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { .n_ref = 1, .fd_raw = -1, .fd = -1, + .fd_broadcast = -1, .address = htobe32(INADDR_ANY), .netmask = htobe32(INADDR_ANY), .ifindex = ifindex, + .bind_to_interface = true, .default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC), .max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC), }; @@ -250,11 +252,12 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) { if (!server) return 0; - server->receive_message = - sd_event_source_unref(server->receive_message); + server->receive_message = sd_event_source_unref(server->receive_message); + server->receive_broadcast = sd_event_source_unref(server->receive_broadcast); server->fd_raw = safe_close(server->fd_raw); server->fd = safe_close(server->fd); + server->fd_broadcast = safe_close(server->fd_broadcast); log_dhcp_server(server, "STOPPED"); @@ -303,8 +306,6 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, .msg_namelen = sizeof(dest.in), .msg_iov = &iov, .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), }; struct cmsghdr *cmsg; struct in_pktinfo *pktinfo; @@ -314,22 +315,27 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, assert(message); assert(len > sizeof(DHCPMessage)); - cmsg = CMSG_FIRSTHDR(&msg); - assert(cmsg); + if (server->bind_to_interface) { + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + cmsg = CMSG_FIRSTHDR(&msg); + assert(cmsg); - /* we attach source interface and address info to the message - rather than binding the socket. This will be mostly useful - when we gain support for arbitrary number of server addresses - */ - pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg); - assert(pktinfo); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - pktinfo->ipi_ifindex = server->ifindex; - pktinfo->ipi_spec_dst.s_addr = server->address; + /* we attach source interface and address info to the message + rather than binding the socket. This will be mostly useful + when we gain support for arbitrary number of server addresses + */ + pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg); + assert(pktinfo); + + pktinfo->ipi_ifindex = server->ifindex; + pktinfo->ipi_spec_dst.s_addr = server->address; + } if (sendmsg(server->fd, &msg, 0) < 0) return -errno; @@ -1013,36 +1019,55 @@ int sd_dhcp_server_start(sd_dhcp_server *server) { r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (r < 0) { r = -errno; - sd_dhcp_server_stop(server); - return r; + goto on_error; } server->fd_raw = r; - r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; - } + if (server->bind_to_interface) + r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1); + else + r = dhcp_network_bind_udp_socket(0, server->address, DHCP_PORT_SERVER, -1); + if (r < 0) + goto on_error; server->fd = r; r = sd_event_add_io(server->event, &server->receive_message, server->fd, EPOLLIN, server_receive_message, server); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; - } + if (r < 0) + goto on_error; r = sd_event_source_set_priority(server->receive_message, server->event_priority); - if (r < 0) { - sd_dhcp_server_stop(server); - return r; + if (r < 0) + goto on_error; + + if (!server->bind_to_interface) { + r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_BROADCAST, DHCP_PORT_SERVER, -1); + if (r < 0) + goto on_error; + + server->fd_broadcast = r; + + r = sd_event_add_io(server->event, &server->receive_broadcast, + server->fd_broadcast, EPOLLIN, + server_receive_message, server); + if (r < 0) + goto on_error; + + r = sd_event_source_set_priority(server->receive_broadcast, + server->event_priority); + if (r < 0) + goto on_error; } log_dhcp_server(server, "STARTED"); return 0; + +on_error: + sd_dhcp_server_stop(server); + return r; } int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { @@ -1069,6 +1094,18 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { return r; } +int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled) { + assert_return(server, -EINVAL); + assert_return(!sd_dhcp_server_is_running(server), -EBUSY); + + if (!!enabled == server->bind_to_interface) + return 0; + + server->bind_to_interface = enabled; + + return 1; +} + int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) { int r; diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index e91b440fe9..f051420708 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -20,7 +20,7 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) { assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); } -static int test_basic(sd_event *event) { +static int test_basic(sd_event *event, bool bind_to_interface) { _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; struct in_addr address_lo = { .s_addr = htobe32(INADDR_LOOPBACK), @@ -33,6 +33,7 @@ static int test_basic(sd_event *event) { /* attach to loopback interface */ assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(server); + server->bind_to_interface = bind_to_interface; assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0); assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY); @@ -234,9 +235,13 @@ int main(int argc, char *argv[]) { assert_se(sd_event_new(&e) >= 0); - r = test_basic(e); + r = test_basic(e, true); if (r != 0) - return log_tests_skipped("cannot start dhcp server"); + return log_tests_skipped("cannot start dhcp server(bound to interface)"); + + r = test_basic(e, false); + if (r != 0) + return log_tests_skipped("cannot start dhcp server(non-bound to interface)"); test_message_handler(); test_client_id_hash(); diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index ad979fb2c4..64bb23f387 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -344,6 +344,10 @@ 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"); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7d38d7077a..8ebeec8640 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -276,6 +276,7 @@ DHCPServer.PoolOffset, config_parse_uint32, DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options) DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options) +DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface) Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index f532536f1c..8ae2315f1b 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -382,10 +382,10 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_pd_manage_temporary_address = true, .dhcp6_pd_subnet_id = -1, + .dhcp_server_bind_to_interface = true, .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true, .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true, .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true, - .dhcp_server_emit_router = true, .dhcp_server_emit_timezone = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 6fe8a76c13..44b1d0205f 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -184,6 +184,7 @@ struct Network { /* DHCP Server Support */ bool dhcp_server; + bool dhcp_server_bind_to_interface; 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-server.h b/src/systemd/sd-dhcp-server.h index e3097ebb31..511c8daf40 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -58,6 +58,7 @@ int sd_dhcp_server_stop(sd_dhcp_server *server); int sd_dhcp_server_configure_pool(sd_dhcp_server *server, const struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); +int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled); int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); |