diff options
Diffstat (limited to 'net/batman-adv/icmp_socket.c')
-rw-r--r-- | net/batman-adv/icmp_socket.c | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c new file mode 100644 index 000000000000..ecf6d7ffab2e --- /dev/null +++ b/net/batman-adv/icmp_socket.c | |||
@@ -0,0 +1,356 @@ | |||
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 "originator.h" | ||
30 | #include "hard-interface.h" | ||
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 | |||
88 | spin_lock_bh(&socket_client->lock); | ||
89 | |||
90 | /* for all packets in the queue ... */ | ||
91 | list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) { | ||
92 | socket_packet = list_entry(list_pos, | ||
93 | struct socket_packet, list); | ||
94 | |||
95 | list_del(list_pos); | ||
96 | kfree(socket_packet); | ||
97 | } | ||
98 | |||
99 | socket_client_hash[socket_client->index] = NULL; | ||
100 | spin_unlock_bh(&socket_client->lock); | ||
101 | |||
102 | kfree(socket_client); | ||
103 | dec_module_count(); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static ssize_t bat_socket_read(struct file *file, char __user *buf, | ||
109 | size_t count, loff_t *ppos) | ||
110 | { | ||
111 | struct socket_client *socket_client = file->private_data; | ||
112 | struct socket_packet *socket_packet; | ||
113 | size_t packet_len; | ||
114 | int error; | ||
115 | |||
116 | if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0)) | ||
117 | return -EAGAIN; | ||
118 | |||
119 | if ((!buf) || (count < sizeof(struct icmp_packet))) | ||
120 | return -EINVAL; | ||
121 | |||
122 | if (!access_ok(VERIFY_WRITE, buf, count)) | ||
123 | return -EFAULT; | ||
124 | |||
125 | error = wait_event_interruptible(socket_client->queue_wait, | ||
126 | socket_client->queue_len); | ||
127 | |||
128 | if (error) | ||
129 | return error; | ||
130 | |||
131 | spin_lock_bh(&socket_client->lock); | ||
132 | |||
133 | socket_packet = list_first_entry(&socket_client->queue_list, | ||
134 | struct socket_packet, list); | ||
135 | list_del(&socket_packet->list); | ||
136 | socket_client->queue_len--; | ||
137 | |||
138 | spin_unlock_bh(&socket_client->lock); | ||
139 | |||
140 | error = __copy_to_user(buf, &socket_packet->icmp_packet, | ||
141 | socket_packet->icmp_len); | ||
142 | |||
143 | packet_len = socket_packet->icmp_len; | ||
144 | kfree(socket_packet); | ||
145 | |||
146 | if (error) | ||
147 | return -EFAULT; | ||
148 | |||
149 | return packet_len; | ||
150 | } | ||
151 | |||
152 | static ssize_t bat_socket_write(struct file *file, const char __user *buff, | ||
153 | size_t len, loff_t *off) | ||
154 | { | ||
155 | struct socket_client *socket_client = file->private_data; | ||
156 | struct bat_priv *bat_priv = socket_client->bat_priv; | ||
157 | struct sk_buff *skb; | ||
158 | struct icmp_packet_rr *icmp_packet; | ||
159 | |||
160 | struct orig_node *orig_node; | ||
161 | struct batman_if *batman_if; | ||
162 | size_t packet_len = sizeof(struct icmp_packet); | ||
163 | uint8_t dstaddr[ETH_ALEN]; | ||
164 | |||
165 | if (len < sizeof(struct icmp_packet)) { | ||
166 | bat_dbg(DBG_BATMAN, bat_priv, | ||
167 | "Error - can't send packet from char device: " | ||
168 | "invalid packet size\n"); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | if (!bat_priv->primary_if) | ||
173 | return -EFAULT; | ||
174 | |||
175 | if (len >= sizeof(struct icmp_packet_rr)) | ||
176 | packet_len = sizeof(struct icmp_packet_rr); | ||
177 | |||
178 | skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr)); | ||
179 | if (!skb) | ||
180 | return -ENOMEM; | ||
181 | |||
182 | skb_reserve(skb, sizeof(struct ethhdr)); | ||
183 | icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len); | ||
184 | |||
185 | if (!access_ok(VERIFY_READ, buff, packet_len)) { | ||
186 | len = -EFAULT; | ||
187 | goto free_skb; | ||
188 | } | ||
189 | |||
190 | if (__copy_from_user(icmp_packet, buff, packet_len)) { | ||
191 | len = -EFAULT; | ||
192 | goto free_skb; | ||
193 | } | ||
194 | |||
195 | if (icmp_packet->packet_type != BAT_ICMP) { | ||
196 | bat_dbg(DBG_BATMAN, bat_priv, | ||
197 | "Error - can't send packet from char device: " | ||
198 | "got bogus packet type (expected: BAT_ICMP)\n"); | ||
199 | len = -EINVAL; | ||
200 | goto free_skb; | ||
201 | } | ||
202 | |||
203 | if (icmp_packet->msg_type != ECHO_REQUEST) { | ||
204 | bat_dbg(DBG_BATMAN, bat_priv, | ||
205 | "Error - can't send packet from char device: " | ||
206 | "got bogus message type (expected: ECHO_REQUEST)\n"); | ||
207 | len = -EINVAL; | ||
208 | goto free_skb; | ||
209 | } | ||
210 | |||
211 | icmp_packet->uid = socket_client->index; | ||
212 | |||
213 | if (icmp_packet->version != COMPAT_VERSION) { | ||
214 | icmp_packet->msg_type = PARAMETER_PROBLEM; | ||
215 | icmp_packet->ttl = COMPAT_VERSION; | ||
216 | bat_socket_add_packet(socket_client, icmp_packet, packet_len); | ||
217 | goto free_skb; | ||
218 | } | ||
219 | |||
220 | if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) | ||
221 | goto dst_unreach; | ||
222 | |||
223 | spin_lock_bh(&bat_priv->orig_hash_lock); | ||
224 | orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, | ||
225 | compare_orig, choose_orig, | ||
226 | icmp_packet->dst)); | ||
227 | |||
228 | if (!orig_node) | ||
229 | goto unlock; | ||
230 | |||
231 | if (!orig_node->router) | ||
232 | goto unlock; | ||
233 | |||
234 | batman_if = orig_node->router->if_incoming; | ||
235 | memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); | ||
236 | |||
237 | spin_unlock_bh(&bat_priv->orig_hash_lock); | ||
238 | |||
239 | if (!batman_if) | ||
240 | goto dst_unreach; | ||
241 | |||
242 | if (batman_if->if_status != IF_ACTIVE) | ||
243 | goto dst_unreach; | ||
244 | |||
245 | memcpy(icmp_packet->orig, | ||
246 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); | ||
247 | |||
248 | if (packet_len == sizeof(struct icmp_packet_rr)) | ||
249 | memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN); | ||
250 | |||
251 | |||
252 | send_skb_packet(skb, batman_if, dstaddr); | ||
253 | |||
254 | goto out; | ||
255 | |||
256 | unlock: | ||
257 | spin_unlock_bh(&bat_priv->orig_hash_lock); | ||
258 | dst_unreach: | ||
259 | icmp_packet->msg_type = DESTINATION_UNREACHABLE; | ||
260 | bat_socket_add_packet(socket_client, icmp_packet, packet_len); | ||
261 | free_skb: | ||
262 | kfree_skb(skb); | ||
263 | out: | ||
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(struct 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 | } | ||