Last active
May 9, 2021 13:13
-
-
Save sfan5/696ad2f03f05a3e13952c44f7b767e81 to your computer and use it in GitHub Desktop.
Minimal slirp(1) replacement based on libvdeslirp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
all: slirp | |
libvdeslirp: | |
@echo "Cloning libvdeslirp" | |
git clone https://github.com/virtualsquare/libvdeslirp | |
libvdeslirp/libvdeslirp.so: libvdeslirp | |
@echo "Building libvdeslirp" | |
@bash -e -c 'pushd libvdeslirp; \ | |
cmake .; \ | |
make; \ | |
popd' | |
slirp: slirp.c libvdeslirp/libvdeslirp.so | |
gcc -o slirp -Wall -O2 -Ilibvdeslirp -Wl,-rpath,$(PWD)/libvdeslirp $^ | |
clean: | |
rm -f slirp | |
.PHONY: clean |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <poll.h> | |
#include <fcntl.h> | |
#include <arpa/inet.h> | |
#include <sys/uio.h> | |
#include <libvdeslirp.h> | |
/* | |
Minimal slirp(1) replacement based on libvdeslirp <https://github.com/virtualsquare/libvdeslirp> | |
Build using accompanied Makefile | |
Use with User Mode Linux: | |
./vmlinux ... eth0=slirp,,/path/to/slirp | |
# ip a add dev eth0 10.0.2.1 && ip l set eth0 up && ip r add default dev eth0 | |
# echo nameserver 10.0.2.3 >/etc/resolv.conf | |
See also <https://kitsunemimi.pw/notes/posts/fully-unprivileged-vms-with-uml-and-slirp-user-networking.html> | |
*/ | |
// SLIP special character codes | |
#define END 0300 // indicates end of packet | |
#define ESC 0333 // indicates byte stuffing | |
#define ESC_END 0334 // ESC ESC_END means END data byte | |
#define ESC_ESC 0335 // ESC ESC_ESC means ESC data byte | |
#define END_S "\300" | |
#define MAXPKT 1600 | |
#define SLIP_MAXPKT (MAXPKT) * 2 + 2 | |
#define MYIP "\x0a\x00\x02\x01" | |
#define MYMAC "\xaa\xaa\xaa\xaa\xaa\xaa" | |
#define GATEWAYMAC "\x52\x55\x0a\x00\x02\x02" /* SLIRP default */ | |
struct slip_recv_state { | |
uint8_t _pad1[14]; // so we can safely prepend eth header without copying | |
uint8_t buf[SLIP_MAXPKT]; | |
uint8_t _pad2[14]; // same | |
uint8_t scratch[MAXPKT]; | |
size_t index, discard; | |
}; | |
static void slip_send(int fd, const uint8_t *pkt, size_t pktlen); | |
static uint8_t *slip_recv(int fd, struct slip_recv_state *s, size_t *pktlen); | |
static void slip_to_outside(struct vdeslirp *myslirp, uint8_t *pkt, size_t pktlen); | |
static void outside_to_slip(struct vdeslirp *myslirp, const uint8_t *pkt, size_t pktlen); | |
int main(int argc, char *argv[]) | |
{ | |
SlirpConfig slirpcfg; | |
vdeslirp_init(&slirpcfg, VDE_INIT_DEFAULT); | |
slirpcfg.in6_enabled = false; | |
struct vdeslirp *myslirp = vdeslirp_open(&slirpcfg); | |
if (!myslirp) { | |
fprintf(stderr, "vdeslirp_open failed\n"); | |
return 1; | |
} | |
#if 0 /* enable for port forwarding */ | |
struct in_addr in_myip; | |
memcpy(&in_myip.s_addr, MYIP, 4); | |
vdeslirp_add_fwd(myslirp, 0, | |
(struct in_addr) {INADDR_ANY}, 2222, // host port | |
in_myip, 22); // guest port | |
#endif | |
int flags = fcntl(STDIN_FILENO, F_GETFL, 0); | |
if (flags == -1 || fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) { | |
perror("fcntl"); | |
return 1; | |
} | |
fprintf(stderr, " --- slirp ready --- \n"); | |
struct pollfd fds[2]; | |
fds[0].fd = vdeslirp_fd(myslirp); | |
fds[0].events = POLLIN; | |
fds[1].fd = STDIN_FILENO; | |
fds[1].events = POLLIN; | |
struct slip_recv_state ss; | |
memset(&ss, 0, sizeof(ss)); | |
while (1) { | |
int n = poll(fds, 2, -1); | |
if (n == -1) | |
return perror("poll"), 1; | |
else if (n == 0) | |
continue; | |
if ((fds[0].revents | fds[1].revents) & POLLHUP) | |
goto exit; | |
if (fds[0].revents) { | |
uint8_t buf[MAXPKT + 14]; | |
ssize_t n = vdeslirp_recv(myslirp, buf, sizeof(buf)); | |
if (n > 0) | |
outside_to_slip(myslirp, buf, n); | |
} | |
if (fds[1].revents) { | |
uint8_t *pkt; | |
size_t pktlen; | |
while (pkt = slip_recv(STDIN_FILENO, &ss, &pktlen)) | |
slip_to_outside(myslirp, pkt, pktlen); | |
} | |
} | |
exit: | |
vdeslirp_close(myslirp); | |
return 0; | |
} | |
static void slip_to_outside(struct vdeslirp *myslirp, uint8_t *pkt, size_t pktlen) | |
{ | |
pkt -= 14; | |
memcpy(pkt, GATEWAYMAC, 6); // dest MAC | |
memcpy(pkt + 6, MYMAC, 6); // src MAC | |
memcpy(pkt + 12, "\x08\x00", 2); // proto = ipv4 | |
vdeslirp_send(myslirp, pkt, pktlen + 14); | |
} | |
static void outside_to_slip(struct vdeslirp *myslirp, const uint8_t *pkt, size_t pktlen) | |
{ | |
if (pktlen < 14) | |
return; | |
uint16_t proto; | |
memcpy(&proto, pkt + 12, 2); | |
if (proto == htons(0x0806)) { // answer ARP requests: | |
if (pktlen < 42 || | |
memcmp(pkt + 14, "\x00\x01\x08\x00", 4) != 0 || // ethernet + ipv4 | |
memcmp(pkt + 20, "\x00\x01", 2) != 0 || // opcode request | |
memcmp(pkt + 38, MYIP, 4) != 0) // target ip | |
return; | |
uint8_t buf[42]; | |
memcpy(buf + 6, MYMAC | |
"\x08\x06" | |
"\x00\x01" "\x08\x00" "\x06" "\x04" | |
"\x00\x02" | |
MYMAC MYIP, 26); | |
memcpy(buf, pkt + 22, 6); // dest MAC | |
memcpy(buf + 32, pkt + 22, 10); // target info | |
vdeslirp_send(myslirp, buf, 42); | |
} else if (proto == htons(0x0800)) { | |
if (memcmp(pkt, MYMAC, 6) != 0) | |
return; | |
slip_send(STDOUT_FILENO, pkt + 14, pktlen - 14); | |
} | |
} | |
static void slip_send(int fd, const uint8_t *pkt, size_t pktlen) | |
{ | |
if (!memchr(pkt, END, pktlen) && !memchr(pkt, ESC, pktlen)) { | |
struct iovec iov[3] = { | |
{ .iov_base = END_S, .iov_len = 1 }, | |
{ .iov_base = pkt, .iov_len = pktlen }, | |
{ .iov_base = END_S, .iov_len = 1 } | |
}; | |
writev(fd, iov, 3); | |
return; | |
} | |
if (pktlen > MAXPKT) | |
pktlen = MAXPKT; | |
uint8_t buf[SLIP_MAXPKT]; | |
size_t i = 0; | |
buf[i++] = END; | |
while (pktlen > 0) { | |
if (*pkt == END) { | |
buf[i++] = ESC; | |
buf[i++] = ESC_END; | |
} else if (*pkt == ESC) { | |
buf[i++] = ESC; | |
buf[i++] = ESC_ESC; | |
} else { | |
buf[i++] = *pkt; | |
} | |
pkt++, pktlen--; | |
} | |
buf[i++] = END; | |
write(fd, buf, i); | |
} | |
static uint8_t *slip_recv(int fd, struct slip_recv_state *s, size_t *pktlen) | |
{ | |
uint8_t *p; | |
if (s->index > s->discard && ( p = memchr(s->buf + s->discard, END, s->index - s->discard) )) | |
goto segment; | |
do { | |
if (s->discard) { | |
s->index -= s->discard; | |
memmove(s->buf, s->buf + s->discard, s->index); | |
s->discard = 0; | |
} | |
if (s->index >= SLIP_MAXPKT) | |
s->index = 0; | |
ssize_t n = read(fd, s->buf + s->index, SLIP_MAXPKT - s->index); | |
if (n <= 0) | |
return NULL; | |
s->index += n; | |
p = memchr(s->buf, END, s->index); | |
} while (!p); | |
segment: (void) 0; | |
uint8_t *base = s->buf + s->discard; | |
*pktlen = p - base; | |
if (!*pktlen) { | |
s->discard++; | |
return slip_recv(fd, s, pktlen); | |
} | |
if (!memchr(base, ESC, *pktlen)) { | |
s->discard += *pktlen; | |
return base; | |
} else { | |
size_t i = 0; | |
for (size_t c = 0; c < *pktlen && i < MAXPKT; c++) { | |
if (base[c] == ESC) | |
s->scratch[i++] = base[++c] == ESC_END ? END : ESC; | |
else | |
s->scratch[i++] = base[c]; | |
} | |
s->discard += *pktlen; | |
*pktlen = i; | |
return s->scratch; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment