--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
This commit is contained in:
Luca Boccassi 2017-10-26 19:42:55 +01:00 committed by Daniel Stenberg
parent b78dce2526
commit 32828cc4fb
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
2 changed files with 32 additions and 24 deletions

View File

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

View File

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