1

I have an application that sends/receives packets at the data-link layer level, i.e. frames sent from MAC address to MAC address over a wired ethernet connection.

I'm using the pcap library to do this.

Running ip a from a terminal returns:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s25: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether 50:7b:9d:84:34:93 brd ff:ff:ff:ff:ff:ff
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 18:5e:0f:8f:d1:3f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global dynamic wlp3s0
       valid_lft 57882sec preferred_lft 57882sec
    inet6 fe80::c215:9ad2:a358:6d3d/64 scope link 
       valid_lft forever preferred_lft forever

My OS is ubuntu 16.04 and its using the systemd predictable network interface naming. The name of the wired ethernet interface in this instance is enp0s25.

Currently I have this hard-coded in my application, i.e.

pcap=pcap_open_live("enp0s25", ETH_SNAPLEN, ETH_PROMISCUOUS, ETH_TIMEOUT, pcap_errbuf);

What I would like to be able to do is retrieve the name of the wired ethernet interface dynamically so it could still work on devices where the ethernet interface name may differ from enp0s25

I've implemented:

/* automatically retrieve the ethernet interface name */
n = pcap_findalldevs(&alldevs, pcap_errbuf);

/* scan the list for a suitable device to capture from */
for (dev = alldevs; dev != NULL; dev = dev->next) {

    /* Name */
    printf("%s\n",dev->name);
}
pcap_freealldevs(alldevs);

This allows me to iterate over all the interface names.

The question I have is how do I pick out the name corresponding to the wired ethernet interface?

I've not yet tested my code on multiple devices so am unsure as to how the interface name may vary from enp0s25.

Would a wired ethernet interface always begin with enp?

In which case maybe the solution is to do a strcmp with that on the 1st 3 characters of the retrieved interface name?

bph
  • 10,728
  • 15
  • 60
  • 135
  • relevant link -> https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/ – bph Mar 23 '18 at 10:20
  • src for systemd naming policy -> https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-net_id.c#L20 – bph Mar 23 '18 at 10:21
  • `pcap_lookupdev()` returns a pointer to a string giving the name of a network device suitable to work with `pcap_open_live()` – 0xtvarun Mar 23 '18 at 10:34
  • note that pcap_lookupdev() was deprecated a long time ago -> https://www.tcpdump.org/manpages/pcap_lookupdev.3pcap.html . Now replaced with pcap_findalldevs which is what I used in the example code in the OP – bph Mar 23 '18 at 11:02

1 Answers1

0

ioctl(fd, SIOCGIWNAME) will return the wireless extension protocol version, which is only available on interfaces that are wireless.

The following function checks if the interface is wireless

bool check_type(const char* ifname) {
  int sock = -1;
  // set the name of the interface in the ioctl request struct
  struct ifreq ifr;
  memset(&ifr, 0, sizeof(struct ifreq));
  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);

  //Create a throw-away socket
  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("socket");
    return FALSE;
  }


  // First check if the interface is up
  // also proves that the interface exists
  int r = ioctl(sock, SIOCGIFFLAGS, &ifr);
  if (r != 0) {
    close(sock);
    return FALSE;
  }

  if (!(ifr.ifr_flags & IFF_UP)) {
    close(sock);
    return FALSE;
  }
  if (ioctl(sock, SIOCGIWNAME, &ifr) != -1) {
    close(sock);
    return TRUE;
  }

  close(sock);
  return FALSE;
}
Xter
  • 63
  • 1
  • 10