~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to net/batman-adv/icmp_socket.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007-2011 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 "hash.h"
 
28
#include "originator.h"
 
29
#include "hard-interface.h"
 
30
 
 
31
static struct socket_client *socket_client_hash[256];
 
32
 
 
33
static void bat_socket_add_packet(struct socket_client *socket_client,
 
34
                                  struct icmp_packet_rr *icmp_packet,
 
35
                                  size_t icmp_len);
 
36
 
 
37
void bat_socket_init(void)
 
38
{
 
39
        memset(socket_client_hash, 0, sizeof(socket_client_hash));
 
40
}
 
41
 
 
42
static int bat_socket_open(struct inode *inode, struct file *file)
 
43
{
 
44
        unsigned int i;
 
45
        struct socket_client *socket_client;
 
46
 
 
47
        nonseekable_open(inode, file);
 
48
 
 
49
        socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL);
 
50
 
 
51
        if (!socket_client)
 
52
                return -ENOMEM;
 
53
 
 
54
        for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) {
 
55
                if (!socket_client_hash[i]) {
 
56
                        socket_client_hash[i] = socket_client;
 
57
                        break;
 
58
                }
 
59
        }
 
60
 
 
61
        if (i == ARRAY_SIZE(socket_client_hash)) {
 
62
                pr_err("Error - can't add another packet client: "
 
63
                       "maximum number of clients reached\n");
 
64
                kfree(socket_client);
 
65
                return -EXFULL;
 
66
        }
 
67
 
 
68
        INIT_LIST_HEAD(&socket_client->queue_list);
 
69
        socket_client->queue_len = 0;
 
70
        socket_client->index = i;
 
71
        socket_client->bat_priv = inode->i_private;
 
72
        spin_lock_init(&socket_client->lock);
 
73
        init_waitqueue_head(&socket_client->queue_wait);
 
74
 
 
75
        file->private_data = socket_client;
 
76
 
 
77
        inc_module_count();
 
78
        return 0;
 
79
}
 
80
 
 
81
static int bat_socket_release(struct inode *inode, struct file *file)
 
82
{
 
83
        struct socket_client *socket_client = file->private_data;
 
84
        struct socket_packet *socket_packet;
 
85
        struct list_head *list_pos, *list_pos_tmp;
 
86
 
 
87
        spin_lock_bh(&socket_client->lock);
 
88
 
 
89
        /* for all packets in the queue ... */
 
90
        list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) {
 
91
                socket_packet = list_entry(list_pos,
 
92
                                           struct socket_packet, list);
 
93
 
 
94
                list_del(list_pos);
 
95
                kfree(socket_packet);
 
96
        }
 
97
 
 
98
        socket_client_hash[socket_client->index] = NULL;
 
99
        spin_unlock_bh(&socket_client->lock);
 
100
 
 
101
        kfree(socket_client);
 
102
        dec_module_count();
 
103
 
 
104
        return 0;
 
105
}
 
106
 
 
107
static ssize_t bat_socket_read(struct file *file, char __user *buf,
 
108
                               size_t count, loff_t *ppos)
 
109
{
 
110
        struct socket_client *socket_client = file->private_data;
 
111
        struct socket_packet *socket_packet;
 
112
        size_t packet_len;
 
113
        int error;
 
114
 
 
115
        if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0))
 
116
                return -EAGAIN;
 
117
 
 
118
        if ((!buf) || (count < sizeof(struct icmp_packet)))
 
119
                return -EINVAL;
 
120
 
 
121
        if (!access_ok(VERIFY_WRITE, buf, count))
 
122
                return -EFAULT;
 
123
 
 
124
        error = wait_event_interruptible(socket_client->queue_wait,
 
125
                                         socket_client->queue_len);
 
126
 
 
127
        if (error)
 
128
                return error;
 
129
 
 
130
        spin_lock_bh(&socket_client->lock);
 
131
 
 
132
        socket_packet = list_first_entry(&socket_client->queue_list,
 
133
                                         struct socket_packet, list);
 
134
        list_del(&socket_packet->list);
 
135
        socket_client->queue_len--;
 
