lx_emul: unify NIC uplink binding

Fix genodelabs/genode#5361
This commit is contained in:
Stefan Kalkowski 2024-10-15 11:04:34 +02:00 committed by Christian Helmuth
parent 77d53f13ca
commit 4f084d1f9e
9 changed files with 319 additions and 434 deletions

View File

@ -16,6 +16,7 @@
#include <linux/netdevice.h>
#include <linux/sched/task.h>
#include <linux/usb.h>
#include <lx_emul/nic.h>
#include <lx_user/init.h>
#include <genode_c_api/uplink.h>
#include <genode_c_api/mac_address_reporter.h>
@ -30,230 +31,17 @@ struct task_struct *lx_user_new_usb_task(int (*func)(void*), void *args,
}
static struct genode_uplink *dev_genode_uplink(struct net_device *dev)
{
return (struct genode_uplink *)dev->ifalias;
}
struct genode_uplink_rx_context
{
struct net_device *dev;
};
struct genode_uplink_tx_packet_context
{
struct sk_buff *skb;
};
static unsigned long uplink_tx_packet_content(struct genode_uplink_tx_packet_context *ctx,
char *dst, unsigned long dst_len)
{
struct sk_buff * const skb = ctx->skb;
skb_push(skb, ETH_HLEN);
if (dst_len < skb->len) {
printk("uplink_tx_packet_content: packet exceeds uplink packet size\n");
memset(dst, 0, dst_len);
return 0;
}
skb_copy_from_linear_data(skb, dst, skb->len);
/* clear unused part of the destination buffer */
memset(dst + skb->len, 0, dst_len - skb->len);
return skb->len;
}
static rx_handler_result_t handle_rx(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct genode_uplink_tx_packet_context ctx = { .skb = skb };
/* if uplink still exists */
if (dev->ifalias) {
bool progress = genode_uplink_tx_packet(dev_genode_uplink(dev),
uplink_tx_packet_content,
&ctx);
if (!progress)
printk("handle_rx: uplink saturated, dropping packet\n");
}
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
/**
* Create Genode uplink for given net device
*
* The uplink is registered at the dev->ifalias pointer.
*/
static void handle_create_uplink(struct net_device *dev)
{
struct genode_uplink_args args;
if (dev_genode_uplink(dev))
return;
if (!netif_carrier_ok(dev))
return;
printk("create uplink for net device %s\n", &dev->name[0]);
memset(&args, 0, sizeof(args));
if (dev->addr_len != sizeof(args.mac_address)) {
printk("error: net device has unexpected addr_len %u\n", dev->addr_len);
return;
}
{
unsigned i;
for (i = 0; i < dev->addr_len; i++)
args.mac_address[i] = dev->dev_addr[i];
}
args.label = &dev->name[0];
dev->ifalias = (struct dev_ifalias *)genode_uplink_create(&args);
}
static void handle_destroy_uplink(struct net_device *dev)
{
struct genode_uplink *uplink = dev_genode_uplink(dev);
if (!uplink)
return;
if (netif_carrier_ok(dev))
return;
printk("destroy uplink for net device %s\n", &dev->name[0]);
genode_uplink_destroy(uplink);
dev->ifalias = NULL;
}
static genode_uplink_rx_result_t uplink_rx_one_packet(struct genode_uplink_rx_context *ctx,
char const *ptr, unsigned long len)
{
struct sk_buff *skb = alloc_skb(len, GFP_KERNEL);
if (!skb) {
printk("alloc_skb failed\n");
return GENODE_UPLINK_RX_RETRY;
}
skb_copy_to_linear_data(skb, ptr, len);
skb_put(skb, len);
skb->dev = ctx->dev;
if (dev_queue_xmit(skb) < 0) {
printk("lx_user: failed to xmit packet\n");
return GENODE_UPLINK_RX_REJECTED;
}
return GENODE_UPLINK_RX_ACCEPTED;
}
/*
* custom MAC address
*/
bool use_mac_address;
unsigned char mac_address[6];
static bool mac_address_configured = false;
static void handle_mac_address(struct net_device *dev)
{
int err;
struct sockaddr addr;
struct genode_mac_address dev_addr;
if (mac_address_configured || !netif_device_present(dev)) return;
if (use_mac_address) {
memcpy(&addr.sa_data, mac_address, ETH_ALEN);
addr.sa_family = dev->type;
err = dev_set_mac_address(dev, &addr, NULL);
if (err < 0)
printk("Warning: Could not set configured MAC address: %pM (err=%d)\n",
mac_address, err);
}
memcpy(dev_addr.addr, dev->dev_addr, sizeof(dev_addr));
genode_mac_address_register(dev->name, dev_addr);
mac_address_configured =true;
}
static int network_loop(void *arg)
{
for (;;) {
struct net_device *dev;
for_each_netdev(&init_net, dev) {
handle_mac_address(dev);
/* enable link sensing, repeated calls are handled by testing IFF_UP */
dev_open(dev, 0);
/* install rx handler once */
if (!netdev_is_rx_handler_busy(dev))
netdev_rx_handler_register(dev, handle_rx, NULL);
/* respond to cable plug/unplug */
handle_create_uplink(dev);
handle_destroy_uplink(dev);
/* transmit packets received from the uplink session */
if (netif_carrier_ok(dev)) {
struct genode_uplink_rx_context ctx = { .dev = dev };
while (genode_uplink_rx(dev_genode_uplink(dev),
uplink_rx_one_packet,
&ctx));
}
};
/* block until lx_emul_task_unblock */
lx_emul_task_schedule(true);
}
return 0;
}
static struct task_struct *net_task;
void lx_user_init(void)
{
pid_t pid;
lx_emul_usb_client_init();
pid = kernel_thread(network_loop, NULL, "network_loop",
CLONE_FS | CLONE_FILES);
net_task = find_task_by_pid_ns(pid, NULL);
lx_emul_nic_init();
}
void lx_user_handle_io(void)
{
lx_emul_usb_client_ticker();
if (net_task) lx_emul_task_unblock(net_task);
lx_emul_nic_handle_io();
}
@ -269,11 +57,10 @@ void rtmsg_ifinfo(int type, struct net_device * dev,
unsigned int change, gfp_t flags,
u32 portid, const struct nlmsghdr *nlh)
{
/* trigger handle_create_uplink / handle_destroy_uplink */
if (net_task) lx_emul_task_unblock(net_task);
lx_emul_nic_handle_io();
if (force_uplink_destroy) {
struct genode_uplink *uplink = dev_genode_uplink(dev);
struct genode_uplink *uplink = (struct genode_uplink *)dev->ifalias;
printk("force destroy uplink for net device %s\n", &dev->name[0]);
genode_uplink_destroy(uplink);
force_uplink_destroy = false;
@ -284,7 +71,9 @@ void rtmsg_ifinfo(int type, struct net_device * dev,
void lx_emul_usb_client_device_unregister_callback(struct usb_device *)
{
force_uplink_destroy = true;
mac_address_configured = false;
/* set mac as unconfigured by setting nothing */
lx_emul_nic_set_mac_address(NULL, 0);
}

View File

@ -23,15 +23,13 @@
#include <lx_kit/env.h>
#include <lx_user/io.h>
#include <lx_emul/init.h>
#include <lx_emul/nic.h>
/* C-interface */
#include <usb_net.h>
using namespace Genode;
extern bool use_mac_address;
extern unsigned char mac_address[6];
struct Main
{
Env &env;
@ -90,12 +88,10 @@ struct Main
usb_config = config_rom.xml().attribute_value("configuration", 0ul);
/* retrieve possible MAC */
use_mac_address = config_rom.xml().has_attribute("mac");
if (use_mac_address) {
if (config_rom.xml().has_attribute("mac")) {
auto const mac = config_rom.xml().attribute_value("mac", Nic::Mac_address{});
mac.copy(mac_address);
use_mac_address = true;
log("Trying to use configured mac: ", mac);
lx_emul_nic_set_mac_address(mac.addr, sizeof(mac.addr));
}
}
};

View File

@ -8,6 +8,7 @@ SRC_C += dummies.c \
lx_emul.c \
lx_user.c
SRC_C += lx_emul/nic.c
SRC_C += lx_emul/virt/shadow/drivers/usb/core/buffer.c
SRC_C += lx_emul/virt/shadow/drivers/usb/core/hcd.c
SRC_C += lx_emul/virt/usb_client.c

View File

@ -0,0 +1,31 @@
/*
* \brief Lx_emul support for NICs
* \author Stefan Kalkowski
* \date 2024-10-15
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__NIC_H_
#define _LX_EMUL__NIC_H_
#ifdef __cplusplus
extern "C" {
#endif
extern void lx_emul_nic_init(void);
extern void lx_emul_nic_handle_io(void);
extern void lx_emul_nic_set_mac_address(const unsigned char *mac,
unsigned long size);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__USB_H_ */

View File

@ -0,0 +1,258 @@
/*
* \brief PC Ethernet driver
* \author Norman Feske
* \author Christian Helmuth
* \author Stefan Kalkowski
* \date 2024-10-15
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/kthread.h>
#include <linux/netdevice.h>
#include <lx_emul/nic.h>
#include <genode_c_api/uplink.h>
#include <genode_c_api/mac_address_reporter.h>
static struct genode_uplink *dev_genode_uplink(struct net_device *dev)
{
return (struct genode_uplink *)dev->ifalias;
}
struct genode_uplink_rx_context
{
struct net_device *dev;
};
struct genode_uplink_tx_packet_context
{
struct sk_buff *skb;
};
static unsigned long uplink_tx_packet_content(struct genode_uplink_tx_packet_context *ctx,
char *dst, unsigned long dst_len)
{
struct sk_buff * const skb = ctx->skb;
skb_push(skb, ETH_HLEN);
if (dst_len < skb->len) {
printk("uplink_tx_packet_content: packet exceeds uplink packet size\n");
memset(dst, 0, dst_len);
return 0;
}
skb_copy_from_linear_data(skb, dst, skb->len);
/* clear unused part of the destination buffer */
memset(dst + skb->len, 0, dst_len - skb->len);
return skb->len;
}
static rx_handler_result_t handle_rx(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct genode_uplink_tx_packet_context ctx = { .skb = skb };
struct genode_uplink *uplink = dev_genode_uplink(dev);
if (!uplink)
return RX_HANDLER_PASS;
{
bool progress = genode_uplink_tx_packet(uplink,
uplink_tx_packet_content,
&ctx);
if (!progress)
printk("handle_rx: uplink saturated, dropping packet\n");
}
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
/**
* Create Genode uplink for given net device
*
* The uplink is registered at the dev->ifalias pointer.
*/
static void handle_create_uplink(struct net_device *dev)
{
struct genode_uplink_args args;
if (dev_genode_uplink(dev))
return;
if (!netif_carrier_ok(dev))
return;
printk("create uplink for net device %s\n", &dev->name[0]);
memset(&args, 0, sizeof(args));
if (dev->addr_len != sizeof(args.mac_address)) {
printk("error: net device has unexpected addr_len %u\n", dev->addr_len);
return;
}
{
unsigned i;
for (i = 0; i < dev->addr_len; i++)
args.mac_address[i] = dev->dev_addr[i];
}
args.label = &dev->name[0];
dev->ifalias = (struct dev_ifalias *)genode_uplink_create(&args);
}
static void handle_destroy_uplink(struct net_device *dev)
{
struct genode_uplink *uplink = dev_genode_uplink(dev);
if (!uplink)
return;
if (netif_carrier_ok(dev))
return;
genode_uplink_destroy(uplink);
dev->ifalias = NULL;
}
static genode_uplink_rx_result_t uplink_rx_one_packet(struct genode_uplink_rx_context *ctx,
char const *ptr, unsigned long len)
{
struct sk_buff *skb = alloc_skb(len, GFP_KERNEL);
if (!skb) {
printk("alloc_skb failed\n");
return GENODE_UPLINK_RX_RETRY;
}
skb_copy_to_linear_data(skb, ptr, len);
skb_put(skb, len);
skb->dev = ctx->dev;
if (dev_queue_xmit(skb) < 0) {
printk("lx_user: failed to xmit packet\n");
return GENODE_UPLINK_RX_REJECTED;
}
return GENODE_UPLINK_RX_ACCEPTED;
}
/*
* custom MAC address
*/
static unsigned char mac_address[6];
static bool mac_address_configured = false;
static void handle_mac_address(struct net_device *dev)
{
int err;
struct sockaddr addr;
struct genode_mac_address dev_addr;
if (mac_address_configured || !netif_device_present(dev)) return;
if (mac_address[0] || mac_address[1] || mac_address[2] ||
mac_address[3] || mac_address[4] || mac_address[5]) {
memcpy(&addr.sa_data, mac_address, ETH_ALEN);
addr.sa_family = dev->type;
err = dev_set_mac_address(dev, &addr, NULL);
if (err < 0)
printk("Warning: Could not set configured MAC address: %pM (err=%d)\n",
mac_address, err);
}
memcpy(dev_addr.addr, dev->dev_addr, sizeof(dev_addr));
genode_mac_address_register(dev->name, dev_addr);
mac_address_configured =true;
}
static int nic_task_function(void *arg)
{
for (;;) {
struct net_device *dev;
for_each_netdev(&init_net, dev) {
handle_mac_address(dev);
/* enable link sensing, repeated calls are handled by testing IFF_UP */
dev_open(dev, 0);
/* install rx handler once */
if (!netdev_is_rx_handler_busy(dev))
netdev_rx_handler_register(dev, handle_rx, NULL);
/* respond to cable plug/unplug */
handle_create_uplink(dev);
handle_destroy_uplink(dev);
/* transmit packets received from the uplink session */
if (netif_carrier_ok(dev)) {
struct genode_uplink_rx_context ctx = { .dev = dev };
while (genode_uplink_rx(dev_genode_uplink(dev),
uplink_rx_one_packet,
&ctx));
}
};
/* block until lx_emul_task_unblock */
lx_emul_task_schedule(true);
}
return 0;
}
static struct task_struct *nic_task_struct_ptr;
void lx_emul_nic_init(void)
{
pid_t pid;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,3,0)
pid = kernel_thread(nic_task_function, NULL, "user_task", CLONE_FS | CLONE_FILES);
#else
pid = kernel_thread(nic_task_function, NULL, CLONE_FS | CLONE_FILES);
#endif
nic_task_struct_ptr = find_task_by_pid_ns(pid, NULL);
}
void lx_emul_nic_handle_io(void)
{
if (nic_task_struct_ptr) lx_emul_task_unblock(nic_task_struct_ptr);
}
void lx_emul_nic_set_mac_address(const unsigned char *mac, unsigned long size)
{
memcpy(mac_address, mac, min(sizeof(mac_address), size));
mac_address_configured = false;
}

View File

@ -11,214 +11,17 @@
* version 2.
*/
#include <linux/kthread.h>
#include <linux/netdevice.h>
#include <lx_user/init.h>
#include <genode_c_api/uplink.h>
#include <genode_c_api/mac_address_reporter.h>
static struct genode_uplink *dev_genode_uplink(struct net_device *dev)
{
return (struct genode_uplink *)dev->ifalias;
}
struct genode_uplink_rx_context
{
struct net_device *dev;
};
struct genode_uplink_tx_packet_context
{
struct sk_buff *skb;
};
static unsigned long uplink_tx_packet_content(struct genode_uplink_tx_packet_context *ctx,
char *dst, unsigned long dst_len)
{
struct sk_buff * const skb = ctx->skb;
skb_push(skb, ETH_HLEN);
if (dst_len < skb->len) {
printk("uplink_tx_packet_content: packet exceeds uplink packet size\n");
memset(dst, 0, dst_len);
return 0;
}
skb_copy_from_linear_data(skb, dst, skb->len);
/* clear unused part of the destination buffer */
memset(dst + skb->len, 0, dst_len - skb->len);
return skb->len;
}
static rx_handler_result_t handle_rx(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct genode_uplink_tx_packet_context ctx = { .skb = skb };
struct genode_uplink *uplink = dev_genode_uplink(dev);
if (!uplink)
return RX_HANDLER_PASS;
{
bool progress = genode_uplink_tx_packet(uplink,
uplink_tx_packet_content,
&ctx);
if (!progress)
printk("handle_rx: uplink saturated, dropping packet\n");
}
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
/**
* Create Genode uplink for given net device
*
* The uplink is registered at the dev->ifalias pointer.
*/
static void handle_create_uplink(struct net_device *dev)
{
struct genode_uplink_args args;
if (dev_genode_uplink(dev))
return;
if (!netif_carrier_ok(dev))
return;
printk("create uplink for net device %s\n", &dev->name[0]);
memset(&args, 0, sizeof(args));
if (dev->addr_len != sizeof(args.mac_address)) {
printk("error: net device has unexpected addr_len %u\n", dev->addr_len);
return;
}
{
unsigned i;
for (i = 0; i < dev->addr_len; i++)
args.mac_address[i] = dev->dev_addr[i];
}
args.label = &dev->name[0];
dev->ifalias = (struct dev_ifalias *)genode_uplink_create(&args);
}
static void handle_destroy_uplink(struct net_device *dev)
{
struct genode_uplink *uplink = dev_genode_uplink(dev);
if (!uplink)
return;
if (netif_carrier_ok(dev))
return;
genode_uplink_destroy(uplink);
dev->ifalias = NULL;
}
static genode_uplink_rx_result_t uplink_rx_one_packet(struct genode_uplink_rx_context *ctx,
char const *ptr, unsigned long len)
{
struct sk_buff *skb = alloc_skb(len, GFP_KERNEL);
if (!skb) {
printk("alloc_skb failed\n");
return GENODE_UPLINK_RX_RETRY;
}
skb_copy_to_linear_data(skb, ptr, len);
skb_put(skb, len);
skb->dev = ctx->dev;
if (dev_queue_xmit(skb) < 0) {
printk("lx_user: failed to xmit packet\n");
return GENODE_UPLINK_RX_REJECTED;
}
return GENODE_UPLINK_RX_ACCEPTED;
}
static int user_task_function(void *arg)
{
for (;;) {
struct net_device *dev;
for_each_netdev(&init_net, dev) {
struct genode_mac_address dev_addr;
/* enable link sensing, repeated calls are handled by testing IFF_UP */
dev_open(dev, 0);
memcpy(dev_addr.addr, dev->dev_addr, sizeof(dev_addr));
genode_mac_address_register(dev->name, dev_addr);
/* install rx handler once */
if (!netdev_is_rx_handler_busy(dev))
netdev_rx_handler_register(dev, handle_rx, NULL);
/* respond to cable plug/unplug */
handle_create_uplink(dev);
handle_destroy_uplink(dev);
/* transmit packets received from the uplink session */
if (netif_carrier_ok(dev)) {
struct genode_uplink_rx_context ctx = { .dev = dev };
while (genode_uplink_rx(dev_genode_uplink(dev),
uplink_rx_one_packet,
&ctx));
}
};
/* block until lx_emul_task_unblock */
lx_emul_task_schedule(true);
}
return 0;
}
struct task_struct *user_task_struct_ptr; /* used by 'Main' for lx_emul_task_unblock */
#include <lx_emul/nic.h>
void lx_user_init(void)
{
pid_t pid;
pid = kernel_thread(user_task_function, NULL, "user_task", CLONE_FS | CLONE_FILES);
user_task_struct_ptr = find_task_by_pid_ns(pid, NULL);
lx_emul_nic_init();
}
#include <linux/rtnetlink.h>
/*
* Called whenever the link state changes
*/
void rtmsg_ifinfo(int type, struct net_device * dev, unsigned int change, gfp_t flags,
u32 portid, const struct nlmsghdr *nlh)
void lx_user_handle_io(void)
{
/* trigger handle_create_uplink / handle_destroy_uplink */
if (user_task_struct_ptr)
lx_emul_task_unblock(user_task_struct_ptr);
lx_emul_nic_handle_io();
}

View File

@ -16,7 +16,7 @@
#include <lx_kit/init.h>
#include <lx_kit/env.h>
#include <lx_emul/init.h>
#include <lx_emul/task.h>
#include <lx_user/io.h>
#include <genode_c_api/uplink.h>
#include <genode_c_api/mac_address_reporter.h>
@ -26,8 +26,6 @@ namespace Pc {
}
extern task_struct *user_task_struct_ptr;
struct Pc::Main
{
Env &_env;
@ -52,11 +50,8 @@ struct Pc::Main
void _handle_signal()
{
if (user_task_struct_ptr)
lx_emul_task_unblock(user_task_struct_ptr);
lx_user_handle_io();
Lx_kit::env().scheduler.execute();
genode_uplink_notify_peers();
}

View File

@ -12,6 +12,7 @@
*/
#include <lx_emul.h>
#include <lx_emul/nic.h>
#include <net/rtnetlink.h>
#include <linux/mutex.h>
@ -56,3 +57,13 @@ void rtnl_unlock(void)
{
netdev_run_todo();
}
/*
* Called whenever the link state changes
*/
void rtmsg_ifinfo(int type, struct net_device * dev, unsigned int change, gfp_t flags,
u32 portid, const struct nlmsghdr *nlh)
{
lx_emul_nic_handle_io();
}

View File

@ -7,6 +7,7 @@ SRC_CC += main.cc
SRC_C += dummies.c
SRC_C += lx_emul.c
SRC_C += lx_emul/common_dummies.c
SRC_C += lx_emul/nic.c
SRC_C += lx_user.c
SRC_C += rtnetlink.c