xref: /openbmc/linux/arch/um/drivers/umcast_user.c (revision 976e3645923bdd2fe7893aae33fd7a21098bfb28)
1*dbddf429SAlex Dewar // SPDX-License-Identifier: GPL-2.0
24ff4d8d3SNolan Leake /*
34ff4d8d3SNolan Leake  * user-mode-linux networking multicast transport
44ff4d8d3SNolan Leake  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
54ff4d8d3SNolan Leake  * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
64ff4d8d3SNolan Leake  *
74ff4d8d3SNolan Leake  * based on the existing uml-networking code, which is
84ff4d8d3SNolan Leake  * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
94ff4d8d3SNolan Leake  * James Leu (jleu@mindspring.net).
104ff4d8d3SNolan Leake  * Copyright (C) 2001 by various other people who didn't put their name here.
114ff4d8d3SNolan Leake  *
124ff4d8d3SNolan Leake  *
134ff4d8d3SNolan Leake  */
144ff4d8d3SNolan Leake 
154ff4d8d3SNolan Leake #include <unistd.h>
164ff4d8d3SNolan Leake #include <errno.h>
174ff4d8d3SNolan Leake #include <netinet/in.h>
184ff4d8d3SNolan Leake #include "umcast.h"
1937185b33SAl Viro #include <net_user.h>
2037185b33SAl Viro #include <um_malloc.h>
214ff4d8d3SNolan Leake 
new_addr(char * addr,unsigned short port)224ff4d8d3SNolan Leake static struct sockaddr_in *new_addr(char *addr, unsigned short port)
234ff4d8d3SNolan Leake {
244ff4d8d3SNolan Leake 	struct sockaddr_in *sin;
254ff4d8d3SNolan Leake 
264ff4d8d3SNolan Leake 	sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
274ff4d8d3SNolan Leake 	if (sin == NULL) {
284ff4d8d3SNolan Leake 		printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
294ff4d8d3SNolan Leake 		       "failed\n");
304ff4d8d3SNolan Leake 		return NULL;
314ff4d8d3SNolan Leake 	}
324ff4d8d3SNolan Leake 	sin->sin_family = AF_INET;
334ff4d8d3SNolan Leake 	if (addr)
344ff4d8d3SNolan Leake 		sin->sin_addr.s_addr = in_aton(addr);
354ff4d8d3SNolan Leake 	else
364ff4d8d3SNolan Leake 		sin->sin_addr.s_addr = INADDR_ANY;
374ff4d8d3SNolan Leake 	sin->sin_port = htons(port);
384ff4d8d3SNolan Leake 	return sin;
394ff4d8d3SNolan Leake }
404ff4d8d3SNolan Leake 
umcast_user_init(void * data,void * dev)414ff4d8d3SNolan Leake static int umcast_user_init(void *data, void *dev)
424ff4d8d3SNolan Leake {
434ff4d8d3SNolan Leake 	struct umcast_data *pri = data;
444ff4d8d3SNolan Leake 
454ff4d8d3SNolan Leake 	pri->remote_addr = new_addr(pri->addr, pri->rport);
464ff4d8d3SNolan Leake 	if (pri->unicast)
474ff4d8d3SNolan Leake 		pri->listen_addr = new_addr(NULL, pri->lport);
484ff4d8d3SNolan Leake 	else
494ff4d8d3SNolan Leake 		pri->listen_addr = pri->remote_addr;
504ff4d8d3SNolan Leake 	pri->dev = dev;
514ff4d8d3SNolan Leake 	return 0;
524ff4d8d3SNolan Leake }
534ff4d8d3SNolan Leake 
umcast_remove(void * data)544ff4d8d3SNolan Leake static void umcast_remove(void *data)
554ff4d8d3SNolan Leake {
564ff4d8d3SNolan Leake 	struct umcast_data *pri = data;
574ff4d8d3SNolan Leake 
584ff4d8d3SNolan Leake 	kfree(pri->listen_addr);
594ff4d8d3SNolan Leake 	if (pri->unicast)
604ff4d8d3SNolan Leake 		kfree(pri->remote_addr);
614ff4d8d3SNolan Leake 	pri->listen_addr = pri->remote_addr = NULL;
624ff4d8d3SNolan Leake }
634ff4d8d3SNolan Leake 
umcast_open(void * data)644ff4d8d3SNolan Leake static int umcast_open(void *data)
654ff4d8d3SNolan Leake {
664ff4d8d3SNolan Leake 	struct umcast_data *pri = data;
674ff4d8d3SNolan Leake 	struct sockaddr_in *lsin = pri->listen_addr;
684ff4d8d3SNolan Leake 	struct sockaddr_in *rsin = pri->remote_addr;
694ff4d8d3SNolan Leake 	struct ip_mreq mreq;
704ff4d8d3SNolan Leake 	int fd, yes = 1, err = -EINVAL;
714ff4d8d3SNolan Leake 
724ff4d8d3SNolan Leake 
734ff4d8d3SNolan Leake 	if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
744ff4d8d3SNolan Leake 	    (rsin->sin_addr.s_addr == 0) ||
754ff4d8d3SNolan Leake 	    (lsin->sin_port == 0) || (rsin->sin_port == 0))
764ff4d8d3SNolan Leake 		goto out;
774ff4d8d3SNolan Leake 
784ff4d8d3SNolan Leake 	fd = socket(AF_INET, SOCK_DGRAM, 0);
794ff4d8d3SNolan Leake 
804ff4d8d3SNolan Leake 	if (fd < 0) {
814ff4d8d3SNolan Leake 		err = -errno;
824ff4d8d3SNolan Leake 		printk(UM_KERN_ERR "umcast_open : data socket failed, "
834ff4d8d3SNolan Leake 		       "errno = %d\n", errno);
844ff4d8d3SNolan Leake 		goto out;
854ff4d8d3SNolan Leake 	}
864ff4d8d3SNolan Leake 
874ff4d8d3SNolan Leake 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
884ff4d8d3SNolan Leake 		err = -errno;
894ff4d8d3SNolan Leake 		printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
904ff4d8d3SNolan Leake 		       "errno = %d\n", errno);
914ff4d8d3SNolan Leake 		goto out_close;
924ff4d8d3SNolan Leake 	}
934ff4d8d3SNolan Leake 
944ff4d8d3SNolan Leake 	if (!pri->unicast) {
954ff4d8d3SNolan Leake 		/* set ttl according to config */
964ff4d8d3SNolan Leake 		if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
974ff4d8d3SNolan Leake 			       sizeof(pri->ttl)) < 0) {
984ff4d8d3SNolan Leake 			err = -errno;
994ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
1004ff4d8d3SNolan Leake 			       "failed, error = %d\n", errno);
1014ff4d8d3SNolan Leake 			goto out_close;
1024ff4d8d3SNolan Leake 		}
1034ff4d8d3SNolan Leake 
1044ff4d8d3SNolan Leake 		/* set LOOP, so data does get fed back to local sockets */
1054ff4d8d3SNolan Leake 		if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
1064ff4d8d3SNolan Leake 			       &yes, sizeof(yes)) < 0) {
1074ff4d8d3SNolan Leake 			err = -errno;
1084ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
1094ff4d8d3SNolan Leake 			       "failed, error = %d\n", errno);
1104ff4d8d3SNolan Leake 			goto out_close;
1114ff4d8d3SNolan Leake 		}
1124ff4d8d3SNolan Leake 	}
1134ff4d8d3SNolan Leake 
1144ff4d8d3SNolan Leake 	/* bind socket to the address */
1154ff4d8d3SNolan Leake 	if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
1164ff4d8d3SNolan Leake 		err = -errno;
1174ff4d8d3SNolan Leake 		printk(UM_KERN_ERR "umcast_open : data bind failed, "
1184ff4d8d3SNolan Leake 		       "errno = %d\n", errno);
1194ff4d8d3SNolan Leake 		goto out_close;
1204ff4d8d3SNolan Leake 	}
1214ff4d8d3SNolan Leake 
1224ff4d8d3SNolan Leake 	if (!pri->unicast) {
1234ff4d8d3SNolan Leake 		/* subscribe to the multicast group */
1244ff4d8d3SNolan Leake 		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
1254ff4d8d3SNolan Leake 		mreq.imr_interface.s_addr = 0;
1264ff4d8d3SNolan Leake 		if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
1274ff4d8d3SNolan Leake 			       &mreq, sizeof(mreq)) < 0) {
1284ff4d8d3SNolan Leake 			err = -errno;
1294ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
1304ff4d8d3SNolan Leake 			       "failed, error = %d\n", errno);
1314ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "There appears not to be a "
1324ff4d8d3SNolan Leake 			       "multicast-capable network interface on the "
1334ff4d8d3SNolan Leake 			       "host.\n");
1344ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "eth0 should be configured in order "
1354ff4d8d3SNolan Leake 			       "to use the multicast transport.\n");
1364ff4d8d3SNolan Leake 			goto out_close;
1374ff4d8d3SNolan Leake 		}
1384ff4d8d3SNolan Leake 	}
1394ff4d8d3SNolan Leake 
1404ff4d8d3SNolan Leake 	return fd;
1414ff4d8d3SNolan Leake 
1424ff4d8d3SNolan Leake  out_close:
1434ff4d8d3SNolan Leake 	close(fd);
1444ff4d8d3SNolan Leake  out:
1454ff4d8d3SNolan Leake 	return err;
1464ff4d8d3SNolan Leake }
1474ff4d8d3SNolan Leake 
umcast_close(int fd,void * data)1484ff4d8d3SNolan Leake static void umcast_close(int fd, void *data)
1494ff4d8d3SNolan Leake {
1504ff4d8d3SNolan Leake 	struct umcast_data *pri = data;
1514ff4d8d3SNolan Leake 
1524ff4d8d3SNolan Leake 	if (!pri->unicast) {
1534ff4d8d3SNolan Leake 		struct ip_mreq mreq;
1544ff4d8d3SNolan Leake 		struct sockaddr_in *lsin = pri->listen_addr;
1554ff4d8d3SNolan Leake 
1564ff4d8d3SNolan Leake 		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
1574ff4d8d3SNolan Leake 		mreq.imr_interface.s_addr = 0;
1584ff4d8d3SNolan Leake 		if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
1594ff4d8d3SNolan Leake 			       &mreq, sizeof(mreq)) < 0) {
1604ff4d8d3SNolan Leake 			printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
1614ff4d8d3SNolan Leake 			       "failed, error = %d\n", errno);
1624ff4d8d3SNolan Leake 		}
1634ff4d8d3SNolan Leake 	}
1644ff4d8d3SNolan Leake 
1654ff4d8d3SNolan Leake 	close(fd);
1664ff4d8d3SNolan Leake }
1674ff4d8d3SNolan Leake 
umcast_user_write(int fd,void * buf,int len,struct umcast_data * pri)1684ff4d8d3SNolan Leake int umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
1694ff4d8d3SNolan Leake {
1704ff4d8d3SNolan Leake 	struct sockaddr_in *data_addr = pri->remote_addr;
1714ff4d8d3SNolan Leake 
1724ff4d8d3SNolan Leake 	return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
1734ff4d8d3SNolan Leake }
1744ff4d8d3SNolan Leake 
1754ff4d8d3SNolan Leake const struct net_user_info umcast_user_info = {
1764ff4d8d3SNolan Leake 	.init	= umcast_user_init,
1774ff4d8d3SNolan Leake 	.open	= umcast_open,
1784ff4d8d3SNolan Leake 	.close	= umcast_close,
1794ff4d8d3SNolan Leake 	.remove	= umcast_remove,
1804ff4d8d3SNolan Leake 	.add_address	= NULL,
1814ff4d8d3SNolan Leake 	.delete_address = NULL,
1824ff4d8d3SNolan Leake 	.mtu	= ETH_MAX_PACKET,
1834ff4d8d3SNolan Leake 	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
1844ff4d8d3SNolan Leake };
185