summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYegor Alexeyev <yegor.alexeyev@gmail.com>2021-03-09 14:57:37 +0300
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-04-14 07:30:40 +0900
commit21b6b87eb326a447ca5ed9e566e1ec7a2f227cfe (patch)
treea08c195b4851769a3d9f50611d5c6ccb5db2562d /src
parentbcaf24cd77d80529f70643e9b96c45a437dbed54 (diff)
downloadsystemd-21b6b87eb326a447ca5ed9e566e1ec7a2f227cfe.tar.gz
dhcp: Implemented BindToInterface= configuration option
Diffstat (limited to 'src')
-rw-r--r--src/libsystemd-network/dhcp-network.c15
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h3
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c99
-rw-r--r--src/libsystemd-network/test-dhcp-server.c11
-rw-r--r--src/network/networkd-dhcp-server.c4
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c2
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/systemd/sd-dhcp-server.h1
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);