This adds a working rump driver for /dev/wmX cards, which are Intel i8254x Gigabit Ethernet devices. (See man.netbsd.org for "wm(4)")
TESTED: - It seems to work 100% on UP with hurd-i386. - On SMP it still works better than netdde but hangs gnumach when a large file is received. Example usage: settrans -fgap /dev/rumpnet /hurd/rumpnet settrans -fgap /dev/wm0 /hurd/devnode -M /dev/rumpnet wm0 settrans -fgap /servers/socket/2 /hurd/pfinet -i /dev/wm0 ifup /dev/wm0 --- Makefile | 2 +- rumpnet/Makefile | 42 ++ rumpnet/if_hdr.h | 104 +++++ rumpnet/ioccom-rump.h | 82 ++++ rumpnet/main.c | 148 +++++++ rumpnet/net-rump.c | 920 ++++++++++++++++++++++++++++++++++++++++++ rumpnet/net-rump.h | 24 ++ 7 files changed, 1321 insertions(+), 1 deletion(-) create mode 100644 rumpnet/Makefile create mode 100644 rumpnet/if_hdr.h create mode 100644 rumpnet/ioccom-rump.h create mode 100644 rumpnet/main.c create mode 100644 rumpnet/net-rump.c create mode 100644 rumpnet/net-rump.h diff --git a/Makefile b/Makefile index 9d9e33c3..c51e8c1c 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ prog-subdirs = auth proc exec term \ rtc ifeq ($(HAVE_LIBRUMP),yes) -prog-subdirs += rumpdisk +prog-subdirs += rumpdisk rumpnet endif ifeq ($(HAVE_SUN_RPC),yes) diff --git a/rumpnet/Makefile b/rumpnet/Makefile new file mode 100644 index 00000000..579197e1 --- /dev/null +++ b/rumpnet/Makefile @@ -0,0 +1,42 @@ +# +# Copyright (C) 2019, 2023 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +RUMPLIBS=rump rumpuser rumpdev rumpdev_miiphy rumpdev_pci rumpvfs rumpdev_pci_if_wm rumpnet rumpnet_net rumpnet_netinet rumpnet_local rumpdev_bpf +# If we have a configured tree, include the configuration so that we +# can conditionally build translators. +ifneq (,$(wildcard ../config.make)) + include ../config.make +endif + +ifeq ($(HAVE_LIBRUMP_VFSNOFIFO),yes) +RUMPLIBS += rumpvfs_nofifofs +endif + +dir := rumpnet +makemode := server + +SRCS = main.c net-rump.c +LCLHDRS = +target = rumpnet +OBJS = $(SRCS:.c=.o) +CFLAGS = -Wno-unused-function -Wno-unused-variable +HURDLIBS = machdev ports trivfs shouldbeinlibc iohelp ihash fshelp irqhelp +LDLIBS += -lpthread -lpciaccess -ldl -lz +rumpnet-LDLIBS += -Wl,--no-as-needed $(RUMPLIBS:%=-l%) -Wl,--as-needed +rumpnet.static-LDLIBS += -Wl,--whole-archive $(RUMPLIBS:%=-l%_pic) -Wl,--no-whole-archive + +include ../Makeconf diff --git a/rumpnet/if_hdr.h b/rumpnet/if_hdr.h new file mode 100644 index 00000000..b71d2110 --- /dev/null +++ b/rumpnet/if_hdr.h @@ -0,0 +1,104 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or software.distribut...@cs.cmu.edu + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Taken from (bsd)net/if.h. Modified for MACH kernel. + */ +/* + * Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. The name of the Laboratory may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if.h 7.3 (Berkeley) 6/27/88 + */ + +#ifndef _IF_HDR_ +#define _IF_HDR_ + +/* + * Header for network interface drivers. + */ +struct ifnet { + short if_unit; /* unit number */ + short if_flags; /* up/down, broadcast, etc. */ + short if_timer; /* time until if_watchdog called */ + short if_mtu; /* maximum transmission unit */ + short if_header_size; /* length of header */ + short if_header_format; /* format of hardware header */ + short if_address_size; /* length of hardware address */ + short if_alloc_size; /* size of read buffer to allocate */ + char *if_address; /* pointer to hardware address */ +// struct ifqueue if_snd; /* output queue */ +// if_filter_list_t port_list; +// pthread_mutex_t if_rcv_port_list_lock;/* lock for input filter list */ +// pthread_mutex_t if_snd_port_list_lock;/* lock for output filter list */ +/* statistics */ + int if_ipackets; /* packets received */ + int if_ierrors; /* input errors */ + int if_opackets; /* packets sent */ + int if_oerrors; /* output errors */ + int if_collisions; /* collisions on csma interfaces */ + int if_rcvdrops; /* packets received but dropped */ +}; + +#define IFF_UP 0x0001 /* interface is up */ +#define IFF_BROADCAST 0x0002 /* interface can broadcast */ +#define IFF_DEBUG 0x0004 /* turn on debugging */ +#define IFF_LOOPBACK 0x0008 /* is a loopback net */ +#define IFF_POINTOPOINT 0x0010 /* point-to-point link */ +#define IFF_RUNNING 0x0040 /* resources allocated */ +#define IFF_NOARP 0x0080 /* no address resolution protocol */ +#define IFF_PROMISC 0x0100 /* receive all packets */ +#define IFF_ALLMULTI 0x0200 /* receive all multicast packets */ +#define IFF_BRIDGE 0x0100 /* support token ring routing field */ +#define IFF_SNAP 0x0200 /* support extended sap header */ + +/* internal flags only: */ +#define IFF_CANTCHANGE (IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING) + +#endif /* _IF_HDR_ */ diff --git a/rumpnet/ioccom-rump.h b/rumpnet/ioccom-rump.h new file mode 100644 index 00000000..6f41b05b --- /dev/null +++ b/rumpnet/ioccom-rump.h @@ -0,0 +1,82 @@ +/* $NetBSD: ioccom.h,v 1.12 2014/12/10 00:16:05 christos Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ioccom.h 8.3 (Berkeley) 1/9/95 + */ + +#ifndef _SYS_IOCCOM_H_ +#define _SYS_IOCCOM_H_ + +/* + * Ioctl's have the command encoded in the lower word, and the size of + * any in or out parameters in the upper word. The high 3 bits of the + * upper word are used to encode the in/out status of the parameter. + * + * 31 29 28 16 15 8 7 0 + * +---------------------------------------------------------------+ + * | I/O | Parameter Length | Command Group | Command | + * +---------------------------------------------------------------+ + */ +#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ +#define IOCPARM_SHIFT 16 +#define IOCGROUP_SHIFT 8 +#define IOCPARM_LEN(x) (((x) >> IOCPARM_SHIFT) & IOCPARM_MASK) +#define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << IOCPARM_SHIFT)) +#define IOCGROUP(x) (((x) >> IOCGROUP_SHIFT) & 0xff) + +#define IOCPARM_MAX NBPG /* max size of ioctl args, mult. of NBPG */ + /* no parameters */ +#define IOC_VOID (unsigned long)0x20000000 + /* copy parameters out */ +#define IOC_OUT (unsigned long)0x40000000 + /* copy parameters in */ +#define IOC_IN (unsigned long)0x80000000 + /* copy parameters in and out */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* mask for IN/OUT/VOID */ +#define IOC_DIRMASK (unsigned long)0xe0000000 + +#define _IOC(inout, group, num, len) \ + ((inout) | (((len) & IOCPARM_MASK) << IOCPARM_SHIFT) | \ + ((group) << IOCGROUP_SHIFT) | (num)) +#define _IO(g,n) _IOC(IOC_VOID, (g), (n), 0) +#define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) +#define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) +/* this should be _IORW, but stdio got there first */ +#define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) + +#define IOCSNPRINTF(buf, len, cmd) \ + snprintf((buf), (len), "_IO%s%s('%c', %hhu)", \ + (((cmd) >> 30) & 1) ? "W" : "", \ + (((cmd) >> 30) & 2) ? "R" : "", \ + (char)IOCGROUP(cmd), (unsigned char)(cmd)) + + +#endif /* !_SYS_IOCCOM_H_ */ diff --git a/rumpnet/main.c b/rumpnet/main.c new file mode 100644 index 00000000..b76001ca --- /dev/null +++ b/rumpnet/main.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <error.h> +#include <argp.h> +#include <version.h> + +#include <pthread.h> +#include <mach.h> +#include <mach/vm_param.h> +#include "libshouldbeinlibc/wire.h" +#include "libmachdev/machdev.h" +#include "net-rump.h" + +mach_port_t bootstrap_resume_task = MACH_PORT_NULL; + +static const struct argp_option options[] = { + {"host-priv-port", 'h', "PORT", 0, "Host private port PORT"}, + {"device-master-port",'d', "PORT", 0, "Device master port PORT"}, + {"next-task", 'N', "TASK", 0, "Next bootstrap task TASK"}, + {0} +}; + + +/* Parse a command line option. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + /* We save our parsed values in this structure, hung off STATE->hook. + Only after parsing all options successfully will we use these values. */ + struct + { + int host_priv; + int dev_master; + int next_task; + } *values = state->hook; + + switch (key) + { + case 'h': + values->host_priv = atoi(arg); + break; + case 'd': + values->dev_master = atoi(arg); + break; + case 'N': + values->next_task = atoi(arg); + break; + + case ARGP_KEY_INIT: + state->child_inputs[0] = state->input; + values = malloc (sizeof *values); + if (values == 0) + return ENOMEM; + state->hook = values; + memset (values, 0, sizeof *values); + break; + + case ARGP_KEY_SUCCESS: + /* All options parsed successfully */ + _hurd_host_priv = values->host_priv; + _hurd_device_master = values->dev_master; + bootstrap_resume_task = values->next_task; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp_child empty_argp_children[] = {{0}}; +static struct argp rumpnet_argp = {options, parse_opt, 0, 0, empty_argp_children}; +static const struct argp *rumpnet_argp_bootup = &rumpnet_argp; + +static void * +rumpnet_multithread_server(void *arg) +{ + do + { + ports_manage_port_operations_multithread (machdev_device_bucket, + machdev_demuxer, + 1000 * 60 * 2, /* 2 minute thread */ + 1000 * 60 * 10, /* 10 minute server */ + 0); + } while (1); + + return NULL; +} + +int +main (int argc, char **argv) +{ + mach_port_t bootstrap = MACH_PORT_NULL; + int err; + pthread_t t; + + setenv ("RUMP_NCPU", "1", 1); + setenv ("RUMP_VERBOSE", "1", 1); + setenv ("RUMP_HOSTNAME", "HURD0", 1); + setenv ("HOSTNAME", "HURD0", 1); + setenv ("RUMP_PANIC", "1", 1); + + err = argp_parse (rumpnet_argp_bootup, argc, argv, 0, 0, NULL); + if (err) + { + error(1, err, "Missing parameters for bootstrap"); + } + + /* Make sure we will not swap out, + * because dma buffers for net drivers don't work otherwise */ + err = wire_task_self (); + if (err) + error (1, err, "cannot lock all memory"); + + rump_register_net (); + machdev_trivfs_init (argc, argv, bootstrap_resume_task, "rumpnet", "/dev/rumpnet", &bootstrap); + machdev_device_init (); + err = pthread_create (&t, NULL, rumpnet_multithread_server, NULL); + if (err) + return err; + pthread_detach (t); + machdev_trivfs_server_startup (bootstrap); + machdev_trivfs_server_loop (NULL); + /* Never reached */ + return 0; +} diff --git a/rumpnet/net-rump.c b/rumpnet/net-rump.c new file mode 100644 index 00000000..8dc84a94 --- /dev/null +++ b/rumpnet/net-rump.c @@ -0,0 +1,920 @@ +/* + * Copyright (C) 2024 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <assert.h> +#include <string.h> +#include <arpa/inet.h> +#include <net/if_ether.h> +#include <error.h> +#include <stdio.h> +#include <stdbool.h> +#include <unistd.h> + +#include <mach.h> +#include <mach/gnumach.h> +#include <mach/vm_param.h> +#include <hurd/machdev.h> +#include <hurd.h> +#include <hurd/ports.h> +#include <device/net_status.h> +#include <net/ethernet.h> + +#define MACH_INCLUDE +#define _STANDALONE + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> +#include <rump/rumperrno2host.h> + +#include "if_hdr.h" +#include "ioccom-rump.h" + +#define SIOCSIFADDR _IOW('i', 12, struct ifreq) /* set ifnet address */ +#define SIOCGIFADDR _IOWR('i', 33, struct ifreq) /* get ifnet address */ +#define SIOCGIFBRDADDR _IOWR('i', 35, struct ifreq) /* get broadcast addr */ +#define SIOCSIFBRDADDR _IOW('i', 19, struct ifreq) /* set broadcast addr */ +#define SIOCGIFNETMASK _IOWR('i', 37, struct ifreq) /* get net addr mask */ +#define SIOCSIFNETMASK _IOW('i', 22, struct ifreq) /* set net addr mask */ + +#define SIOCGLIFADDR _IOWR('i', 29, struct if_laddrreq) /* get IF addr */ + +#define SIOCSIFFLAGS _IOW('i', 16, struct ifreq) /* set ifnet flags */ +#define SIOCGIFFLAGS _IOWR('i', 17, struct ifreq) /* get ifnet flags */ +#define SIOCGIFMETRIC _IOWR('i', 23, struct ifreq) /* get IF metric */ +#define SIOCSIFMETRIC _IOW('i', 24, struct ifreq) /* set IF metric */ +#define SIOCSIFMTU _IOW('i', 127, struct ifreq) /* set ifnet mtu */ +#define SIOCGIFMTU _IOWR('i', 126, struct ifreq) /* get ifnet mtu */ + +#define IF_NAMESIZE 16 + +#define BPF_TIMEOUT_MS 10 + +struct rump_timeval { + int64_t tv_sec; + int32_t tv_usec; +}; + +struct bpf_insn; + +/* + * Structure for BIOCSETF. + */ +struct bpf_program { + u_int bf_len; + struct bpf_insn *bf_insns; +}; + +#define BIOCGBLEN _IOR('B', 102, u_int) +#define BIOCSBLEN _IOWR('B', 102, u_int) +#define BIOCSETF _IOW('B', 103, struct bpf_program) +#define BIOCFLUSH _IO('B', 104) +#define BIOCPROMISC _IO('B', 105) +#define BIOCGDLT _IOR('B', 106, u_int) +#define BIOCGETIF _IOR('B', 107, struct ifreq) +#define BIOCSETIF _IOW('B', 108, struct ifreq) +#define BIOCIMMEDIATE _IOW('B', 112, u_int) +#define BIOCSTCPF _IOW('B', 114, struct bpf_program) +#define BIOCSUDPF _IOW('B', 115, struct bpf_program) +#define BIOCGHDRCMPLT _IOR('B', 116, u_int) +#define BIOCSHDRCMPLT _IOW('B', 117, u_int) +#define BIOCSDLT _IOW('B', 118, u_int) +#define BIOCGDIRECTION _IOR('B', 120, u_int) +#define BIOCSDIRECTION _IOW('B', 121, u_int) +#define BIOCSRTIMEOUT _IOW('B', 122, struct rump_timeval) +#define BIOCGFEEDBACK _IOR('B', 124, u_int) +#define BIOCSFEEDBACK _IOW('B', 125, u_int) +#define BIOCLOCK _IO('B', 126) +#define BIOCSETWF _IOW('B', 127, struct bpf_program) + +#define BPF_D_IN 0 +#define BPF_D_INOUT 1 +#define BPF_D_OUT 2 + +/* + * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST). + */ +#define BPF_MEMWORDS 16 + +#define SIZEOF_BPF_HDR 18 +#define SIZEOF_MTU 1500 +#define SIZEOF_ETH_FRAME (ETH_HLEN + SIZEOF_MTU) + +#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1)) + +#define BPF_BUFSIZE 131072 + +struct bpf_hdr { + int32_t usec; + int32_t sec; + uint32_t bh_caplen; /* length of captured portion */ + uint32_t bh_datalen; /* original length of packet */ + uint16_t bh_hdrlen; /* length of this struct + alignment padding */ +}; + +int socket_aflink = -1; + +struct dl_addr { + uint8_t dl_type; /* interface type */ + uint8_t dl_nlen; /* interface name length, no trailing 0 reqd. */ + uint8_t dl_alen; /* link level address length */ + uint8_t dl_slen; /* link layer selector length */ + char dl_data[24]; /* + * minimum work area, can be larger; contains + * both if name and ll address; big enough for + * IFNAMSIZ plus 8byte ll addr. + */ +}; + +struct sockaddr_dl { + uint8_t sdl_len; /* Total length of sockaddr */ + uint8_t sdl_family; /* AF_LINK */ + uint16_t sdl_index; /* if != 0, system given index for interface */ + struct dl_addr sdl_addr; +#define sdl_type sdl_addr.dl_type +#define sdl_nlen sdl_addr.dl_nlen +#define sdl_alen sdl_addr.dl_alen +#define sdl_slen sdl_addr.dl_slen +#define sdl_data sdl_addr.dl_data +}; + +struct if_laddrreq { + char iflr_name[IF_NAMESIZE]; + unsigned int flags; +#define IFLR_PREFIX 0x8000 /* in: prefix given out: kernel fills id */ +#define IFLR_ACTIVE 0x4000 /* in/out: link-layer address activation */ +#define IFLR_FACTORY 0x2000 /* in/out: factory link-layer address */ + unsigned int prefixlen; /* in/out */ + struct sockaddr_storage addr; /* in/out */ + struct sockaddr_storage dstaddr; /* out */ +}; + +#define satosdl(__sa) ((struct sockaddr_dl *)(__sa)) +#define LLADDR(s) ((char *)((s)->sdl_data + (s)->sdl_nlen)) + +struct ifreq { + char ifr_name[IF_NAMESIZE]; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr_storage ifru_space; + short ifru_flags; + int ifru_addrflags; + int ifru_metric; + int ifru_mtu; + int ifru_dlt; + u_int ifru_value; + void * ifru_data; + struct { + uint32_t b_buflen; + void *b_buf; + } ifru_b; + } ifr_ifru; +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_space ifr_ifru.ifru_space /* sockaddr_storage */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_addrflags ifr_ifru.ifru_addrflags /* addr flags */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_dlt ifr_ifru.ifru_dlt /* data link type (DLT_*) */ +#define ifr_value ifr_ifru.ifru_value /* generic value */ +#define ifr_media ifr_ifru.ifru_metric /* media options (overload) */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface + * XXX deprecated + */ +#define ifr_buf ifr_ifru.ifru_b.b_buf /* new interface ioctls */ +#define ifr_buflen ifr_ifru.ifru_b.b_buflen +#define ifr_index ifr_ifru.ifru_value /* interface index, BSD */ +#define ifr_ifindex ifr_index /* interface index, linux */ +}; + +struct bpf_insn bpf_allow_all[] = { + BPF_STMT(BPF_RET+BPF_K, BPF_BUFSIZE), /* accept */ +}; + +/* One of these is associated with each instance of a device. */ +struct net_data +{ + struct port_info port; /* device port */ + struct machdev_emul_device device; /* generic device structure */ + struct ifreq *dev; /* rump network device structure */ + uint8_t hw_address[ETH_ALEN]; /* MAC address of device */ + mach_port_t dest; /* destination port for recieving packets */ + int bpf_fd; /* bpf file descriptor for communication with device */ + struct net_data *next; +}; + +static struct net_data *nd_head; + +/* Forward declarations. */ + +static void *rcv_process (void *arg); + +static struct machdev_device_emulation_ops rump_net_emulation_ops; + +static mach_msg_type_t header_type = +{ + .msgt_name = MACH_MSG_TYPE_BYTE, + .msgt_size = 8, + .msgt_number = NET_HDW_HDR_MAX, + .msgt_inline = TRUE, + .msgt_longform = FALSE, + .msgt_deallocate = FALSE, + .msgt_unused = 0 +}; + +static mach_msg_type_t packet_type = +{ + .msgt_name = MACH_MSG_TYPE_BYTE, + .msgt_size = 8, + .msgt_number = 0, + .msgt_inline = TRUE, + .msgt_longform = FALSE, + .msgt_deallocate = FALSE +}; + +static struct net_data *search_nd (struct ifreq *dev) +{ + struct net_data *nd = nd_head; + + while (nd) + { + if (strncmp(nd->dev->ifr_name, dev->ifr_name, IF_NAMESIZE) == 0) + return nd; + nd = nd->next; + } + return NULL; +} + +/* Return a send right associated with network device ND. */ +static mach_port_t +dev_to_port (void *nd) +{ + return (nd + ? ports_get_send_right (nd) + : MACH_PORT_NULL); +} + +void socket_init(void) +{ + int err; + + socket_aflink = rump_sys_socket(RUMP_AF_LINK, SOCK_DGRAM, 0); + if (socket_aflink < 0) + mach_print("ERROR rump_sys_socket(RUMP_AF_LINK)\n"); +} + +static int +get_hwaddr(const char *ifname, uint8_t *mac) +{ + struct if_laddrreq iflr; + struct sockaddr_dl *sdl; + + memset(&iflr, 0, sizeof(iflr)); + strlcpy(iflr.iflr_name, ifname, sizeof(iflr.iflr_name)); + iflr.addr.ss_family = RUMP_AF_LINK; + + sdl = satosdl(&iflr.addr); + sdl->sdl_alen = ETH_ALEN; + + if (rump_sys_ioctl(socket_aflink, SIOCGLIFADDR, &iflr) == -1) + { + mach_print("ERROR siocglifaddr failed\n"); + return -1; + } + + memcpy(mac, LLADDR(sdl), ETH_ALEN); + return 0; +} + +static int +cmp_hwaddr(uint8_t *hwaddr, uint8_t *devaddr) +{ + return memcmp(hwaddr, devaddr, ETH_ALEN); +} + +static int +cmp_hwbroadcast(uint8_t *hw) +{ + uint8_t all[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + return memcmp(hw, all, ETH_ALEN); +} + +struct ifreq * +search_interface(const char *ifname) +{ + struct ifreq ifr; + struct ifreq *dev = NULL; + char *last_slash, *name; + + memset(&ifr, 0, sizeof(ifr)); + last_slash = strrchr(ifname, '/'); + if (!last_slash) + name = ifname; + else + name = last_slash + 1; + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + { + mach_print("ERROR: cannot calloc\n"); + return NULL; + } + strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + if (rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, &ifr) == -1) + { + mach_print("siocgifflags failed: "); + mach_print(name); + mach_print("\n"); + goto errexit; + } + *dev = ifr; + goto exit; + +errexit: + free(dev); + dev = NULL; +exit: + return dev; +} + +static io_return_t +init_interface(struct net_data *nd) +{ + unsigned int flag, buf_size; + io_return_t err; + struct bpf_program p; + pthread_t rcv_thread; + struct rump_timeval timeout = { 0, BPF_TIMEOUT_MS * 1000 }; + + if (get_hwaddr(nd->dev->ifr_name, nd->hw_address)) + { + mach_print("ERROR can't get mac address\n"); + return rump_errno2host(errno); + } + + /* Hardcode MTU to 1500 */ + nd->dev->ifr_mtu = SIZEOF_MTU; + if (rump_sys_ioctl(socket_aflink, SIOCSIFMTU, nd->dev) == -1) + { + mach_print("ERROR siocsifmtu\n"); + return rump_errno2host(errno); + } + + nd->bpf_fd = rump_sys_open("/dev/bpf", RUMP_O_RDWR); + if (nd->bpf_fd < 0) + { + mach_print("ERROR rump_sys_open(/dev/bpf)\n"); + return rump_errno2host(errno); + } + + buf_size = BPF_BUFSIZE; + /* ignore return value */ + rump_sys_ioctl (nd->bpf_fd, BIOCSBLEN, &buf_size); + + err = rump_sys_ioctl (nd->bpf_fd, BIOCSETIF, nd->dev); + if (err < 0) + { + mach_print("ERROR: biocsetif failed, buf_size too big?\n"); + errno = rump_errno2host(err); + return errno; + } + + flag = 0; + err = rump_sys_ioctl (nd->bpf_fd, BIOCIMMEDIATE, &flag); + if (err < 0) + { + mach_print("ERROR: biocimmediate failed\n"); + errno = rump_errno2host(err); + return errno; + } + + err = rump_sys_ioctl (nd->bpf_fd, BIOCSRTIMEOUT, &timeout); + if (err < 0) + { + mach_print("ERROR: biocsrtimeout failed\n"); + errno = rump_errno2host(err); + return errno; + } + + /* only capture incoming packets, but still allows sending packets */ + flag = BPF_D_IN; + err = rump_sys_ioctl (nd->bpf_fd, BIOCSDIRECTION, &flag); + if (err < 0) + { + mach_print("ERROR: biocsdirection failed\n"); + errno = rump_errno2host(err); + return -1; + } + + p.bf_len = sizeof(bpf_allow_all) / sizeof(bpf_allow_all[0]); + p.bf_insns = bpf_allow_all; + + err = rump_sys_ioctl (nd->bpf_fd, BIOCSETF, &p); + if (err < 0) + { + mach_print("ERROR: biocsetf failed\n"); + errno = rump_errno2host(err); + return errno; + } + + err = pthread_create(&rcv_thread, 0, rcv_process, nd); + if (err != 0) + { + mach_print("ERROR: pthread_create(rcv)\n"); + return err; + } + pthread_detach(rcv_thread); + + return 0; +} + +int +up_interface(struct ifreq *dev) +{ + int retval; + + if (rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, dev) == 0) + { + if ((dev->ifr_flags & IFF_UP)) + retval = 0; + else + { + dev->ifr_flags |= IFF_UP | IFF_RUNNING; + if (rump_sys_ioctl(socket_aflink, SIOCSIFFLAGS, dev) == 0) + retval = 0; + else + retval = rump_errno2host(errno); + } + } + else + retval = rump_errno2host(errno); + + return retval; +} + +static io_return_t +deliver_msg(struct net_rcv_msg *msg, mach_port_t p) +{ + mach_msg_return_t err; + + msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0); + /* remember message sizes must be rounded up */ + msg->msg_hdr.msgh_local_port = MACH_PORT_NULL; + msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL; + msg->msg_hdr.msgh_id = NET_RCV_MSG_ID; + + msg->msg_hdr.msgh_remote_port = p; + err = mach_msg ((mach_msg_header_t *)msg, + MACH_SEND_MSG, + msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + return err; +} + +static io_return_t +netif_rx_handle (uint8_t *data, int len, mach_port_t dest) +{ + int pack_size; + struct net_rcv_msg net_msg; + struct ether_header *eh; + struct packet_header *ph; + size_t align = sizeof (uintptr_t); + + pack_size = len - sizeof (struct ethhdr); + /* remember message sizes must be rounded up */ + net_msg.msg_hdr.msgh_size = + (((mach_msg_size_t) (offsetof (struct net_rcv_msg, packet) + + sizeof (struct packet_header) + + pack_size)) + align-1) & ~(align-1); + + /* Copy packet into message buffer. */ + eh = (struct ether_header *) (net_msg.header); + ph = (struct packet_header *) (net_msg.packet); + memcpy (eh, data, sizeof (struct ether_header)); + /* packet is prefixed with a struct packet_header, + see include/device/net_status.h. */ + memcpy (ph + 1, data + sizeof (struct ether_header), pack_size); + ph->type = eh->ether_type; + ph->length = pack_size + sizeof (struct packet_header); + + net_msg.sent = FALSE; /* Mark packet as received. */ + + net_msg.header_type = header_type; + net_msg.packet_type = packet_type; + net_msg.net_rcv_msg_packet_count = ph->length; + return deliver_msg (&net_msg, dest); +} + +static io_return_t +rumpnet_device_open (mach_port_t reply_port, + mach_msg_type_name_t reply_port_type, + dev_mode_t mode, const char *name, device_t *devp, + mach_msg_type_name_t *devicePoly) +{ + io_return_t err = D_SUCCESS; + struct ifreq *dev; + struct net_data *nd; + + /* Search for the device. */ + dev = search_interface (name); + if (!dev) + { + fprintf (stderr, "after search_interface: cannot find %s\n", name); + return D_NO_SUCH_DEVICE; + } + + /* Allocate and initialize device data if this is the first open. */ + nd = search_nd (dev); + if (!nd) + { + err = machdev_create_device_port (sizeof (*nd), &nd); + if (err) + { + fprintf (stderr, "after machdev_create_device_port: cannot create a port\n"); + goto out; + } + + nd->dev = dev; + nd->device.emul_data = nd; + nd->device.emul_ops = &rump_net_emulation_ops; + nd->next = nd_head; + nd_head = nd; + + if ((err = init_interface(nd) < 0)) + { + mach_print ("after init_interface: cannot init the device\n"); + goto out; + } + if ((err = up_interface(dev) < 0)) + { + mach_print ("after up_interface: cannot bring up the device\n"); + goto out; + } + +out: + if (err) + { + if (nd) + { + ports_destroy_right (nd); + nd = NULL; + } + } + } + + if (nd) + { + *devp = ports_get_right (nd); + *devicePoly = MACH_MSG_TYPE_MAKE_SEND; + } + return err; +} + +static io_return_t +send_packet (struct net_data *nd, io_buf_ptr_t buf, unsigned int bytes) +{ + io_return_t err; + struct ethhdr *hdr = (struct ethhdr *)buf; + struct iovec iov[2]; + int result; + int flag; + int i; + + if ((ntohs(hdr->h_proto) != ETH_P_IP) + && (ntohs(hdr->h_proto) != ETH_P_ARP) + && (ntohs(hdr->h_proto) != ETH_P_IPV6)) + { + char proto[16] = {0}; + snprintf(proto, 16, "0x%04x\n", ntohs(hdr->h_proto)); + mach_print("ERROR: ethernet frame has wrong protocol: "); + mach_print(proto); + errno = EPROTO; + return -1; + } + + iov[0].iov_base = hdr; + iov[0].iov_len = ETH_HLEN; + iov[1].iov_base = hdr + 1; + iov[1].iov_len = bytes - ETH_HLEN; + + result = rump_sys_writev (nd->bpf_fd, iov, 2); + if (result < 0) + { + errno = EIO; + mach_print("ERROR: rump_sys_writev(bpf)\n"); + return -1; + } + +// for (i = 0; i < bytes; i++) +// { +// char dmp[4] = {0}; +// snprintf(dmp, 4, "%02x ", ((uint8_t *)hdr)[i]); +// mach_print(dmp); +// } + return result; +} + +static io_return_t +receive_packets (struct net_data *nd) +{ + io_return_t err; + int i; + int fragment; + int pkt_length; + int read_length; + int todo; + int buf_size = BPF_BUFSIZE; + size_t buf_inc; + struct bpf_hdr *bp = NULL; + struct ethhdr *hdr = NULL; + bool own_traffic = true; + rpc_phys_addr_t pap; + /* reusable packet buffer */ + static vm_address_t bpf_pkt_addr = 0; + static uint8_t *bpf_pkt = NULL; + + if (!bpf_pkt_addr) + { + err = vm_allocate (mach_task_self (), &bpf_pkt_addr, buf_size, TRUE); + if (err != KERN_SUCCESS) + { + mach_print("ERROR: cannot vm_allocate\n"); + errno = ENOMEM; + return -1; + } + bpf_pkt = (uint8_t *)bpf_pkt_addr; + + { + volatile uint8_t dummy_read __attribute__ ((unused)); + int npages = (buf_size + PAGE_SIZE - 1) / PAGE_SIZE; + int i; + + /* Fault-in the memory pages by reading a single byte of each */ + for (i = 0; i < npages; i++) + dummy_read = ((volatile uint8_t *)bpf_pkt)[i * PAGE_SIZE]; + } + } + +read_again: + read_length = rump_sys_read (nd->bpf_fd, bpf_pkt, buf_size); + if (read_length >= 0 && read_length < SIZEOF_BPF_HDR) + goto read_again; + else if (read_length < 0) + { + switch (rump_errno2host(errno)) + { + case EAGAIN: + case EINTR: + goto read_again; + + case ENXIO: + case EIO: + default: + { + mach_print("ERROR: rump_sys_read(bpf)\n"); + vm_deallocate (mach_task_self (), bpf_pkt_addr, buf_size); + bpf_pkt_addr = 0; + return -2; /* device gone */ + } + } + } + + todo = read_length; + + while (own_traffic || (todo > 0)) + { + bp = (struct bpf_hdr *)bpf_pkt; + hdr = (struct ethhdr *)(bpf_pkt + bp->bh_hdrlen); + fragment = bp->bh_datalen - bp->bh_caplen; + pkt_length = bp->bh_caplen; + buf_inc = BPF_WORDALIGN(pkt_length + bp->bh_hdrlen); + todo -= buf_inc; + + if (fragment) + { + mach_print("fragment rcvd, try again\n"); + return 0; + } + + if (!cmp_hwaddr(hdr->h_source, nd->hw_address)) + { + own_traffic = true; + mach_print("seeing our own traffic\n"); + } + else + { + /* rcv this packet */ + own_traffic = false; + err = netif_rx_handle((uint8_t *)hdr, pkt_length, nd->dest); + /* Ignore errors due to: + * not enough bandwidth in software stack to handle all packets + */ + } + + /* Check for last packet in bpf buffer */ + if (todo > 0) + bpf_pkt += buf_inc; + else + bpf_pkt = (uint8_t *)bpf_pkt_addr; + } + return 0; +} + +static void * +rcv_process(void *arg) +{ + io_return_t err; + struct net_data *nd = (struct net_data *)arg; + int length; + + for (;;) + { + err = receive_packets (nd); + if (err == -1) + { + mach_print("ERROR: cannot rcv any packets, giving up\n"); + return NULL; + } + else if (err == -2) + { + mach_print("ERROR: device gone, retry\n"); + sleep(2); + } + } +} + +static io_return_t +rumpnet_device_write (void *d, mach_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + recnum_t bn, io_buf_ptr_t data, unsigned int count, + int *bytes_written) +{ + struct net_data *nd = (struct net_data *)d; + error_t err; + char msg[16] = {0}; + + err = send_packet(nd, data, count); + if (err < 0) + return errno; + *bytes_written = err; + + if (*bytes_written != count) + { + mach_print("ERROR: bytes_written != count\n"); + return D_IO_ERROR; + } + + vm_deallocate (mach_task_self (), (vm_address_t) data, count); + + return D_SUCCESS; +} + +static io_return_t +device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, + mach_msg_type_number_t *count) +{ + struct net_data *nd = (struct net_data *)d; + io_return_t err; + + if (flavor == NET_FLAGS) + { + if (*count != 1) + return D_INVALID_SIZE; + + err = rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, nd->dev); + if (err < 0) + return D_IO_ERROR; + + *(int *) status = nd->dev->ifr_flags; + } + else if (flavor == NET_STATUS) + { + struct net_status *ns = (struct net_status *)status; + + if (*count < NET_STATUS_COUNT) + return D_INVALID_OPERATION; + + ns->min_packet_size = ETH_HLEN; + ns->max_packet_size = SIZEOF_ETH_FRAME; + ns->header_format = HDR_ETHERNET; + ns->header_size = ETH_HLEN; + ns->address_size = ETH_ALEN; + ns->flags = nd->dev->ifr_flags; + ns->mapped_size = 0; + + *count = NET_STATUS_COUNT; + } + else if (flavor == NET_ADDRESS) + { + err = rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, nd->dev); + if (err < 0) + return D_IO_ERROR; + + err = get_hwaddr(nd->dev->ifr_name, nd->hw_address); + if (err) + return D_IO_ERROR; + + status[0] = + ((nd->hw_address[0] << 24) | + (nd->hw_address[1] << 16) | + (nd->hw_address[2] << 8) | + (nd->hw_address[3])); + + status[1] = + ((nd->hw_address[4] << 24) | + (nd->hw_address[5] << 16)); + + *count = 2; + } + return D_SUCCESS; +} + +static io_return_t +device_set_status(void *d, dev_flavor_t flavor, dev_status_t status, + mach_msg_type_number_t count) +{ + io_return_t err; + struct net_data *nd = (struct net_data *)d; + + if (flavor != NET_FLAGS) + { + mach_print("Some other flavor\n"); + return D_INVALID_OPERATION; + } + + if (count != 1) + return D_INVALID_SIZE; + + err = rump_sys_ioctl(socket_aflink, SIOCGIFFLAGS, nd->dev); + if (err < 0) + return D_IO_ERROR; + + nd->dev->ifr_flags = *((int *)status); + + err = rump_sys_ioctl(socket_aflink, SIOCSIFFLAGS, nd->dev); + if (err < 0) + return D_IO_ERROR; + + return D_SUCCESS; +} + +static io_return_t +device_set_filter (void *d, mach_port_t port, int priority, + filter_t * filter, unsigned filter_count) +{ +// return net_set_filter (&((struct net_data *) d)->ifnet.port_list, +// port, priority, filter, filter_count); + struct net_data *nd = (struct net_data *)d; + nd->dest = port; + + return D_SUCCESS; +} + +static void rumpnet_init (void) +{ + rump_init(); + socket_init(); +} + +static struct machdev_device_emulation_ops rump_net_emulation_ops = +{ + rumpnet_init, + NULL, + NULL, + dev_to_port, + rumpnet_device_open, + NULL, + rumpnet_device_write, + NULL, + NULL, + NULL, + device_set_status, + device_get_status, + device_set_filter, + NULL, + NULL, + NULL, + NULL +}; + +void rump_register_net(void) +{ + machdev_register (&rump_net_emulation_ops); +} diff --git a/rumpnet/net-rump.h b/rumpnet/net-rump.h new file mode 100644 index 00000000..327b80e8 --- /dev/null +++ b/rumpnet/net-rump.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 Free Software Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _RUMP_NET_H_ +#define _RUMP_NET_H_ + +void rump_register_net(void); + +#endif -- 2.45.2