How to insert an ip packet to linux network stack on the egress path?

41 Views Asked by At

I have been experiencing a behavior I do not understand relating to handling packets in the linux kernel. Below a minimal(ish) example which exposes the situation.

Please, kindly consider that I'm an experienced software engineer with a terrible blind spot for networking.

context

suppose I want to write a some form of a loopback interface as a linux module with a twist : unlike the "normal" lo which only insert packets as inbound, I which that packet reinsert could be forwarded normally after being re-injected.

For instance, here is a scenario:

  1. I do ping google.com -I la to forcefully use loop-around to contact google.com
  2. the packet is forced to la
  3. la_xmit captures the packet
  4. la_xmit publish a new packet through the stack using netif_rx
  5. the packet goes into the network stack and is routed naturally
  6. the packet leaves through eth0

What I tried

following, is what I thought to be a minimally viable module

#include <linux/module.h>
#include <linux/init.h>

#include <linux/skbuff.h>
#include <net/ip_tunnels.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("me");
MODULE_DESCRIPTION("re-injector");

struct net_device *my_netdev;

static netdev_tx_t la_xmit(struct sk_buff *captured_skb, struct net_device *dev) {
    int len = captured_skb->len;

    // move to a new skb
    struct sk_buff *skb = alloc_skb(len, GFP_KERNEL);
    memcpy(skb_put(skb, len), captured_skb->data, len);
    dev_kfree_skb(captured_skb);

    // initialize new packet
    skb_tx_timestamp(skb);
    skb_clear_tstamp(skb);
    skb_orphan(skb);
    skb_dst_force(skb);

    // inject into network stack
    int skb_len = skb->len; // copy now, after netif, it's not ours anymore
    int err = netif_rx(skb);
    if (likely(err == NET_RX_SUCCESS)) dev_lstats_add(my_netdev, skb_len);

    return NETDEV_TX_OK;
}

const struct net_device_ops ndo = {
    .ndo_start_xmit = la_xmit
};

static void setup_netdev(struct net_device *dev) {
    dev->netdev_ops = &ndo;
    dev->header_ops = &ip_tunnel_header_ops;

    dev->mtu = 1500;

    dev->flags |= IFF_POINTOPOINT;
    dev->flags |= IFF_NOARP;
    dev->flags |= IFF_MULTICAST;
}

static int __init la_init(void) {
    my_netdev = alloc_netdev(0, "la", NET_NAME_UNKNOWN, setup_netdev);
    register_netdev(my_netdev);
    return 0;
}

static void __exit la_cleanup(void) {
    unregister_netdev(my_netdev);
    free_netdev(my_netdev);
}

module_init(la_init);
module_exit(la_cleanup);

I capture incoming packet, copy it and follow the logic from net/loopback.c to inject the packet but using netif_rx() instead of __netif_rx() since I understand the latter to skips part of the routing process under the assumption of inbound traffic.

What I expected

I hoped to see ICMP traffic out of eth0

What happens instead

My VM hangs suddenly. sudo journalctl -k -b -1 to read logs from last session doesn't bring any error.

0

There are 0 best solutions below