18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * user-mode-linux networking multicast transport
48c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
58c2ecf20Sopenharmony_ci * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * based on the existing uml-networking code, which is
88c2ecf20Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
98c2ecf20Sopenharmony_ci * James Leu (jleu@mindspring.net).
108c2ecf20Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <unistd.h>
168c2ecf20Sopenharmony_ci#include <errno.h>
178c2ecf20Sopenharmony_ci#include <netinet/in.h>
188c2ecf20Sopenharmony_ci#include "umcast.h"
198c2ecf20Sopenharmony_ci#include <net_user.h>
208c2ecf20Sopenharmony_ci#include <um_malloc.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic struct sockaddr_in *new_addr(char *addr, unsigned short port)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct sockaddr_in *sin;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
278c2ecf20Sopenharmony_ci	if (sin == NULL) {
288c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
298c2ecf20Sopenharmony_ci		       "failed\n");
308c2ecf20Sopenharmony_ci		return NULL;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci	sin->sin_family = AF_INET;
338c2ecf20Sopenharmony_ci	if (addr)
348c2ecf20Sopenharmony_ci		sin->sin_addr.s_addr = in_aton(addr);
358c2ecf20Sopenharmony_ci	else
368c2ecf20Sopenharmony_ci		sin->sin_addr.s_addr = INADDR_ANY;
378c2ecf20Sopenharmony_ci	sin->sin_port = htons(port);
388c2ecf20Sopenharmony_ci	return sin;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int umcast_user_init(void *data, void *dev)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct umcast_data *pri = data;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	pri->remote_addr = new_addr(pri->addr, pri->rport);
468c2ecf20Sopenharmony_ci	if (pri->unicast)
478c2ecf20Sopenharmony_ci		pri->listen_addr = new_addr(NULL, pri->lport);
488c2ecf20Sopenharmony_ci	else
498c2ecf20Sopenharmony_ci		pri->listen_addr = pri->remote_addr;
508c2ecf20Sopenharmony_ci	pri->dev = dev;
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void umcast_remove(void *data)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct umcast_data *pri = data;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	kfree(pri->listen_addr);
598c2ecf20Sopenharmony_ci	if (pri->unicast)
608c2ecf20Sopenharmony_ci		kfree(pri->remote_addr);
618c2ecf20Sopenharmony_ci	pri->listen_addr = pri->remote_addr = NULL;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int umcast_open(void *data)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	struct umcast_data *pri = data;
678c2ecf20Sopenharmony_ci	struct sockaddr_in *lsin = pri->listen_addr;
688c2ecf20Sopenharmony_ci	struct sockaddr_in *rsin = pri->remote_addr;
698c2ecf20Sopenharmony_ci	struct ip_mreq mreq;
708c2ecf20Sopenharmony_ci	int fd, yes = 1, err = -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
748c2ecf20Sopenharmony_ci	    (rsin->sin_addr.s_addr == 0) ||
758c2ecf20Sopenharmony_ci	    (lsin->sin_port == 0) || (rsin->sin_port == 0))
768c2ecf20Sopenharmony_ci		goto out;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	fd = socket(AF_INET, SOCK_DGRAM, 0);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (fd < 0) {
818c2ecf20Sopenharmony_ci		err = -errno;
828c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open : data socket failed, "
838c2ecf20Sopenharmony_ci		       "errno = %d\n", errno);
848c2ecf20Sopenharmony_ci		goto out;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
888c2ecf20Sopenharmony_ci		err = -errno;
898c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
908c2ecf20Sopenharmony_ci		       "errno = %d\n", errno);
918c2ecf20Sopenharmony_ci		goto out_close;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (!pri->unicast) {
958c2ecf20Sopenharmony_ci		/* set ttl according to config */
968c2ecf20Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
978c2ecf20Sopenharmony_ci			       sizeof(pri->ttl)) < 0) {
988c2ecf20Sopenharmony_ci			err = -errno;
998c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
1008c2ecf20Sopenharmony_ci			       "failed, error = %d\n", errno);
1018c2ecf20Sopenharmony_ci			goto out_close;
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		/* set LOOP, so data does get fed back to local sockets */
1058c2ecf20Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
1068c2ecf20Sopenharmony_ci			       &yes, sizeof(yes)) < 0) {
1078c2ecf20Sopenharmony_ci			err = -errno;
1088c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
1098c2ecf20Sopenharmony_ci			       "failed, error = %d\n", errno);
1108c2ecf20Sopenharmony_ci			goto out_close;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* bind socket to the address */
1158c2ecf20Sopenharmony_ci	if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
1168c2ecf20Sopenharmony_ci		err = -errno;
1178c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open : data bind failed, "
1188c2ecf20Sopenharmony_ci		       "errno = %d\n", errno);
1198c2ecf20Sopenharmony_ci		goto out_close;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (!pri->unicast) {
1238c2ecf20Sopenharmony_ci		/* subscribe to the multicast group */
1248c2ecf20Sopenharmony_ci		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
1258c2ecf20Sopenharmony_ci		mreq.imr_interface.s_addr = 0;
1268c2ecf20Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
1278c2ecf20Sopenharmony_ci			       &mreq, sizeof(mreq)) < 0) {
1288c2ecf20Sopenharmony_ci			err = -errno;
1298c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
1308c2ecf20Sopenharmony_ci			       "failed, error = %d\n", errno);
1318c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "There appears not to be a "
1328c2ecf20Sopenharmony_ci			       "multicast-capable network interface on the "
1338c2ecf20Sopenharmony_ci			       "host.\n");
1348c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "eth0 should be configured in order "
1358c2ecf20Sopenharmony_ci			       "to use the multicast transport.\n");
1368c2ecf20Sopenharmony_ci			goto out_close;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return fd;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci out_close:
1438c2ecf20Sopenharmony_ci	close(fd);
1448c2ecf20Sopenharmony_ci out:
1458c2ecf20Sopenharmony_ci	return err;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic void umcast_close(int fd, void *data)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct umcast_data *pri = data;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!pri->unicast) {
1538c2ecf20Sopenharmony_ci		struct ip_mreq mreq;
1548c2ecf20Sopenharmony_ci		struct sockaddr_in *lsin = pri->listen_addr;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
1578c2ecf20Sopenharmony_ci		mreq.imr_interface.s_addr = 0;
1588c2ecf20Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
1598c2ecf20Sopenharmony_ci			       &mreq, sizeof(mreq)) < 0) {
1608c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
1618c2ecf20Sopenharmony_ci			       "failed, error = %d\n", errno);
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	close(fd);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciint umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct sockaddr_in *data_addr = pri->remote_addr;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciconst struct net_user_info umcast_user_info = {
1768c2ecf20Sopenharmony_ci	.init	= umcast_user_init,
1778c2ecf20Sopenharmony_ci	.open	= umcast_open,
1788c2ecf20Sopenharmony_ci	.close	= umcast_close,
1798c2ecf20Sopenharmony_ci	.remove	= umcast_remove,
1808c2ecf20Sopenharmony_ci	.add_address	= NULL,
1818c2ecf20Sopenharmony_ci	.delete_address = NULL,
1828c2ecf20Sopenharmony_ci	.mtu	= ETH_MAX_PACKET,
1838c2ecf20Sopenharmony_ci	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
1848c2ecf20Sopenharmony_ci};
185