dhcp: Implemented BindToInterface= configuration option

This commit is contained in:
Yegor Alexeyev 2021-03-09 14:57:37 +03:00 committed by Yu Watanabe
parent bcaf24cd77
commit 21b6b87eb3
11 changed files with 104 additions and 41 deletions

View file

@ -2407,6 +2407,13 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
then all options specified earlier are cleared. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>BindToInterface=</varname></term>
<listitem>Takes a boolean value. When <literal>yes</literal>, DHCP server socket will be bound
to its network interface and all socket communication will be restricted to this interface.
Defaults to <literal>yes</literal>.
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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();

View file

@ -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");

View file

@ -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)

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -350,6 +350,7 @@ EmitTimezone=
DNS=
SendOption=
SendVendorOption=
BindToInterface=
[NextHop]
Id=
Gateway=