2

I'm writing a TCP/IP stack for embedded systems, but I wanted to test it on my host system.

Originally I tried with tap0 + a bridge. But I quickly realized it would make the most sense to bind to the ethernet device.

    // devname is "eth0"

    fd = socket( PF_PACKET, SOCK_RAW, htons(ETH_P_ALL) ); // or AF_PACKET/IPPROTO_RAW / PF_PACKET/htons(ETH_P_ALL)
    struct ifreq if_idx;
    memset(&if_idx, 0, sizeof(struct ifreq));
    strncpy(if_idx.ifr_name, devname, IFNAMSIZ-1);
    if (ioctl(fd, SIOCGIFINDEX, &if_idx) < 0)
        Error

    struct sockaddr_ll socket_address = {
        .sll_ifindex = if_idx.ifr_ifindex,
        .sll_halen = 0,
        .sll_family = AF_PACKET,
        .sll_protocol = htons(ETH_P_ALL),
        .sll_hatype = 0,
        .sll_pkttype = PACKET_BROADCAST,
    };

    if ( bind( fd, (const struct sockaddr*)&socket_address, sizeof( socket_address ) ) < 0 )
        Error

Then I also tried with this (These do not seem required, except to get the network card into promiscuous mode)

    // https://stackoverflow.com/questions/10070008/reading-from-pf-packet-sock-raw-with-read-misses-packets MAYBE?
    struct packet_mreq mr = {
        .mr_ifindex = socket_address.sll_ifindex,
        .mr_type = PACKET_MR_PROMISC,
    };
    if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) {
        FAIL( "Error setting packet membership\n" );
    }

    struct ifreq ifr = { 0 };
    strncpy(ifr.ifr_name, devname, IFNAMSIZ-1);
    if( ioctl( fd, SIOCGIFHWADDR, &ifr ) < 0 )
        Error

    memcpy( mymac, (char*)ifr.ifr_hwaddr.sa_data, 6 );
    int rv = setsockopt( fd, SOL_SOCKET, SO_BINDTODEVICE, devname, strlen(devname) );

I can easily receive packets, with read() and I can reply with write() or send() and everything works perfectly with other computers on the network. But when my local machine sends my IP stack packets, the stack receives the messages and tries sending back. I can see those replies in Wireshark as well when monitoring the device... but the system (userspace/kernel space) does not see the packets.

I tried specifically using sendto() with various sockaddr_ll addresses, and I can't seem to get it to fail, but still, my local system does not receive any of the packets locally.

What am I missing? How do I send packets from a C program to a local network interface and have my local system see them?

Right now, I can't even get my local system to see the arp replies.

EDIT: To clarify, my goal is to make it so my IP stack can reply to other computers on the same network or to the PC the app is running on, as though it were on a LAN.

EDIT: My issue with bridging was that I still gave my ethernet device an address, I should have only given my bridge device an address. With a bridge and code that connects to tap0, I could access the code from both sides and everything worked!

1 Answer 1

3

What am I missing?

Network interfaces work like a pipe. If you send a packet through one end, it always comes out through the other – a packet sent through the 'host' end will not be reflected back to the host regardless of its address.

Only the Wireshark packet capture merges both inbound and outbound packets into a single stream, but that does not mean they're all processed identically.

How do I send packets from a C program to a local network interface and have my local system see them?

Use a tap interface.

For communications only with the host, it doesn't need a bridge.

For communications with the host and an external network, use a bridge, and keep in mind that all interfaces that become "bridge ports" are detached from the host's networking stack and fully taken over by the bridge – which means that the host's IP address now needs to be configured on the bridge 'br0' interface, not anymore on the physical 'eth0' interface.

Sign up to request clarification or add additional context in comments.

8 Comments

I need a way of sending it both to localhost via a tap and to other devices on the network. Do I need multiple interfaces?
Or worse yet... how would I reply on the same subnet? Without a bridge, I don't know how the Linux box would appropriately route traffic.
Well, it seems to work to use both binding to a tap device and an ethernet device, and set both the ethernet and tap to the same subnet. And then doing layer 2 routing in my app. But daaaaang that's janky.
In that case, yes, a bridge would be suitable. Both the tap interface and the host would be separate ports in the bridge, effectively like two separate network entities.
But then I'm back at square zero. If I do a bridge with my ethernet nic + tap0, then the system the bridge is running on cannot access either the network on my nic, or tap0.
In your post you only said that you thought that this other method would "make more sense", you didn't mention anything about having actual problems with the bridge. But those are solvable problems (and warrant their own thread preferably on SuperUser or Unix.StackExchange) – they don't mean a bridge is automatically the wrong tool. (Bridging is literally how one connects 3+ ethernet ports.)
My guess at first is that you forgot to move the host's IP configuration from the physical eth0 onto the bridge. Once eth0 is bridged, it gets detached from the host's network stack and "taken over" by the bridge (and the same applies to tap0) – the OS only communicates with the bridge as a whole, so br0 is what needs to have the IP address (or the DHCP client) instead.
Thanks! This was enough to get my moving again. I will decide later if I want to use the bridge method or open both interfaces and manually route. Definitely accepting this answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.