136
 
 
137
        spin_unlock_bh(&socket_client->lock);
 
138
 
 
139
        error = __copy_to_user(buf, &socket_packet->icmp_packet,
 
140
                               socket_packet->icmp_len);
 
141
 
 
142
        packet_len = socket_packet->icmp_len;
 
143
        kfree(socket_packet);
 
144
 
 
145
        if (error)
 
146
                return -EFAULT;
 
147
 
 
148
        return packet_len;
 
149
}
 
150
 
 
151
static ssize_t bat_socket_write(struct file *file, const char __user *buff,
 
152
                                size_t len, loff_t *off)
 
153
{
 
154
        struct socket_client *socket_client = file->private_data;
 
155
        struct bat_priv *bat_priv = socket_client->bat_priv;
 
156
        struct hard_iface *primary_if = NULL;
 
157
        struct sk_buff *skb;
 
158
        struct icmp_packet_rr *icmp_packet;
 
159
 
 
160
        struct orig_node *orig_node = NULL;
 
161
        struct neigh_node *neigh_node = NULL;
 
162
        size_t packet_len = sizeof(struct icmp_packet);
 
163
 
 
164
        if (len < sizeof(struct icmp_packet)) {
 
165
                bat_dbg(DBG_BATMAN, bat_priv,
 
166
                        "Error - can't send packet from char device: "
 
167
                        "invalid packet size\n");
 
168
                return -EINVAL;
 
169
        }
 
170
 
 
171
        primary_if = primary_if_get_selected(bat_priv);
 
172
 
 
173
        if (!primary_if) {
 
174
                len = -EFAULT;
 
175
                goto out;
 
176
        }
 
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
                len = -ENOMEM;
 
184
                goto out;
 
185
        }
 
186
 
 
187
        skb_reserve(skb, sizeof(struct ethhdr));
 
188
        icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
 
189
 
 
190
        if (!access_ok(VERIFY_READ, buff, packet_len)) {
 
191
                len = -EFAULT;
 
192
                goto free_skb;
 
193
        }
 
194
 
 
195
        if (__copy_from_user(icmp_packet, buff, packet_len)) {
 
196
                len = -EFAULT;
 
197
                goto free_skb;
 
198
        }
 
199
 
 
200
        if (icmp_packet->packet_type != BAT_ICMP) {
 
201
                bat_dbg(DBG_BATMAN, bat_priv,
 
202
                        "Error - can't send packet from char device: "
 
203
                        "got bogus packet type (expected: BAT_ICMP)\n");
 
204
                len = -EINVAL;
 
205
                goto free_skb;
 
206
        }
 
207
 
 
208
        if (icmp_packet->msg_type != ECHO_REQUEST) {
 
209
                bat_dbg(DBG_BATMAN, bat_priv,
 
210
                        "Error - can't send packet from char device: "
 
211
                        "got bogus message type (expected: ECHO_REQUEST)\n");
 
212
                len = -EINVAL;
 
213
                goto free_skb;
 
214
        }
 
215
 
 
216
        icmp_packet->uid = socket_client->index;
 
217
 
 
218
        if (icmp_packet->version != COMPAT_VERSION) {
 
219
                icmp_packet->msg_type = PARAMETER_PROBLEM;
 
220
                icmp_packet->ttl = COMPAT_VERSION;
 
221
                bat_socket_add_packet(socket_client, icmp_packet, packet_len);
 
222
                goto free_skb;
 
223
        }
 
224
 
 
225
        if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
 
226
                goto dst_unreach;
 
227
 
 
228
        orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
 
229
        if (!orig_node)
 
230
                goto dst_unreach;
 
231
 
 
232
        neigh_node = orig_node_get_router(orig_node);
 
233
        if (!neigh_node)
 
234
                goto dst_unreach;
 
235
 
 
236
        if (!neigh_node->if_incoming)
 
237
                goto dst_unreach;
 
238
 
 
239
        if (neigh_node->if_incoming->if_status != IF_ACTIVE)
 
240
                goto dst_unreach;
 
241
 
 
242
        memcpy(icmp_packet->orig,
 
243
               primary_if->net_dev->dev_addr, ETH_ALEN);
 
