linux/arch/um/drivers/umcast_user.c
<<
>>
Prefs
   1/*
   2 * user-mode-linux networking multicast transport
   3 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
   4 * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
   5 *
   6 * based on the existing uml-networking code, which is
   7 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
   8 * James Leu (jleu@mindspring.net).
   9 * Copyright (C) 2001 by various other people who didn't put their name here.
  10 *
  11 * Licensed under the GPL.
  12 *
  13 */
  14
  15#include <unistd.h>
  16#include <errno.h>
  17#include <netinet/in.h>
  18#include "kern_constants.h"
  19#include "umcast.h"
  20#include "net_user.h"
  21#include "um_malloc.h"
  22#include "user.h"
  23
  24static struct sockaddr_in *new_addr(char *addr, unsigned short port)
  25{
  26        struct sockaddr_in *sin;
  27
  28        sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
  29        if (sin == NULL) {
  30                printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
  31                       "failed\n");
  32                return NULL;
  33        }
  34        sin->sin_family = AF_INET;
  35        if (addr)
  36                sin->sin_addr.s_addr = in_aton(addr);
  37        else
  38                sin->sin_addr.s_addr = INADDR_ANY;
  39        sin->sin_port = htons(port);
  40        return sin;
  41}
  42
  43static int umcast_user_init(void *data, void *dev)
  44{
  45        struct umcast_data *pri = data;
  46
  47        pri->remote_addr = new_addr(pri->addr, pri->rport);
  48        if (pri->unicast)
  49                pri->listen_addr = new_addr(NULL, pri->lport);
  50        else
  51                pri->listen_addr = pri->remote_addr;
  52        pri->dev = dev;
  53        return 0;
  54}
  55
  56static void umcast_remove(void *data)
  57{
  58        struct umcast_data *pri = data;
  59
  60        kfree(pri->listen_addr);
  61        if (pri->unicast)
  62                kfree(pri->remote_addr);
  63        pri->listen_addr = pri->remote_addr = NULL;
  64}
  65
  66static int umcast_open(void *data)
  67{
  68        struct umcast_data *pri = data;
  69        struct sockaddr_in *lsin = pri->listen_addr;
  70        struct sockaddr_in *rsin = pri->remote_addr;
  71        struct ip_mreq mreq;
  72        int fd, yes = 1, err = -EINVAL;
  73
  74
  75        if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
  76            (rsin->sin_addr.s_addr == 0) ||
  77            (lsin->sin_port == 0) || (rsin->sin_port == 0))
  78                goto out;
  79
  80        fd = socket(AF_INET, SOCK_DGRAM, 0);
  81
  82        if (fd < 0) {
  83                err = -errno;
  84                printk(UM_KERN_ERR "umcast_open : data socket failed, "
  85                       "errno = %d\n", errno);
  86                goto out;
  87        }
  88
  89        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
  90                err = -errno;
  91                printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
  92                       "errno = %d\n", errno);
  93                goto out_close;
  94        }
  95
  96        if (!pri->unicast) {
  97                /* set ttl according to config */
  98                if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
  99                               sizeof(pri->ttl)) < 0) {
 100                        err = -errno;
 101                        printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
 102                               "failed, error = %d\n", errno);
 103                        goto out_close;
 104                }
 105
 106                /* set LOOP, so data does get fed back to local sockets */
 107                if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
 108                               &yes, sizeof(yes)) < 0) {
 109                        err = -errno;
 110                        printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
 111                               "failed, error = %d\n", errno);
 112                        goto out_close;
 113                }
 114        }
 115
 116        /* bind socket to the address */
 117        if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
 118                err = -errno;
 119                printk(UM_KERN_ERR "umcast_open : data bind failed, "
 120                       "errno = %d\n", errno);
 121                goto out_close;
 122        }
 123
 124        if (!pri->unicast) {
 125                /* subscribe to the multicast group */
 126                mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
 127                mreq.imr_interface.s_addr = 0;
 128                if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
 129                               &mreq, sizeof(mreq)) < 0) {
 130                        err = -errno;
 131                        printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
 132                               "failed, error = %d\n", errno);
 133                        printk(UM_KERN_ERR "There appears not to be a "
 134                               "multicast-capable network interface on the "
 135                               "host.\n");
 136                        printk(UM_KERN_ERR "eth0 should be configured in order "
 137                               "to use the multicast transport.\n");
 138                        goto out_close;
 139                }
 140        }
 141
 142        return fd;
 143
 144 out_close:
 145        close(fd);
 146 out:
 147        return err;
 148}
 149
 150static void umcast_close(int fd, void *data)
 151{
 152        struct umcast_data *pri = data;
 153
 154        if (!pri->unicast) {
 155                struct ip_mreq mreq;
 156                struct sockaddr_in *lsin = pri->listen_addr;
 157
 158                mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
 159                mreq.imr_interface.s_addr = 0;
 160                if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
 161                               &mreq, sizeof(mreq)) < 0) {
 162                        printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
 163                               "failed, error = %d\n", errno);
 164                }
 165        }
 166
 167        close(fd);
 168}
 169
 170int umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
 171{
 172        struct sockaddr_in *data_addr = pri->remote_addr;
 173
 174        return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
 175}
 176
 177const struct net_user_info umcast_user_info = {
 178        .init   = umcast_user_init,
 179        .open   = umcast_open,
 180        .close  = umcast_close,
 181        .remove = umcast_remove,
 182        .add_address    = NULL,
 183        .delete_address = NULL,
 184        .mtu    = ETH_MAX_PACKET,
 185        .max_packet     = ETH_MAX_PACKET + ETH_HEADER_OTHER,
 186};
 187