]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/um/drivers/umcast_user.c
Merge branch 'for-3.11-cpuset' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[karo-tx-linux.git] / arch / um / drivers / umcast_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 "umcast.h"
19 #include <net_user.h>
20 #include <um_malloc.h>
21
22 static struct sockaddr_in *new_addr(char *addr, unsigned short port)
23 {
24         struct sockaddr_in *sin;
25
26         sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
27         if (sin == NULL) {
28                 printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
29                        "failed\n");
30                 return NULL;
31         }
32         sin->sin_family = AF_INET;
33         if (addr)
34                 sin->sin_addr.s_addr = in_aton(addr);
35         else
36                 sin->sin_addr.s_addr = INADDR_ANY;
37         sin->sin_port = htons(port);
38         return sin;
39 }
40
41 static int umcast_user_init(void *data, void *dev)
42 {
43         struct umcast_data *pri = data;
44
45         pri->remote_addr = new_addr(pri->addr, pri->rport);
46         if (pri->unicast)
47                 pri->listen_addr = new_addr(NULL, pri->lport);
48         else
49                 pri->listen_addr = pri->remote_addr;
50         pri->dev = dev;
51         return 0;
52 }
53
54 static void umcast_remove(void *data)
55 {
56         struct umcast_data *pri = data;
57
58         kfree(pri->listen_addr);
59         if (pri->unicast)
60                 kfree(pri->remote_addr);
61         pri->listen_addr = pri->remote_addr = NULL;
62 }
63
64 static int umcast_open(void *data)
65 {
66         struct umcast_data *pri = data;
67         struct sockaddr_in *lsin = pri->listen_addr;
68         struct sockaddr_in *rsin = pri->remote_addr;
69         struct ip_mreq mreq;
70         int fd, yes = 1, err = -EINVAL;
71
72
73         if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
74             (rsin->sin_addr.s_addr == 0) ||
75             (lsin->sin_port == 0) || (rsin->sin_port == 0))
76                 goto out;
77
78         fd = socket(AF_INET, SOCK_DGRAM, 0);
79
80         if (fd < 0) {
81                 err = -errno;
82                 printk(UM_KERN_ERR "umcast_open : data socket failed, "
83                        "errno = %d\n", errno);
84                 goto out;
85         }
86
87         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
88                 err = -errno;
89                 printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
90                        "errno = %d\n", errno);
91                 goto out_close;
92         }
93
94         if (!pri->unicast) {
95                 /* set ttl according to config */
96                 if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
97                                sizeof(pri->ttl)) < 0) {
98                         err = -errno;
99                         printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
100                                "failed, error = %d\n", errno);
101                         goto out_close;
102                 }
103
104                 /* set LOOP, so data does get fed back to local sockets */
105                 if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
106                                &yes, sizeof(yes)) < 0) {
107                         err = -errno;
108                         printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
109                                "failed, error = %d\n", errno);
110                         goto out_close;
111                 }
112         }
113
114         /* bind socket to the address */
115         if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
116                 err = -errno;
117                 printk(UM_KERN_ERR "umcast_open : data bind failed, "
118                        "errno = %d\n", errno);
119                 goto out_close;
120         }
121
122         if (!pri->unicast) {
123                 /* subscribe to the multicast group */
124                 mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
125                 mreq.imr_interface.s_addr = 0;
126                 if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
127                                &mreq, sizeof(mreq)) < 0) {
128                         err = -errno;
129                         printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
130                                "failed, error = %d\n", errno);
131                         printk(UM_KERN_ERR "There appears not to be a "
132                                "multicast-capable network interface on the "
133                                "host.\n");
134                         printk(UM_KERN_ERR "eth0 should be configured in order "
135                                "to use the multicast transport.\n");
136                         goto out_close;
137                 }
138         }
139
140         return fd;
141
142  out_close:
143         close(fd);
144  out:
145         return err;
146 }
147
148 static void umcast_close(int fd, void *data)
149 {
150         struct umcast_data *pri = data;
151
152         if (!pri->unicast) {
153                 struct ip_mreq mreq;
154                 struct sockaddr_in *lsin = pri->listen_addr;
155
156                 mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
157                 mreq.imr_interface.s_addr = 0;
158                 if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
159                                &mreq, sizeof(mreq)) < 0) {
160                         printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
161                                "failed, error = %d\n", errno);
162                 }
163         }
164
165         close(fd);
166 }
167
168 int umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
169 {
170         struct sockaddr_in *data_addr = pri->remote_addr;
171
172         return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
173 }
174
175 const struct net_user_info umcast_user_info = {
176         .init   = umcast_user_init,
177         .open   = umcast_open,
178         .close  = umcast_close,
179         .remove = umcast_remove,
180         .add_address    = NULL,
181         .delete_address = NULL,
182         .mtu    = ETH_MAX_PACKET,
183         .max_packet     = ETH_MAX_PACKET + ETH_HEADER_OTHER,
184 };