From e71791c0ab08b90249b07b679bd56f7466d94e06 Mon Sep 17 00:00:00 2001 From: Ivan Baravy Date: Mon, 6 Feb 2023 16:01:37 +0000 Subject: [PATCH] Implement vnet_null device Such device don't need new threads and files but are valid ethernet devices for the kernel. Convenient for testing. --- linux/vnet/tap.c | 18 ++-------- makefile | 15 +++++--- shell.c | 31 ++++++++++++----- shell.h | 6 ++-- umka.asm | 7 ++-- umka.h | 8 ++++- umka_fuse.c | 2 +- umka_gen_devices_dat.c | 2 +- umka_os.c | 14 ++++---- umka_shell.c | 4 +-- umkaio.c | 4 +-- umkaio.h | 8 +++-- vdisk.c | 4 +-- vdisk.h | 6 ++-- vdisk/qcow2.c | 2 +- vdisk/qcow2.h | 2 +- vdisk/raw.c | 2 +- vdisk/raw.h | 2 +- vnet.c | 22 ++++++++---- vnet.h | 12 +++---- vnet/null.c | 78 ++++++++++++++++++++++++++++++++++++++++++ vnet/null.h | 16 +++++++++ 22 files changed, 194 insertions(+), 71 deletions(-) create mode 100644 vnet/null.c create mode 100644 vnet/null.h diff --git a/linux/vnet/tap.c b/linux/vnet/tap.c index 592ae0b..dfd6f71 100644 --- a/linux/vnet/tap.c +++ b/linux/vnet/tap.c @@ -22,31 +22,20 @@ #include "vnet.h" #define TAP_DEV "/dev/net/tun" +#define UMKA_TAP_NAME "umka%d" static STDCALL void vnet_unload_tap(void) { - printf("vnet_unload_tap\n"); COVERAGE_ON(); COVERAGE_OFF(); } static STDCALL void vnet_reset_tap(void) { - printf("vnet_reset_tap\n"); COVERAGE_ON(); COVERAGE_OFF(); } -/* -static void -dump_net_buff(net_buff_t *buf) { - for (size_t i = 0; i < buf->length; i++) { - printf("%2.2x ", buf->data[i]); - } - putchar('\n'); -} -*/ - static STDCALL int vnet_transmit_tap(net_buff_t *buf) { struct vnet *net; @@ -56,20 +45,17 @@ vnet_transmit_tap(net_buff_t *buf) { : : "memory"); -// printf("vnet_transmit: %d bytes\n", buf->length); -// dump_net_buff(buf); ssize_t written = write(net->fdout, buf->data, buf->length); (void)written; buf->length = 0; COVERAGE_OFF(); COVERAGE_ON(); -// printf("vnet_transmit: %d bytes written\n", written); return 0; } struct vnet * vnet_init_tap(void) { - struct ifreq ifr = {.ifr_name = UMKA_VNET_NAME, + struct ifreq ifr = {.ifr_name = UMKA_TAP_NAME, .ifr_flags = IFF_TAP | IFF_NO_PI}; int fd, err; diff --git a/makefile b/makefile index 3cadb51..c141954 100644 --- a/makefile +++ b/makefile @@ -32,6 +32,10 @@ LDFLAGS=-no-pie LDFLAGS_32=$(LDFLAGS) -m32 LIBS=-lpthread +ifeq ($(HOST),windows) + LIBS=$(LIBS) -lws2_32 +endif + ifeq ($(HOST),linux) FASM_INCLUDE=$(KOLIBRIOS)/kernel/trunk;$(KOLIBRIOS)/programs/develop/libraries/libcrash/hash FASM=INCLUDE="$(FASM_INCLUDE)" $(FASM_EXE) $(FASM_FLAGS) @@ -59,10 +63,10 @@ test: umka_shell umka_shell: umka_shell.o umka.o shell.o trace.o trace_lbr.o vdisk.o \ vdisk/raw.o vdisk/qcow2.o deps/em_inflate/em_inflate.o vnet.o \ - $(HOST)/vnet/tap.o vnet/file.o lodepng.o $(HOST)/pci.o \ + $(HOST)/vnet/tap.o vnet/file.o vnet/null.o lodepng.o $(HOST)/pci.o \ $(HOST)/thread.o umkaio.o umkart.o deps/optparse/optparse.o \ deps/isocline/src/isocline.o - $(CC) $(LDFLAGS_32) $^ -o $@ -T umka.ld $(LIBS) -lws2_32 + $(CC) $(LDFLAGS_32) $^ -o $@ -T umka.ld $(LIBS) umka_fuse: umka_fuse.o umka.o trace.o trace_lbr.o vdisk.o vdisk/raw.o \ vdisk/qcow2.o deps/em_inflate/em_inflate.o $(HOST)/pci.o \ @@ -71,8 +75,8 @@ umka_fuse: umka_fuse.o umka.o trace.o trace_lbr.o vdisk.o vdisk/raw.o \ umka_os: umka_os.o umka.o shell.o lodepng.o vdisk.o vdisk/raw.o vdisk/qcow2.o \ deps/em_inflate/em_inflate.o vnet.o $(HOST)/vnet/tap.o vnet/file.o \ - trace.o trace_lbr.o $(HOST)/pci.o $(HOST)/thread.o umkaio.o umkart.o \ - deps/isocline/src/isocline.o deps/optparse/optparse.o + vnet/null.o trace.o trace_lbr.o $(HOST)/pci.o $(HOST)/thread.o \ + umkaio.o umkart.o deps/isocline/src/isocline.o deps/optparse/optparse.o $(CC) $(LDFLAGS_32) `sdl2-config --libs` $^ -o $@ -T umka.ld umka_gen_devices_dat: umka_gen_devices_dat.o umka.o $(HOST)/pci.o \ @@ -157,6 +161,9 @@ $(HOST)/vnet/tap.o: $(HOST)/vnet/tap.c vnet/tap.h vnet/file.o: vnet/file.c vnet/file.h $(CC) $(CFLAGS_32) -c $< -o $@ +vnet/null.o: vnet/null.c vnet/null.h + $(CC) $(CFLAGS_32) -c $< -o $@ + umka_shell.o: umka_shell.c umka.h trace.h $(CC) $(CFLAGS_32) -c $< diff --git a/shell.c b/shell.c index f1df132..7128441 100644 --- a/shell.c +++ b/shell.c @@ -340,7 +340,7 @@ print_hash(struct shell_ctx *ctx, uint8_t *x, size_t len) { } void *host_load_file(const char *fname) { - FILE *f = fopen(fname, "r"); + FILE *f = fopen(fname, "rb"); if (!f) { return NULL; } @@ -1456,7 +1456,7 @@ cmd_get_keyboard_layout(struct shell_ctx *ctx, int argc, char **argv) { #undef COLS } else if (argc == 5 && !strcmp(argv[3], "-f")) { const char *fname = argv[4]; - FILE *f = fopen(fname, "w"); + FILE *f = fopen(fname, "wb"); if (!f) { fprintf(ctx->fout, "can't open file for writing: %s\n", fname); fputs(usage, ctx->fout); @@ -2898,12 +2898,27 @@ cmd_net_add_device(struct shell_ctx *ctx, int argc, char **argv) { (void)ctx; (void)argv; const char *usage = \ - "usage: net_add_device\n"; - if (argc != 1) { + "usage: net_add_device \n" + " devtype null, file or tap\n"; + if (argc > 2) { fputs(usage, ctx->fout); return; } - struct vnet *vnet = vnet_init(VNET_TAP); // TODO: list like block devices + int devtype = VNET_DEVTYPE_NULL; + const char *devtypestr = argv[1]; + if (devtypestr) { + if (!strcmp(devtypestr, "null")) { + devtype = VNET_DEVTYPE_NULL; + } else if (!strcmp(devtypestr, "file")) { + devtype = VNET_DEVTYPE_FILE; + } else if (!strcmp(devtypestr, "tap")) { + devtype = VNET_DEVTYPE_TAP; + } else { + fprintf(ctx->fout, "bad device type: %s\n", devtypestr); + return; + } + } + struct vnet *vnet = vnet_init(devtype, ctx->running); // TODO: list like block devices COVERAGE_ON(); int32_t dev_num = kos_net_add_device(&vnet->eth.net); COVERAGE_OFF(); @@ -4069,8 +4084,8 @@ run_test(struct shell_ctx *ctx) { } struct shell_ctx * -shell_init(int reproducible, const char *hist_file, struct umka_ctx *umka, - struct umka_io *io, FILE *fin, const atomic_int *running) { +shell_init(const int reproducible, const char *hist_file, + const struct umka_ctx *umka, const struct umka_io *io, FILE *fin) { struct shell_ctx *ctx = malloc(sizeof(struct shell_ctx)); ctx->umka = umka; ctx->io = io; @@ -4079,7 +4094,7 @@ shell_init(int reproducible, const char *hist_file, struct umka_ctx *umka, ctx->var = NULL; ctx->fin = fin; ctx->fout = stdout; - ctx->running = running; + ctx->running = &umka->running; pthread_cond_init(&ctx->cmd_done, NULL); pthread_mutex_init(&ctx->cmd_mutex, NULL); return ctx; diff --git a/shell.h b/shell.h index ec71aed..ca02e26 100644 --- a/shell.h +++ b/shell.h @@ -37,7 +37,7 @@ struct shell_var { struct shell_ctx { const struct umka_ctx *umka; - struct umka_io *io; + const struct umka_io *io; int reproducible; const char *hist_file; struct shell_var *var; @@ -50,8 +50,8 @@ struct shell_ctx { }; struct shell_ctx * -shell_init(int reproducible, const char *hist_file, struct umka_ctx *umka, - struct umka_io *io, FILE *fin, const atomic_int *running); +shell_init(const int reproducible, const char *hist_file, + const struct umka_ctx *umka, const struct umka_io *io, FILE *fin); void shell_close(struct shell_ctx *shell); diff --git a/umka.asm b/umka.asm index 94d141c..363472b 100644 --- a/umka.asm +++ b/umka.asm @@ -114,7 +114,7 @@ pubsym coverage_end pubsym sha3_256_oneshot, 'hash_oneshot' pubsym kos_time_to_epoch -pubsym umka_init +pubsym umka_init, 4 pubsym umka_close, 4 pubsym umka_boot @@ -578,11 +578,12 @@ struct umka_ctx running dd ? ends -proc umka_init c uses ebx esi edi ebp +proc umka_init c uses ebx esi edi ebp, _running call umka._.check_alignment mov eax, umka mov [eax+umka_ctx.booted], 0 - mov [eax+umka_ctx.running], 0 + mov ecx, [_running] + mov [eax+umka_ctx.running], ecx ret endp diff --git a/umka.h b/umka.h index 8e24ca5..b673659 100644 --- a/umka.h +++ b/umka.h @@ -29,6 +29,12 @@ typedef void siginfo_t; #define STDCALL __attribute__((__stdcall__)) +enum { + UMKA_RUNNING_NEVER, + UMKA_RUNNING_NOT_YET, + UMKA_RUNNING_YES, +}; + struct umka_ctx { int booted; atomic_int running; @@ -542,7 +548,7 @@ void irq0(int signo, siginfo_t *info, void *context); struct umka_ctx * -umka_init(void); +umka_init(int running); void umka_close(struct umka_ctx *ctx); diff --git a/umka_fuse.c b/umka_fuse.c index 457b976..dda3c78 100644 --- a/umka_fuse.c +++ b/umka_fuse.c @@ -35,7 +35,7 @@ struct umka_fuse_ctx { static struct umka_fuse_ctx * umka_fuse_init(void) { struct umka_fuse_ctx *ctx = malloc(sizeof(struct umka_fuse_ctx)); - ctx->umka = umka_init(); + ctx->umka = umka_init(UMKA_RUNNING_NEVER); ctx->io = io_init(&ctx->umka->running); return ctx; } diff --git a/umka_gen_devices_dat.c b/umka_gen_devices_dat.c index 492c756..79c9718 100644 --- a/umka_gen_devices_dat.c +++ b/umka_gen_devices_dat.c @@ -152,7 +152,7 @@ main (int argc, char *argv[]) { strcpy(pci_path, PCI_BASE_DIR); - umka_init(); + umka_init(UMKA_RUNNING_NEVER); kos_acpi_aml_init(); ctx = kos_acpi_aml_new_thread(); kos_acpi_dev_size = MAX_PCI_DEVICES*16; diff --git a/umka_os.c b/umka_os.c index 5d45530..fdad2fe 100644 --- a/umka_os.c +++ b/umka_os.c @@ -72,14 +72,15 @@ struct umka_os_ctx * umka_os_init(FILE *fstartup, FILE *fboardlog) { struct umka_os_ctx *ctx = malloc(sizeof(struct umka_os_ctx)); ctx->fboardlog = fboardlog; - ctx->umka = umka_init(); + ctx->umka = umka_init(UMKA_RUNNING_NOT_YET); ctx->io = io_init(&ctx->umka->running); ctx->shell = shell_init(SHELL_LOG_NONREPRODUCIBLE, history_filename, - ctx->umka, ctx->io, fstartup, &ctx->umka->running); + ctx->umka, ctx->io, fstartup); return ctx; } -void build_history_filename(void) { +static void +build_history_filename(void) { const char *dir_name; if (!(dir_name = getenv("HOME"))) { dir_name = "."; @@ -87,7 +88,8 @@ void build_history_filename(void) { sprintf(history_filename, "%s/%s", dir_name, HIST_FILE_BASENAME); } -void umka_thread_net_drv(void); +void +umka_thread_net_drv(void); struct itimerval timeout = {.it_value = {.tv_sec = 0, .tv_usec = 10000}, .it_interval = {.tv_sec = 0, .tv_usec = 10000}}; @@ -401,7 +403,7 @@ main(int argc, char *argv[]) { // load_app("/rd/1/loader"); - struct vnet *vnet = vnet_init(VNET_TAP); + struct vnet *vnet = vnet_init(VNET_DEVTYPE_TAP, &os->umka->running); if (vnet) { kos_net_add_device(&vnet->eth.net); } else { @@ -462,7 +464,7 @@ main(int argc, char *argv[]) { setitimer(ITIMER_REAL, &timeout, NULL); - atomic_store_explicit(&os->umka->running, 1, memory_order_release); + atomic_store_explicit(&os->umka->running, UMKA_RUNNING_YES, memory_order_release); umka_osloop(); // doesn't return if (coverage) diff --git a/umka_shell.c b/umka_shell.c index 23ced68..34f80fc 100644 --- a/umka_shell.c +++ b/umka_shell.c @@ -34,10 +34,10 @@ char history_filename[PATH_MAX]; struct umka_shell_ctx * umka_shell_init(int reproducible, FILE *fin) { struct umka_shell_ctx *ctx = malloc(sizeof(struct umka_shell_ctx)); - ctx->umka = umka_init(); + ctx->umka = umka_init(UMKA_RUNNING_NEVER); ctx->io = io_init(NULL); ctx->shell = shell_init(reproducible, history_filename, ctx->umka, ctx->io, - fin, &ctx->umka->running); + fin); return ctx; } diff --git a/umkaio.c b/umkaio.c index c0619f3..d2d6336 100644 --- a/umkaio.c +++ b/umkaio.c @@ -122,7 +122,7 @@ io_close(struct umka_io *io) { } ssize_t -io_read(int fd, void *buf, size_t count, struct umka_io *io) { +io_read(int fd, void *buf, size_t count, const struct umka_io *io) { ssize_t res; if (!io->running || !*io->running) { res = read(fd, buf, count); @@ -133,7 +133,7 @@ io_read(int fd, void *buf, size_t count, struct umka_io *io) { } ssize_t -io_write(int fd, const void *buf, size_t count, struct umka_io *io) { +io_write(int fd, const void *buf, size_t count, const struct umka_io *io) { ssize_t res; if (!io->running || !*io->running) { res = write(fd, buf, count); diff --git a/umkaio.h b/umkaio.h index 95799e4..77c38bd 100644 --- a/umkaio.h +++ b/umkaio.h @@ -14,6 +14,10 @@ #include #include +#if !defined (O_BINARY) +#define O_BINARY 0 // for Windows +#endif + struct umka_io { const atomic_int *running; pthread_t iot; @@ -73,9 +77,9 @@ void io_close(struct umka_io *io); ssize_t -io_read(int fd, void *buf, size_t count, struct umka_io *io); +io_read(int fd, void *buf, size_t count, const struct umka_io *io); ssize_t -io_write(int fd, const void *buf, size_t count, struct umka_io *io); +io_write(int fd, const void *buf, size_t count, const struct umka_io *io); #endif // UMKAIO_H_INCLUDED diff --git a/vdisk.c b/vdisk.c index 665c91f..dfbcaa3 100644 --- a/vdisk.c +++ b/vdisk.c @@ -41,8 +41,8 @@ vdisk_adjust_cache_size(void *userdata, size_t suggested_size) { } struct vdisk* -vdisk_init(const char *fname, int adjust_cache_size, size_t cache_size, - void *io) { +vdisk_init(const char *fname, const int adjust_cache_size, + const size_t cache_size, const void *io) { size_t fname_len = strlen(fname); size_t dot_raw_len = strlen(RAW_SUFFIX); size_t dot_qcow2_len = strlen(QCOW2_SUFFIX); diff --git a/vdisk.h b/vdisk.h index b2348a8..d575fe1 100644 --- a/vdisk.h +++ b/vdisk.h @@ -19,11 +19,11 @@ struct vdisk { uint64_t sect_cnt; unsigned cache_size; int adjust_cache_size; - void *io; + const void *io; }; struct vdisk* -vdisk_init(const char *fname, int adjust_cache_size, size_t cache_size, - void *io); +vdisk_init(const char *fname, const int adjust_cache_size, + const size_t cache_size, const void *io); #endif // VDISK_H_INCLUDED diff --git a/vdisk/qcow2.c b/vdisk/qcow2.c index 0c621c0..b135db6 100644 --- a/vdisk/qcow2.c +++ b/vdisk/qcow2.c @@ -197,7 +197,7 @@ vdisk_qcow2_write(void *userdata, void *buffer, off_t startsector, } struct vdisk* -vdisk_init_qcow2(const char *fname, struct umka_io *io) { +vdisk_init_qcow2(const char *fname, const struct umka_io *io) { struct vdisk_qcow2 *d = (struct vdisk_qcow2*)calloc(1, sizeof(struct vdisk_qcow2)); if (!d) { diff --git a/vdisk/qcow2.h b/vdisk/qcow2.h index 045450e..da05737 100644 --- a/vdisk/qcow2.h +++ b/vdisk/qcow2.h @@ -17,6 +17,6 @@ #define QCOW2_SUFFIX ".qcow2" struct vdisk* -vdisk_init_qcow2(const char *fname, struct umka_io *io); +vdisk_init_qcow2(const char *fname, const struct umka_io *io); #endif // VDISK_QCOW2_H_INCLUDED diff --git a/vdisk/raw.c b/vdisk/raw.c index a6638a4..42570ab 100644 --- a/vdisk/raw.c +++ b/vdisk/raw.c @@ -54,7 +54,7 @@ vdisk_raw_write(void *userdata, void *buffer, off_t startsector, } struct vdisk* -vdisk_init_raw(const char *fname, struct umka_io *io) { +vdisk_init_raw(const char *fname, const struct umka_io *io) { int fd = open(fname, O_RDONLY | O_BINARY); if (!fd) { printf("[vdisk.raw]: can't open file '%s': %s\n", fname, strerror(errno)); diff --git a/vdisk/raw.h b/vdisk/raw.h index bddee32..be395a1 100644 --- a/vdisk/raw.h +++ b/vdisk/raw.h @@ -17,6 +17,6 @@ #define RAW_SUFFIX ".raw" struct vdisk* -vdisk_init_raw(const char *fname, struct umka_io *io); +vdisk_init_raw(const char *fname, const struct umka_io *io); #endif // VDISK_RAW_H_INCLUDED diff --git a/vnet.c b/vnet.c index 9e97f09..84149c9 100644 --- a/vnet.c +++ b/vnet.c @@ -22,8 +22,9 @@ #include "umkart.h" #include "trace.h" #include "vnet.h" -#include "vnet/tap.h" +#include "vnet/null.h" #include "vnet/file.h" +#include "vnet/tap.h" #ifndef _WIN32 #include @@ -89,13 +90,16 @@ vnet_input_monitor(void *arg) { } struct vnet * -vnet_init(enum vnet_type type) { +vnet_init(enum vnet_type type, const atomic_int *running) { struct vnet *vnet; switch (type) { - case VNET_FILE: + case VNET_DEVTYPE_NULL: + vnet = vnet_init_null(); + break; + case VNET_DEVTYPE_FILE: vnet = vnet_init_file(); break; - case VNET_TAP: + case VNET_DEVTYPE_TAP: vnet = vnet_init_tap(); break; default: @@ -107,6 +111,8 @@ vnet_init(enum vnet_type type) { return NULL; } + vnet->running = running; + vnet->eth.net.link_state = ETH_LINK_FD + ETH_LINK_10M; vnet->eth.net.hwacc = 0; @@ -128,9 +134,11 @@ vnet_init(enum vnet_type type) { // pthread_mutex_lock(&vnet->mutex); kos_attach_int_handler(UMKA_IRQ_NETWORK, vnet_input, vnet); - fprintf(stderr, "[vnet] start input_monitor thread\n"); - pthread_t thread_input_monitor; - pthread_create(&thread_input_monitor, NULL, vnet_input_monitor, vnet); + if (*running != UMKA_RUNNING_NEVER) { + fprintf(stderr, "[vnet] start input_monitor thread\n"); + pthread_t thread_input_monitor; + pthread_create(&thread_input_monitor, NULL, vnet_input_monitor, vnet); + } return vnet; } diff --git a/vnet.h b/vnet.h index 072ac79..43cfca5 100644 --- a/vnet.h +++ b/vnet.h @@ -10,28 +10,28 @@ #ifndef VNET_H_INCLUDED #define VNET_H_INCLUDED +#include #include "umka.h" -#define UMKA_VNET_NAME "umka%d" #define VNET_BUFIN_CAP (NET_BUFFER_SIZE - offsetof(net_buff_t, data)) enum vnet_type { - VNET_FILE, - VNET_TAP, + VNET_DEVTYPE_NULL, + VNET_DEVTYPE_FILE, + VNET_DEVTYPE_TAP, }; struct vnet { struct eth_device eth; uint8_t bufin[VNET_BUFIN_CAP]; size_t bufin_len; -// pthread_cond_t cond; -// pthread_mutex_t mutex; int fdin; int fdout; int input_processed; + const atomic_int *running; }; struct vnet * -vnet_init(enum vnet_type type); +vnet_init(enum vnet_type type, const atomic_int *running); #endif // VNET_H_INCLUDED diff --git a/vnet/null.c b/vnet/null.c new file mode 100644 index 0000000..ab6348e --- /dev/null +++ b/vnet/null.c @@ -0,0 +1,78 @@ +/* + SPDX-License-Identifier: GPL-2.0-or-later + + UMKa - User-Mode KolibriOS developer tools + vnet - virtual network card, null interface + + Copyright (C) 2023 Ivan Baravy +*/ + +#include +#include +#include +#include + +#include "trace.h" +#include "umka.h" +#include "vnet.h" + +static STDCALL void +vnet_unload_null(void) { + COVERAGE_ON(); + COVERAGE_OFF(); +} + +static STDCALL void +vnet_reset_null(void) { + COVERAGE_ON(); + COVERAGE_OFF(); +} + +/* +static void +dump_net_buff(net_buff_t *buf) { + for (size_t i = 0; i < buf->length; i++) { + printf("%2.2x ", buf->data[i]); + } + putchar('\n'); +} +*/ + +static STDCALL int +vnet_transmit_null(net_buff_t *buf) { + struct vnet *net; + __asm__ __inline__ __volatile__ ( + "" + : "=b"(net) + : + : "memory"); + +// dump_net_buff(buf); + buf->length = 0; + COVERAGE_OFF(); + COVERAGE_ON(); + return 0; +} + +struct vnet * +vnet_init_null(void) { + struct vnet *vnet = malloc(sizeof(struct vnet)); + vnet->eth.net.device_type = NET_TYPE_ETH; + vnet->eth.net.mtu = 1514; + char *devname = malloc(8); + sprintf(devname, "UMKNUL%d", 0); // FIXME: support more devices + vnet->eth.net.name = devname; + + vnet->eth.net.unload = vnet_unload_null; + vnet->eth.net.reset = vnet_reset_null; + vnet->eth.net.transmit = vnet_transmit_null; + + vnet->fdin = 0; + vnet->fdout = 0; + vnet->input_processed = 1; + + memcpy(vnet->eth.mac, (uint8_t[]){0x80, 0x2b, 0xf9, 0x3b, 0x6c, 0xca}, + sizeof(vnet->eth.mac)); + + return vnet; +} diff --git a/vnet/null.h b/vnet/null.h new file mode 100644 index 0000000..f8ad787 --- /dev/null +++ b/vnet/null.h @@ -0,0 +1,16 @@ +/* + SPDX-License-Identifier: GPL-2.0-or-later + + UMKa - User-Mode KolibriOS developer tools + vnet - virtual network card, null interface + + Copyright (C) 2023 Ivan Baravy +*/ + +#ifndef VNET_NULL_H_INCLUDED +#define VNET_NULL_H_INCLUDED + +struct vnet * +vnet_init_null(void); + +#endif // VNET_NULL_H_INCLUDED