umcast_user.c (4688B)
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};