diff --git a/src/Makefile b/src/Makefile index fdbb582..223eeda 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,6 +16,7 @@ igloo-objs += igloo_hc.o hooks/syscalls_hc.o hooks/ioctl_hc.o \ hooks/sock_hc.o hooks/uname_hc.o hooks/block_mounts.o \ hooks/igloo_open.o \ hyperfs/hyperfs.o \ + netdevs/igloonet.o \ $(PORTAL_OBJS) KDIR ?= /lib/modules/$(shell uname -r)/build diff --git a/src/igloo_hypercall_consts.h b/src/igloo_hypercall_consts.h index 444a344..2a93439 100644 --- a/src/igloo_hypercall_consts.h +++ b/src/igloo_hypercall_consts.h @@ -50,5 +50,4 @@ enum igloo_hypercall_constants { IGLOO_SYSCALL = 0x6408400B, IGLOO_MODULE_BASE = 0x6408400C, IGLOO_INIT_MODULE = 0x6408400D, - }; \ No newline at end of file diff --git a/src/netdevs/igloonet.c b/src/netdevs/igloonet.c new file mode 100644 index 0000000..d20555e --- /dev/null +++ b/src/netdevs/igloonet.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "igloonet.h" +#include "portal.h" +#include "igloo_hypercall_consts.h" + +/* fake multicast ability */ +static void set_multicast_list(struct net_device *dev) {} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) +// 6.13+ uses dev_lstats for stats +static void igloonet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + dev_lstats_read(dev, &stats->tx_packets, &stats->tx_bytes); +} +#else +// 4.10 uses per-cpu stats +struct pcpu_dstats { + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; +}; + +static struct rtnl_link_stats64 *igloonet_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + int i; + for_each_possible_cpu(i) { + const struct pcpu_dstats *dstats; + u64 tbytes, tpackets; + unsigned int start; + dstats = per_cpu_ptr(dev->dstats, i); + do { + start = u64_stats_fetch_begin_irq(&dstats->syncp); + tbytes = dstats->tx_bytes; + tpackets = dstats->tx_packets; + } while (u64_stats_fetch_retry_irq(&dstats->syncp, start)); + stats->tx_bytes += tbytes; + stats->tx_packets += tpackets; + } + return stats; +} +#endif + +static netdev_tx_t igloonet_xmit(struct sk_buff *skb, struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + dev_lstats_add(dev, skb->len); + skb_tx_timestamp(skb); +#else + struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); + u64_stats_update_begin(&dstats->syncp); + dstats->tx_packets++; + dstats->tx_bytes += skb->len; + u64_stats_update_end(&dstats->syncp); +#endif + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int igloonet_dev_init(struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; + netdev_lockdep_set_classes(dev); + return 0; +#else + dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); + if (!dev->dstats) + return -ENOMEM; + return 0; +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,13,0) +static void igloonet_dev_uninit(struct net_device *dev) +{ + free_percpu(dev->dstats); +} +#endif + +static int igloonet_change_carrier(struct net_device *dev, bool new_carrier) +{ + if (new_carrier) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + +static const struct net_device_ops igloonet_netdev_ops = { + .ndo_init = igloonet_dev_init, +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,13,0) + .ndo_uninit = igloonet_dev_uninit, +#endif + .ndo_start_xmit = igloonet_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_get_stats64 = igloonet_get_stats64, + .ndo_change_carrier = igloonet_change_carrier, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) +static const struct ethtool_ops igloonet_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; +#else +static void igloonet_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, "igloonet", sizeof(info->driver)); + strlcpy(info->version, "1.0", sizeof(info->version)); +} +static const struct ethtool_ops igloonet_ethtool_ops = { + .get_drvinfo = igloonet_get_drvinfo, +}; +#endif + +static void igloonet_setup(struct net_device *dev) +{ + struct igloonet_priv *priv = netdev_priv(dev); + + ether_setup(dev); + + /* Copy the static global templates into this device's private memory */ + memcpy(&priv->netdev_ops, &igloonet_netdev_ops, sizeof(struct net_device_ops)); + memcpy(&priv->ethtool_ops, &igloonet_ethtool_ops, sizeof(struct ethtool_ops)); + + /* Point this device to its OWN private copies */ + dev->netdev_ops = &priv->netdev_ops; + dev->ethtool_ops = &priv->ethtool_ops; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + dev->needs_free_netdev = true; +#else + dev->destructor = free_netdev; +#endif + + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + dev->lltx = true; + dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + dev->features |= NETIF_F_GSO_ENCAP_ALL; + dev->hw_features |= dev->features; + dev->hw_enc_features |= dev->features; + dev->min_mtu = 0; + dev->max_mtu = 0; +#else + dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; + dev->features |= NETIF_F_ALL_TSO | NETIF_F_UFO; + dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX; + dev->features |= NETIF_F_GSO_ENCAP_ALL; + dev->hw_features |= dev->features; + dev->hw_enc_features |= dev->features; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; +#endif + eth_hw_addr_random(dev); +} + +static void igloonet_dellink(struct net_device *dev, struct list_head *head) +{ + pr_info("igloonet: preventing deletion of %s\n", dev->name); + return; + // unregister_netdevice_queue(dev, head); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) +static int igloonet_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +#else +static int igloonet_validate(struct nlattr *tb[], struct nlattr *data[]) +#endif +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + return 0; +} + +static struct rtnl_link_ops igloonet_link_ops __read_mostly = { + .kind = "igloonet", + .setup = igloonet_setup, + .validate = igloonet_validate, + .dellink = igloonet_dellink, +}; + +struct net_device* igloonet_init_one(const char *devname) +{ + struct net_device *dev_igloonet; + struct igloonet_priv *priv; + int err; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + /* allocate without calling the setup callback so we can copy the + * requested name into dev->name before setup runs (setup may + * reference dev->name). We'll call igloonet_setup() ourselves. */ + dev_igloonet = alloc_netdev(0, devname, NET_NAME_USER, igloonet_setup); +#else + dev_igloonet = alloc_netdev(sizeof(struct igloonet_priv), devname, NET_NAME_USER, igloonet_setup); +#endif + if (!dev_igloonet) + return NULL; + + /* Get the private data block */ + priv = netdev_priv(dev_igloonet); + + /* Copy the static template into our per-interface private data */ + memcpy(&priv->link_ops, &igloonet_link_ops, sizeof(struct rtnl_link_ops)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + /* copy the requested name into the netdev so igloonet_setup can use it */ + memcpy(dev_igloonet->name, devname, IFNAMSIZ - 1); + dev_igloonet->name[IFNAMSIZ - 1] = '\0'; +#else + strlcpy(dev_igloonet->name, devname, IFNAMSIZ); +#endif + + /* Assign the dynamically copied ops, NOT the static global one */ + dev_igloonet->rtnl_link_ops = &priv->link_ops; + err = register_netdev(dev_igloonet); + if (err < 0) + goto err; + return dev_igloonet; + +err: + free_netdev(dev_igloonet); + return NULL; +} diff --git a/src/netdevs/igloonet.h b/src/netdevs/igloonet.h new file mode 100644 index 0000000..8ee7761 --- /dev/null +++ b/src/netdevs/igloonet.h @@ -0,0 +1,14 @@ +#ifndef _IGLOONET_H +#define _IGLOONET_H + +#include +#include + +struct igloonet_priv { + struct rtnl_link_ops link_ops; + struct net_device_ops netdev_ops; + struct ethtool_ops ethtool_ops; +}; + +struct net_device* igloonet_init_one(const char *devname); +#endif \ No newline at end of file diff --git a/src/portal/portal_ffi.c b/src/portal/portal_ffi.c index 2b5f3a0..2df1b07 100644 --- a/src/portal/portal_ffi.c +++ b/src/portal/portal_ffi.c @@ -61,7 +61,7 @@ void handle_op_ffi_exec(portal_region *mem_region) /* Validate function pointer */ if (!ffi_data->func_ptr || !igloo_is_kernel_addr((unsigned long)ffi_data->func_ptr)) { - igloo_pr_debug("igloo: Invalid function pointer %p\n", ffi_data->func_ptr); + igloo_pr_debug("igloo: Invalid function pointer %p\n", (void *)ffi_data->func_ptr); mem_region->header.op = HYPER_RESP_READ_FAIL; return; } @@ -74,7 +74,7 @@ void handle_op_ffi_exec(portal_region *mem_region) } igloo_pr_debug("igloo: Calling function at %p with %lu arguments\n", - ffi_data->func_ptr, ffi_data->num_args); + (void *)ffi_data->func_ptr, ffi_data->num_args); /* Call the function with the appropriate number of arguments */ switch (ffi_data->num_args) { diff --git a/src/portal/portal_net.c b/src/portal/portal_net.c new file mode 100644 index 0000000..547ea73 --- /dev/null +++ b/src/portal/portal_net.c @@ -0,0 +1,113 @@ +#include "portal_internal.h" +#include +#include +#include +#include "../netdevs/igloonet.h" + +void handle_op_register_netdev(portal_region *mem_region) +{ + struct net_device *ndev; + char *devname = (char *)PORTAL_DATA(mem_region); + + printk(KERN_EMERG "igloo: register_netdev request for '%s'\n", devname); + + ndev = igloonet_init_one(devname); + if (ndev) { + printk(KERN_EMERG "igloo: registered netdev '%s' returned %p\n", devname, ndev); + mem_region->header.op = HYPER_RESP_READ_NUM; + mem_region->header.size = (uintptr_t)ndev; + } else { + printk(KERN_EMERG "igloo: failed to register netdev '%s'\n", devname); + mem_region->header.op = HYPER_RESP_READ_FAIL; + mem_region->header.size = (uintptr_t)ndev; + } +} + +void handle_op_lookup_netdev(portal_region *mem_region) +{ + char *devname = (char *)PORTAL_DATA(mem_region); + struct net_device *ndev = dev_get_by_name(&init_net, devname); + + printk(KERN_EMERG "igloo: lookup_netdev request for '%s'\n", devname); + + if (ndev) { + printk(KERN_EMERG "igloo: found netdev '%s' at %p\n", devname, ndev); + mem_region->header.op = HYPER_RESP_READ_NUM; + mem_region->header.size = (uintptr_t)ndev; + dev_put(ndev); + } else { + printk(KERN_EMERG "igloo: netdev '%s' not found\n", devname); + mem_region->header.op = HYPER_RESP_READ_FAIL; + mem_region->header.size = 0; + } +} + +void handle_op_set_netdev_state(portal_region *mem_region) +{ + char *devname = (char *)PORTAL_DATA(mem_region); + struct net_device *ndev; + int ret = 0; + unsigned long requested_state = mem_region->header.size; + + printk(KERN_EMERG "igloo: set_netdev_state request for '%s' state=%lu\n", devname, requested_state); + + ndev = dev_get_by_name(&init_net, devname); + if (!ndev) { + printk(KERN_EMERG "igloo: netdev '%s' not found\n", devname); + mem_region->header.op = HYPER_RESP_READ_FAIL; + mem_region->header.size = 0; + return; + } + + rtnl_lock(); + if (requested_state) { + if (!(ndev->flags & IFF_UP)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,13,0) + ret = dev_open(ndev, NULL); +#else + ret = dev_open(ndev); +#endif + } + } else { + if (ndev->flags & IFF_UP) { + dev_close(ndev); // dev_close returns void + ret = 0; + } + } + rtnl_unlock(); + + if (ret == 0) { + printk(KERN_EMERG "igloo: netdev '%s' state set to %lu\n", devname, requested_state); + mem_region->header.op = HYPER_RESP_READ_NUM; + mem_region->header.size = requested_state; + } else { + printk(KERN_EMERG "igloo: failed to set netdev '%s' state to %lu\n", devname, requested_state); + mem_region->header.op = HYPER_RESP_READ_NUM; + mem_region->header.size = ret; + } + dev_put(ndev); +} + +void handle_op_get_netdev_state(portal_region *mem_region) +{ + char *devname = (char *)PORTAL_DATA(mem_region); + struct net_device *ndev; + unsigned int state = 0; + + printk(KERN_EMERG "igloo: get_netdev_state request for '%s'\n", devname); + + ndev = dev_get_by_name(&init_net, devname); + if (!ndev) { + printk(KERN_EMERG "igloo: netdev '%s' not found\n", devname); + mem_region->header.op = HYPER_RESP_READ_FAIL; + mem_region->header.size = 0; + return; + } + + state = !!(ndev->flags & IFF_UP); + printk(KERN_EMERG "igloo: netdev '%s' state is %u\n", devname, state); + + mem_region->header.op = HYPER_RESP_READ_NUM; + mem_region->header.size = state; + dev_put(ndev); +} \ No newline at end of file diff --git a/src/portal/portal_op_list.h b/src/portal/portal_op_list.h index 60812a9..863f19a 100644 --- a/src/portal/portal_op_list.h +++ b/src/portal/portal_op_list.h @@ -10,6 +10,7 @@ X(osi_mappings, OSI_MAPPINGS) \ X(osi_proc_mem, OSI_PROC_MEM) \ X(osi_proc_exe, OSI_PROC_EXE) \ + X(osi_proc_ptregs, OSI_PROC_PTREGS) \ X(read_procargs, READ_PROCARGS) \ X(read_procenv, READ_PROCENV) \ X(read_fds, READ_FDS) \ @@ -24,4 +25,8 @@ X(kallsyms_lookup, KALLSYMS_LOOKUP) \ X(tramp_generate, TRAMP_GENERATE) \ X(hyperfs_add_hyperfile, HYPERFS_ADD_HYPERFILE) \ + X(register_netdev, REGISTER_NETDEV) \ + X(lookup_netdev, LOOKUP_NETDEV) \ + X(set_netdev_state, SET_NETDEV_STATE) \ + X(get_netdev_state, GET_NETDEV_STATE) \ X(copy_buf_guest, COPY_BUF_GUEST) diff --git a/src/portal/portal_osi.c b/src/portal/portal_osi.c index 60ca814..de4a5de 100644 --- a/src/portal/portal_osi.c +++ b/src/portal/portal_osi.c @@ -12,6 +12,7 @@ #include #include #include +#include /* Compat helper for file inode access (changed ~5.15) */ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) @@ -874,3 +875,39 @@ void handle_op_read_time(portal_region *mem_region) mem_region->header.size = ktime_get_ns(); mem_region->header.op = HYPER_RESP_READ_NUM; } + +void handle_op_osi_proc_ptregs(portal_region *mem_region) +{ + struct task_struct *task = current; + struct pt_regs *regs = NULL; + + igloo_debug_osi("igloo: Handling HYPER_OP_OSI_PROC_PTREGS (pid=%d)\n", task ? task->pid : -1); + + if (!task) { + igloo_debug_osi("igloo: No task found for ptregs\n"); + mem_region->header.size = 0; + mem_region->header.op = HYPER_RESP_READ_FAIL; + return; + } + +#if defined(current_pt_regs) + regs = current_pt_regs(); +#elif defined(task_pt_regs) + regs = task_pt_regs(task); +#elif defined(ARCH_HAS_GET_CURRENT_REGS) + regs = get_current_regs(); +#else + regs = NULL; +#endif + if (!regs) { + igloo_debug_osi("igloo: Could not get ptregs pointer for current task\n"); + mem_region->header.size = 0; + mem_region->header.op = HYPER_RESP_READ_FAIL; + return; + } + mem_region->header.size = (uint64_t)(uintptr_t)regs; + mem_region->header.op = HYPER_RESP_READ_NUM; + igloo_debug_osi("igloo: ptregs pointer returned for current task: %p\n", regs); + return; +} + diff --git a/src/portal/portal_types.h b/src/portal/portal_types.h index 18f68ae..94444d4 100644 --- a/src/portal/portal_types.h +++ b/src/portal/portal_types.h @@ -99,7 +99,7 @@ struct osi_result_header { /* Define the FFI execution structure */ struct portal_ffi_call { - void *func_ptr; /* Pointer to the function to call */ + unsigned long func_ptr; /* Pointer to the function to call */ unsigned long num_args; /* Number of arguments (up to 8) */ unsigned long args[8]; /* Array of arguments as unsigned long */ unsigned long result; /* Return value of the function call */