Blame view

net/batman-adv/icmp_socket.c 8.47 KB
c6c8fea29   Sven Eckelmann   net: Add batman-a...
1
  /*
64afe3539   Sven Eckelmann   batman-adv: Updat...
2
   * Copyright (C) 2007-2011 B.A.T.M.A.N. contributors:
c6c8fea29   Sven Eckelmann   net: Add batman-a...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   *
   * Marek Lindner
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of version 2 of the GNU General Public
   * License as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   * 02110-1301, USA
   *
   */
  
  #include "main.h"
  #include <linux/debugfs.h>
  #include <linux/slab.h>
  #include "icmp_socket.h"
  #include "send.h"
c6c8fea29   Sven Eckelmann   net: Add batman-a...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  #include "hash.h"
  #include "originator.h"
  #include "hard-interface.h"
  
  static struct socket_client *socket_client_hash[256];
  
  static void bat_socket_add_packet(struct socket_client *socket_client,
  				  struct icmp_packet_rr *icmp_packet,
  				  size_t icmp_len);
  
  void bat_socket_init(void)
  {
  	memset(socket_client_hash, 0, sizeof(socket_client_hash));
  }
  
  static int bat_socket_open(struct inode *inode, struct file *file)
  {
  	unsigned int i;
  	struct socket_client *socket_client;
  
  	nonseekable_open(inode, file);
704509b8d   Sven Eckelmann   batman-adv: Calcu...
48
  	socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  
  	if (!socket_client)
  		return -ENOMEM;
  
  	for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) {
  		if (!socket_client_hash[i]) {
  			socket_client_hash[i] = socket_client;
  			break;
  		}
  	}
  
  	if (i == ARRAY_SIZE(socket_client_hash)) {
  		pr_err("Error - can't add another packet client: "
  		       "maximum number of clients reached
  ");
  		kfree(socket_client);
  		return -EXFULL;
  	}
  
  	INIT_LIST_HEAD(&socket_client->queue_list);
  	socket_client->queue_len = 0;
  	socket_client->index = i;
  	socket_client->bat_priv = inode->i_private;
  	spin_lock_init(&socket_client->lock);
  	init_waitqueue_head(&socket_client->queue_wait);
  
  	file->private_data = socket_client;
  
  	inc_module_count();
  	return 0;
  }
  
  static int bat_socket_release(struct inode *inode, struct file *file)
  {
  	struct socket_client *socket_client = file->private_data;
  	struct socket_packet *socket_packet;
  	struct list_head *list_pos, *list_pos_tmp;
  
  	spin_lock_bh(&socket_client->lock);
  
  	/* for all packets in the queue ... */
  	list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) {
  		socket_packet = list_entry(list_pos,
  					   struct socket_packet, list);
  
  		list_del(list_pos);
  		kfree(socket_packet);
  	}
  
  	socket_client_hash[socket_client->index] = NULL;
  	spin_unlock_bh(&socket_client->lock);
  
  	kfree(socket_client);
  	dec_module_count();
  
  	return 0;
  }
  
  static ssize_t bat_socket_read(struct file *file, char __user *buf,
  			       size_t count, loff_t *ppos)
  {
  	struct socket_client *socket_client = file->private_data;
  	struct socket_packet *socket_packet;
  	size_t packet_len;
  	int error;
  
  	if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0))
  		return -EAGAIN;
  
  	if ((!buf) || (count < sizeof(struct icmp_packet)))
  		return -EINVAL;
  
  	if (!access_ok(VERIFY_WRITE, buf, count))
  		return -EFAULT;
  
  	error = wait_event_interruptible(socket_client->queue_wait,
  					 socket_client->queue_len);
  
  	if (error)
  		return error;
  
  	spin_lock_bh(&socket_client->lock);
  
  	socket_packet = list_first_entry(&socket_client->queue_list,
  					 struct socket_packet, list);
  	list_del(&socket_packet->list);
  	socket_client->queue_len--;
  
  	spin_unlock_bh(&socket_client->lock);
b5a1eeef0   Sven Eckelmann   batman-adv: Only ...
138
139
  	packet_len = min(count, socket_packet->icmp_len);
  	error = copy_to_user(buf, &socket_packet->icmp_packet, packet_len);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
140

