2007-07-25 13:23:45 +00:00
|
|
|
/*
|
2008-08-07 14:56:54 +00:00
|
|
|
* Copyright (C) 2008 Tobias Brunner
|
2007-07-25 13:23:45 +00:00
|
|
|
* Copyright (C) 2007 Martin Willi
|
|
|
|
* Hochschule fuer Technik Rapperswil
|
|
|
|
* Copyright (C) 2002 Jeff Dike
|
|
|
|
*
|
2007-07-26 13:21:06 +00:00
|
|
|
* Based on the "tunctl" utility from Jeff Dike.
|
2007-07-25 13:23:45 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2007-07-26 07:39:49 +00:00
|
|
|
#include <stdio.h>
|
2007-07-25 13:23:45 +00:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
|
|
|
|
#include <debug.h>
|
2008-07-07 14:56:04 +00:00
|
|
|
#include <utils/linked_list.h>
|
2007-07-25 13:23:45 +00:00
|
|
|
|
|
|
|
#include "iface.h"
|
|
|
|
|
|
|
|
typedef struct private_iface_t private_iface_t;
|
|
|
|
|
|
|
|
struct private_iface_t {
|
|
|
|
/** public interface */
|
|
|
|
iface_t public;
|
|
|
|
/** device name in guest (eth0) */
|
2007-07-26 07:39:49 +00:00
|
|
|
char *guestif;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** device name at host (tap0) */
|
2007-07-26 07:39:49 +00:00
|
|
|
char *hostif;
|
2007-08-08 12:35:24 +00:00
|
|
|
/** bridge this interface is attached to */
|
|
|
|
bridge_t *bridge;
|
2008-07-04 14:21:41 +00:00
|
|
|
/** guest this interface is attached to */
|
|
|
|
guest_t *guest;
|
2007-07-25 13:23:45 +00:00
|
|
|
/** mconsole for guest */
|
|
|
|
mconsole_t *mconsole;
|
|
|
|
};
|
|
|
|
|
2008-04-28 16:43:30 +00:00
|
|
|
/**
|
2008-07-07 14:56:04 +00:00
|
|
|
* bring an interface up or down (host side)
|
2008-04-28 16:43:30 +00:00
|
|
|
*/
|
|
|
|
bool iface_control(char *name, bool up)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
bool good = FALSE;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(struct ifreq));
|
|
|
|
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
|
|
|
|
|
|
|
|
s = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0)
|
|
|
|
{
|
|
|
|
if (up)
|
|
|
|
{
|
|
|
|
ifr.ifr_flags |= IFF_UP;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ifr.ifr_flags &= ~IFF_UP;
|
|
|
|
}
|
|
|
|
if (ioctl(s, SIOCSIFFLAGS, &ifr) == 0)
|
|
|
|
{
|
|
|
|
good = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(s);
|
|
|
|
return good;
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
2007-07-26 07:39:49 +00:00
|
|
|
* Implementation of iface_t.get_guestif.
|
2007-07-25 13:23:45 +00:00
|
|
|
*/
|
2007-07-26 07:39:49 +00:00
|
|
|
static char* get_guestif(private_iface_t *this)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2007-07-26 07:39:49 +00:00
|
|
|
return this->guestif;
|
2007-07-25 13:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-07-26 07:39:49 +00:00
|
|
|
* Implementation of iface_t.get_hostif.
|
2007-07-25 13:23:45 +00:00
|
|
|
*/
|
2007-07-26 07:39:49 +00:00
|
|
|
static char* get_hostif(private_iface_t *this)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2007-07-26 07:39:49 +00:00
|
|
|
return this->hostif;
|
2007-07-25 13:23:45 +00:00
|
|
|
}
|
|
|
|
|
2008-07-07 14:56:04 +00:00
|
|
|
/**
|
|
|
|
* Implementation of iface_t.add_address
|
|
|
|
*/
|
|
|
|
static bool add_address(private_iface_t *this, host_t *addr)
|
|
|
|
{
|
2008-07-30 13:15:18 +00:00
|
|
|
return (this->guest->exec(this->guest, NULL, NULL, "ip addr add %H dev %s",
|
|
|
|
addr, this->guestif) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* compile a list of the addresses of an interface
|
|
|
|
*/
|
|
|
|
static void compile_address_list(linked_list_t *list, char *address)
|
|
|
|
{
|
|
|
|
host_t *host = host_create_from_string(address, 0);
|
|
|
|
if (host)
|
2008-07-07 14:56:04 +00:00
|
|
|
{
|
2008-07-30 13:15:18 +00:00
|
|
|
list->insert_last(list, host);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
2008-07-30 13:15:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* delete the list of addresses
|
|
|
|
*/
|
|
|
|
static void destroy_address_list(linked_list_t *list)
|
|
|
|
{
|
|
|
|
list->destroy_offset(list, offsetof(host_t, destroy));
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of iface_t.create_address_enumerator
|
|
|
|
*/
|
|
|
|
static enumerator_t* create_address_enumerator(private_iface_t *this)
|
|
|
|
{
|
2008-07-30 13:15:18 +00:00
|
|
|
linked_list_t *addresses = linked_list_create();
|
|
|
|
this->guest->exec_str(this->guest, (void(*)(void*,char*))compile_address_list,
|
|
|
|
TRUE, addresses,
|
|
|
|
"ip addr list dev %s scope global | "
|
|
|
|
"grep '^ \\+\\(inet6\\? \\)' | "
|
|
|
|
"awk -F '( +|/)' '{ print $3 }'", this->guestif);
|
|
|
|
return enumerator_create_cleaner(addresses->create_enumerator(addresses),
|
|
|
|
(void(*)(void*))destroy_address_list, addresses);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of iface_t.delete_address
|
|
|
|
*/
|
|
|
|
static bool delete_address(private_iface_t *this, host_t *addr)
|
|
|
|
{
|
2008-07-30 13:15:18 +00:00
|
|
|
return (this->guest->exec(this->guest, NULL, NULL,
|
|
|
|
"ip addr del %H dev %s", addr, this->guestif) == 0);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
|
2007-08-08 12:35:24 +00:00
|
|
|
/**
|
|
|
|
* Implementation of iface_t.set_bridge.
|
|
|
|
*/
|
|
|
|
static void set_bridge(private_iface_t *this, bridge_t *bridge)
|
|
|
|
{
|
2008-07-07 14:56:04 +00:00
|
|
|
if (this->bridge == NULL && bridge)
|
|
|
|
{
|
2008-07-08 14:58:20 +00:00
|
|
|
this->guest->exec(this->guest, NULL, NULL,
|
|
|
|
"ip link set %s up", this->guestif);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
|
|
|
else if (this->bridge && bridge == NULL)
|
|
|
|
{
|
2008-07-08 14:58:20 +00:00
|
|
|
this->guest->exec(this->guest, NULL, NULL,
|
|
|
|
"ip link set %s down", this->guestif);
|
2008-07-07 14:56:04 +00:00
|
|
|
}
|
2007-08-08 12:35:24 +00:00
|
|
|
this->bridge = bridge;
|
|
|
|
}
|
|
|
|
|
2008-07-04 14:21:41 +00:00
|
|
|
/**
|
|
|
|
* Implementation of iface_t.get_bridge
|
|
|
|
*/
|
|
|
|
static bridge_t *get_bridge(private_iface_t *this)
|
|
|
|
{
|
|
|
|
return this->bridge;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of iface_t.get_guest
|
|
|
|
*/
|
|
|
|
static guest_t* get_guest(private_iface_t *this)
|
|
|
|
{
|
|
|
|
return this->guest;
|
|
|
|
}
|
|
|
|
|
2007-07-25 13:23:45 +00:00
|
|
|
/**
|
|
|
|
* destroy the tap device
|
|
|
|
*/
|
|
|
|
static bool destroy_tap(private_iface_t *this)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
2007-07-26 07:39:49 +00:00
|
|
|
int tap;
|
2008-04-28 16:43:30 +00:00
|
|
|
|
|
|
|
if (!iface_control(this->hostif, FALSE))
|
|
|
|
{
|
|
|
|
DBG1("bringing iface down failed: %m");
|
|
|
|
}
|
2007-07-25 13:23:45 +00:00
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
2007-07-26 07:39:49 +00:00
|
|
|
strncpy(ifr.ifr_name, this->hostif, sizeof(ifr.ifr_name) - 1);
|
2007-07-25 13:23:45 +00:00
|
|
|
|
2007-07-26 07:39:49 +00:00
|
|
|
tap = open(TAP_DEVICE, O_RDWR);
|
|
|
|
if (tap < 0)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2007-07-26 07:39:49 +00:00
|
|
|
DBG1("unable to open tap device %s: %m", TAP_DEVICE);
|
2007-07-25 13:23:45 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2007-07-26 07:39:49 +00:00
|
|
|
if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
|
|
|
|
ioctl(tap, TUNSETPERSIST, 0) < 0)
|
|
|
|
{
|
|
|
|
DBG1("removing %s failed: %m", this->hostif);
|
|
|
|
close(tap);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
close(tap);
|
2007-07-25 13:23:45 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create the tap device
|
|
|
|
*/
|
2008-07-04 14:21:41 +00:00
|
|
|
static char* create_tap(private_iface_t *this)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
2007-07-26 07:39:49 +00:00
|
|
|
int tap;
|
2007-07-25 13:23:45 +00:00
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
2008-07-04 14:21:41 +00:00
|
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s-%s",
|
|
|
|
this->guest->get_name(this->guest), this->guestif);
|
2007-07-26 07:39:49 +00:00
|
|
|
|
|
|
|
tap = open(TAP_DEVICE, O_RDWR);
|
|
|
|
if (tap < 0)
|
|
|
|
{
|
|
|
|
DBG1("unable to open tap device %s: %m", TAP_DEVICE);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (ioctl(tap, TUNSETIFF, &ifr) < 0 ||
|
|
|
|
ioctl(tap, TUNSETPERSIST, 1) < 0 ||
|
|
|
|
ioctl(tap, TUNSETOWNER, 0))
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
DBG1("creating new tap device failed: %m");
|
2007-07-26 07:39:49 +00:00
|
|
|
close(tap);
|
2007-07-25 13:23:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-26 07:39:49 +00:00
|
|
|
close(tap);
|
2007-07-25 13:23:45 +00:00
|
|
|
return strdup(ifr.ifr_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of iface_t.destroy.
|
|
|
|
*/
|
|
|
|
static void destroy(private_iface_t *this)
|
|
|
|
{
|
2007-08-08 12:35:24 +00:00
|
|
|
if (this->bridge)
|
|
|
|
{
|
|
|
|
this->bridge->disconnect_iface(this->bridge, &this->public);
|
|
|
|
}
|
2008-07-07 14:56:04 +00:00
|
|
|
/* TODO: iface mgmt is not blocking yet, so wait some ticks */
|
|
|
|
usleep(50000);
|
2007-07-26 07:39:49 +00:00
|
|
|
this->mconsole->del_iface(this->mconsole, this->guestif);
|
2007-07-25 13:23:45 +00:00
|
|
|
destroy_tap(this);
|
2007-07-26 07:39:49 +00:00
|
|
|
free(this->guestif);
|
|
|
|
free(this->hostif);
|
2007-07-25 13:23:45 +00:00
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* create the iface instance
|
|
|
|
*/
|
2008-07-04 14:21:41 +00:00
|
|
|
iface_t *iface_create(char *name, guest_t *guest, mconsole_t *mconsole)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
private_iface_t *this = malloc_thing(private_iface_t);
|
|
|
|
|
2007-07-26 07:39:49 +00:00
|
|
|
this->public.get_hostif = (char*(*)(iface_t*))get_hostif;
|
|
|
|
this->public.get_guestif = (char*(*)(iface_t*))get_guestif;
|
2008-07-07 14:56:04 +00:00
|
|
|
this->public.add_address = (bool(*)(iface_t*, host_t *addr))add_address;
|
|
|
|
this->public.create_address_enumerator = (enumerator_t*(*)(iface_t*))create_address_enumerator;
|
|
|
|
this->public.delete_address = (bool(*)(iface_t*, host_t *addr))delete_address;
|
2007-08-08 12:35:24 +00:00
|
|
|
this->public.set_bridge = (void(*)(iface_t*, bridge_t*))set_bridge;
|
2008-07-04 14:21:41 +00:00
|
|
|
this->public.get_bridge = (bridge_t*(*)(iface_t*))get_bridge;
|
|
|
|
this->public.get_guest = (guest_t*(*)(iface_t*))get_guest;
|
2007-07-25 13:23:45 +00:00
|
|
|
this->public.destroy = (void*)destroy;
|
|
|
|
|
|
|
|
this->mconsole = mconsole;
|
2008-07-04 14:21:41 +00:00
|
|
|
this->guestif = strdup(name);
|
|
|
|
this->guest = guest;
|
|
|
|
this->hostif = create_tap(this);
|
2007-08-08 12:35:24 +00:00
|
|
|
this->bridge = NULL;
|
2007-07-26 07:39:49 +00:00
|
|
|
if (this->hostif == NULL)
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
|
|
|
destroy_tap(this);
|
2007-07-26 07:39:49 +00:00
|
|
|
free(this->guestif);
|
2007-07-25 13:23:45 +00:00
|
|
|
free(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-26 07:39:49 +00:00
|
|
|
if (!this->mconsole->add_iface(this->mconsole, this->guestif, this->hostif))
|
2007-07-25 13:23:45 +00:00
|
|
|
{
|
2007-07-26 07:39:49 +00:00
|
|
|
DBG1("creating interface '%s' in guest failed", this->guestif);
|
2007-07-25 13:23:45 +00:00
|
|
|
destroy_tap(this);
|
2007-07-26 07:39:49 +00:00
|
|
|
free(this->guestif);
|
|
|
|
free(this->hostif);
|
2007-07-25 13:23:45 +00:00
|
|
|
free(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-07-04 06:58:04 +00:00
|
|
|
if (!iface_control(this->hostif, TRUE))
|
|
|
|
{
|
|
|
|
DBG1("bringing iface '%s' up failed: %m", this->hostif);
|
|
|
|
}
|
2007-07-25 13:23:45 +00:00
|
|
|
return &this->public;
|
|
|
|
}
|
|
|
|
|