Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / staging / batman-adv / icmp_socket.c
1 /*
2  * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors:
3  *
4  * Marek Lindner
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  *
20  */
21
22 #include "main.h"
23 #include <linux/debugfs.h>
24 #include <linux/slab.h>
25 #include "icmp_socket.h"
26 #include "send.h"
27 #include "types.h"
28 #include "hash.h"
29 #include "hard-interface.h"
30
31
32 static struct socket_client *socket_client_hash[256];
33
34 static void bat_socket_add_packet(struct socket_client *socket_client,
35                                   struct icmp_packet_rr *icmp_packet,
36                                   size_t icmp_len);
37
38 void bat_socket_init(void)
39 {
40         memset(socket_client_hash, 0, sizeof(socket_client_hash));
41 }
42
43 static int bat_socket_open(struct inode *inode, struct file *file)
44 {
45         unsigned int i;
46         struct socket_client *socket_client;
47
48         nonseekable_open(inode, file);
49
50         socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL);
51
52         if (!socket_client)
53                 return -ENOMEM;
54
55         for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) {
56                 if (!socket_client_hash[i]) {
57                         socket_client_hash[i] = socket_client;
58                         break;
59                 }
60         }
61
62         if (i == ARRAY_SIZE(socket_client_hash)) {
63                 pr_err("Error - can't add another packet client: "
64                        "maximum number of clients reached\n");
65                 kfree(socket_client);
66                 return -EXFULL;
67         }
68
69         INIT_LIST_HEAD(&socket_client->queue_list);
70         socket_client->queue_len = 0;
71         socket_client->index = i;
72         socket_client->bat_priv = inode->i_private;
73         spin_lock_init(&socket_client->lock);
74         init_waitqueue_head(&socket_client->queue_wait);
75
76         file->private_data = socket_client;
77
78         inc_module_count();
79         return 0;
80 }
81
82 static int bat_socket_release(struct inode *inode, struct file *file)
83 {
84         struct socket_client *socket_client = file->private_data;
85         struct socket_packet *socket_packet;
86         struct list_head *list_pos, *list_pos_tmp;
87         unsigned long flags;
88
89         spin_lock_irqsave(&socket_client->lock, flags);
90
91         /* for all packets in the queue ... */
92         list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) {
93                 socket_packet = list_entry(list_pos,
94                                            struct socket_packet, list);
95
96                 list_del(list_pos);
97                 kfree(socket_packet);
98         }
99
100         socket_client_hash[socket_client->index] = NULL;
101         spin_unlock_irqrestore(&socket_client->lock, flags);
102
103         kfree(socket_client);
104         dec_module_count();
105
106         return 0;
107 }
108
109 static ssize_t bat_socket_read(struct file *file, char __user *buf,
110                                size_t count, loff_t *ppos)
111 {
112         struct socket_client *socket_client = file->private_data;
113         struct socket_packet *socket_packet;
114         size_t packet_len;
115         int error;
116         unsigned long flags;
117
118         if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0))
119                 return -EAGAIN;
120
121         if ((!buf) || (count < sizeof(struct icmp_packet)))
122                 return -EINVAL;
123
124         if (!access_ok(VERIFY_WRITE, buf, count))
125                 return -EFAULT;
126
127         error = wait_event_interruptible(socket_client->queue_wait,
128                                          socket_client->queue_len);
129
130         if (error)
131                 return error;
132
133         spin_lock_irqsave(&socket_client->lock, flags);
134
135         socket_packet = list_first_entry(&socket_client->queue_list,
136                                          struct socket_packet, list);
137         list_del(&socket_packet->list);
138         socket_client->queue_len--;
139
140         spin_unlock_irqrestore(&socket_client->lock, flags);
141
142         error = __copy_to_user(buf, &socket_packet->icmp_packet,
143                                socket_packet->icmp_len);
144
145         packet_len = socket_packet->icmp_len;
146         kfree(socket_packet);
147
148         if (error)
149                 return -EFAULT;
150
151         return packet_len;
152 }
153
154 static ssize_t bat_socket_write(struct file *file, const char __user *buff,
155                                 size_t len, loff_t *off)
156 {
157         struct socket_client *socket_client = file->private_data;
158         struct bat_priv *bat_priv = socket_client->bat_priv;
159         struct sk_buff *skb;
160         struct icmp_packet_rr *icmp_packet;
161
162         struct orig_node *orig_node;
163         struct batman_if *batman_if;
164         size_t packet_len = sizeof(struct icmp_packet);
165         uint8_t dstaddr[ETH_ALEN];
166         unsigned long flags;
167
168         if (len < sizeof(struct icmp_packet)) {
169                 bat_dbg(DBG_BATMAN, bat_priv,
170                         "Error - can't send packet from char device: "
171                         "invalid packet size\n");
172                 return -EINVAL;
173         }
174
175         if (!bat_priv->primary_if)
176                 return -EFAULT;
177
178         if (len >= sizeof(struct icmp_packet_rr))
179                 packet_len = sizeof(struct icmp_packet_rr);
180
181         skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
182         if (!skb)
183                 return -ENOMEM;
184
185         skb_reserve(skb, sizeof(struct ethhdr));
186         icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
187
188         if (!access_ok(VERIFY_READ, buff, packet_len)) {
189                 len = -EFAULT;
190                 goto free_skb;
191         }
192
193         if (__copy_from_user(icmp_packet, buff, packet_len)) {
194                 len = -EFAULT;
195                 goto free_skb;
196         }
197
198         if (icmp_packet->packet_type != BAT_ICMP) {
199                 bat_dbg(DBG_BATMAN, bat_priv,
200                         "Error - can't send packet from char device: "
201                         "got bogus packet type (expected: BAT_ICMP)\n");
202                 len = -EINVAL;
203                 goto free_skb;
204         }
205
206         if (icmp_packet->msg_type != ECHO_REQUEST) {
207                 bat_dbg(DBG_BATMAN, bat_priv,
208                         "Error - can't send packet from char device: "
209                         "got bogus message type (expected: ECHO_REQUEST)\n");
210                 len = -EINVAL;
211                 goto free_skb;
212         }
213
214         icmp_packet->uid = socket_client->index;
215
216         if (icmp_packet->version != COMPAT_VERSION) {
217                 icmp_packet->msg_type = PARAMETER_PROBLEM;
218                 icmp_packet->ttl = COMPAT_VERSION;
219                 bat_socket_add_packet(socket_client, icmp_packet, packet_len);
220                 goto free_skb;
221         }
222
223         if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
224                 goto dst_unreach;
225
226         spin_lock_irqsave(&bat_priv->orig_hash_lock, flags);
227         orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
228                                                    icmp_packet->dst));
229
230         if (!orig_node)
231                 goto unlock;
232
233         if (!orig_node->router)
234                 goto unlock;
235
236         batman_if = orig_node->router->if_incoming;
237         memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
238
239         spin_unlock_irqrestore(&bat_priv->orig_hash_lock, flags);
240
241         if (!batman_if)
242                 goto dst_unreach;
243
244         if (batman_if->if_status != IF_ACTIVE)
245                 goto dst_unreach;
246
247         memcpy(icmp_packet->orig,
248                bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
249
250         if (packet_len == sizeof(struct icmp_packet_rr))
251                 memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN);
252
253
254         send_skb_packet(skb, batman_if, dstaddr);
255
256         goto out;
257
258 unlock:
259         spin_unlock_irqrestore(&bat_priv->orig_hash_lock, flags);
260 dst_unreach:
261         icmp_packet->msg_type = DESTINATION_UNREACHABLE;
262         bat_socket_add_packet(socket_client, icmp_packet, packet_len);
263 free_skb:
264         kfree_skb(skb);
265 out:
266         return len;
267 }
268
269 static unsigned int bat_socket_poll(struct file *file, poll_table *wait)
270 {
271         struct socket_client *socket_client = file->private_data;
272
273         poll_wait(file, &socket_client->queue_wait, wait);
274
275         if (socket_client->queue_len > 0)
276                 return POLLIN | POLLRDNORM;
277
278         return 0;
279 }
280
281 static const struct file_operations fops = {
282         .owner = THIS_MODULE,
283         .open = bat_socket_open,
284         .release = bat_socket_release,
285         .read = bat_socket_read,
286         .write = bat_socket_write,
287         .poll = bat_socket_poll,
288         .llseek = no_llseek,
289 };
290
291 int bat_socket_setup(struct bat_priv *bat_priv)
292 {
293         struct dentry *d;
294
295         if (!bat_priv->debug_dir)
296                 goto err;
297
298         d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR,
299                                 bat_priv->debug_dir, bat_priv, &fops);
300         if (d)
301                 goto err;
302
303         return 0;
304
305 err:
306         return 1;
307 }
308
309 static void bat_socket_add_packet(struct socket_client *socket_client,
310                                   struct icmp_packet_rr *icmp_packet,
311                                   size_t icmp_len)
312 {
313         struct socket_packet *socket_packet;
314         unsigned long flags;
315
316         socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC);
317
318         if (!socket_packet)
319                 return;
320
321         INIT_LIST_HEAD(&socket_packet->list);
322         memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
323         socket_packet->icmp_len = icmp_len;
324
325         spin_lock_irqsave(&socket_client->lock, flags);
326
327         /* while waiting for the lock the socket_client could have been
328          * deleted */
329         if (!socket_client_hash[icmp_packet->uid]) {
330                 spin_unlock_irqrestore(&socket_client->lock, flags);
331                 kfree(socket_packet);
332                 return;
333         }
334
335         list_add_tail(&socket_packet->list, &socket_client->queue_list);
336         socket_client->queue_len++;
337
338         if (socket_client->queue_len > 100) {
339                 socket_packet = list_first_entry(&socket_client->queue_list,
340                                                  struct socket_packet, list);
341
342                 list_del(&socket_packet->list);
343                 kfree(socket_packet);
344                 socket_client->queue_len--;
345         }
346
347         spin_unlock_irqrestore(&socket_client->lock, flags);
348
349         wake_up(&socket_client->queue_wait);
350 }
351
352 void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet,
353                                size_t icmp_len)
354 {
355         struct socket_client *hash = socket_client_hash[icmp_packet->uid];
356
357         if (hash)
358                 bat_socket_add_packet(hash, icmp_packet, icmp_len);
359 }