c6c8fea29   Sven Eckelmann   net: Add batman-a...
141
142
143
144
145
146
147
148
149
150
151
152
153
  	kfree(socket_packet);
  
  	if (error)
  		return -EFAULT;
  
  	return packet_len;
  }
  
  static ssize_t bat_socket_write(struct file *file, const char __user *buff,
  				size_t len, loff_t *off)
  {
  	struct socket_client *socket_client = file->private_data;
  	struct bat_priv *bat_priv = socket_client->bat_priv;
32ae9b221   Marek Lindner   batman-adv: Make ...
154
  	struct hard_iface *primary_if = NULL;
c6c8fea29   Sven Eckelmann   net: Add batman-a...
155
156
  	struct sk_buff *skb;
  	struct icmp_packet_rr *icmp_packet;
44524fcdf   Marek Lindner   batman-adv: Corre...
157
158
  	struct orig_node *orig_node = NULL;
  	struct neigh_node *neigh_node = NULL;
c6c8fea29   Sven Eckelmann   net: Add batman-a...
159
  	size_t packet_len = sizeof(struct icmp_packet);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
160
161
162
163
164
165
166
167
  
  	if (len < sizeof(struct icmp_packet)) {
  		bat_dbg(DBG_BATMAN, bat_priv,
  			"Error - can't send packet from char device: "
  			"invalid packet size
  ");
  		return -EINVAL;
  	}
32ae9b221   Marek Lindner   batman-adv: Make ...
168
169
170
171
172
173
  	primary_if = primary_if_get_selected(bat_priv);
  
  	if (!primary_if) {
  		len = -EFAULT;
  		goto out;
  	}
c6c8fea29   Sven Eckelmann   net: Add batman-a...
174
175
176
177
178
  
  	if (len >= sizeof(struct icmp_packet_rr))
  		packet_len = sizeof(struct icmp_packet_rr);
  
  	skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
32ae9b221   Marek Lindner   batman-adv: Make ...
179
180
181
182
  	if (!skb) {
  		len = -ENOMEM;
  		goto out;
  	}
c6c8fea29   Sven Eckelmann   net: Add batman-a...
183
184
185
  
  	skb_reserve(skb, sizeof(struct ethhdr));
  	icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
d18eb4533   Sven Eckelmann   batman-adv: Direc...
186
  	if (copy_from_user(icmp_packet, buff, packet_len)) {
c6c8fea29   Sven Eckelmann   net: Add batman-a...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  		len = -EFAULT;
  		goto free_skb;
  	}
  
  	if (icmp_packet->packet_type != BAT_ICMP) {
  		bat_dbg(DBG_BATMAN, bat_priv,
  			"Error - can't send packet from char device: "
  			"got bogus packet type (expected: BAT_ICMP)
  ");
  		len = -EINVAL;
  		goto free_skb;
  	}
  
  	if (icmp_packet->msg_type != ECHO_REQUEST) {
  		bat_dbg(DBG_BATMAN, bat_priv,
  			"Error - can't send packet from char device: "
  			"got bogus message type (expected: ECHO_REQUEST)
  ");
  		len = -EINVAL;
  		goto free_skb;
  	}
  
  	icmp_packet->uid = socket_client->index;
  
  	if (icmp_packet->version != COMPAT_VERSION) {
  		icmp_packet->msg_type = PARAMETER_PROBLEM;
ad2443127   Marek Lindner   batman-adv: repor...
213
  		icmp_packet->version = COMPAT_VERSION;
c6c8fea29   Sven Eckelmann   net: Add batman-a...
214
215
216
217
218
219
  		bat_socket_add_packet(socket_client, icmp_packet, packet_len);
  		goto free_skb;
  	}
  
  	if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
  		goto dst_unreach;
7aadf889e   Marek Lindner   batman-adv: remov...
220
  	orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
221
  	if (!orig_node)
e1a5382f9   Linus Lüssing   batman-adv: Make ...
222
  		goto dst_unreach;
44524fcdf   Marek Lindner   batman-adv: Corre...
223

e1a5382f9   Linus Lüssing   batman-adv: Make ...
224
  	neigh_node = orig_node_get_router(orig_node);
44524fcdf   Marek Lindner   batman-adv: Corre...
225
  	if (!neigh_node)
e1a5382f9   Linus Lüssing   batman-adv: Make ...
226
  		goto dst_unreach;
c6c8fea29   Sven Eckelmann   net: Add batman-a...
227

d0072609b   Marek Lindner   batman-adv: remov...
228
  	if (!neigh_node->if_incoming)
c6c8fea29   Sven Eckelmann   net: Add batman-a...
229
  		goto dst_unreach;
d0072609b   Marek Lindner   batman-adv: remov...
230
  	if (neigh_node->if_incoming->if_status != IF_ACTIVE)
c6c8fea29   Sven Eckelmann   net: Add batman-a...
231
232
233
  		goto dst_unreach;
  
  	memcpy(icmp_packet->orig,
32ae9b221   Marek Lindner   batman-adv: Make ...
234
  	       primary_if->net_dev->dev_addr, ETH_ALEN);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
235
236
  
  	if (packet_len == sizeof(struct icmp_packet_rr))
44524fcdf   Marek Lindner   batman-adv: Corre...
237
  		memcpy(icmp_packet->rr,
d0072609b   Marek Lindner   batman-adv: remov...
238
  		       neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
239

d0072609b   Marek Lindner   batman-adv: remov...
240
  	send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
241
  	goto out;
c6c8fea29   Sven Eckelmann   net: Add batman-a...
242
243
244
245
246
247
  dst_unreach:
  	icmp_packet->msg_type = DESTINATION_UNREACHABLE;
  	bat_socket_add_packet(socket_client, icmp_packet, packet_len);
  free_skb:
  	kfree_skb(skb);
  out:
32ae9b221   Marek Lindner   batman-adv: Make ...
248
249
  	if (primary_if)
  		hardif_free_ref(primary_if);
44524fcdf   Marek Lindner   batman-adv: Corre...
250
251
252
  	if (neigh_node)
  		neigh_node_free_ref(neigh_node);
  	if (orig_node)
7b36e8eef   Marek Lindner   batman-adv: Corre...
253
  		orig_node_free_ref(orig_node);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  	return len;
  }
  
  static unsigned int bat_socket_poll(struct file *file, poll_table *wait)
  {
  	struct socket_client *socket_client = file->private_data;
  
  	poll_wait(file, &socket_client->queue_wait, wait);
  
  	if (socket_client->queue_len > 0)
  		return POLLIN | POLLRDNORM;
  
  	return 0;
  }
  
  static const struct file_operations fops = {
  	.owner = THIS_MODULE,
  	.open = bat_socket_open,
  	.release = bat_socket_release,
  	.read = bat_socket_read,
  	.write = bat_socket_write,
  	.poll = bat_socket_poll,
  	.llseek = no_llseek,
  };
  
  int bat_socket_setup(struct bat_priv *bat_priv)
  {
  	struct dentry *d;
  
  	if (!bat_priv->debug_dir)
  		goto err;
  
  	d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR,
  				bat_priv->debug_dir, bat_priv, &fops);
  	if (d)
  		goto err;
  
  	return 0;
  
  err:
  	return 1;
  }
  
  static void bat_socket_add_packet(struct socket_client *socket_client,
  				  struct icmp_packet_rr *icmp_packet,
  				  size_t icmp_len)
  {
  	struct socket_packet *socket_packet;
704509b8d   Sven Eckelmann   batman-adv: Calcu...
302
  	socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC);
c6c8fea29   Sven Eckelmann   net: Add batman-a...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  
  	if (!socket_packet)
  		return;
  
  	INIT_LIST_HEAD(&socket_packet->list);
  	memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len);
  	socket_packet->icmp_len = icmp_len;
  
  	spin_lock_bh(&socket_client->lock);
  
  	/* while waiting for the lock the socket_client could have been
  	 * deleted */
  	if (!socket_client_hash[icmp_packet->uid]) {
  		spin_unlock_bh(&socket_client->lock);
  		kfree(socket_packet);
  		return;
  	}
  
  	list_add_tail(&socket_packet->list, &socket_client->queue_list);
  	socket_client->queue_len++;
  
  	if (socket_client->queue_len > 100) {
  		socket_packet = list_first_entry(&socket_client->queue_list,
  						 struct socket_packet, list);
  
  		list_del(&socket_packet->list);
  		kfree(socket_packet);
  		socket_client->queue_len--;
  	}
  
  	spin_unlock_bh(&socket_client->lock);
  
  	wake_up(&socket_client->queue_wait);
  }
  
  void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet,
  			       size_t icmp_len)
  {
  	struct socket_client *hash = socket_client_hash[icmp_packet->uid];
  
  	if (hash)
  		bat_socket_add_packet(hash, icmp_packet, icmp_len);
  }