Unverified Commit 32828cc4 authored by Luca Boccassi's avatar Luca Boccassi Committed by Daniel Stenberg
Browse files

--interface: add support for Linux VRF

The --interface command (CURLOPT_INTERFACE option) already uses
SO_BINDTODEVICE on Linux, but it tries to parse it as an interface or IP
address first, which fails in case the user passes a VRF.

Try to use the socket option immediately and parse it as a fallback
instead.  Update the documentation to mention this feature, and that it
requires the binary to be ran by root or with CAP_NET_RAW capabilities
for this to work.

Closes #2024
parent b78dce25
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -10,3 +10,7 @@ name, IP address or host name. An example could look like:
 curl --interface eth0:1 https://www.example.com/
 curl --interface eth0:1 https://www.example.com/


If this option is used several times, the last one will be used.
If this option is used several times, the last one will be used.

On Linux it can be used to specify a VRF, but the binary needs to either
have CAP_NET_RAW or to be ran as root. More information about Linux VRF:
https://www.kernel.org/doc/Documentation/networking/vrf.txt
+28 −24
Original line number Original line Diff line number Diff line
@@ -285,6 +285,34 @@ static CURLcode bindlocal(struct connectdata *conn,


    /* interface */
    /* interface */
    if(!is_host) {
    if(!is_host) {
#ifdef SO_BINDTODEVICE
      /* I am not sure any other OSs than Linux that provide this feature,
       * and at the least I cannot test. --Ben
       *
       * This feature allows one to tightly bind the local socket to a
       * particular interface.  This will force even requests to other
       * local interfaces to go out the external interface.
       *
       *
       * Only bind to the interface when specified as interface, not just
       * as a hostname or ip address.
       *
       * interface might be a VRF, eg: vrf-blue, which means it cannot be
       * converted to an IP address and would fail Curl_if2ip. Simply try
       * to use it straight away.
       */
      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                    dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
        /* This is typically "errno 1, error: Operation not permitted" if
         * you're not running as root or another suitable privileged
         * user.
         * If it succeeds it means the parameter was a valid interface and
         * not an IP address. Return immediately.
         */
        return CURLE_OK;
      }
#endif

      switch(Curl_if2ip(af, scope, conn->scope_id, dev,
      switch(Curl_if2ip(af, scope, conn->scope_id, dev,
                        myhost, sizeof(myhost))) {
                        myhost, sizeof(myhost))) {
        case IF2IP_NOT_FOUND:
        case IF2IP_NOT_FOUND:
@@ -305,30 +333,6 @@ static CURLcode bindlocal(struct connectdata *conn,
          infof(data, "Local Interface %s is ip %s using address family %i\n",
          infof(data, "Local Interface %s is ip %s using address family %i\n",
                dev, myhost, af);
                dev, myhost, af);
          done = 1;
          done = 1;

#ifdef SO_BINDTODEVICE
          /* I am not sure any other OSs than Linux that provide this feature,
           * and at the least I cannot test. --Ben
           *
           * This feature allows one to tightly bind the local socket to a
           * particular interface.  This will force even requests to other
           * local interfaces to go out the external interface.
           *
           *
           * Only bind to the interface when specified as interface, not just
           * as a hostname or ip address.
           */
          if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                        dev, (curl_socklen_t)strlen(dev) + 1) != 0) {
            error = SOCKERRNO;
            infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
                  " will do regular bind\n",
                  dev, error, Curl_strerror(conn, error));
            /* This is typically "errno 1, error: Operation not permitted" if
               you're not running as root or another suitable privileged
               user */
          }
#endif
          break;
          break;
      }
      }
    }
    }