Commit 5d61c168 authored by Jeff Dike's avatar Jeff Dike

Merged the network fixes from the 2.4 pool.

parent 5be2bc3c
...@@ -5,30 +5,177 @@ menu "Network Devices" ...@@ -5,30 +5,177 @@ menu "Network Devices"
# UML virtual driver # UML virtual driver
config UML_NET config UML_NET
bool "Virtual network device" bool "Virtual network device"
help
While the User-Mode port cannot directly talk to any physical
hardware devices, this choice and the following transport options
provide one or more virtual network devices through which the UML
kernels can talk to each other, the host, and with the host's help,
machines on the outside world.
For more information, including explanations of the networking and
sample configurations, see
<http://user-mode-linux.sourceforge.net/networking.html>.
If you'd like to be able to enable networking in the User-Mode
linux environment, say Y; otherwise say N. Note that you must
enable at least one of the following transport options to actually
make use of UML networking.
config UML_NET_ETHERTAP config UML_NET_ETHERTAP
bool "Ethertap transport" bool "Ethertap transport"
depends on UML_NET depends on UML_NET
help
The Ethertap User-Mode Linux network transport allows a single
running UML to exchange packets with its host over one of the
host's Ethertap devices, such as /dev/tap0. Additional running
UMLs can use additional Ethertap devices, one per running UML.
While the UML believes it's on a (multi-device, broadcast) virtual
Ethernet network, it's in fact communicating over a point-to-point
link with the host.
To use this, your host kernel must have support for Ethertap
devices. Also, if your host kernel is 2.4.x, it must have
CONFIG_NETLINK_DEV configured as Y or M.
For more information, see
<http://user-mode-linux.sourceforge.net/networking.html> That site
has examples of the UML command line to use to enable Ethertap
networking.
If you'd like to set up an IP network with the host and/or the
outside world, say Y to this, the Daemon Transport and/or the
Slip Transport. You'll need at least one of them, but may choose
more than one without conflict. If you don't need UML networking,
say N.
config UML_NET_TUNTAP config UML_NET_TUNTAP
bool "TUN/TAP transport" bool "TUN/TAP transport"
depends on UML_NET depends on UML_NET
help
The UML TUN/TAP network transport allows a UML instance to exchange
packets with the host over a TUN/TAP device. This option will only
work with a 2.4 host, unless you've applied the TUN/TAP patch to
your 2.2 host kernel.
To use this transport, your host kernel must have support for TUN/TAP
devices, either built-in or as a module.
config UML_NET_SLIP config UML_NET_SLIP
bool "SLIP transport" bool "SLIP transport"
depends on UML_NET depends on UML_NET
help
The slip User-Mode Linux network transport allows a running UML to
network with its host over a point-to-point link. Unlike Ethertap,
which can carry any Ethernet frame (and hence even non-IP packets),
the slip transport can only carry IP packets.
To use this, your host must support slip devices.
For more information, see
<http://user-mode-linux.sourceforge.net/networking.html>. That site
has examples of the UML command line to use to enable slip
networking, and details of a few quirks with it.
The Ethertap Transport is preferred over slip because of its
limitations. If you prefer slip, however, say Y here. Otherwise
choose the Multicast transport (to network multiple UMLs on
multiple hosts), Ethertap (to network with the host and the
outside world), and/or the Daemon transport (to network multiple
UMLs on a single host). You may choose more than one without
conflict. If you don't need UML networking, say N.
config UML_NET_DAEMON config UML_NET_DAEMON
bool "Daemon transport" bool "Daemon transport"
depends on UML_NET depends on UML_NET
help
This User-Mode Linux network transport allows one or more running
UMLs on a single host to communicate with each other, but not to
the host.
To use this form of networking, you'll need to run the UML
networking daemon on the host.
For more information, see
<http://user-mode-linux.sourceforge.net/networking.html> That site
has examples of the UML command line to use to enable Daemon
networking.
If you'd like to set up a network with other UMLs on a single host,
say Y. If you need a network between UMLs on multiple physical
hosts, choose the Multicast Transport. To set up a network with
the host and/or other IP machines, say Y to the Ethertap or Slip
transports. You'll need at least one of them, but may choose
more than one without conflict. If you don't need UML networking,
say N.
config UML_NET_MCAST config UML_NET_MCAST
bool "Multicast transport" bool "Multicast transport"
depends on UML_NET depends on UML_NET
help
This Multicast User-Mode Linux network transport allows multiple
UMLs (even ones running on different host machines!) to talk to
each other over a virtual ethernet network. However, it requires
at least one UML with one of the other transports to act as a
bridge if any of them need to be able to talk to their hosts or any
other IP machines.
To use this, your host kernel(s) must support IP Multicasting.
For more information, see
<http://user-mode-linux.sourceforge.net/networking.html> That site
has examples of the UML command line to use to enable Multicast
networking, and notes about the security of this approach.
If you need UMLs on multiple physical hosts to communicate as if
they shared an Ethernet network, say Y. If you need to communicate
with other IP machines, make sure you select one of the other
transports (possibly in addition to Multicast; they're not
exclusive). If you don't need to network UMLs say N to each of
the transports.
config UML_NET_PCAP config UML_NET_PCAP
bool "pcap transport" bool "pcap transport"
depends on UML_NET depends on UML_NET
help
The pcap transport makes a pcap packet stream on the host look
like an ethernet device inside UML. This is useful for making
UML act as a network monitor for the host. You must have libcap
installed in order to build the pcap transport into UML.
For more information, see
<http://user-mode-linux.sourceforge.net/networking.html> That site
has examples of the UML command line to use to enable this option.
If you intend to use UML as a network monitor for the host, say
Y here. Otherwise, say N.
config UML_NET_SLIRP
bool "SLiRP transport"
depends on UML_NET
help
The SLiRP User-Mode Linux network transport allows a running UML
to network by invoking a program that can handle SLIP encapsulated
packets. This is commonly (but not limited to) the application
known as SLiRP, a program that can re-socket IP packets back onto
the host on which it is run. Only IP packets are supported,
unlike other network transports that can handle all Ethernet
frames. In general, slirp allows the UML the same IP connectivity
to the outside world that the host user is permitted, and unlike
other transports, SLiRP works without the need of root level
privleges, setuid binaries, or SLIP devices on the host. This
also means not every type of connection is possible, but most
situations can be accomodated with carefully crafted slirp
commands that can be passed along as part of the network device's
setup string. The effect of this transport on the UML is similar
that of a host behind a firewall that masquerades all network
connections passing through it (but is less secure).
To use this you should first have slirp compiled somewhere
accessible on the host, and have read its documentation. If you
don't need UML networking, say N.
Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp"
# Below are hardware-independent drivers mirrored from # Below are hardware-independent drivers mirrored from
# drivers/net/Config.in. It would be nice if Linux # drivers/net/Config.in. It would be nice if Linux
......
...@@ -39,7 +39,6 @@ static int uml_net_rx(struct net_device *dev) ...@@ -39,7 +39,6 @@ static int uml_net_rx(struct net_device *dev)
/* If we can't allocate memory, try again next round. */ /* If we can't allocate memory, try again next round. */
if ((skb = dev_alloc_skb(dev->mtu)) == NULL) { if ((skb = dev_alloc_skb(dev->mtu)) == NULL) {
lp->stats.rx_dropped++; lp->stats.rx_dropped++;
reactivate_fd(lp->fd, UM_ETH_IRQ);
return 0; return 0;
} }
...@@ -48,7 +47,6 @@ static int uml_net_rx(struct net_device *dev) ...@@ -48,7 +47,6 @@ static int uml_net_rx(struct net_device *dev)
skb->mac.raw = skb->data; skb->mac.raw = skb->data;
pkt_len = (*lp->read)(lp->fd, &skb, lp); pkt_len = (*lp->read)(lp->fd, &skb, lp);
reactivate_fd(lp->fd, UM_ETH_IRQ);
if (pkt_len > 0) { if (pkt_len > 0) {
skb_trim(skb, pkt_len); skb_trim(skb, pkt_len);
skb->protocol = (*lp->protocol)(skb); skb->protocol = (*lp->protocol)(skb);
...@@ -69,18 +67,22 @@ void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -69,18 +67,22 @@ void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct uml_net_private *lp = dev->priv; struct uml_net_private *lp = dev->priv;
int err; int err;
if (netif_running(dev)) { if(!netif_running(dev))
spin_lock(&lp->lock); return;
while((err = uml_net_rx(dev)) > 0) ;
if(err < 0) { spin_lock(&lp->lock);
printk(KERN_ERR while((err = uml_net_rx(dev)) > 0) ;
"Device '%s' read returned %d, shutting it " if(err < 0) {
"down\n", dev->name, err); printk(KERN_ERR
dev->flags &= ~IFF_UP; "Device '%s' read returned %d, shutting it down\n",
dev_close(dev); dev->name, err);
} dev_close(dev);
spin_unlock(&lp->lock); goto out;
} }
reactivate_fd(lp->fd, UM_ETH_IRQ);
out:
spin_unlock(&lp->lock);
} }
static int uml_net_open(struct net_device *dev) static int uml_net_open(struct net_device *dev)
...@@ -250,6 +252,37 @@ void uml_net_user_timer_expire(unsigned long _conn) ...@@ -250,6 +252,37 @@ void uml_net_user_timer_expire(unsigned long _conn)
#endif #endif
} }
/*
* default do nothing hard header packet routines for struct net_device init.
* real ethernet transports will overwrite with real routines.
*/
static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr, unsigned len)
{
return(0); /* no change */
}
static int uml_net_rebuild_header(struct sk_buff *skb)
{
return(0); /* ignore */
}
static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh)
{
return(-1); /* fail */
}
static void uml_net_header_cache_update(struct hh_cache *hh,
struct net_device *dev, unsigned char * haddr)
{
/* ignore */
}
static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr)
{
return(0); /* nothing */
}
static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED; static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED;
static struct list_head devices = LIST_HEAD_INIT(devices); static struct list_head devices = LIST_HEAD_INIT(devices);
...@@ -261,21 +294,25 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -261,21 +294,25 @@ static int eth_configure(int n, void *init, char *mac,
struct uml_net_private *lp; struct uml_net_private *lp;
int save, err, size; int save, err, size;
size = transport->private_size + sizeof(struct uml_net_private) +
sizeof(((struct uml_net_private *) 0)->user);
device = kmalloc(sizeof(*device), GFP_KERNEL); device = kmalloc(sizeof(*device), GFP_KERNEL);
if(device == NULL){ if(device == NULL){
printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); printk(KERN_ERR "eth_configure failed to allocate uml_net\n");
return(1); return(1);
} }
*device = ((struct uml_net) { .list = LIST_HEAD_INIT(device->list),
.dev = NULL,
.index = n,
.mac = { [ 0 ... 5 ] = 0 },
.have_mac = 0 });
spin_lock(&devices_lock); spin_lock(&devices_lock);
list_add(&device->list, &devices); list_add(&device->list, &devices);
spin_unlock(&devices_lock); spin_unlock(&devices_lock);
device->index = n;
size = transport->private_size + sizeof(struct uml_net_private) +
sizeof(((struct uml_net_private *) 0)->user);
if(setup_etheraddr(mac, device->mac)) if(setup_etheraddr(mac, device->mac))
device->have_mac = 1; device->have_mac = 1;
...@@ -290,10 +327,18 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -290,10 +327,18 @@ static int eth_configure(int n, void *init, char *mac,
printk(KERN_ERR "eth_configure: failed to allocate device\n"); printk(KERN_ERR "eth_configure: failed to allocate device\n");
return(1); return(1);
} }
memset(dev, 0, sizeof(*dev) + size);
snprintf(dev->name, sizeof(dev->name), "eth%d", n); snprintf(dev->name, sizeof(dev->name), "eth%d", n);
dev->priv = (void *) &dev[1]; dev->priv = (void *) &dev[1];
device->dev = dev; device->dev = dev;
dev->hard_header = uml_net_hard_header;
dev->rebuild_header = uml_net_rebuild_header;
dev->hard_header_cache = uml_net_header_cache;
dev->header_cache_update= uml_net_header_cache_update;
dev->hard_header_parse = uml_net_header_parse;
(*transport->kern->init)(dev, init); (*transport->kern->init)(dev, init);
dev->mtu = transport->user->max_packet; dev->mtu = transport->user->max_packet;
...@@ -308,32 +353,6 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -308,32 +353,6 @@ static int eth_configure(int n, void *init, char *mac,
dev->do_ioctl = uml_net_ioctl; dev->do_ioctl = uml_net_ioctl;
dev->watchdog_timeo = (HZ >> 1); dev->watchdog_timeo = (HZ >> 1);
dev->irq = UM_ETH_IRQ; dev->irq = UM_ETH_IRQ;
dev->init = NULL;
dev->master = NULL;
dev->neigh_setup = NULL;
dev->owner = NULL;
dev->state = 0;
dev->next_sched = 0;
dev->get_wireless_stats = 0;
dev->wireless_handlers = 0;
dev->gflags = 0;
dev->mc_list = NULL;
dev->mc_count = 0;
dev->promiscuity = 0;
dev->atalk_ptr = NULL;
dev->ip_ptr = NULL;
dev->dn_ptr = NULL;
dev->ip6_ptr = NULL;
dev->ec_ptr = NULL;
atomic_set(&dev->refcnt, 0);
dev->features = 0;
dev->uninit = NULL;
dev->destructor = NULL;
dev->set_config = NULL;
dev->accept_fastpath = 0;
dev->br_port = 0;
dev->mem_start = 0;
dev->mem_end = 0;
rtnl_lock(); rtnl_lock();
err = register_netdevice(dev); err = register_netdevice(dev);
...@@ -372,6 +391,7 @@ static int eth_configure(int n, void *init, char *mac, ...@@ -372,6 +391,7 @@ static int eth_configure(int n, void *init, char *mac,
if(transport->user->init) if(transport->user->init)
(*transport->user->init)(&lp->user, dev); (*transport->user->init)(&lp->user, dev);
if(device->have_mac) if(device->have_mac)
set_ether_mac(dev, device->mac); set_ether_mac(dev, device->mac);
return(0); return(0);
...@@ -486,7 +506,6 @@ void register_transport(struct transport *new) ...@@ -486,7 +506,6 @@ void register_transport(struct transport *new)
kfree(init); kfree(init);
} }
list_del(&eth->list); list_del(&eth->list);
return;
} }
} }
...@@ -580,7 +599,7 @@ static int net_remove(char *str) ...@@ -580,7 +599,7 @@ static int net_remove(char *str)
int n; int n;
n = simple_strtoul(str, &end, 0); n = simple_strtoul(str, &end, 0);
if(*end != '\0') if((*end != '\0') || (end == str))
return(-1); return(-1);
device = find_device(n); device = find_device(n);
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
#define __UM_SLIP_H #define __UM_SLIP_H
#define BUF_SIZE 1500 #define BUF_SIZE 1500
/* two bytes each for a (pathological) max packet of escaped chars + *
* terminating END char + initial END char */
#define ENC_BUF_SIZE (2 * BUF_SIZE + 2)
struct slip_data { struct slip_data {
void *dev; void *dev;
...@@ -9,10 +12,9 @@ struct slip_data { ...@@ -9,10 +12,9 @@ struct slip_data {
char *addr; char *addr;
char *gate_addr; char *gate_addr;
int slave; int slave;
/* two bytes each for a (pathological) max packet of escaped chars + char ibuf[ENC_BUF_SIZE];
* terminating END char + inital END char char obuf[ENC_BUF_SIZE];
*/ int more; /* more data: do not read fd until ibuf has been drained */
char buf[2 * BUF_SIZE + 2];
int pos; int pos;
int esc; int esc;
}; };
......
#include "linux/config.h"
#include "linux/kernel.h" #include "linux/kernel.h"
#include "linux/stddef.h" #include "linux/stddef.h"
#include "linux/init.h" #include "linux/init.h"
...@@ -24,21 +25,19 @@ void slip_init(struct net_device *dev, void *data) ...@@ -24,21 +25,19 @@ void slip_init(struct net_device *dev, void *data)
{ name : { '\0' }, { name : { '\0' },
addr: NULL, addr: NULL,
gate_addr : init->gate_addr, gate_addr : init->gate_addr,
slave : 0, slave : -1,
buf : { '\0' }, ibuf : { '\0' },
obuf : { '\0' },
pos : 0, pos : 0,
esc : 0, esc : 0,
dev : dev }); dev : dev });
strncpy(dev->name, "umn", IFNAMSIZ);
dev->init = NULL; dev->init = NULL;
dev->hard_header_len = 0; dev->hard_header_len = 0;
dev->addr_len = 4; dev->addr_len = 4;
dev->type = ARPHRD_ETHER; dev->type = ARPHRD_ETHER;
dev->tx_queue_len = 256; dev->tx_queue_len = 256;
dev->flags = IFF_NOARP; dev->flags = IFF_NOARP;
if(register_netdev(dev))
printk(KERN_ERR "Couldn't initialize umn\n");
printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr); printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "user.h" #include "user.h"
#include "net_user.h" #include "net_user.h"
#include "slip.h" #include "slip.h"
#include "slip_proto.h"
#include "helper.h" #include "helper.h"
#include "os.h" #include "os.h"
...@@ -66,7 +67,7 @@ static void slip_pre_exec(void *arg) ...@@ -66,7 +67,7 @@ static void slip_pre_exec(void *arg)
if(data->stdin != -1) dup2(data->stdin, 0); if(data->stdin != -1) dup2(data->stdin, 0);
dup2(data->stdout, 1); dup2(data->stdout, 1);
close(data->close_me); if(data->close_me != -1) close(data->close_me);
} }
static int slip_tramp(char **argv, int fd) static int slip_tramp(char **argv, int fd)
...@@ -156,7 +157,7 @@ static int slip_open(void *data) ...@@ -156,7 +157,7 @@ static int slip_open(void *data)
} }
sencap = 0; sencap = 0;
if(ioctl(sfd, SIOCSIFENCAP, &sencap) < 0){ if(ioctl(sfd, SIOCSIFENCAP, &sencap) < 0){
printk("Failed to sett slip encapsulation - " printk("Failed to set slip encapsulation - "
"errno = %d\n", errno); "errno = %d\n", errno);
return(-errno); return(-errno);
} }
...@@ -186,103 +187,48 @@ static void slip_close(int fd, void *data) ...@@ -186,103 +187,48 @@ static void slip_close(int fd, void *data)
pri->slave = -1; pri->slave = -1;
} }
/* SLIP protocol characters. */
#define END 0300 /* indicates end of frame */
#define ESC 0333 /* indicates byte stuffing */
#define ESC_END 0334 /* ESC ESC_END means END 'data' */
#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
static int slip_unesc(struct slip_data *sl, unsigned char c)
{
int ret;
switch(c){
case END:
sl->esc = 0;
ret = sl->pos;
sl->pos = 0;
return(ret);
case ESC:
sl->esc = 1;
return(0);
case ESC_ESC:
if(sl->esc){
sl->esc = 0;
c = ESC;
}
break;
case ESC_END:
if(sl->esc){
sl->esc = 0;
c = END;
}
break;
}
sl->buf[sl->pos++] = c;
return(0);
}
int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) int slip_user_read(int fd, void *buf, int len, struct slip_data *pri)
{ {
int i, n, size, start; int i, n, size, start;
n = net_read(fd, &pri->buf[pri->pos], sizeof(pri->buf) - pri->pos); if(pri->more>0) {
i = 0;
while(i < pri->more) {
size = slip_unesc(pri->ibuf[i++],
pri->ibuf, &pri->pos, &pri->esc);
if(size){
memcpy(buf, pri->ibuf, size);
memmove(pri->ibuf, &pri->ibuf[i], pri->more-i);
pri->more=pri->more-i;
return(size);
}
}
pri->more=0;
}
n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos);
if(n <= 0) return(n); if(n <= 0) return(n);
start = pri->pos; start = pri->pos;
for(i = 0; i < n; i++){ for(i = 0; i < n; i++){
size = slip_unesc(pri, pri->buf[start + i]); size = slip_unesc(pri->ibuf[start + i],
pri->ibuf, &pri->pos, &pri->esc);
if(size){ if(size){
memcpy(buf, pri->buf, size); memcpy(buf, pri->ibuf, size);
memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1));
pri->more=n-(i+1);
return(size); return(size);
} }
} }
return(0); return(0);
} }
static int slip_esc(unsigned char *s, unsigned char *d, int len)
{
unsigned char *ptr = d;
unsigned char c;
/*
* Send an initial END character to flush out any
* data that may have accumulated in the receiver
* due to line noise.
*/
*ptr++ = END;
/*
* For each byte in the packet, send the appropriate
* character sequence, according to the SLIP protocol.
*/
while (len-- > 0) {
switch(c = *s++) {
case END:
*ptr++ = ESC;
*ptr++ = ESC_END;
break;
case ESC:
*ptr++ = ESC;
*ptr++ = ESC_ESC;
break;
default:
*ptr++ = c;
break;
}
}
*ptr++ = END;
return (ptr - d);
}
int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) int slip_user_write(int fd, void *buf, int len, struct slip_data *pri)
{ {
int actual, n; int actual, n;
actual = slip_esc(buf, pri->buf, len); actual = slip_esc(buf, pri->obuf, len);
n = net_write(fd, pri->buf, actual); n = net_write(fd, pri->obuf, actual);
if(n < 0) return(n); if(n < 0) return(n);
else return(len); else return(len);
} }
......
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __UM_NET_KERN_H #ifndef __UM_NET_KERN_H
#define __UM_NET_KERN_H #define __UM_NET_KERN_H
......
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*/
#ifndef __UM_NET_USER_H__ #ifndef __UM_NET_USER_H__
#define __UM_NET_USER_H__ #define __UM_NET_USER_H__
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment