@@ -14,3 +14,12 @@ LDLIBS_fs2tar-$(LKL_HOST_CONFIG_NEEDS_LARGP) += -largp
progs-$(LKL_HOST_CONFIG_FUSE) += lklfuse
LDLIBS_lklfuse-y := -lfuse
+
+ifneq ($(LKL_HOST_CONFIG_BSD),y)
+libs-$(LKL_HOST_CONFIG_POSIX) += lib/hijack/liblkl-hijack
+endif
+LDFLAGS_lib/hijack/liblkl-hijack-y += -shared -nodefaultlibs
+LDLIBS_lib/hijack/liblkl-hijack-y += -ldl
+LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_ARM) += -lgcc -lc
+LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_AARCH64) += -lc
+LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_I386) += -lc_nonshared
new file mode 100755
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+
+##
+## This wrapper script works to replace system calls symbols such as
+## socket(2), recvmsg(2) for the redirection to LKL. Ideally it works
+## with any applications, but in practice (tm) it depends on the maturity
+## of hijack library (liblkl-hijack.so).
+##
+## Since LD_PRELOAD technique with setuid/setgid binary is tricky, you may
+## need to use sudo (or equivalents) to do it (e.g., ping).
+##
+## % sudo hijack.sh ping 127.0.0.1
+##
+
+script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
+
+export LD_LIBRARY_PATH=${script_dir}/../lib/hijack
+if [ -n ${LKL_HIJACK_DEBUG+x} ]
+then
+ trap '' TSTP
+fi
+LD_PRELOAD=liblkl-hijack.so $*
new file mode 100644
@@ -0,0 +1,4 @@
+liblkl-hijack-y += hijack.o
+liblkl-hijack-y += init.o
+liblkl-hijack-y += xlate.o
+
new file mode 100644
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * system calls hijack code
+ * Copyright (c) 2015 Hajime Tazaki
+ *
+ * Author: Hajime Tazaki <tazaki@sfc.wide.ad.jp>
+ *
+ * Note: some of the code is picked from rumpkernel, written by Antti Kantee.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#define __USE_GNU
+#include <dlfcn.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/epoll.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+#include <pthread.h>
+#include <lkl.h>
+#include <lkl_host.h>
+
+#include "xlate.h"
+#include "init.h"
+
+static int is_lklfd(int fd)
+{
+ if (fd < LKL_FD_OFFSET)
+ return 0;
+
+ return 1;
+}
+
+static void *resolve_sym(const char *sym)
+{
+ void *resolv;
+
+ resolv = dlsym(RTLD_NEXT, sym);
+ if (!resolv) {
+ fprintf(stderr, "dlsym fail %s (%s)\n", sym, dlerror());
+ assert(0);
+ }
+ return resolv;
+}
+
+typedef long (*host_call)(long p1, long p2, long p3, long p4, long p5, long p6);
+
+static host_call host_calls[__lkl__NR_syscalls];
+/* internally managed fd list for epoll */
+int dual_fds[LKL_FD_OFFSET];
+
+#define HOOK_FD_CALL(name) \
+ static void __attribute__((constructor(101))) \
+ init_host_##name(void) \
+ { \
+ host_calls[__lkl__NR_##name] = resolve_sym(#name); \
+ } \
+ \
+ long name##_hook(long p1, long p2, long p3, long p4, long p5, \
+ long p6) \
+ { \
+ long p[6] = {p1, p2, p3, p4, p5, p6 }; \
+ \
+ if (!host_calls[__lkl__NR_##name]) \
+ host_calls[__lkl__NR_##name] = resolve_sym(#name); \
+ if (!is_lklfd(p1)) \
+ return host_calls[__lkl__NR_##name](p1, p2, p3, \
+ p4, p5, p6); \
+ \
+ return lkl_set_errno(lkl_syscall(__lkl__NR_##name, p)); \
+ } \
+ asm(".global " #name); \
+ asm(".set " #name "," #name "_hook")
+
+#define HOOK_CALL_USE_HOST_BEFORE_START(name) \
+ static void __attribute__((constructor(101))) \
+ init_host_##name(void) \
+ { \
+ host_calls[__lkl__NR_##name] = resolve_sym(#name); \
+ } \
+ \
+ long name##_hook(long p1, long p2, long p3, long p4, long p5, \
+ long p6) \
+ { \
+ long p[6] = {p1, p2, p3, p4, p5, p6 }; \
+ \
+ if (!host_calls[__lkl__NR_##name]) \
+ host_calls[__lkl__NR_##name] = resolve_sym(#name); \
+ if (!lkl_running) \
+ return host_calls[__lkl__NR_##name](p1, p2, p3, \
+ p4, p5, p6); \
+ \
+ return lkl_set_errno(lkl_syscall(__lkl__NR_##name, p)); \
+ } \
+ asm(".global " #name); \
+ asm(".set " #name "," #name "_hook")
+
+#define HOST_CALL(name) \
+ static long (*host_##name)(); \
+ static void __attribute__((constructor(101))) \
+ init2_host_##name(void) \
+ { \
+ host_##name = resolve_sym(#name); \
+ }
+
+#define HOOK_CALL(name) \
+ long name##_hook(long p1, long p2, long p3, long p4, long p5, \
+ long p6) \
+ { \
+ long p[6] = {p1, p2, p3, p4, p5, p6}; \
+ \
+ return lkl_set_errno(lkl_syscall(__lkl__NR_##name, p)); \
+ } \
+ asm(".global " #name); \
+ asm(".set " #name "," #name "_hook")
+
+#define CHECK_HOST_CALL(name) \
+ do { \
+ if (!host_##name) \
+ host_##name = resolve_sym(#name); \
+ } while (0)
+
+static int lkl_call(int nr, int args, ...)
+{
+ long params[6];
+ va_list vl;
+ int i;
+
+ va_start(vl, args);
+ for (i = 0; i < args; i++)
+ params[i] = va_arg(vl, long);
+ va_end(vl);
+
+ return lkl_set_errno(lkl_syscall(nr, params));
+}
+
+HOOK_FD_CALL(recvmsg);
+HOOK_FD_CALL(sendmsg);
+HOOK_FD_CALL(sendmmsg);
+HOOK_FD_CALL(getsockname);
+HOOK_FD_CALL(getpeername);
+HOOK_FD_CALL(bind);
+HOOK_FD_CALL(connect);
+HOOK_FD_CALL(listen);
+HOOK_FD_CALL(shutdown);
+HOOK_FD_CALL(accept);
+HOOK_FD_CALL(write);
+HOOK_FD_CALL(writev);
+HOOK_FD_CALL(sendto);
+HOOK_FD_CALL(read);
+HOOK_FD_CALL(readv);
+HOOK_FD_CALL(recvfrom);
+HOOK_FD_CALL(splice);
+HOOK_FD_CALL(vmsplice);
+
+HOOK_CALL_USE_HOST_BEFORE_START(accept4);
+HOOK_CALL_USE_HOST_BEFORE_START(pipe2);
+
+HOST_CALL(write);
+HOST_CALL(pipe2);
+
+HOST_CALL(setsockopt);
+int setsockopt(int fd, int level, int optname, const void *optval,
+ socklen_t optlen)
+{
+ CHECK_HOST_CALL(setsockopt);
+ if (!is_lklfd(fd))
+ return host_setsockopt(fd, level, optname, optval, optlen);
+ return lkl_call(__lkl__NR_setsockopt, 5, fd, lkl_solevel_xlate(level),
+ lkl_soname_xlate(optname), (void *)optval, optlen);
+}
+
+HOST_CALL(getsockopt);
+int getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
+{
+ CHECK_HOST_CALL(getsockopt);
+ if (!is_lklfd(fd))
+ return host_getsockopt(fd, level, optname, optval, optlen);
+ return lkl_call(__lkl__NR_getsockopt, 5, fd, lkl_solevel_xlate(level),
+ lkl_soname_xlate(optname), optval, (int *)optlen);
+}
+
+HOST_CALL(socket);
+int socket(int domain, int type, int protocol)
+{
+ CHECK_HOST_CALL(socket);
+ if (domain == AF_UNIX || domain == PF_PACKET)
+ return host_socket(domain, type, protocol);
+
+ if (!lkl_running)
+ return host_socket(domain, type, protocol);
+
+ return lkl_call(__lkl__NR_socket, 3, domain, type, protocol);
+}
+
+HOST_CALL(ioctl);
+int ioctl(int fd, unsigned long req, ...)
+{
+ va_list vl;
+ long arg;
+
+ va_start(vl, req);
+ arg = va_arg(vl, long);
+ va_end(vl);
+
+ CHECK_HOST_CALL(ioctl);
+
+ if (!is_lklfd(fd))
+ return host_ioctl(fd, req, arg);
+ return lkl_call(__lkl__NR_ioctl, 3, fd, lkl_ioctl_req_xlate(req), arg);
+}
+
+
+HOST_CALL(fcntl);
+int fcntl(int fd, int cmd, ...)
+{
+ va_list vl;
+ long arg;
+
+ va_start(vl, cmd);
+ arg = va_arg(vl, long);
+ va_end(vl);
+
+ CHECK_HOST_CALL(fcntl);
+
+ if (!is_lklfd(fd))
+ return host_fcntl(fd, cmd, arg);
+ return lkl_call(__lkl__NR_fcntl, 3, fd, lkl_fcntl_cmd_xlate(cmd), arg);
+}
+
+HOST_CALL(poll);
+int poll(struct pollfd *fds, nfds_t nfds, int timeout)
+{
+ unsigned int i, lklfds = 0, hostfds = 0;
+
+ CHECK_HOST_CALL(poll);
+
+ for (i = 0; i < nfds; i++) {
+ if (is_lklfd(fds[i].fd))
+ lklfds = 1;
+ else
+ hostfds = 1;
+ }
+
+ /* FIXME: need to handle mixed case of hostfd and lklfd. */
+ if (lklfds && hostfds)
+ return lkl_set_errno(-LKL_EOPNOTSUPP);
+
+
+ if (hostfds)
+ return host_poll(fds, nfds, timeout);
+
+ return lkl_sys_poll((struct lkl_pollfd *)fds, nfds, timeout);
+}
+
+int __poll(struct pollfd *, nfds_t, int) __attribute__((alias("poll")));
+
+HOST_CALL(select);
+int select(int nfds, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
+{
+ int fd, hostfds = 0, lklfds = 0;
+
+ CHECK_HOST_CALL(select);
+
+ for (fd = 0; fd < nfds; fd++) {
+ if (r != 0 && FD_ISSET(fd, r)) {
+ if (is_lklfd(fd))
+ lklfds = 1;
+ else
+ hostfds = 1;
+ }
+ if (w != 0 && FD_ISSET(fd, w)) {
+ if (is_lklfd(fd))
+ lklfds = 1;
+ else
+ hostfds = 1;
+ }
+ if (e != 0 && FD_ISSET(fd, e)) {
+ if (is_lklfd(fd))
+ lklfds = 1;
+ else
+ hostfds = 1;
+ }
+ }
+
+ /* FIXME: handle mixed case of hostfd and lklfd */
+ if (lklfds && hostfds)
+ return lkl_set_errno(-LKL_EOPNOTSUPP);
+
+ if (hostfds)
+ return host_select(nfds, r, w, e, t);
+
+ return lkl_sys_select(nfds, (lkl_fd_set *)r, (lkl_fd_set *)w,
+ (lkl_fd_set *)e, (struct lkl_timeval *)t);
+}
+
+HOST_CALL(close);
+int close(int fd)
+{
+ CHECK_HOST_CALL(close);
+
+ if (!is_lklfd(fd)) {
+ /* handle epoll's dual_fd */
+ if ((dual_fds[fd] != -1) && lkl_running) {
+ lkl_call(__lkl__NR_close, 1, dual_fds[fd]);
+ dual_fds[fd] = -1;
+ }
+
+ return host_close(fd);
+ }
+
+ return lkl_call(__lkl__NR_close, 1, fd);
+}
+
+HOST_CALL(epoll_create);
+int epoll_create(int size)
+{
+ int host_fd;
+
+ CHECK_HOST_CALL(epoll_create);
+
+ host_fd = host_epoll_create(size);
+ if (!host_fd) {
+ fprintf(stderr, "%s fail (%d)\n", __func__, errno);
+ return -1;
+ }
+
+ if (!lkl_running)
+ return host_fd;
+
+ dual_fds[host_fd] = lkl_sys_epoll_create(size);
+
+ /* always returns the host fd */
+ return host_fd;
+}
+
+HOST_CALL(epoll_create1);
+int epoll_create1(int flags)
+{
+ int host_fd;
+
+ CHECK_HOST_CALL(epoll_create1);
+
+ host_fd = host_epoll_create1(flags);
+ if (!host_fd) {
+ fprintf(stderr, "%s fail (%d)\n", __func__, errno);
+ return -1;
+ }
+
+ if (!lkl_running)
+ return host_fd;
+
+ dual_fds[host_fd] = lkl_sys_epoll_create1(flags);
+
+ /* always returns the host fd */
+ return host_fd;
+}
+
+
+HOST_CALL(epoll_ctl);
+int epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event)
+{
+ CHECK_HOST_CALL(epoll_ctl);
+
+ if (!is_lklfd(fd))
+ return host_epoll_ctl(epollfd, op, fd, event);
+
+ return lkl_call(__lkl__NR_epoll_ctl, 4, dual_fds[epollfd],
+ op, fd, event);
+}
+
+struct epollarg {
+ int epfd;
+ struct epoll_event *events;
+ int maxevents;
+ int timeout;
+ int pipefd;
+ int errnum;
+};
+
+HOST_CALL(epoll_wait)
+static void *host_epollwait(void *arg)
+{
+ struct epollarg *earg = arg;
+ int ret;
+
+ ret = host_epoll_wait(earg->epfd, earg->events,
+ earg->maxevents, earg->timeout);
+ if (ret == -1)
+ earg->errnum = errno;
+ lkl_call(__lkl__NR_write, 3, earg->pipefd, &ret, sizeof(ret));
+
+ return (void *)(intptr_t)ret;
+}
+
+int epoll_wait(int epfd, struct epoll_event *events,
+ int maxevents, int timeout)
+{
+ CHECK_HOST_CALL(epoll_wait);
+ CHECK_HOST_CALL(pipe2);
+
+ int l_pipe[2] = {-1, -1}, h_pipe[2] = {-1, -1};
+ struct epoll_event host_ev, lkl_ev;
+ int ret_events = maxevents;
+ struct epoll_event h_events[ret_events], l_events[ret_events];
+ struct epollarg earg;
+ pthread_t thread;
+ void *trv_val;
+ int i, ret, ret_lkl, ret_host;
+
+ ret = lkl_sys_pipe(l_pipe);
+ if (ret == -1) {
+ fprintf(stderr, "lkl pipe error(errno=%d)\n", errno);
+ return -1;
+ }
+
+ ret = host_pipe2(h_pipe, 0);
+ if (ret == -1) {
+ fprintf(stderr, "host pipe error(errno=%d)\n", errno);
+ return -1;
+ }
+
+ if (dual_fds[epfd] == -1) {
+ fprintf(stderr, "epollfd isn't available (%d)\n", epfd);
+ abort();
+ }
+
+ /* wait pipe at host/lkl epoll_fd */
+ memset(&lkl_ev, 0, sizeof(lkl_ev));
+ lkl_ev.events = EPOLLIN;
+ lkl_ev.data.fd = l_pipe[0];
+ ret = lkl_call(__lkl__NR_epoll_ctl, 4, dual_fds[epfd], EPOLL_CTL_ADD,
+ l_pipe[0], &lkl_ev);
+ if (ret == -1) {
+ fprintf(stderr, "epoll_ctl error(epfd=%d:%d, fd=%d, err=%d)\n",
+ epfd, dual_fds[epfd], l_pipe[0], errno);
+ return -1;
+ }
+
+ memset(&host_ev, 0, sizeof(host_ev));
+ host_ev.events = EPOLLIN;
+ host_ev.data.fd = h_pipe[0];
+ ret = host_epoll_ctl(epfd, EPOLL_CTL_ADD, h_pipe[0], &host_ev);
+ if (ret == -1) {
+ fprintf(stderr, "host epoll_ctl error(%d, %d, %d, %d)\n",
+ epfd, h_pipe[0], h_pipe[1], errno);
+ return -1;
+ }
+
+
+ /* now wait by epoll_wait on 2 threads */
+ memset(h_events, 0, sizeof(struct epoll_event) * ret_events);
+ memset(l_events, 0, sizeof(struct epoll_event) * ret_events);
+ earg.epfd = epfd;
+ earg.events = h_events;
+ earg.maxevents = maxevents;
+ earg.timeout = timeout;
+ earg.pipefd = l_pipe[1];
+ pthread_create(&thread, NULL, host_epollwait, &earg);
+
+ ret_lkl = lkl_sys_epoll_wait(dual_fds[epfd],
+ (struct lkl_epoll_event *)l_events,
+ maxevents, timeout);
+ if (ret_lkl == -1) {
+ fprintf(stderr,
+ "lkl_%s_wait error(epfd=%d:%d, fd=%d, err=%d)\n",
+ __func__, epfd, dual_fds[epfd], l_pipe[0], errno);
+ return -1;
+ }
+ host_write(h_pipe[1], &ret, sizeof(ret));
+ pthread_join(thread, &trv_val);
+ ret_host = (int)(intptr_t)trv_val;
+ if (ret_host == -1) {
+ fprintf(stderr,
+ "host epoll_ctl error(%d, %d, %d, %d)\n", epfd,
+ h_pipe[0], h_pipe[1], errno);
+ return -1;
+ }
+
+ ret = lkl_call(__lkl__NR_epoll_ctl, 4, dual_fds[epfd], EPOLL_CTL_DEL,
+ l_pipe[0], &lkl_ev);
+ if (ret == -1) {
+ fprintf(stderr,
+ "lkl epoll_ctl error(epfd=%d:%d, fd=%d, err=%d)\n",
+ epfd, dual_fds[epfd], l_pipe[0], errno);
+ return -1;
+ }
+
+ ret = host_epoll_ctl(epfd, EPOLL_CTL_DEL, h_pipe[0], &host_ev);
+ if (ret == -1) {
+ fprintf(stderr, "host epoll_ctl error(%d, %d, %d, %d)\n",
+ epfd, h_pipe[0], h_pipe[1], errno);
+ return -1;
+ }
+
+ memset(events, 0, sizeof(struct epoll_event) * maxevents);
+ ret = 0;
+ if (ret_host > 0) {
+ for (i = 0; i < ret_host; i++) {
+ if (h_events[i].data.fd == h_pipe[0])
+ continue;
+ if (is_lklfd(h_events[i].data.fd))
+ continue;
+
+ memcpy(events, &(h_events[i]),
+ sizeof(struct epoll_event));
+ events++;
+ ret++;
+ }
+ }
+ if (ret_lkl > 0) {
+ for (i = 0; i < ret_lkl; i++) {
+ if (l_events[i].data.fd == l_pipe[0])
+ continue;
+ if (!is_lklfd(l_events[i].data.fd))
+ continue;
+
+ memcpy(events, &(l_events[i]),
+ sizeof(struct epoll_event));
+ events++;
+ ret++;
+ }
+ }
+
+ lkl_call(__lkl__NR_close, 1, l_pipe[0]);
+ lkl_call(__lkl__NR_close, 1, l_pipe[1]);
+ host_close(h_pipe[0]);
+ host_close(h_pipe[1]);
+
+ return ret;
+}
+
+int eventfd(unsigned int count, int flags)
+{
+ if (!lkl_running) {
+ int (*f)(unsigned int a, int b) = resolve_sym("eventfd");
+
+ return f(count, flags);
+ }
+
+ return lkl_sys_eventfd2(count, flags);
+}
+
+HOST_CALL(eventfd_read);
+int eventfd_read(int fd, uint64_t *value)
+{
+ CHECK_HOST_CALL(eventfd_read);
+
+ if (!is_lklfd(fd))
+ return host_eventfd_read(fd, value);
+
+ return lkl_sys_read(fd, (void *) value,
+ sizeof(*value)) != sizeof(*value) ? -1 : 0;
+}
+
+HOST_CALL(eventfd_write);
+int eventfd_write(int fd, uint64_t value)
+{
+ CHECK_HOST_CALL(eventfd_write);
+
+ if (!is_lklfd(fd))
+ return host_eventfd_write(fd, value);
+
+ return lkl_sys_write(fd, (void *) &value,
+ sizeof(value)) != sizeof(value) ? -1 : 0;
+}
+
+HOST_CALL(mmap)
+void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
+{
+ CHECK_HOST_CALL(mmap);
+
+ if (addr != NULL || flags != (MAP_ANONYMOUS|MAP_PRIVATE) ||
+ prot != (PROT_READ|PROT_WRITE) || fd != -2 || offset != 0)
+ return (void *)host_mmap(addr, length, prot, flags, fd, offset);
+ return lkl_sys_mmap(addr, length, prot, flags, fd, offset);
+}
+
+ssize_t send(int fd, const void *buf, size_t len, int flags)
+{
+ return sendto(fd, buf, len, flags, 0, 0);
+}
+
+ssize_t recv(int fd, void *buf, size_t len, int flags)
+{
+ return recvfrom(fd, buf, len, flags, 0, 0);
+}
+
+extern int pipe2(int fd[2], int flag);
+int pipe(int fd[2])
+{
+ if (!lkl_running)
+ return host_calls[__lkl__NR_pipe2]((long)fd, 0, 0, 0, 0, 0);
+
+ return pipe2(fd, 0);
+
+}
new file mode 100644
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * system calls hijack code
+ * Copyright (c) 2015 Hajime Tazaki
+ *
+ * Author: Hajime Tazaki <tazaki@sfc.wide.ad.jp>
+ *
+ * Note: some of the code is picked from rumpkernel, written by Antti Kantee.
+ */
+
+#include <stdio.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <lkl.h>
+#include <lkl_host.h>
+#include <lkl_config.h>
+
+#include "xlate.h"
+#include "init.h"
+
+#define __USE_GNU
+#include <dlfcn.h>
+
+#define _GNU_SOURCE
+#include <sched.h>
+
+/* Mount points are named after filesystem types so they should never
+ * be longer than ~6 characters.
+ */
+#define MAX_FSTYPE_LEN 50
+
+static void PinToCpus(const cpu_set_t *cpus)
+{
+ if (sched_setaffinity(0, sizeof(cpu_set_t), cpus))
+ perror("sched_setaffinity");
+}
+
+static void PinToFirstCpu(const cpu_set_t *cpus)
+{
+ int j;
+ cpu_set_t pinto;
+
+ CPU_ZERO(&pinto);
+ for (j = 0; j < CPU_SETSIZE; j++) {
+ if (CPU_ISSET(j, cpus)) {
+ lkl_printf("LKL: Pin To CPU %d\n", j);
+ CPU_SET(j, &pinto);
+ PinToCpus(&pinto);
+ return;
+ }
+ }
+}
+
+int lkl_debug, lkl_running;
+
+static struct lkl_config *cfg;
+
+static int config_load(void)
+{
+ int len, ret = -1;
+ char *buf;
+ int fd;
+ char *path = getenv("LKL_HIJACK_CONFIG_FILE");
+
+ cfg = (struct lkl_config *)malloc(sizeof(struct lkl_config));
+ if (!cfg) {
+ perror("config malloc");
+ return -1;
+ }
+ memset(cfg, 0, sizeof(struct lkl_config));
+
+ ret = lkl_load_config_env(cfg);
+ if (ret < 0)
+ return ret;
+
+ if (path)
+ fd = open(path, O_RDONLY, 0);
+ else if (access("lkl-hijack.json", R_OK) == 0)
+ fd = open("lkl-hijack.json", O_RDONLY, 0);
+ else
+ return 0;
+ if (fd < 0) {
+ fprintf(stderr, "config_file open %s: %s\n",
+ path, strerror(errno));
+ return -1;
+ }
+ len = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ if (len < 0) {
+ perror("config size check (lseek)");
+ return -1;
+ } else if (len == 0) {
+ return 0;
+ }
+ buf = (char *)malloc(len * sizeof(char) + 1);
+ if (!buf) {
+ perror("config buf malloc");
+ return -1;
+ }
+ ret = read(fd, buf, len);
+ if (ret < 0) {
+ perror("config file read");
+ free(buf);
+ return -1;
+ }
+ ret = lkl_load_config_json(cfg, buf);
+ free(buf);
+ return ret;
+}
+
+void __attribute__((constructor))
+hijack_init(void)
+{
+ int ret, i, dev_null;
+ int single_cpu_mode = 0;
+ cpu_set_t ori_cpu;
+
+ ret = config_load();
+ if (ret < 0)
+ return;
+
+ /* reflect pre-configuration */
+ lkl_load_config_pre(cfg);
+
+ /* hijack library specific configurations */
+ if (cfg->debug)
+ lkl_register_dbg_handler();
+
+ if (lkl_debug & 0x200) {
+ char c;
+
+ printf("press 'enter' to continue\n");
+ if (scanf("%c", &c) <= 0) {
+ fprintf(stderr, "scanf() fails\n");
+ return;
+ }
+ }
+ if (cfg->single_cpu) {
+ single_cpu_mode = atoi(cfg->single_cpu);
+ switch (single_cpu_mode) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+ default:
+ fprintf(stderr, "single cpu mode must be 0~2.\n");
+ single_cpu_mode = 0;
+ break;
+ }
+ }
+
+ if (single_cpu_mode) {
+ if (sched_getaffinity(0, sizeof(cpu_set_t), &ori_cpu)) {
+ perror("sched_getaffinity");
+ single_cpu_mode = 0;
+ }
+ }
+
+ /* Pin to a single cpu.
+ * Any children thread created after it are pinned to the same CPU.
+ */
+ if (single_cpu_mode == 2)
+ PinToFirstCpu(&ori_cpu);
+
+ if (single_cpu_mode == 1)
+ PinToFirstCpu(&ori_cpu);
+
+ ret = lkl_start_kernel(&lkl_host_ops, cfg->boot_cmdline);
+ if (ret) {
+ fprintf(stderr, "can't start kernel: %s\n", lkl_strerror(ret));
+ return;
+ }
+
+ lkl_running = 1;
+
+ /* initialize epoll manage list */
+ memset(dual_fds, -1, sizeof(int) * LKL_FD_OFFSET);
+
+ /* restore cpu affinity */
+ if (single_cpu_mode)
+ PinToCpus(&ori_cpu);
+
+ ret = lkl_set_fd_limit(65535);
+ if (ret)
+ fprintf(stderr, "lkl_set_fd_limit failed: %s\n",
+ lkl_strerror(ret));
+
+ /* fillup FDs up to LKL_FD_OFFSET */
+ ret = lkl_sys_mknod("/dev_null", LKL_S_IFCHR | 0600, LKL_MKDEV(1, 3));
+ dev_null = lkl_sys_open("/dev_null", LKL_O_RDONLY, 0);
+ if (dev_null < 0) {
+ fprintf(stderr, "failed to open /dev/null: %s\n",
+ lkl_strerror(dev_null));
+ return;
+ }
+
+ for (i = 1; i < LKL_FD_OFFSET; i++)
+ lkl_sys_dup(dev_null);
+
+ /* lo iff_up */
+ lkl_if_up(1);
+
+ /* reflect post-configuration */
+ lkl_load_config_post(cfg);
+}
+
+void __attribute__((destructor))
+hijack_fini(void)
+{
+ int i;
+ int err;
+
+ /* The following pauses the kernel before exiting allowing one
+ * to debug or collect stattistics/diagnosis info from it.
+ */
+ if (lkl_debug & 0x100) {
+ while (1)
+ pause();
+ }
+
+ if (!cfg)
+ return;
+
+ lkl_unload_config(cfg);
+ free(cfg);
+
+ if (!lkl_running)
+ return;
+
+ for (i = 0; i < LKL_FD_OFFSET; i++)
+ lkl_sys_close(i);
+
+ err = lkl_sys_halt();
+ if (err)
+ fprintf(stderr, "lkl_sys_halt: %s\n", lkl_strerror(err));
+}
new file mode 100644
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LKL_HIJACK_INIT_H
+#define _LKL_HIJACK_INIT_H
+
+extern int lkl_running;
+extern int dual_fds[];
+
+#endif /*_LKL_HIJACK_INIT_H */
new file mode 100644
@@ -0,0 +1,613 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#define __USE_GNU
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#undef st_atime
+#undef st_mtime
+#undef st_ctime
+#include <lkl.h>
+
+#include "xlate.h"
+
+long lkl_set_errno(long err)
+{
+ if (err >= 0)
+ return err;
+
+ switch (err) {
+ case -LKL_EPERM:
+ errno = EPERM;
+ break;
+ case -LKL_ENOENT:
+ errno = ENOENT;
+ break;
+ case -LKL_ESRCH:
+ errno = ESRCH;
+ break;
+ case -LKL_EINTR:
+ errno = EINTR;
+ break;
+ case -LKL_EIO:
+ errno = EIO;
+ break;
+ case -LKL_ENXIO:
+ errno = ENXIO;
+ break;
+ case -LKL_E2BIG:
+ errno = E2BIG;
+ break;
+ case -LKL_ENOEXEC:
+ errno = ENOEXEC;
+ break;
+ case -LKL_EBADF:
+ errno = EBADF;
+ break;
+ case -LKL_ECHILD:
+ errno = ECHILD;
+ break;
+ case -LKL_EAGAIN:
+ errno = EAGAIN;
+ break;
+ case -LKL_ENOMEM:
+ errno = ENOMEM;
+ break;
+ case -LKL_EACCES:
+ errno = EACCES;
+ break;
+ case -LKL_EFAULT:
+ errno = EFAULT;
+ break;
+ case -LKL_ENOTBLK:
+ errno = ENOTBLK;
+ break;
+ case -LKL_EBUSY:
+ errno = EBUSY;
+ break;
+ case -LKL_EEXIST:
+ errno = EEXIST;
+ break;
+ case -LKL_EXDEV:
+ errno = EXDEV;
+ break;
+ case -LKL_ENODEV:
+ errno = ENODEV;
+ break;
+ case -LKL_ENOTDIR:
+ errno = ENOTDIR;
+ break;
+ case -LKL_EISDIR:
+ errno = EISDIR;
+ break;
+ case -LKL_EINVAL:
+ errno = EINVAL;
+ break;
+ case -LKL_ENFILE:
+ errno = ENFILE;
+ break;
+ case -LKL_EMFILE:
+ errno = EMFILE;
+ break;
+ case -LKL_ENOTTY:
+ errno = ENOTTY;
+ break;
+ case -LKL_ETXTBSY:
+ errno = ETXTBSY;
+ break;
+ case -LKL_EFBIG:
+ errno = EFBIG;
+ break;
+ case -LKL_ENOSPC:
+ errno = ENOSPC;
+ break;
+ case -LKL_ESPIPE:
+ errno = ESPIPE;
+ break;
+ case -LKL_EROFS:
+ errno = EROFS;
+ break;
+ case -LKL_EMLINK:
+ errno = EMLINK;
+ break;
+ case -LKL_EPIPE:
+ errno = EPIPE;
+ break;
+ case -LKL_EDOM:
+ errno = EDOM;
+ break;
+ case -LKL_ERANGE:
+ errno = ERANGE;
+ break;
+ case -LKL_EDEADLK:
+ errno = EDEADLK;
+ break;
+ case -LKL_ENAMETOOLONG:
+ errno = ENAMETOOLONG;
+ break;
+ case -LKL_ENOLCK:
+ errno = ENOLCK;
+ break;
+ case -LKL_ENOSYS:
+ errno = ENOSYS;
+ break;
+ case -LKL_ENOTEMPTY:
+ errno = ENOTEMPTY;
+ break;
+ case -LKL_ELOOP:
+ errno = ELOOP;
+ break;
+ case -LKL_ENOMSG:
+ errno = ENOMSG;
+ break;
+ case -LKL_EIDRM:
+ errno = EIDRM;
+ break;
+ case -LKL_ECHRNG:
+ errno = ECHRNG;
+ break;
+ case -LKL_EL2NSYNC:
+ errno = EL2NSYNC;
+ break;
+ case -LKL_EL3HLT:
+ errno = EL3HLT;
+ break;
+ case -LKL_EL3RST:
+ errno = EL3RST;
+ break;
+ case -LKL_ELNRNG:
+ errno = ELNRNG;
+ break;
+ case -LKL_EUNATCH:
+ errno = EUNATCH;
+ break;
+ case -LKL_ENOCSI:
+ errno = ENOCSI;
+ break;
+ case -LKL_EL2HLT:
+ errno = EL2HLT;
+ break;
+ case -LKL_EBADE:
+ errno = EBADE;
+ break;
+ case -LKL_EBADR:
+ errno = EBADR;
+ break;
+ case -LKL_EXFULL:
+ errno = EXFULL;
+ break;
+ case -LKL_ENOANO:
+ errno = ENOANO;
+ break;
+ case -LKL_EBADRQC:
+ errno = EBADRQC;
+ break;
+ case -LKL_EBADSLT:
+ errno = EBADSLT;
+ break;
+ case -LKL_EBFONT:
+ errno = EBFONT;
+ break;
+ case -LKL_ENOSTR:
+ errno = ENOSTR;
+ break;
+ case -LKL_ENODATA:
+ errno = ENODATA;
+ break;
+ case -LKL_ETIME:
+ errno = ETIME;
+ break;
+ case -LKL_ENOSR:
+ errno = ENOSR;
+ break;
+ case -LKL_ENONET:
+ errno = ENONET;
+ break;
+ case -LKL_ENOPKG:
+ errno = ENOPKG;
+ break;
+ case -LKL_EREMOTE:
+ errno = EREMOTE;
+ break;
+ case -LKL_ENOLINK:
+ errno = ENOLINK;
+ break;
+ case -LKL_EADV:
+ errno = EADV;
+ break;
+ case -LKL_ESRMNT:
+ errno = ESRMNT;
+ break;
+ case -LKL_ECOMM:
+ errno = ECOMM;
+ break;
+ case -LKL_EPROTO:
+ errno = EPROTO;
+ break;
+ case -LKL_EMULTIHOP:
+ errno = EMULTIHOP;
+ break;
+ case -LKL_EDOTDOT:
+ errno = EDOTDOT;
+ break;
+ case -LKL_EBADMSG:
+ errno = EBADMSG;
+ break;
+ case -LKL_EOVERFLOW:
+ errno = EOVERFLOW;
+ break;
+ case -LKL_ENOTUNIQ:
+ errno = ENOTUNIQ;
+ break;
+ case -LKL_EBADFD:
+ errno = EBADFD;
+ break;
+ case -LKL_EREMCHG:
+ errno = EREMCHG;
+ break;
+ case -LKL_ELIBACC:
+ errno = ELIBACC;
+ break;
+ case -LKL_ELIBBAD:
+ errno = ELIBBAD;
+ break;
+ case -LKL_ELIBSCN:
+ errno = ELIBSCN;
+ break;
+ case -LKL_ELIBMAX:
+ errno = ELIBMAX;
+ break;
+ case -LKL_ELIBEXEC:
+ errno = ELIBEXEC;
+ break;
+ case -LKL_EILSEQ:
+ errno = EILSEQ;
+ break;
+ case -LKL_ERESTART:
+ errno = ERESTART;
+ break;
+ case -LKL_ESTRPIPE:
+ errno = ESTRPIPE;
+ break;
+ case -LKL_EUSERS:
+ errno = EUSERS;
+ break;
+ case -LKL_ENOTSOCK:
+ errno = ENOTSOCK;
+ break;
+ case -LKL_EDESTADDRREQ:
+ errno = EDESTADDRREQ;
+ break;
+ case -LKL_EMSGSIZE:
+ errno = EMSGSIZE;
+ break;
+ case -LKL_EPROTOTYPE:
+ errno = EPROTOTYPE;
+ break;
+ case -LKL_ENOPROTOOPT:
+ errno = ENOPROTOOPT;
+ break;
+ case -LKL_EPROTONOSUPPORT:
+ errno = EPROTONOSUPPORT;
+ break;
+ case -LKL_ESOCKTNOSUPPORT:
+ errno = ESOCKTNOSUPPORT;
+ break;
+ case -LKL_EOPNOTSUPP:
+ errno = EOPNOTSUPP;
+ break;
+ case -LKL_EPFNOSUPPORT:
+ errno = EPFNOSUPPORT;
+ break;
+ case -LKL_EAFNOSUPPORT:
+ errno = EAFNOSUPPORT;
+ break;
+ case -LKL_EADDRINUSE:
+ errno = EADDRINUSE;
+ break;
+ case -LKL_EADDRNOTAVAIL:
+ errno = EADDRNOTAVAIL;
+ break;
+ case -LKL_ENETDOWN:
+ errno = ENETDOWN;
+ break;
+ case -LKL_ENETUNREACH:
+ errno = ENETUNREACH;
+ break;
+ case -LKL_ENETRESET:
+ errno = ENETRESET;
+ break;
+ case -LKL_ECONNABORTED:
+ errno = ECONNABORTED;
+ break;
+ case -LKL_ECONNRESET:
+ errno = ECONNRESET;
+ break;
+ case -LKL_ENOBUFS:
+ errno = ENOBUFS;
+ break;
+ case -LKL_EISCONN:
+ errno = EISCONN;
+ break;
+ case -LKL_ENOTCONN:
+ errno = ENOTCONN;
+ break;
+ case -LKL_ESHUTDOWN:
+ errno = ESHUTDOWN;
+ break;
+ case -LKL_ETOOMANYREFS:
+ errno = ETOOMANYREFS;
+ break;
+ case -LKL_ETIMEDOUT:
+ errno = ETIMEDOUT;
+ break;
+ case -LKL_ECONNREFUSED:
+ errno = ECONNREFUSED;
+ break;
+ case -LKL_EHOSTDOWN:
+ errno = EHOSTDOWN;
+ break;
+ case -LKL_EHOSTUNREACH:
+ errno = EHOSTUNREACH;
+ break;
+ case -LKL_EALREADY:
+ errno = EALREADY;
+ break;
+ case -LKL_EINPROGRESS:
+ errno = EINPROGRESS;
+ break;
+ case -LKL_ESTALE:
+ errno = ESTALE;
+ break;
+ case -LKL_EUCLEAN:
+ errno = EUCLEAN;
+ break;
+ case -LKL_ENOTNAM:
+ errno = ENOTNAM;
+ break;
+ case -LKL_ENAVAIL:
+ errno = ENAVAIL;
+ break;
+ case -LKL_EISNAM:
+ errno = EISNAM;
+ break;
+ case -LKL_EREMOTEIO:
+ errno = EREMOTEIO;
+ break;
+ case -LKL_EDQUOT:
+ errno = EDQUOT;
+ break;
+ case -LKL_ENOMEDIUM:
+ errno = ENOMEDIUM;
+ break;
+ case -LKL_EMEDIUMTYPE:
+ errno = EMEDIUMTYPE;
+ break;
+ case -LKL_ECANCELED:
+ errno = ECANCELED;
+ break;
+ case -LKL_ENOKEY:
+ errno = ENOKEY;
+ break;
+ case -LKL_EKEYEXPIRED:
+ errno = EKEYEXPIRED;
+ break;
+ case -LKL_EKEYREVOKED:
+ errno = EKEYREVOKED;
+ break;
+ case -LKL_EKEYREJECTED:
+ errno = EKEYREJECTED;
+ break;
+ case -LKL_EOWNERDEAD:
+ errno = EOWNERDEAD;
+ break;
+ case -LKL_ENOTRECOVERABLE:
+ errno = ENOTRECOVERABLE;
+ break;
+ case -LKL_ERFKILL:
+ errno = ERFKILL;
+ break;
+ case -LKL_EHWPOISON:
+ errno = EHWPOISON;
+ break;
+ }
+
+ return -1;
+}
+
+int lkl_soname_xlate(int soname)
+{
+ switch (soname) {
+ case SO_DEBUG:
+ return LKL_SO_DEBUG;
+ case SO_REUSEADDR:
+ return LKL_SO_REUSEADDR;
+ case SO_TYPE:
+ return LKL_SO_TYPE;
+ case SO_ERROR:
+ return LKL_SO_ERROR;
+ case SO_DONTROUTE:
+ return LKL_SO_DONTROUTE;
+ case SO_BROADCAST:
+ return LKL_SO_BROADCAST;
+ case SO_SNDBUF:
+ return LKL_SO_SNDBUF;
+ case SO_RCVBUF:
+ return LKL_SO_RCVBUF;
+ case SO_SNDBUFFORCE:
+ return LKL_SO_SNDBUFFORCE;
+ case SO_RCVBUFFORCE:
+ return LKL_SO_RCVBUFFORCE;
+ case SO_KEEPALIVE:
+ return LKL_SO_KEEPALIVE;
+ case SO_OOBINLINE:
+ return LKL_SO_OOBINLINE;
+ case SO_NO_CHECK:
+ return LKL_SO_NO_CHECK;
+ case SO_PRIORITY:
+ return LKL_SO_PRIORITY;
+ case SO_LINGER:
+ return LKL_SO_LINGER;
+ case SO_BSDCOMPAT:
+ return LKL_SO_BSDCOMPAT;
+#ifdef SO_REUSEPORT
+ case SO_REUSEPORT:
+ return LKL_SO_REUSEPORT;
+#endif
+ case SO_PASSCRED:
+ return LKL_SO_PASSCRED;
+ case SO_PEERCRED:
+ return LKL_SO_PEERCRED;
+ case SO_RCVLOWAT:
+ return LKL_SO_RCVLOWAT;
+ case SO_SNDLOWAT:
+ return LKL_SO_SNDLOWAT;
+ case SO_RCVTIMEO:
+ return LKL_SO_RCVTIMEO;
+ case SO_SNDTIMEO:
+ return LKL_SO_SNDTIMEO;
+ case SO_SECURITY_AUTHENTICATION:
+ return LKL_SO_SECURITY_AUTHENTICATION;
+ case SO_SECURITY_ENCRYPTION_TRANSPORT:
+ return LKL_SO_SECURITY_ENCRYPTION_TRANSPORT;
+ case SO_SECURITY_ENCRYPTION_NETWORK:
+ return LKL_SO_SECURITY_ENCRYPTION_NETWORK;
+ case SO_BINDTODEVICE:
+ return LKL_SO_BINDTODEVICE;
+ case SO_ATTACH_FILTER:
+ return LKL_SO_ATTACH_FILTER;
+ case SO_DETACH_FILTER:
+ return LKL_SO_DETACH_FILTER;
+ case SO_PEERNAME:
+ return LKL_SO_PEERNAME;
+ case SO_TIMESTAMP:
+ return LKL_SO_TIMESTAMP;
+ case SO_ACCEPTCONN:
+ return LKL_SO_ACCEPTCONN;
+ case SO_PEERSEC:
+ return LKL_SO_PEERSEC;
+ case SO_PASSSEC:
+ return LKL_SO_PASSSEC;
+ case SO_TIMESTAMPNS:
+ return LKL_SO_TIMESTAMPNS;
+ case SO_MARK:
+ return LKL_SO_MARK;
+ case SO_TIMESTAMPING:
+ return LKL_SO_TIMESTAMPING;
+ case SO_PROTOCOL:
+ return LKL_SO_PROTOCOL;
+ case SO_DOMAIN:
+ return LKL_SO_DOMAIN;
+ case SO_RXQ_OVFL:
+ return LKL_SO_RXQ_OVFL;
+#ifdef SO_WIFI_STATUS
+ case SO_WIFI_STATUS:
+ return LKL_SO_WIFI_STATUS;
+#endif
+#ifdef SO_PEEK_OFF
+ case SO_PEEK_OFF:
+ return LKL_SO_PEEK_OFF;
+#endif
+#ifdef SO_NOFCS
+ case SO_NOFCS:
+ return LKL_SO_NOFCS;
+#endif
+#ifdef SO_LOCK_FILTER
+ case SO_LOCK_FILTER:
+ return LKL_SO_LOCK_FILTER;
+#endif
+#ifdef SO_SELECT_ERR_QUEUE
+ case SO_SELECT_ERR_QUEUE:
+ return LKL_SO_SELECT_ERR_QUEUE;
+#endif
+#ifdef SO_BUSY_POLL
+ case SO_BUSY_POLL:
+ return LKL_SO_BUSY_POLL;
+#endif
+#ifdef SO_MAX_PACING_RATE
+ case SO_MAX_PACING_RATE:
+ return LKL_SO_MAX_PACING_RATE;
+#endif
+ }
+
+ return soname;
+}
+
+int lkl_solevel_xlate(int solevel)
+{
+ switch (solevel) {
+ case SOL_SOCKET:
+ return LKL_SOL_SOCKET;
+ }
+
+ return solevel;
+}
+
+unsigned long lkl_ioctl_req_xlate(unsigned long req)
+{
+ switch (req) {
+ case FIOSETOWN:
+ return LKL_FIOSETOWN;
+ case SIOCSPGRP:
+ return LKL_SIOCSPGRP;
+ case FIOGETOWN:
+ return LKL_FIOGETOWN;
+ case SIOCGPGRP:
+ return LKL_SIOCGPGRP;
+ case SIOCATMARK:
+ return LKL_SIOCATMARK;
+ case SIOCGSTAMP:
+ return LKL_SIOCGSTAMP;
+ case SIOCGSTAMPNS:
+ return LKL_SIOCGSTAMPNS;
+ }
+
+ /* TODO: asm/termios.h translations */
+
+ return req;
+}
+
+int lkl_fcntl_cmd_xlate(int cmd)
+{
+ switch (cmd) {
+ case F_DUPFD:
+ return LKL_F_DUPFD;
+ case F_GETFD:
+ return LKL_F_GETFD;
+ case F_SETFD:
+ return LKL_F_SETFD;
+ case F_GETFL:
+ return LKL_F_GETFL;
+ case F_SETFL:
+ return LKL_F_SETFL;
+ case F_GETLK:
+ return LKL_F_GETLK;
+ case F_SETLK:
+ return LKL_F_SETLK;
+ case F_SETLKW:
+ return LKL_F_SETLKW;
+ case F_SETOWN:
+ return LKL_F_SETOWN;
+ case F_GETOWN:
+ return LKL_F_GETOWN;
+ case F_SETSIG:
+ return LKL_F_SETSIG;
+ case F_GETSIG:
+ return LKL_F_GETSIG;
+#ifndef LKL_CONFIG_64BIT
+ case F_GETLK64:
+ return LKL_F_GETLK64;
+ case F_SETLK64:
+ return LKL_F_SETLK64;
+ case F_SETLKW64:
+ return LKL_F_SETLKW64;
+#endif
+ case F_SETOWN_EX:
+ return LKL_F_SETOWN_EX;
+ case F_GETOWN_EX:
+ return LKL_F_GETOWN_EX;
+ }
+
+ return cmd;
+}
+
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LKL_HIJACK_XLATE_H
+#define _LKL_HIJACK_XLATE_H
+
+long lkl_set_errno(long err);
+int lkl_soname_xlate(int soname);
+int lkl_solevel_xlate(int solevel);
+unsigned long lkl_ioctl_req_xlate(unsigned long req);
+int lkl_fcntl_cmd_xlate(int cmd);
+
+#define LKL_FD_OFFSET (FD_SETSIZE/2)
+
+#endif /* _LKL_HIJACK_XLATE_H */
new file mode 100755
@@ -0,0 +1,737 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+
+script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
+
+clear_wdir()
+{
+ test -f ${VDESWITCH}.pid && kill $(cat ${VDESWITCH}.pid)
+ rm -rf ${wdir}
+ tap_cleanup
+ tap_cleanup 1
+}
+
+set_cfgjson()
+{
+ cfgjson=${wdir}/hijack-test$1.conf
+
+ cat > ${cfgjson}
+
+ export_vars cfgjson
+}
+
+run_hijack_cfg()
+{
+ lkl_test_cmd LKL_HIJACK_CONFIG_FILE=$cfgjson $hijack $@
+}
+
+run_hijack()
+{
+ lkl_test_cmd $hijack $@
+}
+
+run_netperf()
+{
+ lkl_test_cmd TEST_NETSERVER_PORT=$TEST_NETSERVER_PORT \
+ LKL_HIJACK_CONFIG_FILE=$cfgjson $netperf $@
+}
+
+test_ping()
+{
+ set -e
+
+ run_hijack ${ping} -c 1 127.0.0.1
+}
+
+test_ping6()
+{
+ set -e
+
+ run_hijack ${ping6} -c 1 ::1
+}
+
+test_mount_and_dump()
+{
+ set -e
+
+ set_cfgjson << EOF
+ {
+ "mount":"proc,sysfs",
+ "dump":"/sysfs/class/net/lo/mtu,/sysfs/class/net/lo/dev_id",
+ "debug": "1"
+ }
+EOF
+
+ ans=$(run_hijack_cfg $(lkl_test_cmd which true))
+ echo "$ans"
+ echo "$ans" | grep "^65536" # lo's MTU
+ echo "$ans" | grep "0x0" # lo's dev_id
+}
+
+test_boot_cmdline()
+{
+ set -e
+
+ set_cfgjson << EOF
+ {
+ "debug":"1",
+ "boot_cmdline":"loglevel=1"
+ }
+EOF
+
+ ans=$(run_hijack_cfg $(lkl_test_cmd which true))
+ echo "$ans"
+ [ $(echo "$ans" | wc -l) = 1 ]
+}
+
+
+test_pipe_setup()
+{
+ set -e
+
+ mkfifo ${fifo1}
+ mkfifo ${fifo2}
+
+ set_cfgjson << EOF
+ {
+ "interfaces":
+ [
+ {
+ "type":"pipe",
+ "param":"${fifo1}|${fifo2}",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "mac":"$TEST_MAC0",
+ }
+ ]
+ }
+EOF
+
+ # Make sure our device has the addresses we expect
+ addr=$(run_hijack_cfg ip addr)
+ echo "$addr" | grep eth0
+ echo "$addr" | grep $(ip_lkl)
+ echo "$addr" | grep "$TEST_MAC0"
+}
+
+test_pipe_ping()
+{
+ set -e
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_lkl)",
+ "gateway6":"$(ip6_lkl)",
+ "interfaces":
+ [
+ {
+ "type":"pipe",
+ "param":"${fifo1}|${fifo2}",
+ "ip":"$(ip_host)",
+ "masklen":"$TEST_IP_NETMASK",
+ "mac":"$TEST_MAC0",
+ "ipv6":"$(ip6_host)",
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ run_hijack_cfg $(lkl_test_cmd which sleep) 10 &
+
+ set_cfgjson 2 << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"pipe",
+ "param":"${fifo2}|${fifo1}",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "mac":"$TEST_MAC0",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ # Ping under LKL
+ run_hijack_cfg ${ping} -c 1 -w 10 $(ip_host)
+
+ # Ping 6 under LKL
+ run_hijack_cfg ${ping6} -c 1 -w 10 $(ip6_host)
+
+ wait
+}
+
+test_tap_setup()
+{
+ set -e
+
+ # Set up the TAP device we'd like to use
+ tap_setup
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "debug":"1",
+ "interfaces": [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac": "$TEST_MAC0"
+ }
+ ]
+ }
+EOF
+
+ # Make sure our device has the addresses we expect
+ addr=$(run_hijack_cfg ip addr)
+ echo "$addr" | grep eth0
+ echo "$addr" | grep $(ip_lkl)
+ echo "$addr" | grep "$TEST_MAC0"
+ echo "$addr" | grep "$(ip6_lkl)"
+ ! echo "$addr" | grep "WARN: failed to free"
+}
+
+test_tap_cleanup()
+{
+ tap_cleanup
+ tap_cleanup 1
+}
+
+test_tap_ping_host()
+{
+ set -e
+
+ # Make sure we can ping the host from inside LKL
+ run_hijack_cfg ${ping} -c 1 $(ip_host)
+ run_hijack_cfg ${ping6} -c 1 $(ip6_host)
+}
+
+test_tap_ping_lkl()
+{
+ set -e
+
+ # Now let's check that the host can see LKL.
+ lkl_test_cmd sudo ip -6 neigh del $(ip6_lkl) dev $(tap_ifname)
+ lkl_test_cmd sudo ip neigh del $(ip_lkl) dev $(tap_ifname)
+ run_hijack_cfg $(lkl_test_cmd which sleep) 3 &
+ sleep 2
+ lkl_test_cmd sudo ping -i 0.01 -c 65 $(ip_lkl)
+ lkl_test_cmd sudo ping6 -i 0.01 -c 65 $(ip6_lkl)
+}
+
+test_tap_neighbours()
+{
+ set -e
+
+ neigh1="$(ip_add 100)|12:34:56:78:9a:bc"
+ neigh2="$(ip6_add 100)|12:34:56:78:9a:be"
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "neigh":"${neigh1};${neigh2}"
+ }
+ ]
+ }
+EOF
+
+ # add neighbor entries
+ ans=$(run_hijack_cfg ip neighbor show) || true
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:bc"
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:be"
+
+ # gateway
+ ans=$(run_hijack_cfg ip route show) || true
+ echo "$ans" | tail -n 15 | grep "$(ip_host)"
+
+ # gateway v6
+ ans=$(run_hijack_cfg ip -6 route show) || true
+ echo "$ans" | tail -n 15 | grep "$(ip6_host)"
+}
+
+test_tap_netperf_stream_tso_csum()
+{
+ set -e
+
+ # offload
+ # LKL_VIRTIO_NET_F_HOST_TSO4 && LKL_VIRTIO_NET_F_GUEST_TSO4
+ # LKL_VIRTIO_NET_F_CSUM && LKL_VIRTIO_NET_F_GUEST_CSUM
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "offload":"0x883",
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ run_netperf $(ip_host) TCP_STREAM
+}
+
+test_tap_netperf_maerts_csum_tso()
+{
+ run_netperf $(ip_host) TCP_MAERTS
+}
+
+test_tap_netperf_stream_csum_tso_mrgrxbuf()
+{
+ set -e
+
+ # offload
+ # LKL_VIRTIO_NET_F_HOST_TSO4 && LKL_VIRTIO_NET_F_MRG_RXBUF
+ # LKL_VIRTIO_NET_F_CSUM && LKL_VIRTIO_NET_F_GUEST_CSUM
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "offload":"0x8803",
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ run_netperf $(ip_host) TCP_MAERTS
+}
+
+test_tap_netperf_tcp_rr()
+{
+ set -e
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ run_netperf $(ip_host) TCP_RR
+}
+
+test_tap_netperf_tcp_stream()
+{
+ set -e
+
+ run_netperf $(ip_host) TCP_STREAM
+}
+
+test_tap_netperf_tcp_maerts()
+{
+ set -e
+
+ run_netperf $(ip_host) TCP_MAERTS
+}
+
+
+test_tap_qdisc()
+{
+ set -e
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC0",
+ "qdisc":"root|fq"
+ }
+ ]
+ }
+EOF
+
+ qdisc=$(run_hijack_cfg tc -s -d qdisc show)
+ echo "$qdisc"
+ echo "$qdisc" | grep "qdisc fq" > /dev/null
+ echo "$qdisc" | grep throttled > /dev/null
+}
+
+test_tap_multi_if_setup()
+{
+ set -e
+
+ # Set up 2nd TAP device we'd like to use
+ tap_setup 1
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC0"
+ },
+ {
+ "type":"tap",
+ "param":"$(tap_ifname 1)",
+ "ip":"$(ip_lkl 1)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl 1)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC1"
+ }
+ ]
+ }
+EOF
+
+ # Make sure our device has the addresses we expect
+ addr=$(run_hijack_cfg ip addr)
+ echo "$addr" | grep eth0
+ echo "$addr" | grep $(ip_lkl)
+ echo "$addr" | grep "$TEST_MAC0"
+ echo "$addr" | grep "$(ip6_lkl)"
+ echo "$addr" | grep eth1
+ echo "$addr" | grep $(ip_lkl 1)
+ echo "$addr" | grep "$TEST_MAC1"
+ echo "$addr" | grep "$(ip6_lkl 1)"
+ ! echo "$addr" | grep "WARN: failed to free"
+}
+
+test_tap_multi_if_ping()
+{
+ run_hijack_cfg ${ping} -c 1 $(ip_host)
+ run_hijack_cfg ${ping6} -c 1 $(ip6_host)
+ run_hijack_cfg ${ping} -c 1 $(ip_host 1)
+ run_hijack_cfg ${ping6} -c 1 $(ip6_host 1)
+}
+
+test_tap_multi_if_neigh()
+{
+
+ neigh1="$(ip_host)00|12:34:56:78:9a:bc"
+ neigh2="$(ip6_host)00|12:34:56:78:9a:be"
+ neigh3="$(ip_host 1)00|12:34:56:78:9a:bd"
+ neigh4="$(ip6_host 1)00|12:34:56:78:9a:bf"
+
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC0",
+ "neigh":"${neigh1};${neigh2}"
+ },
+ {
+ "type":"tap",
+ "param":"$(tap_ifname 1)",
+ "ip":"$(ip_lkl 1)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl 1)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC1",
+ "neigh":"${neigh3};${neigh4}"
+ }
+ ]
+ }
+EOF
+
+ # add neighbor entries
+ ans=$(run_hijack_cfg ip neighbor show) || true
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:bc"
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:be"
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:bd"
+ echo "$ans" | tail -n 15 | grep "12:34:56:78:9a:bf"
+}
+
+test_tap_multi_if_gateway()
+{
+ ans=$(run_hijack_cfg ip route show) || true
+ echo "$ans" | tail -n 15 | grep "$(ip_host)"
+}
+
+test_tap_multi_if_gateway_v6()
+{
+ ans=$(run_hijack_cfg ip -6 route show) || true
+ echo "$ans" | tail -n 15 | grep "$(ip6_host)"
+}
+
+
+test_tap_multitable_setup()
+{
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"tap",
+ "param":"$(tap_ifname)",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ifgateway":"$(ip_host)",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "ifgateway6":"$(ip6_host)",
+ "mac":"$TEST_MAC0",
+ "neigh":"${neigh1};${neigh2}"
+ },
+ {
+ "type":"tap",
+ "param":"$(tap_ifname 1)",
+ "ip":"$(ip_lkl 1)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ifgateway":"$(ip_host 1)",
+ "ipv6":"$(ip6_lkl 1)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "ifgateway6":"$(ip6_host 1)",
+ "mac":"$TEST_MAC1",
+ "neigh":"${neigh3};${neigh4}"
+ }
+ ]
+ }
+EOF
+}
+
+test_tap_multitable_ipv4_rule()
+{
+ addr=$(run_hijack_cfg ip rule show)
+ echo "$addr" | grep $(ip_lkl)
+ echo "$addr" | grep $(ip_lkl 1)
+}
+
+test_tap_multitable_ipv6_rule()
+{
+ addr=$(run_hijack_cfg ip -6 rule show)
+ echo "$addr" | grep $(ip6_lkl)
+ echo "$addr" | grep $(ip6_lkl 1)
+}
+
+test_tap_multitable_ipv4_rule_table_4()
+{
+ addr=$(run_hijack_cfg ip route show table 4)
+ echo "$addr" | grep $(ip_host)
+}
+
+test_tap_multitable_ipv6_rule_table_5()
+{
+ addr=$(run_hijack_cfg ip -6 route show table 5)
+ echo "$addr" | grep fc03::
+ echo "$addr" | grep $(ip6_host)
+}
+
+test_tap_multitable_ipv6_rule_table_6()
+{
+ addr=$(run_hijack_cfg ip route show table 6)
+ echo "$addr" | grep $(ip_host 1)
+}
+
+test_tap_multitable_ipv6_rule_table_7()
+{
+ addr=$(run_hijack_cfg ip -6 route show table 7)
+ echo "$addr" | grep fc04::
+ echo "$addr" | grep $(ip6_host 1)
+}
+
+test_vde_setup()
+{
+ set_cfgjson << EOF
+ {
+ "gateway":"$(ip_host)",
+ "gateway6":"$(ip6_host)",
+ "interfaces":
+ [
+ {
+ "type":"vde",
+ "param":"${VDESWITCH}",
+ "ip":"$(ip_lkl)",
+ "masklen":"$TEST_IP_NETMASK",
+ "ipv6":"$(ip6_lkl)",
+ "masklen6":"$TEST_IP6_NETMASK",
+ "mac":"$TEST_MAC0",
+ "neigh":"${neigh1};${neigh2}"
+ }
+ ]
+ }
+EOF
+
+ tap_setup
+
+ sleep 2
+ vde_switch -d -t $(tap_ifname) -s ${VDESWITCH} -p ${VDESWITCH}.pid
+
+ # Make sure our device has the addresses we expect
+ addr=$(run_hijack_cfg ip addr)
+ echo "$addr" | grep eth0
+ echo "$addr" | grep $(ip_lkl)
+ echo "$addr" | grep "$TEST_MAC0"
+}
+
+test_vde_cleanup()
+{
+ tap_cleanup
+}
+
+test_vde_ping_host()
+{
+ run_hijack_cfg ./ping $(ip_host) -c 1
+}
+
+test_vde_ping_lkl()
+{
+ lkl_test_cmd sudo arp -d $(ip_lkl)
+ lkl_test_cmd sudo ping -i 0.01 -c 65 $(ip_lkl) &
+ run_hijack_cfg sleep 3
+}
+
+source ${script_dir}/test.sh
+source ${script_dir}/net-setup.sh
+
+if [[ ! -e ${basedir}/lib/hijack/liblkl-hijack.so ]]; then
+ lkl_test_plan 0 "hijack tests"
+ echo "missing liblkl-hijack.so"
+ exit 0
+fi
+
+# Make a temporary directory to run tests in, since we'll be copying
+# things there.
+wdir=$(mktemp -d)
+cp `which ping` ${wdir}
+cp `which ping6` ${wdir}
+ping=${wdir}/ping
+ping6=${wdir}/ping6
+hijack=$basedir/bin/lkl-hijack.sh
+netperf=$basedir/tests/run_netperf.sh
+
+fifo1=${wdir}/fifo1
+fifo2=${wdir}/fifo2
+VDESWITCH=${wdir}/vde_switch
+
+# And make sure we clean up when we're done
+trap "clear_wdir &>/dev/null" EXIT
+
+lkl_test_plan 5 "hijack basic tests"
+lkl_test_run 1 run_hijack ip addr
+lkl_test_run 2 run_hijack ip route
+lkl_test_run 3 test_ping
+lkl_test_run 4 test_ping6
+lkl_test_run 5 test_mount_and_dump
+lkl_test_run 6 test_boot_cmdline
+
+if [ -z "$(QUIET=1 lkl_test_cmd which mkfifo)" ]; then
+ lkl_test_plan 0 "hijack pipe backend tests"
+ echo "no mkfifo command"
+else
+ lkl_test_plan 2 "hijack pipe backend tests"
+ lkl_test_run 1 test_pipe_setup
+ lkl_test_run 2 test_pipe_ping
+fi
+
+tap_prepare
+
+if ! lkl_test_cmd test -c /dev/net/tun &>/dev/null; then
+ lkl_test_plan 0 "hijack tap backend tests"
+ echo "missing /dev/net/tun"
+else
+ lkl_test_plan 23 "hijack tap backend tests"
+ lkl_test_run 1 test_tap_setup
+ lkl_test_run 2 test_tap_ping_host
+ lkl_test_run 3 test_tap_ping_lkl
+ lkl_test_run 4 test_tap_neighbours
+ lkl_test_run 5 test_tap_netperf_tcp_rr
+ lkl_test_run 6 test_tap_netperf_tcp_stream
+ lkl_test_run 7 test_tap_netperf_tcp_maerts
+ lkl_test_run 8 test_tap_netperf_stream_tso_csum
+ lkl_test_run 9 test_tap_netperf_maerts_csum_tso
+ lkl_test_run 10 test_tap_netperf_stream_csum_tso_mrgrxbuf
+ lkl_test_run 11 test_tap_qdisc
+ lkl_test_run 12 test_tap_multi_if_setup
+ lkl_test_run 13 test_tap_multi_if_ping
+ lkl_test_run 14 test_tap_multi_if_neigh
+ lkl_test_run 15 test_tap_multi_if_gateway
+ lkl_test_run 16 test_tap_multi_if_gateway_v6
+ lkl_test_run 17 test_tap_multitable_setup
+ lkl_test_run 18 test_tap_multitable_ipv4_rule
+ lkl_test_run 19 test_tap_multitable_ipv6_rule
+ lkl_test_run 20 test_tap_multitable_ipv4_rule_table_4
+ lkl_test_run 21 test_tap_multitable_ipv6_rule_table_5
+ lkl_test_run 22 test_tap_multitable_ipv6_rule_table_6
+ lkl_test_run 23 test_tap_multitable_ipv6_rule_table_7
+ lkl_test_run 24 test_tap_cleanup
+fi
+
+if [ -z "$LKL_HOST_CONFIG_VIRTIO_NET_VDE" ]; then
+ lkl_test_plan 0 "vde tests"
+ echo "vde not supported"
+elif [ ! -x "$(which vde_switch)" ]; then
+ lkl_test_plan 0 "hijack vde tests"
+ echo "could not find a vde_switch executable"
+else
+ lkl_test_plan 3 "hijack vde tests"
+ lkl_test_run 1 test_vde_setup
+ lkl_test_run 2 test_vde_ping_host
+ lkl_test_run 3 test_vde_ping_lkl
+ lkl_test_run 4 test_vde_cleanup
+fi
new file mode 100755
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Usage
+# ./run_netperf.sh [ip] [test_name] [use_taskset] [num_runs]
+
+set -e
+
+script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
+hijack_script=${script_dir}/../bin/lkl-hijack.sh
+
+num_runs="1"
+test_name="TCP_STREAM"
+use_taskset="0"
+host_ip="localhost"
+taskset_cmd="taskset -c 1"
+test_len=10 # second
+
+if [ ! -x "$(which netperf)" ]; then
+ echo "WARNING: Cannot find a netserver executable, skipping netperf tests."
+ exit $TEST_SKIP
+fi
+
+if [ $# -ge 1 ]; then
+ host_ip=$1
+fi
+if [ $# -ge 2 ]; then
+ test_name=$2
+fi
+if [ $# -ge 3 ]; then
+ use_taskset=$2
+fi
+if [ $# -ge 4 ]; then
+ num_runs=$3
+fi
+if [ $# -ge 5 ]; then
+ echo "BAD NUMBER of INPUTS."
+ exit 1
+fi
+
+if [ $use_taskset = "0" ]; then
+ taskset_cmd=""
+fi
+
+clean() {
+ kill %1 || true
+}
+
+clean_with_tap() {
+ tap_cleanup &> /dev/null || true
+ clean
+ rm -rf ${work_dir}
+}
+
+# LKL_HIJACK_CONFIG_FILE is not set, which means it's not called from
+# hijack-test.sh. Needs to set up things first.
+if [ -z ${LKL_HIJACK_CONFIG_FILE+x} ]; then
+
+ # Setting up environmental vars and TAP
+ work_dir=$(mktemp -d)
+ cfgjson=${work_dir}/hijack-test.conf
+ export LKL_HIJACK_CONFIG_FILE=$cfgjson
+
+ cat <<EOF > ${cfgjson}
+ {
+ "interfaces": [
+ {
+ "type": "tap"
+ "param": "$(tap_ifname)"
+ "ip": "$(ip_lkl)"
+ "masklen":"$TEST_IP_NETMASK"
+ "ipv6":"$(ip6_lkl)"
+ "masklen6":"$TEST_IP6_NETMASK"
+ }
+ ]
+ }
+EOF
+
+ . $script_dir/net-setup.sh
+ host_ip=$(ip_host)
+
+ tap_prepare
+ tap_setup
+ trap clean_with_tap EXIT
+fi
+
+netserver -D -N -p $TEST_NETSERVER_PORT &
+
+trap clean EXIT
+
+echo NUM=$num_runs, TEST=$test_name, TASKSET=$use_taskset
+for i in `seq $num_runs`; do
+ echo Test: $i
+ set -x
+ $taskset_cmd ${hijack_script} netperf -p $TEST_NETSERVER_PORT -H $host_ip \
+ -t $test_name -l $test_len
+ set +x
+done