diff options
author | Sven Eckelmann <sven@narfation.org> | 2010-12-13 06:19:28 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-12-16 16:44:24 -0500 |
commit | c6c8fea29769d998d94fcec9b9f14d4b52b349d3 (patch) | |
tree | 2c8dc8d1a64d48c5737a5745e3c510ff53a23047 /net/batman-adv/soft-interface.c | |
parent | b236da6931e2482bfe44a7865dd4e7bb036f3496 (diff) |
net: Add batman-adv meshing protocol
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is a routing
protocol for multi-hop ad-hoc mesh networks. The networks may be wired or
wireless. See http://www.open-mesh.org/ for more information and user space
tools.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/batman-adv/soft-interface.c')
-rw-r--r-- | net/batman-adv/soft-interface.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c new file mode 100644 index 000000000000..e89ede192ed0 --- /dev/null +++ b/net/batman-adv/soft-interface.c | |||
@@ -0,0 +1,697 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: | ||
3 | * | ||
4 | * Marek Lindner, Simon Wunderlich | ||
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 "soft-interface.h" | ||
24 | #include "hard-interface.h" | ||
25 | #include "routing.h" | ||
26 | #include "send.h" | ||
27 | #include "bat_debugfs.h" | ||
28 | #include "translation-table.h" | ||
29 | #include "types.h" | ||
30 | #include "hash.h" | ||
31 | #include "gateway_common.h" | ||
32 | #include "gateway_client.h" | ||
33 | #include "send.h" | ||
34 | #include "bat_sysfs.h" | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/ethtool.h> | ||
37 | #include <linux/etherdevice.h> | ||
38 | #include <linux/if_vlan.h> | ||
39 | #include "unicast.h" | ||
40 | #include "routing.h" | ||
41 | |||
42 | |||
43 | static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); | ||
44 | static void bat_get_drvinfo(struct net_device *dev, | ||
45 | struct ethtool_drvinfo *info); | ||
46 | static u32 bat_get_msglevel(struct net_device *dev); | ||
47 | static void bat_set_msglevel(struct net_device *dev, u32 value); | ||
48 | static u32 bat_get_link(struct net_device *dev); | ||
49 | static u32 bat_get_rx_csum(struct net_device *dev); | ||
50 | static int bat_set_rx_csum(struct net_device *dev, u32 data); | ||
51 | |||
52 | static const struct ethtool_ops bat_ethtool_ops = { | ||
53 | .get_settings = bat_get_settings, | ||
54 | .get_drvinfo = bat_get_drvinfo, | ||
55 | .get_msglevel = bat_get_msglevel, | ||
56 | .set_msglevel = bat_set_msglevel, | ||
57 | .get_link = bat_get_link, | ||
58 | .get_rx_csum = bat_get_rx_csum, | ||
59 | .set_rx_csum = bat_set_rx_csum | ||
60 | }; | ||
61 | |||
62 | int my_skb_head_push(struct sk_buff *skb, unsigned int len) | ||
63 | { | ||
64 | int result; | ||
65 | |||
66 | /** | ||
67 | * TODO: We must check if we can release all references to non-payload | ||
68 | * data using skb_header_release in our skbs to allow skb_cow_header to | ||
69 | * work optimally. This means that those skbs are not allowed to read | ||
70 | * or write any data which is before the current position of skb->data | ||
71 | * after that call and thus allow other skbs with the same data buffer | ||
72 | * to write freely in that area. | ||
73 | */ | ||
74 | result = skb_cow_head(skb, len); | ||
75 | if (result < 0) | ||
76 | return result; | ||
77 | |||
78 | skb_push(skb, len); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static void softif_neigh_free_ref(struct kref *refcount) | ||
83 | { | ||
84 | struct softif_neigh *softif_neigh; | ||
85 | |||
86 | softif_neigh = container_of(refcount, struct softif_neigh, refcount); | ||
87 | kfree(softif_neigh); | ||
88 | } | ||
89 | |||
90 | static void softif_neigh_free_rcu(struct rcu_head *rcu) | ||
91 | { | ||
92 | struct softif_neigh *softif_neigh; | ||
93 | |||
94 | softif_neigh = container_of(rcu, struct softif_neigh, rcu); | ||
95 | kref_put(&softif_neigh->refcount, softif_neigh_free_ref); | ||
96 | } | ||
97 | |||
98 | void softif_neigh_purge(struct bat_priv *bat_priv) | ||
99 | { | ||
100 | struct softif_neigh *softif_neigh, *softif_neigh_tmp; | ||
101 | struct hlist_node *node, *node_tmp; | ||
102 | |||
103 | spin_lock_bh(&bat_priv->softif_neigh_lock); | ||
104 | |||
105 | hlist_for_each_entry_safe(softif_neigh, node, node_tmp, | ||
106 | &bat_priv->softif_neigh_list, list) { | ||
107 | |||
108 | if ((!time_after(jiffies, softif_neigh->last_seen + | ||
109 | msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) && | ||
110 | (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) | ||
111 | continue; | ||
112 | |||
113 | hlist_del_rcu(&softif_neigh->list); | ||
114 | |||
115 | if (bat_priv->softif_neigh == softif_neigh) { | ||
116 | bat_dbg(DBG_ROUTES, bat_priv, | ||
117 | "Current mesh exit point '%pM' vanished " | ||
118 | "(vid: %d).\n", | ||
119 | softif_neigh->addr, softif_neigh->vid); | ||
120 | softif_neigh_tmp = bat_priv->softif_neigh; | ||
121 | bat_priv->softif_neigh = NULL; | ||
122 | kref_put(&softif_neigh_tmp->refcount, | ||
123 | softif_neigh_free_ref); | ||
124 | } | ||
125 | |||
126 | call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); | ||
127 | } | ||
128 | |||
129 | spin_unlock_bh(&bat_priv->softif_neigh_lock); | ||
130 | } | ||
131 | |||
132 | static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, | ||
133 | uint8_t *addr, short vid) | ||
134 | { | ||
135 | struct softif_neigh *softif_neigh; | ||
136 | struct hlist_node *node; | ||
137 | |||
138 | rcu_read_lock(); | ||
139 | hlist_for_each_entry_rcu(softif_neigh, node, | ||
140 | &bat_priv->softif_neigh_list, list) { | ||
141 | if (memcmp(softif_neigh->addr, addr, ETH_ALEN) != 0) | ||
142 | continue; | ||
143 | |||
144 | if (softif_neigh->vid != vid) | ||
145 | continue; | ||
146 | |||
147 | softif_neigh->last_seen = jiffies; | ||
148 | goto found; | ||
149 | } | ||
150 | |||
151 | softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); | ||
152 | if (!softif_neigh) | ||
153 | goto out; | ||
154 | |||
155 | memcpy(softif_neigh->addr, addr, ETH_ALEN); | ||
156 | softif_neigh->vid = vid; | ||
157 | softif_neigh->last_seen = jiffies; | ||
158 | kref_init(&softif_neigh->refcount); | ||
159 | |||
160 | INIT_HLIST_NODE(&softif_neigh->list); | ||
161 | spin_lock_bh(&bat_priv->softif_neigh_lock); | ||
162 | hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); | ||
163 | spin_unlock_bh(&bat_priv->softif_neigh_lock); | ||
164 | |||
165 | found: | ||
166 | kref_get(&softif_neigh->refcount); | ||
167 | out: | ||
168 | rcu_read_unlock(); | ||
169 | return softif_neigh; | ||
170 | } | ||
171 | |||
172 | int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) | ||
173 | { | ||
174 | struct net_device *net_dev = (struct net_device *)seq->private; | ||
175 | struct bat_priv *bat_priv = netdev_priv(net_dev); | ||
176 | struct softif_neigh *softif_neigh; | ||
177 | struct hlist_node *node; | ||
178 | size_t buf_size, pos; | ||
179 | char *buff; | ||
180 | |||
181 | if (!bat_priv->primary_if) { | ||
182 | return seq_printf(seq, "BATMAN mesh %s disabled - " | ||
183 | "please specify interfaces to enable it\n", | ||
184 | net_dev->name); | ||
185 | } | ||
186 | |||
187 | seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); | ||
188 | |||
189 | buf_size = 1; | ||
190 | /* Estimate length for: " xx:xx:xx:xx:xx:xx\n" */ | ||
191 | rcu_read_lock(); | ||
192 | hlist_for_each_entry_rcu(softif_neigh, node, | ||
193 | &bat_priv->softif_neigh_list, list) | ||
194 | buf_size += 30; | ||
195 | rcu_read_unlock(); | ||
196 | |||
197 | buff = kmalloc(buf_size, GFP_ATOMIC); | ||
198 | if (!buff) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | buff[0] = '\0'; | ||
202 | pos = 0; | ||
203 | |||
204 | rcu_read_lock(); | ||
205 | hlist_for_each_entry_rcu(softif_neigh, node, | ||
206 | &bat_priv->softif_neigh_list, list) { | ||
207 | pos += snprintf(buff + pos, 31, "%s %pM (vid: %d)\n", | ||
208 | bat_priv->softif_neigh == softif_neigh | ||
209 | ? "=>" : " ", softif_neigh->addr, | ||
210 | softif_neigh->vid); | ||
211 | } | ||
212 | rcu_read_unlock(); | ||
213 | |||
214 | seq_printf(seq, "%s", buff); | ||
215 | kfree(buff); | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, | ||
220 | short vid) | ||
221 | { | ||
222 | struct bat_priv *bat_priv = netdev_priv(dev); | ||
223 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; | ||
224 | struct batman_packet *batman_packet; | ||
225 | struct softif_neigh *softif_neigh, *softif_neigh_tmp; | ||
226 | |||
227 | if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) | ||
228 | batman_packet = (struct batman_packet *) | ||
229 | (skb->data + ETH_HLEN + VLAN_HLEN); | ||
230 | else | ||
231 | batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); | ||
232 | |||
233 | if (batman_packet->version != COMPAT_VERSION) | ||
234 | goto err; | ||
235 | |||
236 | if (batman_packet->packet_type != BAT_PACKET) | ||
237 | goto err; | ||
238 | |||
239 | if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) | ||
240 | goto err; | ||
241 | |||
242 | if (is_my_mac(batman_packet->orig)) | ||
243 | goto err; | ||
244 | |||
245 | softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); | ||
246 | |||
247 | if (!softif_neigh) | ||
248 | goto err; | ||
249 | |||
250 | if (bat_priv->softif_neigh == softif_neigh) | ||
251 | goto out; | ||
252 | |||
253 | /* we got a neighbor but its mac is 'bigger' than ours */ | ||
254 | if (memcmp(bat_priv->primary_if->net_dev->dev_addr, | ||
255 | softif_neigh->addr, ETH_ALEN) < 0) | ||
256 | goto out; | ||
257 | |||
258 | /* switch to new 'smallest neighbor' */ | ||
259 | if ((bat_priv->softif_neigh) && | ||
260 | (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr, | ||
261 | ETH_ALEN) < 0)) { | ||
262 | bat_dbg(DBG_ROUTES, bat_priv, | ||
263 | "Changing mesh exit point from %pM (vid: %d) " | ||
264 | "to %pM (vid: %d).\n", | ||
265 | bat_priv->softif_neigh->addr, | ||
266 | bat_priv->softif_neigh->vid, | ||
267 | softif_neigh->addr, softif_neigh->vid); | ||
268 | softif_neigh_tmp = bat_priv->softif_neigh; | ||
269 | bat_priv->softif_neigh = softif_neigh; | ||
270 | kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref); | ||
271 | /* we need to hold the additional reference */ | ||
272 | goto err; | ||
273 | } | ||
274 | |||
275 | /* close own batX device and use softif_neigh as exit node */ | ||
276 | if ((!bat_priv->softif_neigh) && | ||
277 | (memcmp(softif_neigh->addr, | ||
278 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { | ||
279 | bat_dbg(DBG_ROUTES, bat_priv, | ||
280 | "Setting mesh exit point to %pM (vid: %d).\n", | ||
281 | softif_neigh->addr, softif_neigh->vid); | ||
282 | bat_priv->softif_neigh = softif_neigh; | ||
283 | /* we need to hold the additional reference */ | ||
284 | goto err; | ||
285 | } | ||
286 | |||
287 | out: | ||
288 | kref_put(&softif_neigh->refcount, softif_neigh_free_ref); | ||
289 | err: | ||
290 | kfree_skb(skb); | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | static int interface_open(struct net_device *dev) | ||
295 | { | ||
296 | netif_start_queue(dev); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int interface_release(struct net_device *dev) | ||
301 | { | ||
302 | netif_stop_queue(dev); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static struct net_device_stats *interface_stats(struct net_device *dev) | ||
307 | { | ||
308 | struct bat_priv *bat_priv = netdev_priv(dev); | ||
309 | return &bat_priv->stats; | ||
310 | } | ||
311 | |||
312 | static int interface_set_mac_addr(struct net_device *dev, void *p) | ||
313 | { | ||
314 | struct bat_priv *bat_priv = netdev_priv(dev); | ||
315 | struct sockaddr *addr = p; | ||
316 | |||
317 | if (!is_valid_ether_addr(addr->sa_data)) | ||
318 | return -EADDRNOTAVAIL; | ||
319 | |||
320 | /* only modify hna-table if it has been initialised before */ | ||
321 | if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) { | ||
322 | hna_local_remove(bat_priv, dev->dev_addr, | ||
323 | "mac address changed"); | ||
324 | hna_local_add(dev, addr->sa_data); | ||
325 | } | ||
326 | |||
327 | memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static int interface_change_mtu(struct net_device *dev, int new_mtu) | ||
332 | { | ||
333 | /* check ranges */ | ||
334 | if ((new_mtu < 68) || (new_mtu > hardif_min_mtu(dev))) | ||
335 | return -EINVAL; | ||
336 | |||
337 | dev->mtu = new_mtu; | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) | ||
343 | { | ||
344 | struct ethhdr *ethhdr = (struct ethhdr *)skb->data; | ||
345 | struct bat_priv *bat_priv = netdev_priv(soft_iface); | ||
346 | struct bcast_packet *bcast_packet; | ||
347 | struct vlan_ethhdr *vhdr; | ||
348 | int data_len = skb->len, ret; | ||
349 | short vid = -1; | ||
350 | bool do_bcast = false; | ||
351 | |||
352 | if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) | ||
353 | goto dropped; | ||
354 | |||
355 | soft_iface->trans_start = jiffies; | ||
356 | |||
357 | switch (ntohs(ethhdr->h_proto)) { | ||
358 | case ETH_P_8021Q: | ||
359 | vhdr = (struct vlan_ethhdr *)skb->data; | ||
360 | vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; | ||
361 | |||
362 | if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) | ||
363 | break; | ||
364 | |||
365 | /* fall through */ | ||
366 | case ETH_P_BATMAN: | ||
367 | softif_batman_recv(skb, soft_iface, vid); | ||
368 | goto end; | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * if we have a another chosen mesh exit node in range | ||
373 | * it will transport the packets to the mesh | ||
374 | */ | ||
375 | if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) | ||
376 | goto dropped; | ||
377 | |||
378 | /* TODO: check this for locks */ | ||
379 | hna_local_add(soft_iface, ethhdr->h_source); | ||
380 | |||
381 | if (is_multicast_ether_addr(ethhdr->h_dest)) { | ||
382 | ret = gw_is_target(bat_priv, skb); | ||
383 | |||
384 | if (ret < 0) | ||
385 | goto dropped; | ||
386 | |||
387 | if (ret == 0) | ||
388 | do_bcast = true; | ||
389 | } | ||
390 | |||
391 | /* ethernet packet should be broadcasted */ | ||
392 | if (do_bcast) { | ||
393 | if (!bat_priv->primary_if) | ||
394 | goto dropped; | ||
395 | |||
396 | if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0) | ||
397 | goto dropped; | ||
398 | |||
399 | bcast_packet = (struct bcast_packet *)skb->data; | ||
400 | bcast_packet->version = COMPAT_VERSION; | ||
401 | bcast_packet->ttl = TTL; | ||
402 | |||
403 | /* batman packet type: broadcast */ | ||
404 | bcast_packet->packet_type = BAT_BCAST; | ||
405 | |||
406 | /* hw address of first interface is the orig mac because only | ||
407 | * this mac is known throughout the mesh */ | ||
408 | memcpy(bcast_packet->orig, | ||
409 | bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); | ||
410 | |||
411 | /* set broadcast sequence number */ | ||
412 | bcast_packet->seqno = | ||
413 | htonl(atomic_inc_return(&bat_priv->bcast_seqno)); | ||
414 | |||
415 | add_bcast_packet_to_list(bat_priv, skb); | ||
416 | |||
417 | /* a copy is stored in the bcast list, therefore removing | ||
418 | * the original skb. */ | ||
419 | kfree_skb(skb); | ||
420 | |||
421 | /* unicast packet */ | ||
422 | } else { | ||
423 | ret = unicast_send_skb(skb, bat_priv); | ||
424 | if (ret != 0) | ||
425 | goto dropped_freed; | ||
426 | } | ||
427 | |||
428 | bat_priv->stats.tx_packets++; | ||
429 | bat_priv->stats.tx_bytes += data_len; | ||
430 | goto end; | ||
431 | |||
432 | dropped: | ||
433 | kfree_skb(skb); | ||
434 | dropped_freed: | ||
435 | bat_priv->stats.tx_dropped++; | ||
436 | end: | ||
437 | return NETDEV_TX_OK; | ||
438 | } | ||
439 | |||
440 | void interface_rx(struct net_device *soft_iface, | ||
441 | struct sk_buff *skb, struct batman_if *recv_if, | ||
442 | int hdr_size) | ||
443 | { | ||
444 | struct bat_priv *bat_priv = netdev_priv(soft_iface); | ||
445 | struct unicast_packet *unicast_packet; | ||
446 | struct ethhdr *ethhdr; | ||
447 | struct vlan_ethhdr *vhdr; | ||
448 | short vid = -1; | ||
449 | int ret; | ||
450 | |||
451 | /* check if enough space is available for pulling, and pull */ | ||
452 | if (!pskb_may_pull(skb, hdr_size)) | ||
453 | goto dropped; | ||
454 | |||
455 | skb_pull_rcsum(skb, hdr_size); | ||
456 | skb_reset_mac_header(skb); | ||
457 | |||
458 | ethhdr = (struct ethhdr *)skb_mac_header(skb); | ||
459 | |||
460 | switch (ntohs(ethhdr->h_proto)) { | ||
461 | case ETH_P_8021Q: | ||
462 | vhdr = (struct vlan_ethhdr *)skb->data; | ||
463 | vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; | ||
464 | |||
465 | if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) | ||
466 | break; | ||
467 | |||
468 | /* fall through */ | ||
469 | case ETH_P_BATMAN: | ||
470 | goto dropped; | ||
471 | } | ||
472 | |||
473 | /** | ||
474 | * if we have a another chosen mesh exit node in range | ||
475 | * it will transport the packets to the non-mesh network | ||
476 | */ | ||
477 | if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) { | ||
478 | skb_push(skb, hdr_size); | ||
479 | unicast_packet = (struct unicast_packet *)skb->data; | ||
480 | |||
481 | if ((unicast_packet->packet_type != BAT_UNICAST) && | ||
482 | (unicast_packet->packet_type != BAT_UNICAST_FRAG)) | ||
483 | goto dropped; | ||
484 | |||
485 | skb_reset_mac_header(skb); | ||
486 | |||
487 | memcpy(unicast_packet->dest, | ||
488 | bat_priv->softif_neigh->addr, ETH_ALEN); | ||
489 | ret = route_unicast_packet(skb, recv_if, hdr_size); | ||
490 | if (ret == NET_RX_DROP) | ||
491 | goto dropped; | ||
492 | |||
493 | goto out; | ||
494 | } | ||
495 | |||
496 | /* skb->dev & skb->pkt_type are set here */ | ||
497 | if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) | ||
498 | goto dropped; | ||
499 | skb->protocol = eth_type_trans(skb, soft_iface); | ||
500 | |||
501 | /* should not be neccesary anymore as we use skb_pull_rcsum() | ||
502 | * TODO: please verify this and remove this TODO | ||
503 | * -- Dec 21st 2009, Simon Wunderlich */ | ||
504 | |||
505 | /* skb->ip_summed = CHECKSUM_UNNECESSARY;*/ | ||
506 | |||
507 | bat_priv->stats.rx_packets++; | ||
508 | bat_priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr); | ||
509 | |||
510 | soft_iface->last_rx = jiffies; | ||
511 | |||
512 | netif_rx(skb); | ||
513 | return; | ||
514 | |||
515 | dropped: | ||
516 | kfree_skb(skb); | ||
517 | out: | ||
518 | return; | ||
519 | } | ||
520 | |||
521 | #ifdef HAVE_NET_DEVICE_OPS | ||
522 | static const struct net_device_ops bat_netdev_ops = { | ||
523 | .ndo_open = interface_open, | ||
524 | .ndo_stop = interface_release, | ||
525 | .ndo_get_stats = interface_stats, | ||
526 | .ndo_set_mac_address = interface_set_mac_addr, | ||
527 | .ndo_change_mtu = interface_change_mtu, | ||
528 | .ndo_start_xmit = interface_tx, | ||
529 | .ndo_validate_addr = eth_validate_addr | ||
530 | }; | ||
531 | #endif | ||
532 | |||
533 | static void interface_setup(struct net_device *dev) | ||
534 | { | ||
535 | struct bat_priv *priv = netdev_priv(dev); | ||
536 | char dev_addr[ETH_ALEN]; | ||
537 | |||
538 | ether_setup(dev); | ||
539 | |||
540 | #ifdef HAVE_NET_DEVICE_OPS | ||
541 | dev->netdev_ops = &bat_netdev_ops; | ||
542 | #else | ||
543 | dev->open = interface_open; | ||
544 | dev->stop = interface_release; | ||
545 | dev->get_stats = interface_stats; | ||
546 | dev->set_mac_address = interface_set_mac_addr; | ||
547 | dev->change_mtu = interface_change_mtu; | ||
548 | dev->hard_start_xmit = interface_tx; | ||
549 | #endif | ||
550 | dev->destructor = free_netdev; | ||
551 | |||
552 | /** | ||
553 | * can't call min_mtu, because the needed variables | ||
554 | * have not been initialized yet | ||
555 | */ | ||
556 | dev->mtu = ETH_DATA_LEN; | ||
557 | dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the | ||
558 | * skbuff for our header */ | ||
559 | |||
560 | /* generate random address */ | ||
561 | random_ether_addr(dev_addr); | ||
562 | memcpy(dev->dev_addr, dev_addr, ETH_ALEN); | ||
563 | |||
564 | SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); | ||
565 | |||
566 | memset(priv, 0, sizeof(struct bat_priv)); | ||
567 | } | ||
568 | |||
569 | struct net_device *softif_create(char *name) | ||
570 | { | ||
571 | struct net_device *soft_iface; | ||
572 | struct bat_priv *bat_priv; | ||
573 | int ret; | ||
574 | |||
575 | soft_iface = alloc_netdev(sizeof(struct bat_priv) , name, | ||
576 | interface_setup); | ||
577 | |||
578 | if (!soft_iface) { | ||
579 | pr_err("Unable to allocate the batman interface: %s\n", name); | ||
580 | goto out; | ||
581 | } | ||
582 | |||
583 | ret = register_netdev(soft_iface); | ||
584 | if (ret < 0) { | ||
585 | pr_err("Unable to register the batman interface '%s': %i\n", | ||
586 | name, ret); | ||
587 | goto free_soft_iface; | ||
588 | } | ||
589 | |||
590 | bat_priv = netdev_priv(soft_iface); | ||
591 | |||
592 | atomic_set(&bat_priv->aggregated_ogms, 1); | ||
593 | atomic_set(&bat_priv->bonding, 0); | ||
594 | atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); | ||
595 | atomic_set(&bat_priv->gw_mode, GW_MODE_OFF); | ||
596 | atomic_set(&bat_priv->gw_sel_class, 20); | ||
597 | atomic_set(&bat_priv->gw_bandwidth, 41); | ||
598 | atomic_set(&bat_priv->orig_interval, 1000); | ||
599 | atomic_set(&bat_priv->hop_penalty, 10); | ||
600 | atomic_set(&bat_priv->log_level, 0); | ||
601 | atomic_set(&bat_priv->fragmentation, 1); | ||
602 | atomic_set(&bat_priv->bcast_queue_left, BCAST_QUEUE_LEN); | ||
603 | atomic_set(&bat_priv->batman_queue_left, BATMAN_QUEUE_LEN); | ||
604 | |||
605 | atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); | ||
606 | atomic_set(&bat_priv->bcast_seqno, 1); | ||
607 | atomic_set(&bat_priv->hna_local_changed, 0); | ||
608 | |||
609 | bat_priv->primary_if = NULL; | ||
610 | bat_priv->num_ifaces = 0; | ||
611 | bat_priv->softif_neigh = NULL; | ||
612 | |||
613 | ret = sysfs_add_meshif(soft_iface); | ||
614 | if (ret < 0) | ||
615 | goto unreg_soft_iface; | ||
616 | |||
617 | ret = debugfs_add_meshif(soft_iface); | ||
618 | if (ret < 0) | ||
619 | goto unreg_sysfs; | ||
620 | |||
621 | ret = mesh_init(soft_iface); | ||
622 | if (ret < 0) | ||
623 | goto unreg_debugfs; | ||
624 | |||
625 | return soft_iface; | ||
626 | |||
627 | unreg_debugfs: | ||
628 | debugfs_del_meshif(soft_iface); | ||
629 | unreg_sysfs: | ||
630 | sysfs_del_meshif(soft_iface); | ||
631 | unreg_soft_iface: | ||
632 | unregister_netdev(soft_iface); | ||
633 | return NULL; | ||
634 | |||
635 | free_soft_iface: | ||
636 | free_netdev(soft_iface); | ||
637 | out: | ||
638 | return NULL; | ||
639 | } | ||
640 | |||
641 | void softif_destroy(struct net_device *soft_iface) | ||
642 | { | ||
643 | debugfs_del_meshif(soft_iface); | ||
644 | sysfs_del_meshif(soft_iface); | ||
645 | mesh_free(soft_iface); | ||
646 | unregister_netdevice(soft_iface); | ||
647 | } | ||
648 | |||
649 | /* ethtool */ | ||
650 | static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||
651 | { | ||
652 | cmd->supported = 0; | ||
653 | cmd->advertising = 0; | ||
654 | cmd->speed = SPEED_10; | ||
655 | cmd->duplex = DUPLEX_FULL; | ||
656 | cmd->port = PORT_TP; | ||
657 | cmd->phy_address = 0; | ||
658 | cmd->transceiver = XCVR_INTERNAL; | ||
659 | cmd->autoneg = AUTONEG_DISABLE; | ||
660 | cmd->maxtxpkt = 0; | ||
661 | cmd->maxrxpkt = 0; | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static void bat_get_drvinfo(struct net_device *dev, | ||
667 | struct ethtool_drvinfo *info) | ||
668 | { | ||
669 | strcpy(info->driver, "B.A.T.M.A.N. advanced"); | ||
670 | strcpy(info->version, SOURCE_VERSION); | ||
671 | strcpy(info->fw_version, "N/A"); | ||
672 | strcpy(info->bus_info, "batman"); | ||
673 | } | ||
674 | |||
675 | static u32 bat_get_msglevel(struct net_device *dev) | ||
676 | { | ||
677 | return -EOPNOTSUPP; | ||
678 | } | ||
679 | |||
680 | static void bat_set_msglevel(struct net_device *dev, u32 value) | ||
681 | { | ||
682 | } | ||
683 | |||
684 | static u32 bat_get_link(struct net_device *dev) | ||
685 | { | ||
686 | return 1; | ||
687 | } | ||
688 | |||
689 | static u32 bat_get_rx_csum(struct net_device *dev) | ||
690 | { | ||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int bat_set_rx_csum(struct net_device *dev, u32 data) | ||
695 | { | ||
696 | return -EOPNOTSUPP; | ||
697 | } | ||