162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * user-mode-linux networking multicast transport
462306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
562306a36Sopenharmony_ci * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * based on the existing uml-networking code, which is
862306a36Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
962306a36Sopenharmony_ci * James Leu (jleu@mindspring.net).
1062306a36Sopenharmony_ci * Copyright (C) 2001 by various other people who didn't put their name here.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci#include <errno.h>
1762306a36Sopenharmony_ci#include <netinet/in.h>
1862306a36Sopenharmony_ci#include "umcast.h"
1962306a36Sopenharmony_ci#include <net_user.h>
2062306a36Sopenharmony_ci#include <um_malloc.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic struct sockaddr_in *new_addr(char *addr, unsigned short port)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct sockaddr_in *sin;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
2762306a36Sopenharmony_ci	if (sin == NULL) {
2862306a36Sopenharmony_ci		printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
2962306a36Sopenharmony_ci		       "failed\n");
3062306a36Sopenharmony_ci		return NULL;
3162306a36Sopenharmony_ci	}
3262306a36Sopenharmony_ci	sin->sin_family = AF_INET;
3362306a36Sopenharmony_ci	if (addr)
3462306a36Sopenharmony_ci		sin->sin_addr.s_addr = in_aton(addr);
3562306a36Sopenharmony_ci	else
3662306a36Sopenharmony_ci		sin->sin_addr.s_addr = INADDR_ANY;
3762306a36Sopenharmony_ci	sin->sin_port = htons(port);
3862306a36Sopenharmony_ci	return sin;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int umcast_user_init(void *data, void *dev)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct umcast_data *pri = data;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	pri->remote_addr = new_addr(pri->addr, pri->rport);
4662306a36Sopenharmony_ci	if (pri->unicast)
4762306a36Sopenharmony_ci		pri->listen_addr = new_addr(NULL, pri->lport);
4862306a36Sopenharmony_ci	else
4962306a36Sopenharmony_ci		pri->listen_addr = pri->remote_addr;
5062306a36Sopenharmony_ci	pri->dev = dev;
5162306a36Sopenharmony_ci	return 0;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void umcast_remove(void *data)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct umcast_data *pri = data;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	kfree(pri->listen_addr);
5962306a36Sopenharmony_ci	if (pri->unicast)
6062306a36Sopenharmony_ci		kfree(pri->remote_addr);
6162306a36Sopenharmony_ci	pri->listen_addr = pri->remote_addr = NULL;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int umcast_open(void *data)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct umcast_data *pri = data;
6762306a36Sopenharmony_ci	struct sockaddr_in *lsin = pri->listen_addr;
6862306a36Sopenharmony_ci	struct sockaddr_in *rsin = pri->remote_addr;
6962306a36Sopenharmony_ci	struct ip_mreq mreq;
7062306a36Sopenharmony_ci	int fd, yes = 1, err = -EINVAL;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
7462306a36Sopenharmony_ci	    (rsin->sin_addr.s_addr == 0) ||
7562306a36Sopenharmony_ci	    (lsin->sin_port == 0) || (rsin->sin_port == 0))
7662306a36Sopenharmony_ci		goto out;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	fd = socket(AF_INET, SOCK_DGRAM, 0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (fd < 0) {
8162306a36Sopenharmony_ci		err = -errno;
8262306a36Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open : data socket failed, "
8362306a36Sopenharmony_ci		       "errno = %d\n", errno);
8462306a36Sopenharmony_ci		goto out;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
8862306a36Sopenharmony_ci		err = -errno;
8962306a36Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
9062306a36Sopenharmony_ci		       "errno = %d\n", errno);
9162306a36Sopenharmony_ci		goto out_close;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (!pri->unicast) {
9562306a36Sopenharmony_ci		/* set ttl according to config */
9662306a36Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
9762306a36Sopenharmony_ci			       sizeof(pri->ttl)) < 0) {
9862306a36Sopenharmony_ci			err = -errno;
9962306a36Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
10062306a36Sopenharmony_ci			       "failed, error = %d\n", errno);
10162306a36Sopenharmony_ci			goto out_close;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		/* set LOOP, so data does get fed back to local sockets */
10562306a36Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
10662306a36Sopenharmony_ci			       &yes, sizeof(yes)) < 0) {
10762306a36Sopenharmony_ci			err = -errno;
10862306a36Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
10962306a36Sopenharmony_ci			       "failed, error = %d\n", errno);
11062306a36Sopenharmony_ci			goto out_close;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* bind socket to the address */
11562306a36Sopenharmony_ci	if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
11662306a36Sopenharmony_ci		err = -errno;
11762306a36Sopenharmony_ci		printk(UM_KERN_ERR "umcast_open : data bind failed, "
11862306a36Sopenharmony_ci		       "errno = %d\n", errno);
11962306a36Sopenharmony_ci		goto out_close;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (!pri->unicast) {
12362306a36Sopenharmony_ci		/* subscribe to the multicast group */
12462306a36Sopenharmony_ci		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
12562306a36Sopenharmony_ci		mreq.imr_interface.s_addr = 0;
12662306a36Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
12762306a36Sopenharmony_ci			       &mreq, sizeof(mreq)) < 0) {
12862306a36Sopenharmony_ci			err = -errno;
12962306a36Sopenharmony_ci			printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
13062306a36Sopenharmony_ci			       "failed, error = %d\n", errno);
13162306a36Sopenharmony_ci			printk(UM_KERN_ERR "There appears not to be a "
13262306a36Sopenharmony_ci			       "multicast-capable network interface on the "
13362306a36Sopenharmony_ci			       "host.\n");
13462306a36Sopenharmony_ci			printk(UM_KERN_ERR "eth0 should be configured in order "
13562306a36Sopenharmony_ci			       "to use the multicast transport.\n");
13662306a36Sopenharmony_ci			goto out_close;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return fd;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci out_close:
14362306a36Sopenharmony_ci	close(fd);
14462306a36Sopenharmony_ci out:
14562306a36Sopenharmony_ci	return err;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void umcast_close(int fd, void *data)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct umcast_data *pri = data;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (!pri->unicast) {
15362306a36Sopenharmony_ci		struct ip_mreq mreq;
15462306a36Sopenharmony_ci		struct sockaddr_in *lsin = pri->listen_addr;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
15762306a36Sopenharmony_ci		mreq.imr_interface.s_addr = 0;
15862306a36Sopenharmony_ci		if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
15962306a36Sopenharmony_ci			       &mreq, sizeof(mreq)) < 0) {
16062306a36Sopenharmony_ci			printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
16162306a36Sopenharmony_ci			       "failed, error = %d\n", errno);
16262306a36Sopenharmony_ci		}
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	close(fd);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct sockaddr_in *data_addr = pri->remote_addr;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciconst struct net_user_info umcast_user_info = {
17662306a36Sopenharmony_ci	.init	= umcast_user_init,
17762306a36Sopenharmony_ci	.open	= umcast_open,
17862306a36Sopenharmony_ci	.close	= umcast_close,
17962306a36Sopenharmony_ci	.remove	= umcast_remove,
18062306a36Sopenharmony_ci	.add_address	= NULL,
18162306a36Sopenharmony_ci	.delete_address = NULL,
18262306a36Sopenharmony_ci	.mtu	= ETH_MAX_PACKET,
18362306a36Sopenharmony_ci	.max_packet	= ETH_MAX_PACKET + ETH_HEADER_OTHER,
18462306a36Sopenharmony_ci};
185