blob: b2d4f4d667010121a66f9b2de4a6ca06494e9eee [file] [log] [blame]
/*
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
* Licensed under the GPL.
*/
#include <stdio.h>
#include <unistd.h>
#include <stddef.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <net/if.h>
#include "user.h"
#include "kern_util.h"
#include "net_user.h"
#include "etap.h"
#include "helper.h"
#include "os.h"
#define MAX_PACKET ETH_MAX_PACKET
void etap_user_init(void *data, void *dev)
{
struct ethertap_data *pri = data;
pri->dev = dev;
}
struct addr_change {
enum { ADD_ADDR, DEL_ADDR } what;
unsigned char addr[4];
unsigned char netmask[4];
};
static void etap_change(int op, unsigned char *addr, unsigned char *netmask,
int fd)
{
struct addr_change change;
void *output;
change.what = op;
memcpy(change.addr, addr, sizeof(change.addr));
memcpy(change.netmask, netmask, sizeof(change.netmask));
if(write(fd, &change, sizeof(change)) != sizeof(change))
printk("etap_change - request failed, errno = %d\n",
errno);
output = um_kmalloc(page_size());
if(output == NULL)
printk("etap_change : Failed to allocate output buffer\n");
read_output(fd, output, page_size());
if(output != NULL){
printk("%s", output);
kfree(output);
}
}
static void etap_open_addr(unsigned char *addr, unsigned char *netmask,
void *arg)
{
etap_change(ADD_ADDR, addr, netmask, *((int *) arg));
}
static void etap_close_addr(unsigned char *addr, unsigned char *netmask,
void *arg)
{
etap_change(DEL_ADDR, addr, netmask, *((int *) arg));
}
struct etap_pre_exec_data {
int control_remote;
int control_me;
int data_me;
};
static void etap_pre_exec(void *arg)
{
struct etap_pre_exec_data *data = arg;
dup2(data->control_remote, 1);
close(data->data_me);
close(data->control_me);
}
static int etap_tramp(char *dev, char *gate, int control_me,
int control_remote, int data_me, int data_remote)
{
struct etap_pre_exec_data pe_data;
int pid, status, err;
char version_buf[sizeof("nnnnn\0")];
char data_fd_buf[sizeof("nnnnnn\0")];
char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
char *setup_args[] = { "uml_net", version_buf, "ethertap", dev,
data_fd_buf, gate_buf, NULL };
char *nosetup_args[] = { "uml_net", version_buf, "ethertap",
dev, data_fd_buf, NULL };
char **args, c;
sprintf(data_fd_buf, "%d", data_remote);
sprintf(version_buf, "%d", UML_NET_VERSION);
if(gate != NULL){
strcpy(gate_buf, gate);
args = setup_args;
}
else args = nosetup_args;
err = 0;
pe_data.control_remote = control_remote;
pe_data.control_me = control_me;
pe_data.data_me = data_me;
pid = run_helper(etap_pre_exec, &pe_data, args, NULL);
if(pid < 0) err = errno;
close(data_remote);
close(control_remote);
if(read(control_me, &c, sizeof(c)) != sizeof(c)){
printk("etap_tramp : read of status failed, errno = %d\n",
errno);
return(EINVAL);
}
if(c != 1){
printk("etap_tramp : uml_net failed\n");
err = EINVAL;
if(waitpid(pid, &status, 0) < 0) err = errno;
else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)){
printk("uml_net didn't exit with status 1\n");
}
}
return(err);
}
static int etap_open(void *data)
{
struct ethertap_data *pri = data;
char *output;
int data_fds[2], control_fds[2], err, output_len;
err = tap_open_common(pri->dev, pri->gate_addr);
if(err) return(err);
err = os_pipe(data_fds, 0);
if(err){
printk("data os_pipe failed - errno = %d\n", -err);
return(err);
}
err = os_pipe(control_fds, 1);
if(err){
printk("control os_pipe failed - errno = %d\n", -err);
return(err);
}
err = etap_tramp(pri->dev_name, pri->gate_addr, control_fds[0],
control_fds[1], data_fds[0], data_fds[1]);
output_len = page_size();
output = um_kmalloc(output_len);
read_output(control_fds[0], output, output_len);
if(output == NULL)
printk("etap_open : failed to allocate output buffer\n");
else {
printk("%s", output);
kfree(output);
}
if(err != 0){
printk("etap_tramp failed - errno = %d\n", err);
return(-err);
}
pri->data_fd = data_fds[0];
pri->control_fd = control_fds[0];
iter_addresses(pri->dev, etap_open_addr, &pri->control_fd);
return(data_fds[0]);
}
static void etap_close(int fd, void *data)
{
struct ethertap_data *pri = data;
iter_addresses(pri->dev, etap_close_addr, &pri->control_fd);
close(fd);
shutdown(pri->data_fd, SHUT_RDWR);
close(pri->data_fd);
pri->data_fd = -1;
close(pri->control_fd);
pri->control_fd = -1;
}
static int etap_set_mtu(int mtu, void *data)
{
return(mtu);
}
static void etap_add_addr(unsigned char *addr, unsigned char *netmask,
void *data)
{
struct ethertap_data *pri = data;
tap_check_ips(pri->gate_addr, addr);
if(pri->control_fd == -1) return;
etap_open_addr(addr, netmask, &pri->control_fd);
}
static void etap_del_addr(unsigned char *addr, unsigned char *netmask,
void *data)
{
struct ethertap_data *pri = data;
if(pri->control_fd == -1) return;
etap_close_addr(addr, netmask, &pri->control_fd);
}
struct net_user_info ethertap_user_info = {
init: etap_user_init,
open: etap_open,
close: etap_close,
remove: NULL,
set_mtu: etap_set_mtu,
add_address: etap_add_addr,
delete_address: etap_del_addr,
max_packet: MAX_PACKET - ETH_HEADER_ETHERTAP
};
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/