Skip to content

Instantly share code, notes, and snippets.

@thediveo
Last active March 22, 2019 07:38
Show Gist options
  • Save thediveo/d0834baacc92a7ab25300fd8c7468b61 to your computer and use it in GitHub Desktop.
Save thediveo/d0834baacc92a7ab25300fd8c7468b61 to your computer and use it in GitHub Desktop.
TAP client supporting specifying TAP ifname and TAP client network namespace
/* modified from: https://github.com/a34729t/exp/blob/master/tunclient.c */
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdarg.h>
int tun_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
/* Arguments taken by the function:
*
* char *dev: the name of an interface (or '\0'). MUST have enough
* space to hold the interface name if '\0' is passed
* int flags: interface flags (eg, IFF_TUN etc.)
*/
/* open the clone device */
if( (fd = open(clonedev, O_RDWR)) < 0 ) {
return fd;
}
/* preparation of the struct ifr, of type "struct ifreq" */
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
if (*dev) {
/* if a device name was specified, put it in the structure; otherwise,
* the kernel will try to allocate the "next" device of the
* specified type */
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
close(fd);
return err;
}
/* if the operation was successful, write back the name of the
* interface to the variable "dev", so the caller can know
* it. Note that the caller MUST reserve space in *dev (see calling
* code below) */
strcpy(dev, ifr.ifr_name);
/* this is the special file descriptor that the caller will use to talk
* with the virtual interface */
return fd;
}
int main(int argc, char *argv[]) {
char tun_name[IFNAMSIZ];
int tun_fd, nread;
char buffer[2000];
/* Connect to the device */
if (argc < 2) {
strcpy(tun_name, "");
} else {
strcpy(tun_name, argv[1]);
}
tun_fd = tun_alloc(tun_name, IFF_TAP | IFF_NO_PI); /* tun interface */
if(argc >= 3) {
int netnsfd;
netnsfd = open(argv[2], O_RDONLY);
if(netnsfd < 0){
perror("Network namespace reference");
exit(1);
}
if(setns(netnsfd, CLONE_NEWNET) < 0){
perror("Switching network namespace");
exit(1);
}
printf("Switched into network namespace %s\n", argv[2]);
}
if(tun_fd < 0){
perror("Allocating interface");
exit(1);
}
printf("Reading from interface %s\n", tun_name);
/* Now read data coming from the kernel */
while(1) {
/* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
nread = read(tun_fd,buffer,sizeof(buffer));
if(nread < 0) {
perror("Reading from interface");
close(tun_fd);
exit(1);
}
/* Do whatever with the data */
printf("Read %d bytes from device %s\n", nread, tun_name);
}
}
#!/bin/bash
# remove stale tapclient(s)
for PID in $(ps a|grep "tapclient"|grep -v "grep"|awk '{print $1}'); do
echo "Killing tapclient $PID"
kill $PID
done
# remove stale netns
for NETNS in $(findmnt -n -t nsfs|awk '/\/run\/netns\//{n=split($1,P,"/");print P[n]}'|sort -u); do
echo "Removing old netns $NETNS"
ip netns del $NETNS
done
# set up netns
ip netns add ahh
ip netns add beh
# start tapclient, create TAP, move TAP to ahh, switch tapclient to beh
./tapclient tap123 /run/netns/beh >/dev/null &
export TAPPID=$!
ip link set tap123 netns ahh
# show results
echo "----"
echo host: $(readlink /proc/self/ns/net)
for NETNS in $(findmnt -n -t nsfs|awk '/\/run\/netns\//{n=split($1,P,"/");print P[n]}'|sort -u); do
echo $NETNS: $(findmnt -n /run/netns/$NETNS|sort -u|grep -o "net:\[[0-9]*\]")
done
echo tapclient: $(readlink /proc/$TAPPID/ns/net)
echo
grep -e "iff:" -e "netns:" /proc/$TAPPID/fdinfo/3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment