Merge branch 'fixes-jgarzik' of git://git.kernel.org/pub/scm/linux/kernel/git/linvill...
[linux-2.6] / arch / um / drivers / mcast_user.c
1 /*
2  * user-mode-linux networking multicast transport
3  * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4  * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
5  *
6  * based on the existing uml-networking code, which is
7  * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
8  * 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
15 #include <unistd.h>
16 #include <errno.h>
17 #include <netinet/in.h>
18 #include "mcast.h"
19 #include "net_user.h"
20 #include "um_malloc.h"
21 #include "user.h"
22
23 static struct sockaddr_in *new_addr(char *addr, unsigned short port)
24 {
25         struct sockaddr_in *sin;
26
27         sin = kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
28         if (sin == NULL) {
29                 printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
30                        "failed\n");
31                 return NULL;
32         }
33         sin->sin_family = AF_INET;
34         sin->sin_addr.s_addr = in_aton(addr);
35         sin->sin_port = htons(port);
36         return sin;
37 }
38
39 static int mcast_user_init(void *data, void *dev)
40 {
41         struct mcast_data *pri = data;
42
43         pri->mcast_addr = new_addr(pri->addr, pri->port);
44         pri->dev = dev;
45         return 0;
46 }
47
48 static 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
56 static 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;
61         int fd, yes = 1, err = -EINVAL;
62
63
64         if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0))
65                 goto out;
66
67         fd = socket(AF_INET, SOCK_DGRAM, 0);
68
69         if (fd < 0) {
70                 err = -errno;
71                 printk(UM_KERN_ERR "mcast_open : data socket failed, "
72                        "errno = %d\n", errno);
73                 goto out;
74         }
75
76         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
77                 err = -errno;
78                 printk(UM_KERN_ERR "mcast_open: SO_REUSEADDR failed, "
79                        "errno = %d\n", errno);
80                 goto out_close;
81         }
82
83         /* set ttl according to config */
84         if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
85                        sizeof(pri->ttl)) < 0) {
86                 err = -errno;
87                 printk(UM_KERN_ERR "mcast_open: IP_MULTICAST_TTL failed, "
88                        "error = %d\n", errno);
89                 goto out_close;
90         }
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) {
94                 err = -errno;
95                 printk(UM_KERN_ERR "mcast_open: IP_MULTICAST_LOOP failed, "
96                        "error = %d\n", errno);
97                 goto out_close;
98         }
99
100         /* bind socket to mcast address */
101         if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) {
102                 err = -errno;
103                 printk(UM_KERN_ERR "mcast_open : data bind failed, "
104                        "errno = %d\n", errno);
105                 goto out_close;
106         }
107
108         /* subscribe to the multicast group */
109         mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr;
110         mreq.imr_interface.s_addr = 0;
111         if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
112                        &mreq, sizeof(mreq)) < 0) {
113                 err = -errno;
114                 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");
120                 goto out_close;
121         }
122
123         return fd;
124
125  out_close:
126         close(fd);
127  out:
128         return err;
129 }
130
131 static 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) {
141                 printk(UM_KERN_ERR "mcast_open: IP_DROP_MEMBERSHIP failed, "
142                        "error = %d\n", errno);
143         }
144
145         close(fd);
146 }
147
148 int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri)
149 {
150         struct sockaddr_in *data_addr = pri->mcast_addr;
151
152         return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
153 }
154
155 const struct net_user_info mcast_user_info = {
156         .init           = mcast_user_init,
157         .open           = mcast_open,
158         .close          = mcast_close,
159         .remove         = mcast_remove,
160         .add_address    = NULL,
161         .delete_address = NULL,
162         .mtu            = ETH_MAX_PACKET,
163         .max_packet     = ETH_MAX_PACKET + ETH_HEADER_OTHER,
164 };