]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp: Implemented BindToInterface= configuration option
authorYegor Alexeyev <yegor.alexeyev@gmail.com>
Tue, 9 Mar 2021 11:57:37 +0000 (14:57 +0300)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 13 Apr 2021 22:30:40 +0000 (07:30 +0900)
man/systemd.network.xml
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-server.c
src/network/networkd-dhcp-server.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/systemd/sd-dhcp-server.h
test/fuzz/fuzz-network-parser/directives.network

index 66759607d3468ce78363eff7173e085c2e22f0e2..a49e6011e349b991ed844f58a2e3fb831653fbfe 100644 (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>
index 656482bf83810a82f8d34f01639f7e5f39d45714..85059102b1c64955184f947d4dc5d066a68ff142 100644 (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)
index 33e236627f08b89e38fdcfe8f833b4f063a85903..d30d91082f9fe5c366390667c5ea8ca9027ba92a 100644 (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;
index 0036cddbf93cd031e601e486eb978ecb759cfc53..be61474758d03aaa3dcb86253e12585ec32f450d 100644 (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;
 
index e91b440fe90952d9cbd301d0405cf0a60a8ba8f5..f0514207080133c0320c218e6856291369a46315 100644 (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();
index ad979fb2c4e7697a7f53e24e4aa1e11d5a2892d4..64bb23f3870b842108a977fa57d366e2f9fcc48c 100644 (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");
index 7d38d7077aaf0cb274e20731ac2995d9117a409b..8ebeec8640d5b5dcb0e9bf40b40d5b016d4867b0 100644 (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)
index f532536f1cfed75b5588145c2b05cbd22efa6c0d..8ae2315f1b8bb2c4e06f815b1bcd323a4206cb77 100644 (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,
 
index 6fe8a76c13f2453eabef7d2d3e29146a541ffddb..44b1d0205faf3f1300f523a9bb3729528ab8e0cc 100644 (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;
index e3097ebb312eb3c53fb156dfbc94998fb4b70cfe..511c8daf4077f48394b364f62d7c7a2d44a1e4d6 100644 (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);
 
index ef32cbcad0c1fc1ccf562577db06cbc66cfe5faf..399eeafa98c541bdc0f6c7badc5cc2d54f3932da 100644 (file)
@@ -350,6 +350,7 @@ EmitTimezone=
 DNS=
 SendOption=
 SendVendorOption=
+BindToInterface=
 [NextHop]
 Id=
 Gateway=