Skip to content

Basic net functionality#762

Merged
lacraig2 merged 21 commits intomainfrom
netdev2
Apr 1, 2026
Merged

Basic net functionality#762
lacraig2 merged 21 commits intomainfrom
netdev2

Conversation

@lacraig2
Copy link
Copy Markdown
Collaborator

This PR supersedes #709.

Review

In older versions of penguin we worked with network devices by listing them in interfaces and handling their interactions at the syscall layer when possible (e.g. ioctl).

Further, we used /proc/penguin_net and a series of shell scripts to set up dummy devices and run them.

New system

In our new system by default we want to express the same behavior. If you list interfaces and handle ioctl interactions it will all work just fine. The default code acts very similar to the original dummy interface we have for the older devices.

However, you now have a new path for defining much more complex network devices in Python outside the guest.

You may asynchronously register network devices with a backing class using the net interface:

class NetworkDeviceTest(Plugin):
    def __init__(self):
        # Register some sample netdevs for testing
        # Register is an alias for register_netdev, so both should work
        plugins.net.register("sample0", SampleNetDev)
        plugins.net.register_netdev("sample1", SampleNetDev)

There is no requirement that the interface be listed in the network interfaces in the config, but listing it doesn't hurt anything.

There is a Netdev class defined in net.py which you should use for your backing class.

Backing classes can modify the core net_device structure and set up operations in the setup method of the class. https://elixir.bootlin.com/linux/v6.19.10/source/include/linux/netdevice.h#L1788

class SampleNetDev(Netdev):
    def setup(self, netdev):
        self.logger.info(f"Setting up altered network device {self.name}")
        netdev_ops = yield from kffi.deref(netdev.netdev_ops)
        netdev_ops.ndo_do_ioctl = yield from kffi.callback(self.ioctl_handler)
        netdev_ops.ndo_get_stats64 = yield from kffi.callback(self.stats64_handler)
        yield from mem.write_bytes(netdev.netdev_ops.address, bytes(netdev_ops))
    
    def stats64_handler(self, pt_regs, netdev_ptr, stats64_ptr):
        netdevs = yield from kffi.read_type(netdev_ptr, "net_device")
        stats64 = yield from kffi.read_type(stats64_ptr, "rtnl_link_stats64")
        self.logger.info(f"Getting stats64 for device {kffi.ffi.string(netdevs.name)}")
        stats64.rx_packets = 1337
        stats64.tx_packets = 1338
        stats64.rx_bytes = 1339
        stats64.tx_bytes = 1340
        # Just return zeroed stats for now
        yield from mem.write_bytes(stats64_ptr, bytes(stats64))
    
    def ioctl_handler(self, pt_regs, netdev_ptr, ifreq_ptr, cmd):
        args = yield from plugins.osi.get_args()
        netdevs = yield from kffi.read_type(netdev_ptr, "net_device")
        name = kffi.ffi.string(netdevs.name)
        self.logger.info((name, args, cmd))
        return 0

We also have functionality to look through the net_device structure and automatically determine possible names/functions mapping from these devices. However, I think a simpler path here might be to provide the names of functions that will be auto-added to the structure if the correct name is added, but I'm open to suggestions.

github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

@lacraig2 lacraig2 merged commit 5819213 into main Apr 1, 2026
14 checks passed
@lacraig2 lacraig2 deleted the netdev2 branch April 1, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant