cunixnetwork-programmingposixl2tp

Listening to virtual network interface


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


Solution

  • 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);