0

I'm trying to bind my socket to eth0 using SO_BINDTODEVICE. I am passing eth0 to the variable opt. My socket name is socketfd

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0) {
        perror("socket(): error ");
        return 0;
    }
    struct ifreq ifr;

    memset(&ifr, 0, sizeof(struct ifreq));
    fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
    ioctl(sockfd, SIOCGIFINDEX, &ifr);
    setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt));
    fprintf(stderr, "The bind is done on interface %s \n", opt);
    fprintf(stderr, "opt length is %zu \n", strlen(opt));

In order to do some debugging, I'm redirecting the value of opt and strlen(opt) to a log file and they get the values correctly.

cat /home/cayman/log.txt
The bind is done on interface eth0
opt length is 4

I also do a verification by capturing packets on eth0 but I don't see the traffic going through eth0. All what is see is other network related traffic as follows:

tshark -i eth0
  1   0.000000 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  2   0.326720 192.168.78.1 -> 224.0.0.13   PIMv2 70 Hello
  3   1.030538 Cisco_51:fd:09 -> Cisco_51:fd:09 LOOP 62 Reply
  4   2.004544 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  5   3.254223 192.168.78.1 -> 224.0.0.10   EIGRP 76 Hello
  6   4.010168 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  7   6.014839 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009
  8   7.896252 192.168.78.1 -> 224.0.0.10   EIGRP 76 Hello
  9   8.023003 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00  Cost = 0  Port = 0x8009

I also had a look on this thread and I made sure that I'm running my code as a super-user. I looked also at the man page of SO_BINDTODEVICE but I am not sure what could be missing. Any suggestions please ?

Community
  • 1
  • 1
  • I am curious: why are you binding your socket to eth0? – Robᵩ Dec 08 '16 at 19:33
  • I'm implementing a UDP redirection feature. According to user's input in the front end (eth0/eth1) the back-end would bind the socket accordingly and make the traffic redirection –  Dec 08 '16 at 19:43

2 Answers2

1

You're not checking the return codes for most of the calls you're making. One or more of them may be failing in a way that reveals additional information. You should (always) check the return values for errors.

Edit: I added error handling to your code:

#include <sys/socket.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>

int main() {
  const char* opt = "wlp4s0";
  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  if(sockfd < 0) {
    perror("socket(): error ");
    return 1;
  }
  struct ifreq ifr;

  memset(&ifr, 0, sizeof(struct ifreq));
  fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
  if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl(): error ");
    return 2;
  }
  if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) > 0) {
    perror("setsockopt(): error ");
    return 3;
  }
  fprintf(stderr, "The bind is done on interface %s \n", opt);
  fprintf(stderr, "opt length is %zu \n", strlen(opt));
}

And found the ioctl() is failing. Then I noticed you're not initializing ifr.ifr_name. If you copy opt into ifr.ifr_name, does your problem go away?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Jean-Paul Calderone
  • 47,755
  • 6
  • 94
  • 122
  • Thanks for the help. Yes, ifr.ifr_name wasn't initialized. That solved the problem! –  Dec 08 '16 at 19:25
0

Your first fprintf() statement is wrong. You are not outputting the opt value to stderr correctly, and you are not copying opt into ifr.ifr_name at all before calling SIOCGIFINDEX.

Try something more like this instead:

if (strlen(opt) >= sizeof(ifr.ifr_name)) {
    fprintf(stderr, "%s", "interface name is too long \n");
    return 0;
}

...

struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, opt, sizeof(ifr.ifr_name));

if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
    perror("ioctl(): error ");
    close(sockfd);
    return 0;
}

See Get the index number of a Linux network interface in C using SIOCGIFINDEX

That being said, you don't need to use SIOCGIFINDEX before calling SO_BINDTODEVICE if you are going to bind to an interface name. Just call SO_BINDTODEVICE by itself:

sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
    perror("socket(): error ");
    return 0;
}

if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) < 0) {
    perror("setsockopt(): error ");
    close(sockfd);
    return 0;
}

Using SIOCGIFINDEX does not make sense in this context. What would make sense is if you used SIOCGIFADDR to retrieve the interface's IP address (see Get the IP address of a network interface in C using SIOCGIFADDR) and then passed that IP to bind() instead of passing the interface name to SO_BINDTODEVICE.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770