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