244
 
 
245
        if (packet_len == sizeof(struct icmp_packet_rr))
 
246
                memcpy(icmp_packet->rr,
 
247
                       neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
 
248
 
 
249
        send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
 
250
        goto out;
 
251
 
 
252
dst_unreach:
 
253
        icmp_packet->msg_type = DESTINATION_UNREACHABLE;
 
254
        bat_socket_add_packet(socket_client, icmp_packet, packet_len);
 
255
free_skb:
 
256
        kfree_skb(skb);
 
257
out:
 
258
        if (primary_if)
 
259
                hardif_free_ref(primary_if);
 
260
        if (neigh_node)
 
261
                neigh_node_free_ref(neigh_node);
 
262
        if (orig_node)
 
263
                orig_node_free_ref(orig_node);
 
264
        return len;
 
265
}
 
266
 
 
267
static unsigned int bat_socket_poll(struct file *file, poll_table *wait)
 
268
{
 
269
        struct socket_client *socket_client = file->private_data;
 
270
 
 
271
        poll_wait(file, &socket_client->queue_wait, wait);
 
272
 
 
273
        if (socket_client->queue_len > 0)
 
274
                return POLLIN | POLLRDNORM;
 
275
 
 
276
        return 0;
 
277
}
 
278
 
 
279
static const struct file_operations fops = {
 
280
        .owner = THIS_MODULE,
 
281
        .open = bat_socket_open,
 
282
        .release = bat_socket_release,
 
283
        .read = bat_socket_read,
 
284
        .write = bat_socket_write,
 
285
        .poll = bat_socket_poll,
 
286
        .llseek = no_llseek,
 
287
};
 
288
 
 
289
int bat_socket_setup(struct bat_priv *bat_priv)
 
290
{
 
291
        struct dentry *d;
 
292
 
 
293
        if (!bat_priv->debug_dir)
 
294
                goto err;
 
295
 
 
296
        d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR,
 
297
                                bat_priv->debug_dir, bat_priv, &fops);
 
298
        if (d)
 
299
                goto err;
 
300
 
 
301
        return 0;
 
302
 
 
303
err:
 
304
        return 1;
 
305
}
 
306
 
 
307
static void bat_socket_add_packet(struct socket_client *socket_client,
 
308
                                  struct icmp_packet_rr *icmp_packet,
 
309
                                  size_t icmp_len)
 
310
{
 
311
        struct socket_packet *socket_packet;
 
312
 
 
313
        socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
 
314
 
 
315
        if (!socket_packet)
 
316
                return;
 
317
 
 
318
        INIT_LIST_HEAD(&socket_packet->list);
 
319
        memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
 
320
        socket_packet->icmp_len = icmp_len;
 
321
 
 
322
        spin_lock_bh(&socket_client->lock);
 
323
 
 
324
        /* while waiting for the lock the socket_client could have been
 
325
         * deleted */
 
326
        if (!socket_client_hash[icmp_packet->uid]) {
 
327
                spin_unlock_bh(&socket_client->lock);
 
328
                kfree(socket_packet);
 
329
                return;
 
330
        }
 
331
 
 
332
        list_add_tail(&socket_packet->list, &socket_client->queue_list);
 
333
        socket_client->queue_len++;
 
334
 
 
335
        if (socket_client->queue_len > 100) {
 
336
                socket_packet = list_first_entry(&socket_client->queue_list,
 
337
                                                 struct socket_packet, list);
 
338
 
 
339
                list_del(&socket_packet->list);
 
340
                kfree(socket_packet);
 
341
                socket_client->queue_len--;
 
342
        }
 
343
 
 
344
        spin_unlock_bh(&socket_client->lock);
 
345
 
 
346
        wake_up(&socket_client->queue_wait);
 
347
}
 
348
 
 
349
void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet,
 
350
                               size_t icmp_len)
 
351
{
 
352
        struct socket_client *hash = socket_client_hash[icmp_packet->uid];
 
353
 
 
354
        if (hash)
 
355
                bat_socket_add_packet(hash, icmp_packet, icmp_len);
 
356
}