blob: 5f647d7a7292946dfdd36fecf0759198802c35a7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * user-mode-linux networking multicast transport
Jeff Dikecd1ae0e2007-10-16 01:27:29 -07003 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
5 *
6 * based on the existing uml-networking code, which is
Jeff Dikecd1ae0e2007-10-16 01:27:29 -07007 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * James Leu (jleu@mindspring.net).
9 * Copyright (C) 2001 by various other people who didn't put their name here.
10 *
11 * Licensed under the GPL.
12 *
13 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <unistd.h>
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070016#include <errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <netinet/in.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "mcast.h"
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070019#include "net_user.h"
Paolo 'Blaisorblade' Giarrussoc13e5692006-10-19 23:28:20 -070020#include "um_malloc.h"
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070021#include "user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Linus Torvalds1da177e2005-04-16 15:20:36 -070023static struct sockaddr_in *new_addr(char *addr, unsigned short port)
24{
25 struct sockaddr_in *sin;
26
Jeff Dikee4c4bf92007-07-15 23:38:56 -070027 sin = kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070028 if (sin == NULL) {
29 printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
30 "failed\n");
Jeff Dike56bd1942007-05-06 14:51:02 -070031 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 }
33 sin->sin_family = AF_INET;
34 sin->sin_addr.s_addr = in_aton(addr);
Jeff Dike7c00c312005-05-20 13:59:09 -070035 sin->sin_port = htons(port);
Jeff Dike56bd1942007-05-06 14:51:02 -070036 return sin;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037}
38
Jeff Dikef34d9d22007-05-06 14:51:04 -070039static int mcast_user_init(void *data, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070040{
41 struct mcast_data *pri = data;
42
43 pri->mcast_addr = new_addr(pri->addr, pri->port);
44 pri->dev = dev;
Jeff Dikef34d9d22007-05-06 14:51:04 -070045 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046}
47
Paolo 'Blaisorblade' Giarrusso83f4e8a2007-03-07 20:41:09 -080048static void mcast_remove(void *data)
49{
50 struct mcast_data *pri = data;
51
52 kfree(pri->mcast_addr);
53 pri->mcast_addr = NULL;
54}
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056static int mcast_open(void *data)
57{
58 struct mcast_data *pri = data;
59 struct sockaddr_in *sin = pri->mcast_addr;
60 struct ip_mreq mreq;
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -080061 int fd, yes = 1, err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63
Jeff Dike7c00c312005-05-20 13:59:09 -070064 if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67 fd = socket(AF_INET, SOCK_DGRAM, 0);
Jeff Dike7c00c312005-05-20 13:59:09 -070068
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070069 if (fd < 0) {
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -080070 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070071 printk(UM_KERN_ERR "mcast_open : data socket failed, "
72 "errno = %d\n", errno);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 goto out;
74 }
75
76 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -080077 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070078 printk(UM_KERN_ERR "mcast_open: SO_REUSEADDR failed, "
79 "errno = %d\n", errno);
Jeff Dike7c00c312005-05-20 13:59:09 -070080 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 }
82
83 /* set ttl according to config */
84 if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
85 sizeof(pri->ttl)) < 0) {
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -080086 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070087 printk(UM_KERN_ERR "mcast_open: IP_MULTICAST_TTL failed, "
88 "error = %d\n", errno);
Jeff Dike7c00c312005-05-20 13:59:09 -070089 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 }
91
92 /* set LOOP, so data does get fed back to local sockets */
93 if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -080094 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -070095 printk(UM_KERN_ERR "mcast_open: IP_MULTICAST_LOOP failed, "
96 "error = %d\n", errno);
Jeff Dike7c00c312005-05-20 13:59:09 -070097 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 }
99
100 /* bind socket to mcast address */
101 if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) {
Jeff Dikeb4fd3102005-09-16 19:27:49 -0700102 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700103 printk(UM_KERN_ERR "mcast_open : data bind failed, "
104 "errno = %d\n", errno);
Jeff Dike7c00c312005-05-20 13:59:09 -0700105 goto out_close;
Jeff Dike56bd1942007-05-06 14:51:02 -0700106 }
107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 /* subscribe to the multicast group */
109 mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr;
110 mreq.imr_interface.s_addr = 0;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700111 if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 &mreq, sizeof(mreq)) < 0) {
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -0800113 err = -errno;
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700114 printk(UM_KERN_ERR "mcast_open: IP_ADD_MEMBERSHIP failed, "
115 "error = %d\n", errno);
116 printk(UM_KERN_ERR "There appears not to be a multicast-"
117 "capable network interface on the host.\n");
118 printk(UM_KERN_ERR "eth0 should be configured in order to use "
119 "the multicast transport.\n");
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -0800120 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 }
122
Jeff Dike7c00c312005-05-20 13:59:09 -0700123 return fd;
124
125 out_close:
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700126 close(fd);
Jeff Dikeb4fd3102005-09-16 19:27:49 -0700127 out:
Paolo 'Blaisorblade' Giarrussoc50d2c42005-11-13 16:07:07 -0800128 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
131static void mcast_close(int fd, void *data)
132{
133 struct ip_mreq mreq;
134 struct mcast_data *pri = data;
135 struct sockaddr_in *sin = pri->mcast_addr;
136
137 mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr;
138 mreq.imr_interface.s_addr = 0;
139 if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
140 &mreq, sizeof(mreq)) < 0) {
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700141 printk(UM_KERN_ERR "mcast_open: IP_DROP_MEMBERSHIP failed, "
142 "error = %d\n", errno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
144
Jeff Dikecd1ae0e2007-10-16 01:27:29 -0700145 close(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
148int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri)
149{
150 struct sockaddr_in *data_addr = pri->mcast_addr;
151
Jeff Dike56bd1942007-05-06 14:51:02 -0700152 return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153}
154
Jeff Dike5e7672e2006-09-27 01:50:33 -0700155const struct net_user_info mcast_user_info = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 .init = mcast_user_init,
157 .open = mcast_open,
158 .close = mcast_close,
Paolo 'Blaisorblade' Giarrusso83f4e8a2007-03-07 20:41:09 -0800159 .remove = mcast_remove,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .add_address = NULL,
161 .delete_address = NULL,
Jeff Dikeb53f35a2007-10-16 01:27:31 -0700162 .mtu = ETH_MAX_PACKET,
163 .max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164};