summaryrefslogtreecommitdiff
path: root/arch/um/drivers/umcast_user.c
diff options
context:
space:
mode:
authorNolan Leake <nolan@sigbus.net>2011-05-25 04:13:02 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-25 19:39:41 +0400
commit4ff4d8d342fe25c4d1106fb0ffdd310a43d0ace0 (patch)
treeb41ea0ecc007f1c90eceada0db081dc0fb81700c /arch/um/drivers/umcast_user.c
parentd634f194d4e2e58d57927c812aca097e67a2287d (diff)
downloadlinux-4ff4d8d342fe25c4d1106fb0ffdd310a43d0ace0.tar.xz
um: add ucast ethernet transport
The ucast transport is similar to the mcast transport (and, in fact, shares most of its code), only it uses UDP unicast to move packets. Obviously this is only useful for point-to-point connections between virtual ethernet devices. Signed-off-by: Nolan Leake <nolan@cumulusnetworks.com> Signed-off-by: Richard Weinberger <richard@nod.at> Cc: David Miller <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/um/drivers/umcast_user.c')
-rw-r--r--arch/um/drivers/umcast_user.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/arch/um/drivers/umcast_user.c b/arch/um/drivers/umcast_user.c
new file mode 100644
index 000000000000..59c56fd6f52a
--- /dev/null
+++ b/arch/um/drivers/umcast_user.c
@@ -0,0 +1,186 @@
+/*
+ * user-mode-linux networking multicast transport
+ * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
+ * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
+ *
+ * based on the existing uml-networking code, which is
+ * 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 <unistd.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include "kern_constants.h"
+#include "umcast.h"
+#include "net_user.h"
+#include "um_malloc.h"
+#include "user.h"
+
+static struct sockaddr_in *new_addr(char *addr, unsigned short port)
+{
+ struct sockaddr_in *sin;
+
+ sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
+ if (sin == NULL) {
+ printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
+ "failed\n");
+ return NULL;
+ }
+ sin->sin_family = AF_INET;
+ if (addr)
+ sin->sin_addr.s_addr = in_aton(addr);
+ else
+ sin->sin_addr.s_addr = INADDR_ANY;
+ sin->sin_port = htons(port);
+ return sin;
+}
+
+static int umcast_user_init(void *data, void *dev)
+{
+ struct umcast_data *pri = data;
+
+ pri->remote_addr = new_addr(pri->addr, pri->rport);
+ if (pri->unicast)
+ pri->listen_addr = new_addr(NULL, pri->lport);
+ else
+ pri->listen_addr = pri->remote_addr;
+ pri->dev = dev;
+ return 0;
+}
+
+static void umcast_remove(void *data)
+{
+ struct umcast_data *pri = data;
+
+ kfree(pri->listen_addr);
+ if (pri->unicast)
+ kfree(pri->remote_addr);
+ pri->listen_addr = pri->remote_addr = NULL;
+}
+
+static int umcast_open(void *data)
+{
+ struct umcast_data *pri = data;
+ struct sockaddr_in *lsin = pri->listen_addr;
+ struct sockaddr_in *rsin = pri->remote_addr;
+ struct ip_mreq mreq;
+ int fd, yes = 1, err = -EINVAL;
+
+
+ if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
+ (rsin->sin_addr.s_addr == 0) ||
+ (lsin->sin_port == 0) || (rsin->sin_port == 0))
+ goto out;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (fd < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open : data socket failed, "
+ "errno = %d\n", errno);
+ goto out;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
+ "errno = %d\n", errno);
+ goto out_close;
+ }
+
+ if (!pri->unicast) {
+ /* set ttl according to config */
+ if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
+ sizeof(pri->ttl)) < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
+ "failed, error = %d\n", errno);
+ goto out_close;
+ }
+
+ /* set LOOP, so data does get fed back to local sockets */
+ if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
+ &yes, sizeof(yes)) < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
+ "failed, error = %d\n", errno);
+ goto out_close;
+ }
+ }
+
+ /* bind socket to the address */
+ if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open : data bind failed, "
+ "errno = %d\n", errno);
+ goto out_close;
+ }
+
+ if (!pri->unicast) {
+ /* subscribe to the multicast group */
+ mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = 0;
+ if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ err = -errno;
+ printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
+ "failed, error = %d\n", errno);
+ printk(UM_KERN_ERR "There appears not to be a "
+ "multicast-capable network interface on the "
+ "host.\n");
+ printk(UM_KERN_ERR "eth0 should be configured in order "
+ "to use the multicast transport.\n");
+ goto out_close;
+ }
+ }
+
+ return fd;
+
+ out_close:
+ close(fd);
+ out:
+ return err;
+}
+
+static void umcast_close(int fd, void *data)
+{
+ struct umcast_data *pri = data;
+
+ if (!pri->unicast) {
+ struct ip_mreq mreq;
+ struct sockaddr_in *lsin = pri->listen_addr;
+
+ mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = 0;
+ if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
+ "failed, error = %d\n", errno);
+ }
+ }
+
+ close(fd);
+}
+
+int umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
+{
+ struct sockaddr_in *data_addr = pri->remote_addr;
+
+ return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
+}
+
+const struct net_user_info umcast_user_info = {
+ .init = umcast_user_init,
+ .open = umcast_open,
+ .close = umcast_close,
+ .remove = umcast_remove,
+ .add_address = NULL,
+ .delete_address = NULL,
+ .mtu = ETH_MAX_PACKET,
+ .max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
+};