--- /dev/null
+/*
+ Copyright (C) 2000 Daniel Ryde
+ Small amendment by Daniel Lange, 2010
+ Changes (C) 2019 by volpol and irrwahn:
+ * added sendto() and sendmsg()
+ * do not pollute global namespace
+ * proper debug output
+ * fixed several minor issues
+ * renamed to fbind to avoid name clashes
+ * added call script and Makefile
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+*/
+
+/*
+ LD_PRELOAD library to make bind and connect to use a virtual
+ IP address as localaddress. Specified via the enviroment
+ variable BIND_ADDR.
+
+ Compile on Linux with:
+ gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE
+
+
+ Example in bash to make inetd only listen to the localhost
+ lo interface, thus disabling remote connections and only
+ enable to/from localhost:
+
+ BIND_ADDR="127.0.0.1" LD_PRELOAD=./bind.so /sbin/inetd
+
+
+ Example in bash to use your virtual IP as your outgoing
+ sourceaddress for ircII:
+
+ BIND_ADDR="your-virt-ip" LD_PRELOAD=./bind.so ircII
+
+ Note that you have to set up your servers virtual IP first.
+
+
+ This program was made by Daniel Ryde
+ email: daniel@ryde.net
+ web: http://www.ryde.net/
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#ifdef DEBUG
+ #define DBGPRINT(...) fprintf(stderr, "[bind.so] "__VA_ARGS__)
+#else
+ #define DBGPRINT(...)
+#endif
+
+int (*real_bind)(int, const struct sockaddr *, socklen_t);
+int (*real_connect)(int, const struct sockaddr *, socklen_t);
+int (*real_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t);
+int (*real_sendmsg)(int, const struct msghdr *, int);
+
+static char *bind_addr_env;
+static unsigned long int bind_addr_saddr;
+static unsigned long int inaddr_any_saddr;
+static struct sockaddr_in local_sockaddr_in[] = { 0 };
+
+void _init(void) {
+ const char *err;
+
+ real_bind = dlsym(RTLD_NEXT, "bind");
+ if ((err = dlerror()) != NULL) {
+ fprintf(stderr, "dlsym(bind): %s\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ real_connect = dlsym(RTLD_NEXT, "connect");
+ if ((err = dlerror()) != NULL) {
+ fprintf(stderr, "dlsym(connect): %s\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ real_sendto = dlsym(RTLD_NEXT, "sendto");
+ if ((err = dlerror()) != NULL) {
+ fprintf(stderr, "dlsym(sendto): %s\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ real_sendmsg = dlsym(RTLD_NEXT, "sendmsg");
+ if ((err = dlerror()) != NULL) {
+ fprintf(stderr, "dlsym(sendmsg): %s\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ inaddr_any_saddr = htonl(INADDR_ANY);
+ if (NULL != (bind_addr_env = getenv("BIND_ADDR"))) {
+ DBGPRINT("_init: bind_addr_env='%s'\n", bind_addr_env);
+ bind_addr_saddr = inet_addr(bind_addr_env);
+ local_sockaddr_in->sin_family = AF_INET;
+ local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
+ local_sockaddr_in->sin_port = htons(0);
+ DBGPRINT("_init: %s:%d\n", inet_ntoa(local_sockaddr_in->sin_addr), ntohs(local_sockaddr_in->sin_port));
+ }
+}
+
+int bind(int fd, const struct sockaddr *sk, socklen_t sl) {
+ static struct sockaddr_in *lsk_in;
+
+ lsk_in = (struct sockaddr_in *)sk;
+ if ((lsk_in->sin_family == AF_INET)
+ && (lsk_in->sin_addr.s_addr == inaddr_any_saddr)
+ && (bind_addr_env)) {
+ lsk_in->sin_addr.s_addr = bind_addr_saddr;
+ DBGPRINT("[*] bind(INADDR_ANY): %d %s:%d\n", fd, inet_ntoa(lsk_in->sin_addr), ntohs(lsk_in->sin_port));
+ } else {
+ DBGPRINT("[-] bind: %d %s:%d\n", fd, inet_ntoa(lsk_in->sin_addr), ntohs(lsk_in->sin_port));
+ }
+ return real_bind(fd, sk, sl);
+}
+
+int connect(int fd, const struct sockaddr *sk, socklen_t sl) {
+ static struct sockaddr_in *rsk_in;
+
+ rsk_in = (struct sockaddr_in *)sk;
+ if ((rsk_in->sin_family == AF_INET) && bind_addr_env) {
+ real_bind(fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
+ DBGPRINT("[*] connect: %d %s:%d (bound to %s:%d)\n",
+ fd, inet_ntoa(rsk_in->sin_addr), ntohs(rsk_in->sin_port),
+ inet_ntoa(local_sockaddr_in->sin_addr), ntohs(local_sockaddr_in->sin_port));
+ } else {
+ DBGPRINT("[-] connect: %d %s:%d\n", fd, inet_ntoa(rsk_in->sin_addr), ntohs(rsk_in->sin_port));
+ }
+ return real_connect(fd, sk, sl);
+}
+
+ssize_t sendto(int fd, const void *buf, size_t l, int flags, const struct sockaddr *sa, socklen_t sal) {
+ static struct sockaddr_in *rsk_in;
+
+ rsk_in = (struct sockaddr_in *)sa;
+ if ((rsk_in->sin_family == AF_INET) && bind_addr_env) {
+ real_bind(fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
+ DBGPRINT("[*] sendto: %d %s:%d (bound to %s:%d)\n",
+ fd, inet_ntoa(rsk_in->sin_addr),ntohs(rsk_in->sin_port),
+ inet_ntoa(local_sockaddr_in->sin_addr),ntohs(local_sockaddr_in->sin_port));
+ } else {
+ DBGPRINT("[-] sendto: %d %s:%d\n", fd, inet_ntoa(rsk_in->sin_addr),ntohs(rsk_in->sin_port));
+ }
+ return real_sendto(fd, buf, l, flags, sa, sal);
+}
+
+ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
+ struct sockaddr_storage _local;
+ struct sockaddr_in *local = (struct sockaddr_in*) &_local;
+ socklen_t s_local = sizeof(_local);
+
+ if ( 0 == getsockname(fd, (struct sockaddr *)&_local, &s_local)
+ && s_local == sizeof (struct sockaddr_in) && local->sin_family == AF_INET) {
+#ifdef DEBUG
+ struct sockaddr_in *haddr;
+ haddr = (struct sockaddr_in *)(msg->msg_name);
+ char *destaddr = inet_ntoa(haddr->sin_addr);
+ int destport = ntohs(haddr->sin_port);
+#endif
+ if ( local->sin_addr.s_addr == INADDR_ANY && bind_addr_env) {
+ real_bind(fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
+ DBGPRINT("[*] sendmsg: %d %s:%d (bound to %s:%d)\n",
+ fd, destaddr, destport,
+ inet_ntoa(local_sockaddr_in->sin_addr),ntohs(local_sockaddr_in->sin_port));
+ } else {
+ DBGPRINT("[-] sendmsg: %d %s:%d\n", fd, destaddr, destport);
+ }
+ }
+ else {
+ DBGPRINT("[-] sendmsg: %d\n", fd);
+ }
+ return real_sendmsg(fd, msg, flags);
+}
--- /dev/null
+#!/bin/bash
+#
+# This script will setup a network namespace with a macvlan
+# which obtains its IP address via dhclient from the LAN on which the host is
+# placed
+#
+
+set -x
+
+# It will open an xterm window in the new network namespace; if anything
+# else is required, change the statement below.
+
+export XTERM1=xterm
+
+# The script will temporarily activate ip forwarding for you. If you
+# do not wish to retain this feature, you will have to issue, at the
+# end of this session, the command
+# echo 0 > /proc/sys/net/ipv4/ip_forward
+# yourself.
+
+###############################################################################
+
+BASE_IF=vpn_tap1
+VETH_HOST=veth_host
+VETH_GUEST=veth_guest
+
+export WHEREIS=/usr/bin/whereis
+
+# First of all, check that the script is run by root:
+
+[ "root" != "$USER" ] && exec sudo -E $0 "$@"
+
+if [ $# != 2 ]; then
+ echo "Usage $0 name action"
+ echo "where name is the network namespace name,"
+ echo " and action is one of start| stop| reload."
+ exit 1
+fi
+
+# Do we have all it takes?
+
+IERROR1=0
+IERROR2=0
+IERROR3=0
+
+export IP=$($WHEREIS -b ip | /usr/bin/awk '{print $2}')
+export IPTABLES=$($WHEREIS -b iptables | /usr/bin/awk '{print $2}')
+export XTERM=$($WHEREIS -b $XTERM1 | /usr/bin/awk '{print $2}')
+
+if [ "x$IP" = "x" ] ; then
+ echo "please install the iproute2 package"
+ IERROR1=1
+fi
+
+if [ "x$IPTABLES" = "x" ] ; then
+ echo "please install the iptables package"
+ IERROR2=1
+fi
+
+if [ "x$XTERM" = "x" ] ; then
+ echo "please install the xterm package"
+ IERROR3=1
+fi
+
+if [[ $IERROR1 == 0 && $IERROR2 == 0 && $IERROR3 == 0 ]]
+then
+ :
+else
+ exit 1
+fi
+
+
+prelim() {
+
+# Perform some preliminary setup. First, clear the proposed
+# namespace name of blank characters; then create a directory
+# for logging info, and a pid file in it; lastly, enable IPv4
+# forwarding.
+
+ VAR=$1
+ export NNSNAME=${VAR//[[:space:]]}
+
+ export OUTDIR=/var/log/newns/$NNSNAME
+
+ if [ ! -d $OUTDIR ]; then
+ /bin/mkdir -p $OUTDIR
+ fi
+ export PID=$OUTDIR/pid$NNSNAME
+
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+
+}
+
+start_nns() {
+
+# Check whether a namespace with the same name already exists.
+
+ $IP netns list | /bin/grep $1 2> /dev/null
+ if [ $? == 0 ]; then
+ echo "Network namespace $1 already exists,"
+ echo "please choose another name"
+ exit 1
+ fi
+
+# Here we take care of DNS
+
+ /bin/mkdir -p /etc/netns/$1
+ echo "nameserver 8.8.8.8" > /etc/netns/$1/resolv.conf
+ echo "nameserver 8.8.4.4" >> /etc/netns/$1/resolv.conf
+
+# The following creates the new namespace, and the veth interface
+
+ $IP netns add $1
+ ip link add $VETH_HOST type veth peer name $VETH_GUEST
+ brctl addbr br0
+ brctl addif br0 $BASE_IF
+ brctl addif br0 $VETH_HOST
+ ifconfig $VETH_HOST up
+ ifconfig br0 192.168.30.3 up
+# This assigns the macvlan interface, mac$1, to the new
+# namespace, asks for an IP address via a call to dhclient,
+# brings up this and the (essential) lo interface,
+# creates a new terminal in the new namespace and
+# stores its pid for the purpose of tearing it cleanly, later.
+
+# $IP link set mac$1 netns $1
+ $IP link set $VETH_GUEST netns $1
+# $IP netns exec $1 /sbin/dhclient -pf /var/run/dhclient_$1.pid -v mac$1 1> /dev/null 2>&1
+ $IP netns exec $1 /sbin/dhclient -pf /var/run/dhclient_$1.pid -v $VETH_GUEST 1> /dev/null 2>&1
+ $IP netns exec $1 $IP link set dev lo up
+ $IP netns exec $1 su -p -c $XTERM $SUDO_USER &
+ $IP netns exec $1 echo "$!" > $PID
+ iptables -t nat -A POSTROUTING --source 192.168.30.0/24 --jump MASQUERADE
+ $IP netns exec $1 /sbin/route add default gw 192.168.30.3
+
+}
+
+stop_nns() {
+
+# Check that the namespace to be torn down really exists
+
+ $IP netns list | /bin/grep $1 2>&1 1> /dev/null
+ if [ ! $? == 0 ]; then
+ echo "Network namespace $1 does not exist,"
+ echo "please choose another name"
+ exit 1
+ fi
+
+# This kills the terminal in the separate namespace and
+# removes the file and the directory where it is stored.
+
+ /bin/kill -TERM $(cat $PID) 2> /dev/null 1> /dev/null
+ /bin/kill -TERM $(cat /var/run/dhclient_$1.pid) 2> /dev/null 1> /dev/null
+ /bin/rm $PID
+ /bin/rmdir $OUTDIR
+
+ ifconfig br0 down
+ brctl delif br0 $BASE_IF
+ brctl delif br0 $VETH_HOST
+ brctl delbr br0
+
+ $IP netns exec $1 $IP link del $VETH_GUEST
+ $IP netns del $1
+ $IP link del $VETH_HOST
+ iptables -t nat -D POSTROUTING --source 192.168.30.0/24 --jump MASQUERADE
+
+# This deletes the file and direcotory connected with the DNSes.
+
+ /bin/rm /etc/netns/$1/resolv.conf
+ /bin/rmdir /etc/netns/$1
+
+}
+
+
+case $2 in
+ start)
+ prelim "$1"
+ start_nns $NNSNAME
+ ;;
+ stop)
+ prelim "$1"
+ stop_nns $NNSNAME
+ ;;
+ reload)
+ prelim "$1"
+ stop_nns $NNSNAME
+ prelim "$1"
+ start_nns $NNSNAME
+ ;;
+ *)
+# This removes the absolute path from the command name
+
+ NAME1=$0
+ NAMESHORT=${NAME1##*/}
+
+ echo "Usage:" $NAMESHORT "name action,"
+ echo "where name is the name of the network namespace,"
+ echo "and action is one of start|stop|reload"
+ ;;
+esac