#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <sys/param.h>
-#include <sched.h>
#include "config.h"
#include "utils.h"
-
-#if ISTEST
-#define CONF_FILE "/tmp/lxc-usernet"
-#define DB_FILE "/tmp/nics"
-#else
-#define CONF_FILE LXC_USERNIC_CONF
-#define DB_FILE LXC_USERNIC_DB
-#endif
-
-#include "nl.h"
-
-#ifndef IFLA_LINKMODE
-# define IFLA_LINKMODE 17
-#endif
-
-#ifndef IFLA_LINKINFO
-# define IFLA_LINKINFO 18
-#endif
-
-#ifndef IFLA_NET_NS_PID
-# define IFLA_NET_NS_PID 19
-#endif
-
-#ifndef IFLA_INFO_KIND
-# define IFLA_INFO_KIND 1
-#endif
-
-#ifndef IFLA_VLAN_ID
-# define IFLA_VLAN_ID 1
-#endif
-
-#ifndef IFLA_INFO_DATA
-# define IFLA_INFO_DATA 2
-#endif
-
-#ifndef VETH_INFO_PEER
-# define VETH_INFO_PEER 1
-#endif
-
-#ifndef IFLA_MACVLAN_MODE
-# define IFLA_MACVLAN_MODE 1
-#endif
+#include "network.h"
void usage(char *me, bool fail)
{
*/
static int get_alloted(char *me, char *intype, char *link)
{
- FILE *fin = fopen(CONF_FILE, "r");
+ FILE *fin = fopen(LXC_USERNIC_CONF, "r");
char *line = NULL;
char user[100], type[100], br[100];
size_t len = 0;
int n = -1, ret;
if (!fin) {
- fprintf(stderr, "Failed to open %s: %s\n", CONF_FILE,
+ fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF,
strerror(errno));
return -1;
}
int ret;
struct stat sb;
-#if ISTEST
- ret = snprintf(path, MAXPATHLEN, "/tmp/lxcnettest/%s", nic);
-#else
ret = snprintf(path, MAXPATHLEN, "/sys/class/net/%s", nic);
-#endif
if (ret < 0 || ret >= MAXPATHLEN) // should never happen!
return true;
ret = stat(path, &sb);
return true;
}
-struct link_req {
- struct nlmsg nlmsg;
- struct ifinfomsg ifinfomsg;
-};
-
-#if ! ISTEST
-
-static int lxc_veth_create(const char *name1, const char *name2)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- struct rtattr *nest1, *nest2, *nest3;
- int len, err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -EINVAL;
- len = strlen(name1);
- if (len == 1 || len >= IFNAMSIZ)
- goto out;
-
- len = strlen(name2);
- if (len == 1 || len >= IFNAMSIZ)
- goto out;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- answer = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!answer)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags =
- NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- err = -EINVAL;
- nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO);
- if (!nest1)
- goto out;
-
- if (nla_put_string(nlmsg, IFLA_INFO_KIND, "veth"))
- goto out;
-
- nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA);
- if (!nest2)
- goto out;
-
- nest3 = nla_begin_nested(nlmsg, VETH_INFO_PEER);
- if (!nest3)
- goto out;
-
- nlmsg->nlmsghdr.nlmsg_len += sizeof(struct ifinfomsg);
-
- if (nla_put_string(nlmsg, IFLA_IFNAME, name2))
- goto out;
-
- nla_end_nested(nlmsg, nest3);
-
- nla_end_nested(nlmsg, nest2);
-
- nla_end_nested(nlmsg, nest1);
-
- if (nla_put_string(nlmsg, IFLA_IFNAME, name1))
- goto out;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(answer);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int lxc_netdev_move(char *ifname, pid_t pid)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL;
- struct link_req *link_req;
- int err, index;
-
- index = if_nametoindex(ifname);
- if (!ifname)
- return -EINVAL;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = index;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid))
- goto out;
-
- err = netlink_transaction(&nlh, nlmsg, nlmsg);
-out:
- netlink_close(&nlh);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int setup_private_host_hw_addr(char *veth1)
-{
- struct ifreq ifr;
- int err;
- int sockfd;
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- return -errno;
-
- snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1);
- err = ioctl(sockfd, SIOCGIFHWADDR, &ifr);
- if (err < 0) {
- close(sockfd);
- return -errno;
- }
-
- ifr.ifr_hwaddr.sa_data[0] = 0xfe;
- err = ioctl(sockfd, SIOCSIFHWADDR, &ifr);
- close(sockfd);
- if (err < 0)
- return -errno;
-
- return 0;
-}
-
-static int netdev_set_flag(const char *name, int flag)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- int index, len, err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -EINVAL;
- len = strlen(name);
- if (len == 1 || len >= IFNAMSIZ)
- goto out;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- answer = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!answer)
- goto out;
-
- err = -EINVAL;
- index = if_nametoindex(name);
- if (!index)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = index;
- link_req->ifinfomsg.ifi_change |= IFF_UP;
- link_req->ifinfomsg.ifi_flags |= flag;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(nlmsg);
- nlmsg_free(answer);
- return err;
-}
-
static int instanciate_veth(char *n1, char **n2)
{
int err;
return netdev_set_flag(n1, IFF_UP);
}
-static int lxc_bridge_attach(const char *bridge, const char *ifname)
-{
- int fd, index, err;
- struct ifreq ifr;
-
- if (strlen(ifname) >= IFNAMSIZ)
- return -EINVAL;
-
- index = if_nametoindex(ifname);
- if (!index)
- return -EINVAL;
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd < 0)
- return -errno;
-
- strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1);
- ifr.ifr_name[IFNAMSIZ-1] = '\0';
- ifr.ifr_ifindex = index;
- err = ioctl(fd, SIOCBRADDIF, &ifr);
- close(fd);
- if (err)
- err = -errno;
-
- return err;
-}
-
-static int lxc_netdev_delete_by_index(int ifindex)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- int err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- answer = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!answer)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = ifindex;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST;
- nlmsg->nlmsghdr.nlmsg_type = RTM_DELLINK;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(answer);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int lxc_netdev_delete_by_name(const char *name)
-{
- int index;
-
- index = if_nametoindex(name);
- if (!index)
- return -EINVAL;
-
- return lxc_netdev_delete_by_index(index);
-}
-#else
-static int lxc_netdev_delete_by_name(const char *name)
-{
- char path[200];
- sprintf(path, "/tmp/lxcnettest/%s", name);
- return unlink(path);
-}
-
-#endif
-
static bool create_nic(char *nic, char *br, int pid, char **cnic)
{
-#if ISTEST
- char path[200];
- sprintf(path, "/tmp/lxcnettest/%s", nic);
- int fd = open(path, O_RDWR|O_CREAT, S_IWUSR | S_IRUSR);
- if (fd < 0)
- return false;
- close(fd);
- return true;
-#else
char *veth1buf, *veth2buf;
veth1buf = alloca(IFNAMSIZ);
veth2buf = alloca(IFNAMSIZ);
}
/* pass veth2 to target netns */
- ret = lxc_netdev_move(veth2buf, pid);
+ ret = lxc_netdev_move_by_name(veth2buf, pid);
if (ret < 0) {
fprintf(stderr, "Error moving %s to netns %d\n", veth2buf, pid);
goto out_del;
out_del:
lxc_netdev_delete_by_name(veth1buf);
return false;
-#endif
}
/*
goto again;
}
-static int lxc_netdev_rename_by_index(int ifindex, const char *newname)
-{
- struct nl_handler nlh;
- struct nlmsg *nlmsg = NULL, *answer = NULL;
- struct link_req *link_req;
- int len, err;
-
- err = netlink_open(&nlh, NETLINK_ROUTE);
- if (err)
- return err;
-
- len = strlen(newname);
- if (len == 1 || len >= IFNAMSIZ)
- goto out;
-
- err = -ENOMEM;
- nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!nlmsg)
- goto out;
-
- answer = nlmsg_alloc(NLMSG_GOOD_SIZE);
- if (!answer)
- goto out;
-
- link_req = (struct link_req *)nlmsg;
- link_req->ifinfomsg.ifi_family = AF_UNSPEC;
- link_req->ifinfomsg.ifi_index = ifindex;
- nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST;
- nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK;
-
- if (nla_put_string(nlmsg, IFLA_IFNAME, newname))
- goto out;
-
- err = netlink_transaction(&nlh, nlmsg, answer);
-out:
- netlink_close(&nlh);
- nlmsg_free(answer);
- nlmsg_free(nlmsg);
- return err;
-}
-
-static int lxc_netdev_rename_by_name(const char *oldname, const char *newname)
-{
- int len, index;
-
- len = strlen(oldname);
- if (len == 1 || len >= IFNAMSIZ)
- return -EINVAL;
-
- index = if_nametoindex(oldname);
- if (!index) {
- fprintf(stderr, "Error getting ifindex for %s\n", oldname);
- return -EINVAL;
- }
-
- return lxc_netdev_rename_by_index(index, newname);
-}
-
static int rename_in_ns(int pid, char *oldname, char *newname)
{
char nspath[MAXPATHLEN];
exit(1);
}
- if (!create_db_dir(DB_FILE)) {
+ if (!create_db_dir(LXC_USERNIC_DB)) {
fprintf(stderr, "Failed to create directory for db file\n");
exit(1);
}
- if ((fd = open_and_lock(DB_FILE)) < 0) {
- fprintf(stderr, "Failed to lock %s\n", DB_FILE);
+ if ((fd = open_and_lock(LXC_USERNIC_DB)) < 0) {
+ fprintf(stderr, "Failed to lock %s\n", LXC_USERNIC_DB);
exit(1);
}
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-conffile="/tmp/lxc-usernet"
-dbfile="/tmp/nics"
-sysfsdir=/tmp/lxcnettest
+cleanup() {
+ sed -i '/usernic-user/d' /var/run/lxc/nics /etc/lxc/lxc-usernet
+ ifconfig usernic-br0 down
+ ifconfig usernic-br1 down
+ sudo brctl delbr usernic-br0
+ sudo brctl delbr usernic-br1
+ sudo deluser usernic-user
+ su -l usernic-user -c "lxc-stop -P /tmp/usernic-test/lxcbase -n b1"
+ rm -rf /tmp/usernic-test
+ exit $1
+}
-rm -f $conffile $dbfile
+# create a test user
+deluser usernic-user || true
+useradd usernic-user
+sudo mkdir -p /home/usernic-user
+sudo chown usernic-user /home/usernic-user
+usermod -v 910000-919999 -w 910000-919999 usernic-user
+mkdir -p /tmp/usernic-test/lxcbase
+chown usernic-user /tmp/usernic-test/lxcbase
+uid=$(id -u usernic-user)
+cat > /home/usernic-user/.bashrc << EOF
+export XDG_RUNTIME_DIR=/run/user/$uid
+EOF
+XDG_RUNTIME_DIR=/run/user/$uid
+export XDG_RUNTIME_DIR=/run/user/$uid
+mkdir -p /run/user/$uid
+chown usernic-user /run/user/$uid
+env
+echo XXX[
+su -l usernic-user -c "env"
+sleep 20
+
+#
+cat > /tmp/lxc-usernic.conf << EOF
+lxc.network.type = empty
+lxc.id_map = u 0 911000 10000
+lxc.id_map = g 0 911000 10000
+EOF
-rm -rf $sysfsdir
-mkdir -p $sysfsdir
+# Create two test bridges
-# there is no conffile, so we have no permissions
-lxc-usernic-test 1111 veth lxcbr0 > /dev/null 2>&1
+brctl addbr usernic-br0
+brctl addbr usernic-br1
+ifconfig usernic-br0 0.0.0.0 up
+ifconfig usernic-br1 0.0.0.0 up
+
+# Create three containers
+su -l usernic-user -c "lxc-create -P /tmp/usernic-test/lxcbase -t busybox -n b1 -f /tmp/lxc-usernic.conf"
+su -l usernic-user -c "lxc-start -P /tmp/usernic-test/lxcbase -n b1 -d"
+p1=`lxc-info -P /tmp/usernic-test/lxcbase -n b1 -p | awk -F: '{ print $2 }'`
+
+# Assign one veth, should fail as no allowed entries yet
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br0 xx1"
if [ $? -eq 0 ]; then
- echo "Fail: empty conffile should not allow me a nic"
- exit 1
+ echo "FAIL: able to create nic with no entries"
+ cleanup 1
fi
-cat > $conffile << EOF
-$(id -un) veth lxcbr0 1
-EOF
+# Give him a quota of two
+echo "lxc-usernet veth usernic-br0 2" >> /etc/lxc/lxc-usernet
+
+# Assign one veth to second bridge, should fail
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br1 xx1"
+if [ $? -eq 0 ]; then
+ echo "FAIL: able to create nic with no entries"
+ cleanup 1
+fi
-# Should be allowed one but not two
-lxc-usernic-test 1111 veth lxcbr0 > /dev/null 2>&1
+# Assign two veths, should succeed
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br0 xx2"
+if [ $? -ne 0 ]; then
+ echo "FAIL: unable to create first nic"
+ cleanup 1
+fi
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br0 xx3"
if [ $? -ne 0 ]; then
- echo "Failed to get one allowed nic"
- exit 1
+ echo "FAIL: unable to create second nic"
+ cleanup 1
fi
-lxc-usernic-test 1111 veth lxcbr0 > /dev/null 2>&1
+# Assign one more veth, should fail.
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br0 xx4"
if [ $? -eq 0 ]; then
- echo "Fail: was able to get a second nic"
- exit 1
+ echo "FAIL: able to create third nic"
+ cleanup 1
fi
-# now remove the 'existing nic' and make sure we're allowed to create
-# a new one
-lxc-usernic-test 1111 veth lxcbr0 > /dev/null 2>&1
-rm -rf $sysfsdir
-mkdir -p $sysfsdir
+# Shut down and restart the container, should be able to assign more nics
+su -l usernic-user -c "lxc-stop -P /tmp/usernic-test/lxcbase -n b1"
+su -l usernic-user -c "lxc-start -P /tmp/usernic-test/lxcbase -n b1 -d"
+p1=`lxc-info -P /tmp/usernic-test/lxcbase -n b1 -p | awk -F: '{ print $2 }'`
+su -l usernic-user -c "lxc-user-nic $p1 veth usernic-br0 xx5"
if [ $? -ne 0 ]; then
- echo "Fail: was unable to get a replacement nic"
- exit 1
+ echo "FAIL: unable to create nic after destroying the old"
+ cleanup 1
+fi
+
+su -l usernic-user -c "lxc-stop -P /tmp/usernic-test/lxcbase -n b1"
+
+# Create a root-owned ns
+lxc-create -t busybox -n usernic-c1
+lxc-start -n usernic-c1 -d
+p2=`lxc-info -n usernic-c1 -p | awk -F: '{ print $2}'`
+
+# assign veth to it - should fail
+su -l usernic-user -c "lxc-user-nic $p2 veth usernic-br0 xx6"
+ret=$?
+lxc-stop -n usernic-c1
+lxc-destroy -n usernic-c1
+if [ $ret -eq 0 ]; then
+ echo "FAIL: able to attach nic to root-owned container"
+ cleanup 1
fi
echo "All tests passed"