Last active
December 17, 2017 23:55
-
-
Save dsouzae/b54248996bcb747c43c6609cd7856423 to your computer and use it in GitHub Desktop.
SmartOS lx-zone support for tuntap (Unofficial)
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
If you need tuntap in an lx-zone, you are in luck, it's possible. | |
You need follow the instructions to build SmartOS on SmartOS: | |
https://wiki.smartos.org/display/DOC/Building+SmartOS+on+SmartOS | |
If you have built SmartOS once, this first part is easy. | |
1) In your "smartos-live" directory, do the following: | |
[... ~/smartos-live]# cp ./projects/illumos/usr/src/lib/brand/lx/zone/platform.xml ./overlay/generic/usr/lib/brand/lx/platform.xml | |
2) Now you need to add the patches to the platform.xml in the overlay see platform.xml.patch | |
3) Also update the overlay manifest to include the new file added, apply the patch overlay.manifest.patch | |
Part One done. Go build SmartOS, and boot your new version. | |
... | |
So you should be already running your new changes. Now you should already be running an lx-zone by now. | |
And of course you tried to think that's it. but you notice the tuntap device doesn't work. | |
uh..your running SmartOS, everything is Solaris, there is no lx-branding for the tun/tap device. | |
So instead I did something very evil. I run a solaris native binary inside the lx-zone, and transfer a tun socket to a linux process via a unix fifo. | |
See client.c and server.c for more details. I borrowed the initial code from https://github.com/kaizawa/tuntap. hattip to @kaizawa | |
You need to compile in a smartos zone: | |
gcc -Wall -D_XOPEN_SOURCE=1 -D_XOPEN_SOURCE_EXTENDED=1 -D__EXTENSIONS__=1 client.c -lsocket -o createtun | |
In you lx-zone you can compile the server as following. | |
gcc -Wall -D_XOPEN_SOURCE=1 -D_XOPEN_SOURCE_EXTENDED=1 -D__EXTENSIONS__=1 server.c -lsocket -o server | |
Disclaimer: Now, I probably forgot to mention this. I'm only showing you how to create a tun device in a lx-branded zone. Not to support various OSS project to use tuntap in SmartOS lx-brand zones. | |
Now you run the "server" first like this: | |
# ./server /tmp/.tun.fifo | |
in another shell run this: | |
# ./createtun 0 /tmp/.tun.fifo | |
The "server" should in theory start outputing something at this point. | |
What I did for the project I was working on was to create the server first, and then fork a process of to call "createtun", | |
after that I get the tun device name and a file descriptor associated with that device. And luckily the rest of the code didn't crash. | |
Now, go forth and mutate my code. | |
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 <stdlib.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <net/if.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/ioctl.h> | |
#if 0 | |
#define I_STR (0x5308) | |
#define I_PUSH (0x5302) | |
/* | |
* User level ioctl format for ioctls that go downstream (I_STR) | |
*/ | |
struct strioctl { | |
int ic_cmd; /* command */ | |
int ic_timout; /* timeout value */ | |
int ic_len; /* length of data */ | |
char *ic_dp; /* pointer to data */ | |
}; | |
#else | |
#include <sys/stropts.h> | |
#include <sys/sockio.h> | |
#endif | |
#ifndef TUNNEWPPA | |
#define TUNNEWPPA (('T'<<16) | 0x0001) | |
#endif | |
int sendFd(int destsock, int sendsock, const char *iname) | |
{ | |
struct msghdr msg = {}; | |
struct cmsghdr *cmsg; | |
char buf[CMSG_SPACE(sizeof(int) * 1)]; | |
struct iovec type = {}; | |
char ifname[LIFNAMSIZ+1] = {}; | |
int *fdptr; | |
if (iname) { | |
strncpy(ifname, iname, sizeof(ifname)-1); | |
} | |
msg.msg_control = buf; | |
msg.msg_controllen = CMSG_SPACE(1 * sizeof(int)); | |
cmsg = CMSG_FIRSTHDR(&msg); | |
cmsg->cmsg_level = SOL_SOCKET; | |
cmsg->cmsg_type = SCM_RIGHTS; | |
cmsg->cmsg_len = CMSG_LEN(1 * sizeof(int)); | |
/* Initialize the payload: */ | |
fdptr = (int*)CMSG_DATA(cmsg); | |
fdptr[0] = sendsock; | |
type.iov_base = ifname; | |
type.iov_len = sizeof(ifname); | |
msg.msg_iov = &type; | |
msg.msg_iovlen = 1; | |
if (sendmsg(destsock, &msg, 0) < 0) | |
{ | |
return -1; | |
} | |
return 0; | |
} | |
int closedevice(char *devname) { | |
int ip_fd = open("/dev/ip", O_RDWR, 0); | |
if(ip_fd < 0) { | |
fprintf(stdout, "failed to open /dev/ip\n"); | |
return -1; | |
} | |
struct lifreq ifr = {}; | |
strncpy(ifr.lifr_name, devname, sizeof ifr.lifr_name); | |
if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) { | |
int muxid = ifr.lifr_arp_muxid; | |
ioctl(ip_fd, I_PUNLINK, muxid); | |
muxid = ifr.lifr_ip_muxid; | |
ioctl(ip_fd, I_PUNLINK, muxid); | |
} | |
close(ip_fd); | |
return 0; | |
} | |
int main(int argc, char *argv[]) { | |
int ip_fd; | |
int device_fd; | |
int if_fd; | |
if (argc == 3 && strcmp(argv[1], "close") == 0) { | |
return closedevice(argv[2]); | |
} | |
if (argc != 3) { | |
return -1; | |
} | |
char *device = "/dev/tun"; | |
char *path = NULL; | |
int ppa = -1; | |
ppa = atoi(argv[1]); | |
path = argv[2]; | |
if (access(path, F_OK) != 0) { | |
return -1; | |
} | |
ip_fd = open("/dev/ip", O_RDWR, 0); | |
if(ip_fd < 0) { | |
fprintf(stdout, "failed to open /dev/ip\n"); | |
return -1; | |
} | |
device_fd = open(device, O_RDWR, 0); | |
if(device_fd < 0) { | |
fprintf(stdout, "failed to open /dev/tun\n"); | |
return -1; | |
} | |
struct strioctl strioc_ppa = { | |
.ic_cmd = TUNNEWPPA, | |
.ic_len = sizeof ppa, | |
.ic_dp = (char *)&ppa, | |
}; | |
fprintf(stdout, "device_fd %d, ip_fd %d, sizeof %d, sizeof %d, sizeof %d\n", device_fd, ip_fd, sizeof(struct strioctl), sizeof ppa, sizeof(void *)); | |
int found = 0; | |
while(!found && ppa < 64) { | |
int new_ppa = ioctl(device_fd, I_STR, &strioc_ppa); | |
if(new_ppa >= 0) { | |
ppa = new_ppa; | |
found = 1; | |
break; | |
} | |
ppa++; | |
} | |
if(!found) { | |
fprintf(stdout, "Could not find a free PPA\n"); | |
return -1;; | |
} | |
fprintf(stdout, "Found %d\n", ppa); | |
if_fd = open(device, O_RDWR, 0); | |
if (if_fd < 0) { | |
fprintf(stdout, "Could not open %s: %s\n", device, strerror(errno)); | |
return -1; | |
} | |
int rc = ioctl(if_fd, I_PUSH, "ip"); | |
if (rc < 0) { | |
fprintf(stdout, "Could not push IP module onto %s!", device); | |
return -1; | |
} | |
if((if_fd = open(device, O_RDWR, 0)) < 0) { | |
fprintf(stdout, "Could not open %s: %s\n", device, strerror(errno)); | |
return -1; | |
} | |
if(ioctl(if_fd, I_PUSH, "ip") < 0) { | |
fprintf(stdout, "Could not push IP module onto %s!", device); | |
return -1; | |
} | |
char iface[128] = {}; | |
sprintf(iface, "%s%d", "tun", ppa); | |
{ | |
/* Remove muxes just in case they are left over from a crashed tincd */ | |
struct lifreq ifr = {}; | |
strncpy(ifr.lifr_name, iface, sizeof ifr.lifr_name); | |
if(ioctl(ip_fd, SIOCGLIFMUXID, &ifr) >= 0) { | |
int muxid = ifr.lifr_arp_muxid; | |
ioctl(ip_fd, I_PUNLINK, muxid); | |
muxid = ifr.lifr_ip_muxid; | |
ioctl(ip_fd, I_PUNLINK, muxid); | |
} | |
} | |
/* Assign ppa according to the unit number returned by tun device */ | |
if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0) { | |
fprintf(stdout, "Could not set PPA %d on %s!", ppa, device); | |
return -1; | |
} | |
int ip_muxid; | |
if((ip_muxid = ioctl(ip_fd, I_PLINK, if_fd)) < 0) { | |
fprintf(stdout, "Could not link %s to IP", device); | |
return -1; | |
} | |
struct lifreq ifr = {}; | |
strncpy(ifr.lifr_name, iface, sizeof(ifr.lifr_name)); | |
ifr.lifr_ip_muxid = ip_muxid; | |
if(ioctl(ip_fd, SIOCSLIFMUXID, &ifr) < 0) { | |
ioctl(ip_fd, I_PUNLINK, ip_muxid); | |
fprintf(stdout, "Could not set multiplexor id for %s", device); | |
return -1; | |
} | |
close(if_fd); | |
#ifdef FD_CLOEXEC | |
fcntl(device_fd, F_SETFD, FD_CLOEXEC); | |
fcntl(ip_fd, F_SETFD, FD_CLOEXEC); | |
#endif | |
{ | |
int rc; | |
int sock; | |
struct sockaddr_un addr = {}; | |
addr.sun_family = AF_UNIX; | |
strncpy(addr.sun_path, path, sizeof(addr.sun_path)); | |
sock = socket(AF_LOCAL, SOCK_STREAM, 0); | |
if (sock < 0) { | |
return -1; | |
} | |
rc = connect(sock, (struct sockaddr*)&addr, sizeof addr); | |
if (rc < 0) { | |
return -1; | |
} | |
rc = sendFd(sock, device_fd, iface); | |
if (rc < 0) { | |
return -1; | |
} | |
close(sock); | |
} | |
return 0; | |
} |
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
diff --git a/overlay/generic/manifest b/overlay/generic/manifest | |
index 2b621fe..2b1ce3e 100644 | |
--- a/overlay/generic/manifest | |
+++ b/overlay/generic/manifest | |
@@ -164,6 +165,7 @@ d usr/lib/brand/lx 0755 root bin | |
f usr/lib/brand/lx/config.xml 0444 root root | |
f usr/lib/brand/lx/lx_install 0755 root root | |
f usr/lib/brand/lx/lx_uninstall 0755 root root | |
+f usr/lib/brand/lx/platform.xml 0444 root root | |
f usr/lib/brand/lx/poststate 0755 root root | |
f usr/lib/brand/lx/prestate 0755 root root | |
f usr/lib/brand/lx/statechange 0755 root root |
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
--- ./overlay/generic/usr/lib/brand/lx/platform.xml 2016-02-04 21:06:08.456731599 +0000 | |
+++ ./overlay/generic/usr/lib/brand/lx/platform.xml 2016-02-04 06:36:28.468416619 +0000 | |
@@ -83,10 +81,12 @@ | |
<device match="pts/*" /> | |
<device match="random" /> | |
<device match="signalfd" /> | |
+ <device match="tap" /> | |
<device match="tcp" /> | |
<device match="tcp6" /> | |
<device match="timerfd" /> | |
<device match="tty" /> | |
+ <device match="tun" /> | |
<device match="udp" /> | |
<device match="udp6" /> | |
<device match="urandom" /> |
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 <stdlib.h> | |
#include <errno.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <net/if.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/ioctl.h> | |
#include <sys/poll.h> | |
int recvFd(int cltsock, int *recvsock, char *iname) | |
{ | |
struct msghdr msg = {}; | |
struct cmsghdr *cmsg; | |
char buf[CMSG_SPACE(sizeof(int) * 1)] = {}; | |
struct iovec type = {}; | |
char ifname[LIFNAMSIZ+1] = {}; | |
msg.msg_control = buf; | |
msg.msg_controllen = CMSG_SPACE(1 * sizeof(int)); | |
cmsg = CMSG_FIRSTHDR(&msg); | |
cmsg->cmsg_level = SOL_SOCKET; | |
cmsg->cmsg_type = SCM_RIGHTS; | |
cmsg->cmsg_len = CMSG_LEN(1 * sizeof(int)); | |
type.iov_base = ifname; | |
type.iov_len = sizeof(ifname); | |
msg.msg_iov = &type; | |
msg.msg_iovlen = 1; | |
if (recvmsg(cltsock, &msg, 0) < 0) | |
{ | |
return -1; | |
} | |
cmsg = CMSG_FIRSTHDR(&msg); | |
if (cmsg == NULL) { | |
return -1; | |
} | |
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) | |
return -1; | |
*recvsock = *(int*)CMSG_DATA(cmsg); | |
strcpy(iname, ifname); | |
return 0; | |
} | |
int main(int argc, char *argv[]) { | |
if (argc != 2) { | |
return -1; | |
} | |
int rc; | |
int sock; | |
struct sockaddr_un addr = {}; | |
addr.sun_family = AF_UNIX; | |
strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path)); | |
unlink(addr.sun_path); | |
sock = socket(AF_LOCAL, SOCK_STREAM, 0); | |
if (sock < 0) { | |
return -1; | |
} | |
rc = bind(sock, (struct sockaddr*)&addr, sizeof addr); | |
if (rc < 0) { | |
perror("bind"); | |
return -1; | |
} | |
listen(sock, 5); | |
struct pollfd pfds = {}; | |
pfds.fd = sock; | |
pfds.events = POLLIN; | |
char iface[128] = {}; | |
int device_fd = -1; | |
while (1) { | |
rc = poll(&pfds, 1, 1000); | |
if (rc > 0) { | |
int cltfd; | |
fprintf(stdout, "ACCEPT\n"); | |
cltfd = accept(sock, NULL, NULL); | |
if (cltfd >= 0) { | |
rc = recvFd(cltfd, &device_fd, iface); | |
fprintf(stdout, "RECVFD %d %d %s\n", rc, device_fd, iface); | |
} | |
break; | |
} | |
} | |
close(sock); | |
sock = -1; | |
if (device_fd > 0) | |
{ | |
struct pollfd pfds = {}; | |
pfds.fd = device_fd; | |
pfds.events = POLLIN; | |
while(1) { | |
rc = poll(&pfds, 1, 1000); | |
if (rc > 0) { | |
fprintf(stdout, "poll %d %x\n", pfds.fd, pfds.revents); | |
ssize_t sz; | |
char buf[10240]; | |
sz = recv(device_fd, buf, sizeof(buf), 0); | |
if (sz < 0) { | |
perror("recv"); | |
break; | |
} | |
if (sz == 0) { | |
printf("Done\n"); | |
break; | |
} | |
fprintf(stdout, "Recv %d bytes", sz); | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment