Skip to content
Advertisement

How to select the interface used to perform a hostname lookup

I am working in an application embedded in a device running Linux and BusyBox. The hardware has 2 communication interfaces: Wi-Fi and 3G.

In each connection, the application must try to connect using wi-fi first and, if it fails, the application tries again using 3G.

I am forcing the connection to use the selected interface binding it like this:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <net/if.h>

static void resolveDns(const char *hostname, struct addrinfo **destInfo)
{
    int err;
    struct addrinfo hints;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;

    if ((err = getaddrinfo(hostname, "80", &hints, destInfo)) != 0) {
        fprintf(stderr, "getaddrinfo error: %sn", gai_strerror(err));
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in *addr = (struct sockaddr_in *)((*destInfo)->ai_addr);
    printf("Destination IP: %sn", inet_ntoa(addr->sin_addr));
}

static void getComInterface(const char *iface, struct ifreq *ifr)
{
    ifr->ifr_addr.sa_family = AF_INET;
    strcpy(ifr->ifr_name, iface);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    int err = ioctl(sock, SIOCGIFADDR, ifr);
    close(sock);

    if (err) {
        fprintf(stderr, "ioctl error: %dn", err);
        exit(EXIT_FAILURE);
    }

    printf("Interface IP: %sn", inet_ntoa(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr));
}

int main()
{
    int err;

    struct ifreq ifr;
    getComInterface("wlan0", &ifr);

    struct addrinfo *destInfo;
    resolveDns("www.google.com", &destInfo);

    int s = socket(AF_INET, SOCK_STREAM, 0);
    err = bind(s, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
    if (err) {
        fprintf(stderr, "bind error = %d, %dn", err, errno);
        exit(EXIT_FAILURE);
    }

    err = connect(s, destInfo->ai_addr, destInfo->ai_addrlen);
    if (err) {
        fprintf(stderr, "connect error = %d, %d n", err, errno);
        exit(EXIT_FAILURE);
    }

    printf("Ok!n");

    freeaddrinfo(destInfo);
    close(s);
    return EXIT_SUCCESS;
}

But this doesn’t solve the problem in DNS lookup.

Is there a way to force getaddrinfo to use the selected interface? Or, even better, is there a way to force all connections to use the selected interface without disconnecting the other?

P.S.: If you know how to do this in more complex SO, like Ubuntu for instance, please share your solution.

Thanks

Advertisement

Answer

I am afraid there is no way to do it just via the standard C library, i.e. you need to change the default gateway, and set gateway for each connection. Please consider the pseudocode below:

  • System startup:

    • Establish connection via the wifi
    • Establish connection via the 3G
    • Configure the wifi interface as the default gateway
  • On new connection request:

    • Do DNS lookup(happens via the default route)
      • If it succeeds
        • Save the IP address together with the file descriptor
        • Configure routing to this IP via the interface that is the current gateway
        • Open socket to the IP address, the traffic will go through the given interface
      • If not
        • Change the default gateway to another interface(3G), and try again
  • On disconnection:

    • Find the IP address by the file descriptor of the disconnected connection
    • Remove the IP from the routing table
    • Go to “On new connection request” (this is up to your application logic)

In this way, you will be able to change the default gateway for new connections but keep for existing ones.

The change of the routing table can be done via Linux shell commands like ip route etc. they can be launched from the C via system, e.g. system( "ip route show" ); You may also write scripts with more complicated logic and launch them from your C code.

However, this solution has one flaw, usually, if your current interface has no Internet connection, it means that all the connections using this interface might eventually fail.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement