Skip to content

Instantly share code, notes, and snippets.

@sfan5
Last active May 9, 2021 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sfan5/696ad2f03f05a3e13952c44f7b767e81 to your computer and use it in GitHub Desktop.
Save sfan5/696ad2f03f05a3e13952c44f7b767e81 to your computer and use it in GitHub Desktop.
Minimal slirp(1) replacement based on libvdeslirp
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
#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