]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
dhcp: Don't use get_source_addr() to determine source address
authorTobias Brunner <tobias@strongswan.org>
Fri, 10 Mar 2023 09:43:24 +0000 (10:43 +0100)
committerTobias Brunner <tobias@strongswan.org>
Tue, 21 Mar 2023 15:13:22 +0000 (16:13 +0100)
That method is subject to interface filtering, which isn't ideal for
DHCP traffic that probably uses an internal interface on which the IKE
daemon might be disabled.  In that case `giaddr` is set to an incorrect
public IP, which in turn might prevent the plugin from receiving the
DHCP server's unicast response, in particular if the DHCP socket
is bound to the internal interface.

This new approach connects the client socket and thereby determines the
source address to reach the DHCP server.

Closes strongswan/strongswan#1573

src/libcharon/plugins/dhcp/dhcp_socket.c

index 815d2c65c8e9d2a3c9e751186449bde8758be225..a3572766da09f8f3fe51c7d51b7a5d0a8fce0ab4 100644 (file)
@@ -111,6 +111,11 @@ struct private_dhcp_socket_t {
         * Force configured destination address
         */
        bool force_dst;
+
+       /**
+        * Source IP if destination address is unicast
+        */
+       struct sockaddr_in src;
 };
 
 /**
@@ -202,7 +207,6 @@ static int prepare_dhcp(private_dhcp_socket_t *this,
        identification_t *identity;
        dhcp_option_t *option;
        int optlen = 0, remaining;
-       host_t *src;
        uint32_t id;
 
        memset(dhcp, 0, sizeof(*dhcp));
@@ -219,14 +223,8 @@ static int prepare_dhcp(private_dhcp_socket_t *this,
        }
        else
        {
-               /* act as relay agent */
-               src = charon->kernel->get_source_addr(charon->kernel, this->dst, NULL);
-               if (src)
-               {
-                       memcpy(&dhcp->gateway_address, src->get_address(src).ptr,
-                                  sizeof(dhcp->gateway_address));
-                       src->destroy(src);
-               }
+               memcpy(&dhcp->gateway_address, &this->src.sin_addr,
+                          sizeof(dhcp->gateway_address));
        }
 
        identity = transaction->get_identity(transaction);
@@ -736,6 +734,7 @@ dhcp_socket_t *dhcp_socket_create()
                        .s_addr = INADDR_ANY,
                },
        };
+       socklen_t addr_len;
        char *iface;
        int on = 1, rcvbuf = 0;
        struct sock_filter dhcp_filter_code[] = {
@@ -888,6 +887,25 @@ dhcp_socket_t *dhcp_socket_create()
                        return NULL;
                }
        }
+       if (!is_broadcast(this->dst))
+       {
+               if (connect(this->send, this->dst->get_sockaddr(this->dst),
+                                       *this->dst->get_sockaddr_len(this->dst)) < 0)
+               {
+                       DBG1(DBG_CFG, "unable to connect DHCP send socket: %s",
+                                strerror(errno));
+                       destroy(this);
+                       return NULL;
+               }
+               addr_len = sizeof(this->src);
+               if (getsockname(this->send, &this->src, &addr_len) < 0)
+               {
+                       DBG1(DBG_CFG, "unable to determine source address for DHCP: %s",
+                                strerror(errno));
+                       destroy(this);
+                       return NULL;
+               }
+       }
 
        lib->watcher->add(lib->watcher, this->receive, WATCHER_READ,
                                          (watcher_cb_t)receive_dhcp, this);