0

Don't get confused by me talking about L2TP. Although my problem is related to L2TP it is not an L2TP problem per se. It's more of an networking problem.

Background

I'm writing an application working with L2TP. This is my first time working with L2TP and the linux L2TP subysytem, so I hope I got all this right. When creating an L2TP Ethernet session the subsystem automatically creates a virtual network interface.
After bringing the interface up I can check with Wireshark and indeed the desired data is sent to the interface. This is without any packaging tho. It's not inside an ethernet frame or anything, but just the data bytes which were included in the L2TP packet.
I have no control over actually creating the device, but I can query its name and therefore its index etc., so so far so good.

The actual problem

My question is actually pretty simple: How do I get the data which is sent to a virtual interface into my userspace application?
I don't have a lot of experience with networking on unix but my expectation would be that this is a fairly simple problem, solvable by either obtaining an file descriptor with which I can use read / recv or somehow binding a socket to just that network interface.
I couldn't find any (gen-)netlink / ioctl API (or anything else) to do this or something comparable.

Although my application is written in GO not in C, a solution in C would be completely sufficient. Tbh at this point I would be happy about any approach to solve this issue programmatically. :)

Thanks a lot in advance

  • *I can query its name and therefore its index*. How? Do you use any command? – kiner_shah Jul 06 '21 at 12:29
  • @kiner_shah You get the name via the [netlink api for L2TP](https://www.kernel.org/doc/html/latest/networking/l2tp.html#netlink-api) (`L2TP_CMD_SESSION_GET` command). For getting the index you can use the `net` std lib in go ([`net.InterfaceByName`](https://golang.org/pkg/net/#InterfaceByName)) and in C you could use either [netlink](https://man7.org/linux/man-pages/man7/rtnetlink.7.html) or [ioctl](https://man7.org/linux/man-pages/man7/netdevice.7.html). – Jonas Schade Jul 06 '21 at 15:27
  • By "getting the data", I think you wanna listen to the data which is received on a virtual interface. To know which interfaces are virtual refer [this link](https://unix.stackexchange.com/a/432251). To listen to a virtual network interface, [this link](https://stackoverflow.com/q/20100842/4688321) may help. – kiner_shah Jul 07 '21 at 07:17
  • 1
    @kiner_shah I think I already tried setting up a socket with the `sockaddr` from `getifaddrs` (from your second link) and it didn’t work for me, but I’ll gladly try again later. Maybe I did something wrong. I’ll let you know if it works. – Jonas Schade Jul 08 '21 at 03:27
  • One interface is mapped to multiple sockets. So I think `getifaddrs` will return list of socket addresses and you need to listen to each socket to know where the data is coming from - at least that's what I understand. – kiner_shah Jul 08 '21 at 09:40
  • 1
    I tried `getifaddrs` again and couldn't get it working at first, because one address of my interface was of family `AF_PACKET` (the last time I testet, I discarded all addresses, which weren't either family `AF_INET` or `AF_INET6`). `AF_PACKET`sockets don't work like the usual `AF_INET` socket and while researching those I came across [this tutorial](http://www.microhowto.info/howto/capture_ethernet_frames_using_an_af_packet_socket_in_c.html), which answered all my questions. Thanks a lot for the help ❤ – Jonas Schade Jul 12 '21 at 10:22

1 Answers1

1

I just found a tutorial which answers my own question. It was actually really easy using AF_PACKET sockets.

There is a lovely tutorial on microhowto.info, which explains how AF_PACKET sockets work, better than I ever could. It even includes a section "Capture only from a particular network interface".


Here is a minimal example, which worked for my use case:

#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>

// [...]

// Create socket
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
    perror("ERROR socket");
    exit(1);
}

// Interface index (i.e. obtainable via ioctl SIOCGIFINDEX)
int ifindex = 1337;

// create link layer socket address
struct sockaddr_ll addr = {0};
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
addr.sll_protocol = htons(ETH_P_ALL)

if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
    perror("ERROR bind");
    exit(1);
}

char buffer[65535];
ssize_t len;    
do { 
    len = recv(fd, buffer, sizeof(buffer) -1, 0);
    if (len < 0) {
        perror("ERROR recvfrom");
        exit(1);
    }
    printf("recived data (length: %i)\n", (int) len);
} while (len > 0);
  • Glad that you found a solution :-) But, currently this answer is "link-only". Can you please edit the answer to include necessary sections from the link? – kiner_shah Jul 12 '21 at 13:04
  • 1
    Of course. I added a minimal example. I hope that's enough. If not just say so :D. I just don't want to copy paste someone else's work. – Jonas Schade Jul 12 '21 at 21:44