Skip to content

Instantly share code, notes, and snippets.

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 teotikalki/e64454b94630622cf5d81107724316bb to your computer and use it in GitHub Desktop.
Save teotikalki/e64454b94630622cf5d81107724316bb to your computer and use it in GitHub Desktop.
SmartOS lx-zone support for tuntap (Unofficial)
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.
#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;
}
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
--- ./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" />
#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