Commit b8cec4a4 authored by David S. Miller's avatar David S. Miller

Merge branch 'batman-adv/next' of git://git.open-mesh.org/ecsv/linux-merge

parents 5e2b61f7 e44d8fe2
...@@ -35,7 +35,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet, ...@@ -35,7 +35,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet,
int packet_len, int packet_len,
unsigned long send_time, unsigned long send_time,
bool directlink, bool directlink,
struct batman_if *if_incoming, struct hard_iface *if_incoming,
struct forw_packet *forw_packet) struct forw_packet *forw_packet)
{ {
struct batman_packet *batman_packet = struct batman_packet *batman_packet =
...@@ -99,7 +99,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet, ...@@ -99,7 +99,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet,
/* create a new aggregated packet and add this packet to it */ /* create a new aggregated packet and add this packet to it */
static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, static void new_aggregated_packet(unsigned char *packet_buff, int packet_len,
unsigned long send_time, bool direct_link, unsigned long send_time, bool direct_link,
struct batman_if *if_incoming, struct hard_iface *if_incoming,
int own_packet) int own_packet)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
...@@ -188,7 +188,7 @@ static void aggregate(struct forw_packet *forw_packet_aggr, ...@@ -188,7 +188,7 @@ static void aggregate(struct forw_packet *forw_packet_aggr,
void add_bat_packet_to_list(struct bat_priv *bat_priv, void add_bat_packet_to_list(struct bat_priv *bat_priv,
unsigned char *packet_buff, int packet_len, unsigned char *packet_buff, int packet_len,
struct batman_if *if_incoming, char own_packet, struct hard_iface *if_incoming, char own_packet,
unsigned long send_time) unsigned long send_time)
{ {
/** /**
...@@ -247,7 +247,7 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv, ...@@ -247,7 +247,7 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv,
/* unpack the aggregated packets and process them one by one */ /* unpack the aggregated packets and process them one by one */
void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
int packet_len, struct batman_if *if_incoming) int packet_len, struct hard_iface *if_incoming)
{ {
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
int buff_pos = 0; int buff_pos = 0;
......
...@@ -35,9 +35,9 @@ static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna) ...@@ -35,9 +35,9 @@ static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna)
void add_bat_packet_to_list(struct bat_priv *bat_priv, void add_bat_packet_to_list(struct bat_priv *bat_priv,
unsigned char *packet_buff, int packet_len, unsigned char *packet_buff, int packet_len,
struct batman_if *if_incoming, char own_packet, struct hard_iface *if_incoming, char own_packet,
unsigned long send_time); unsigned long send_time);
void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff,
int packet_len, struct batman_if *if_incoming); int packet_len, struct hard_iface *if_incoming);
#endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ #endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */
...@@ -441,16 +441,16 @@ static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, ...@@ -441,16 +441,16 @@ static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr,
char *buff) char *buff)
{ {
struct net_device *net_dev = kobj_to_netdev(kobj); struct net_device *net_dev = kobj_to_netdev(kobj);
struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
ssize_t length; ssize_t length;
if (!batman_if) if (!hard_iface)
return 0; return 0;
length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ? length = sprintf(buff, "%s\n", hard_iface->if_status == IF_NOT_IN_USE ?
"none" : batman_if->soft_iface->name); "none" : hard_iface->soft_iface->name);
kref_put(&batman_if->refcount, hardif_free_ref); hardif_free_ref(hard_iface);
return length; return length;
} }
...@@ -459,11 +459,11 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, ...@@ -459,11 +459,11 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
char *buff, size_t count) char *buff, size_t count)
{ {
struct net_device *net_dev = kobj_to_netdev(kobj); struct net_device *net_dev = kobj_to_netdev(kobj);
struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
int status_tmp = -1; int status_tmp = -1;
int ret; int ret = count;
if (!batman_if) if (!hard_iface)
return count; return count;
if (buff[count - 1] == '\n') if (buff[count - 1] == '\n')
...@@ -472,7 +472,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, ...@@ -472,7 +472,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
if (strlen(buff) >= IFNAMSIZ) { if (strlen(buff) >= IFNAMSIZ) {
pr_err("Invalid parameter for 'mesh_iface' setting received: " pr_err("Invalid parameter for 'mesh_iface' setting received: "
"interface name too long '%s'\n", buff); "interface name too long '%s'\n", buff);
kref_put(&batman_if->refcount, hardif_free_ref); hardif_free_ref(hard_iface);
return -EINVAL; return -EINVAL;
} }
...@@ -481,30 +481,31 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, ...@@ -481,30 +481,31 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
else else
status_tmp = IF_I_WANT_YOU; status_tmp = IF_I_WANT_YOU;
if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) && if (hard_iface->if_status == status_tmp)
(strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) { goto out;
kref_put(&batman_if->refcount, hardif_free_ref);
return count; if ((hard_iface->soft_iface) &&
} (strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0))
goto out;
if (status_tmp == IF_NOT_IN_USE) { if (status_tmp == IF_NOT_IN_USE) {
rtnl_lock(); rtnl_lock();
hardif_disable_interface(batman_if); hardif_disable_interface(hard_iface);
rtnl_unlock(); rtnl_unlock();
kref_put(&batman_if->refcount, hardif_free_ref); goto out;
return count;
} }
/* if the interface already is in use */ /* if the interface already is in use */
if (batman_if->if_status != IF_NOT_IN_USE) { if (hard_iface->if_status != IF_NOT_IN_USE) {
rtnl_lock(); rtnl_lock();
hardif_disable_interface(batman_if); hardif_disable_interface(hard_iface);
rtnl_unlock(); rtnl_unlock();
} }
ret = hardif_enable_interface(batman_if, buff); ret = hardif_enable_interface(hard_iface, buff);
kref_put(&batman_if->refcount, hardif_free_ref);
out:
hardif_free_ref(hard_iface);
return ret; return ret;
} }
...@@ -512,13 +513,13 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, ...@@ -512,13 +513,13 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr,
char *buff) char *buff)
{ {
struct net_device *net_dev = kobj_to_netdev(kobj); struct net_device *net_dev = kobj_to_netdev(kobj);
struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
ssize_t length; ssize_t length;
if (!batman_if) if (!hard_iface)
return 0; return 0;
switch (batman_if->if_status) { switch (hard_iface->if_status) {
case IF_TO_BE_REMOVED: case IF_TO_BE_REMOVED:
length = sprintf(buff, "disabling\n"); length = sprintf(buff, "disabling\n");
break; break;
...@@ -537,7 +538,7 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, ...@@ -537,7 +538,7 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr,
break; break;
} }
kref_put(&batman_if->refcount, hardif_free_ref); hardif_free_ref(hard_iface);
return length; return length;
} }
......
...@@ -28,58 +28,75 @@ ...@@ -28,58 +28,75 @@
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
static void gw_node_free_ref(struct kref *refcount) static void gw_node_free_rcu(struct rcu_head *rcu)
{ {
struct gw_node *gw_node; struct gw_node *gw_node;
gw_node = container_of(refcount, struct gw_node, refcount); gw_node = container_of(rcu, struct gw_node, rcu);
kfree(gw_node); kfree(gw_node);
} }
static void gw_node_free_rcu(struct rcu_head *rcu) static void gw_node_free_ref(struct gw_node *gw_node)
{ {
struct gw_node *gw_node; if (atomic_dec_and_test(&gw_node->refcount))
call_rcu(&gw_node->rcu, gw_node_free_rcu);
gw_node = container_of(rcu, struct gw_node, rcu);
kref_put(&gw_node->refcount, gw_node_free_ref);
} }
void *gw_get_selected(struct bat_priv *bat_priv) void *gw_get_selected(struct bat_priv *bat_priv)
{ {
struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; struct gw_node *curr_gateway_tmp;
struct orig_node *orig_node = NULL;
rcu_read_lock();
curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
if (!curr_gateway_tmp) if (!curr_gateway_tmp)
return NULL; goto out;
orig_node = curr_gateway_tmp->orig_node;
if (!orig_node)
goto out;
return curr_gateway_tmp->orig_node; if (!atomic_inc_not_zero(&orig_node->refcount))
orig_node = NULL;
out:
rcu_read_unlock();
return orig_node;
} }
void gw_deselect(struct bat_priv *bat_priv) void gw_deselect(struct bat_priv *bat_priv)
{ {
struct gw_node *gw_node = bat_priv->curr_gw; struct gw_node *gw_node;
bat_priv->curr_gw = NULL; spin_lock_bh(&bat_priv->gw_list_lock);
gw_node = rcu_dereference(bat_priv->curr_gw);
rcu_assign_pointer(bat_priv->curr_gw, NULL);
spin_unlock_bh(&bat_priv->gw_list_lock);
if (gw_node) if (gw_node)
kref_put(&gw_node->refcount, gw_node_free_ref); gw_node_free_ref(gw_node);
} }
static struct gw_node *gw_select(struct bat_priv *bat_priv, static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
struct gw_node *new_gw_node)
{ {
struct gw_node *curr_gw_node = bat_priv->curr_gw; struct gw_node *curr_gw_node;
if (new_gw_node) if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount))
kref_get(&new_gw_node->refcount); new_gw_node = NULL;
spin_lock_bh(&bat_priv->gw_list_lock);
curr_gw_node = rcu_dereference(bat_priv->curr_gw);
rcu_assign_pointer(bat_priv->curr_gw, new_gw_node);
spin_unlock_bh(&bat_priv->gw_list_lock);
bat_priv->curr_gw = new_gw_node; if (curr_gw_node)
return curr_gw_node; gw_node_free_ref(curr_gw_node);
} }
void gw_election(struct bat_priv *bat_priv) void gw_election(struct bat_priv *bat_priv)
{ {
struct hlist_node *node; struct hlist_node *node;
struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL;
uint8_t max_tq = 0; uint8_t max_tq = 0;
uint32_t max_gw_factor = 0, tmp_gw_factor = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0;
int down, up; int down, up;
...@@ -93,19 +110,23 @@ void gw_election(struct bat_priv *bat_priv) ...@@ -93,19 +110,23 @@ void gw_election(struct bat_priv *bat_priv)
if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT)
return; return;
if (bat_priv->curr_gw) rcu_read_lock();
curr_gw = rcu_dereference(bat_priv->curr_gw);
if (curr_gw) {
rcu_read_unlock();
return; return;
}
rcu_read_lock();
if (hlist_empty(&bat_priv->gw_list)) { if (hlist_empty(&bat_priv->gw_list)) {
rcu_read_unlock();
if (bat_priv->curr_gw) { if (curr_gw) {
rcu_read_unlock();
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Removing selected gateway - " "Removing selected gateway - "
"no gateway in range\n"); "no gateway in range\n");
gw_deselect(bat_priv); gw_deselect(bat_priv);
} } else
rcu_read_unlock();
return; return;
} }
...@@ -154,12 +175,12 @@ void gw_election(struct bat_priv *bat_priv) ...@@ -154,12 +175,12 @@ void gw_election(struct bat_priv *bat_priv)
max_gw_factor = tmp_gw_factor; max_gw_factor = tmp_gw_factor;
} }
if (bat_priv->curr_gw != curr_gw_tmp) { if (curr_gw != curr_gw_tmp) {
if ((bat_priv->curr_gw) && (!curr_gw_tmp)) if ((curr_gw) && (!curr_gw_tmp))
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Removing selected gateway - " "Removing selected gateway - "
"no gateway in range\n"); "no gateway in range\n");
else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) else if ((!curr_gw) && (curr_gw_tmp))
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Adding route to gateway %pM " "Adding route to gateway %pM "
"(gw_flags: %i, tq: %i)\n", "(gw_flags: %i, tq: %i)\n",
...@@ -174,43 +195,43 @@ void gw_election(struct bat_priv *bat_priv) ...@@ -174,43 +195,43 @@ void gw_election(struct bat_priv *bat_priv)
curr_gw_tmp->orig_node->gw_flags, curr_gw_tmp->orig_node->gw_flags,
curr_gw_tmp->orig_node->router->tq_avg); curr_gw_tmp->orig_node->router->tq_avg);
old_gw_node = gw_select(bat_priv, curr_gw_tmp); gw_select(bat_priv, curr_gw_tmp);
} }
rcu_read_unlock(); rcu_read_unlock();
/* the kfree() has to be outside of the rcu lock */
if (old_gw_node)
kref_put(&old_gw_node->refcount, gw_node_free_ref);
} }
void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
{ {
struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; struct gw_node *curr_gateway_tmp;
uint8_t gw_tq_avg, orig_tq_avg; uint8_t gw_tq_avg, orig_tq_avg;
rcu_read_lock();
curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw);
if (!curr_gateway_tmp) if (!curr_gateway_tmp)
return; goto out_rcu;
if (!curr_gateway_tmp->orig_node) if (!curr_gateway_tmp->orig_node)
goto deselect; goto deselect_rcu;
if (!curr_gateway_tmp->orig_node->router) if (!curr_gateway_tmp->orig_node->router)
goto deselect; goto deselect_rcu;
/* this node already is the gateway */ /* this node already is the gateway */
if (curr_gateway_tmp->orig_node == orig_node) if (curr_gateway_tmp->orig_node == orig_node)
return; goto out_rcu;
if (!orig_node->router) if (!orig_node->router)
return; goto out_rcu;
gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg;
rcu_read_unlock();
orig_tq_avg = orig_node->router->tq_avg; orig_tq_avg = orig_node->router->tq_avg;
/* the TQ value has to be better */ /* the TQ value has to be better */
if (orig_tq_avg < gw_tq_avg) if (orig_tq_avg < gw_tq_avg)
return; goto out;
/** /**
* if the routing class is greater than 3 the value tells us how much * if the routing class is greater than 3 the value tells us how much
...@@ -218,15 +239,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) ...@@ -218,15 +239,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node)
**/ **/
if ((atomic_read(&bat_priv->gw_sel_class) > 3) && if ((atomic_read(&bat_priv->gw_sel_class) > 3) &&
(orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class)))
return; goto out;
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Restarting gateway selection: better gateway found (tq curr: " "Restarting gateway selection: better gateway found (tq curr: "
"%i, tq new: %i)\n", "%i, tq new: %i)\n",
gw_tq_avg, orig_tq_avg); gw_tq_avg, orig_tq_avg);
goto deselect;
out_rcu:
rcu_read_unlock();
goto out;
deselect_rcu:
rcu_read_unlock();
deselect: deselect:
gw_deselect(bat_priv); gw_deselect(bat_priv);
out:
return;
} }
static void gw_node_add(struct bat_priv *bat_priv, static void gw_node_add(struct bat_priv *bat_priv,
...@@ -242,7 +271,7 @@ static void gw_node_add(struct bat_priv *bat_priv, ...@@ -242,7 +271,7 @@ static void gw_node_add(struct bat_priv *bat_priv,
memset(gw_node, 0, sizeof(struct gw_node)); memset(gw_node, 0, sizeof(struct gw_node));
INIT_HLIST_NODE(&gw_node->list); INIT_HLIST_NODE(&gw_node->list);
gw_node->orig_node = orig_node; gw_node->orig_node = orig_node;
kref_init(&gw_node->refcount); atomic_set(&gw_node->refcount, 1);
spin_lock_bh(&bat_priv->gw_list_lock); spin_lock_bh(&bat_priv->gw_list_lock);
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list);
...@@ -283,7 +312,7 @@ void gw_node_update(struct bat_priv *bat_priv, ...@@ -283,7 +312,7 @@ void gw_node_update(struct bat_priv *bat_priv,
"Gateway %pM removed from gateway list\n", "Gateway %pM removed from gateway list\n",
orig_node->orig); orig_node->orig);
if (gw_node == bat_priv->curr_gw) { if (gw_node == rcu_dereference(bat_priv->curr_gw)) {
rcu_read_unlock(); rcu_read_unlock();
gw_deselect(bat_priv); gw_deselect(bat_priv);
return; return;
...@@ -321,11 +350,11 @@ void gw_node_purge(struct bat_priv *bat_priv) ...@@ -321,11 +350,11 @@ void gw_node_purge(struct bat_priv *bat_priv)
atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)
continue; continue;
if (bat_priv->curr_gw == gw_node) if (rcu_dereference(bat_priv->curr_gw) == gw_node)
gw_deselect(bat_priv); gw_deselect(bat_priv);
hlist_del_rcu(&gw_node->list); hlist_del_rcu(&gw_node->list);
call_rcu(&gw_node->rcu, gw_node_free_rcu); gw_node_free_ref(gw_node);
} }
...@@ -335,12 +364,16 @@ void gw_node_purge(struct bat_priv *bat_priv) ...@@ -335,12 +364,16 @@ void gw_node_purge(struct bat_priv *bat_priv)
static int _write_buffer_text(struct bat_priv *bat_priv, static int _write_buffer_text(struct bat_priv *bat_priv,
struct seq_file *seq, struct gw_node *gw_node) struct seq_file *seq, struct gw_node *gw_node)
{ {
int down, up; struct gw_node *curr_gw;
int down, up, ret;
gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up);
return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", rcu_read_lock();
(bat_priv->curr_gw == gw_node ? "=>" : " "), curr_gw = rcu_dereference(bat_priv->curr_gw);
ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n",
(curr_gw == gw_node ? "=>" : " "),
gw_node->orig_node->orig, gw_node->orig_node->orig,
gw_node->orig_node->router->tq_avg, gw_node->orig_node->router->tq_avg,
gw_node->orig_node->router->addr, gw_node->orig_node->router->addr,
...@@ -350,6 +383,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv, ...@@ -350,6 +383,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv,
(down > 2048 ? "MBit" : "KBit"), (down > 2048 ? "MBit" : "KBit"),
(up > 2048 ? up / 1024 : up), (up > 2048 ? up / 1024 : up),
(up > 2048 ? "MBit" : "KBit")); (up > 2048 ? "MBit" : "KBit"));
rcu_read_unlock();
return ret;
} }
int gw_client_seq_print_text(struct seq_file *seq, void *offset) int gw_client_seq_print_text(struct seq_file *seq, void *offset)
...@@ -470,8 +506,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) ...@@ -470,8 +506,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)
return -1; return -1;
if (!bat_priv->curr_gw) rcu_read_lock();
if (!rcu_dereference(bat_priv->curr_gw)) {
rcu_read_unlock();
return 0; return 0;
}
rcu_read_unlock();
return 1; return 1;
} }
...@@ -31,8 +31,8 @@ ...@@ -31,8 +31,8 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
/* protect update critical side of if_list - but not the content */ /* protect update critical side of hardif_list - but not the content */
static DEFINE_SPINLOCK(if_list_lock); static DEFINE_SPINLOCK(hardif_list_lock);
static int batman_skb_recv(struct sk_buff *skb, static int batman_skb_recv(struct sk_buff *skb,
...@@ -40,33 +40,31 @@ static int batman_skb_recv(struct sk_buff *skb, ...@@ -40,33 +40,31 @@ static int batman_skb_recv(struct sk_buff *skb,
struct packet_type *ptype, struct packet_type *ptype,
struct net_device *orig_dev); struct net_device *orig_dev);
static void hardif_free_rcu(struct rcu_head *rcu) void hardif_free_rcu(struct rcu_head *rcu)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
batman_if = container_of(rcu, struct batman_if, rcu); hard_iface = container_of(rcu, struct hard_iface, rcu);
dev_put(batman_if->net_dev); dev_put(hard_iface->net_dev);
kref_put(&batman_if->refcount, hardif_free_ref); kfree(hard_iface);
} }
struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->net_dev == net_dev) if (hard_iface->net_dev == net_dev &&
atomic_inc_not_zero(&hard_iface->refcount))
goto out; goto out;
} }
batman_if = NULL; hard_iface = NULL;
out: out:
if (batman_if)
kref_get(&batman_if->refcount);
rcu_read_unlock(); rcu_read_unlock();
return batman_if; return hard_iface;
} }
static int is_valid_iface(struct net_device *net_dev) static int is_valid_iface(struct net_device *net_dev)
...@@ -81,13 +79,8 @@ static int is_valid_iface(struct net_device *net_dev) ...@@ -81,13 +79,8 @@ static int is_valid_iface(struct net_device *net_dev)
return 0; return 0;
/* no batman over batman */ /* no batman over batman */
#ifdef HAVE_NET_DEVICE_OPS if (softif_is_valid(net_dev))
if (net_dev->netdev_ops->ndo_start_xmit == interface_tx)
return 0;
#else
if (net_dev->hard_start_xmit == interface_tx)
return 0; return 0;
#endif
/* Device is being bridged */ /* Device is being bridged */
/* if (net_dev->priv_flags & IFF_BRIDGE_PORT) /* if (net_dev->priv_flags & IFF_BRIDGE_PORT)
...@@ -96,27 +89,25 @@ static int is_valid_iface(struct net_device *net_dev) ...@@ -96,27 +89,25 @@ static int is_valid_iface(struct net_device *net_dev)
return 1; return 1;
} }
static struct batman_if *get_active_batman_if(struct net_device *soft_iface) static struct hard_iface *hardif_get_active(struct net_device *soft_iface)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->soft_iface != soft_iface) if (hard_iface->soft_iface != soft_iface)
continue; continue;
if (batman_if->if_status == IF_ACTIVE) if (hard_iface->if_status == IF_ACTIVE &&
atomic_inc_not_zero(&hard_iface->refcount))
goto out; goto out;
} }
batman_if = NULL; hard_iface = NULL;
out: out:
if (batman_if)
kref_get(&batman_if->refcount);
rcu_read_unlock(); rcu_read_unlock();
return batman_if; return hard_iface;
} }
static void update_primary_addr(struct bat_priv *bat_priv) static void update_primary_addr(struct bat_priv *bat_priv)
...@@ -132,24 +123,24 @@ static void update_primary_addr(struct bat_priv *bat_priv) ...@@ -132,24 +123,24 @@ static void update_primary_addr(struct bat_priv *bat_priv)
} }
static void set_primary_if(struct bat_priv *bat_priv, static void set_primary_if(struct bat_priv *bat_priv,
struct batman_if *batman_if) struct hard_iface *hard_iface)
{ {
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
struct batman_if *old_if; struct hard_iface *old_if;
if (batman_if) if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount))
kref_get(&batman_if->refcount); hard_iface = NULL;
old_if = bat_priv->primary_if; old_if = bat_priv->primary_if;
bat_priv->primary_if = batman_if; bat_priv->primary_if = hard_iface;
if (old_if) if (old_if)
kref_put(&old_if->refcount, hardif_free_ref); hardif_free_ref(old_if);
if (!bat_priv->primary_if) if (!bat_priv->primary_if)
return; return;
batman_packet = (struct batman_packet *)(batman_if->packet_buff); batman_packet = (struct batman_packet *)(hard_iface->packet_buff);
batman_packet->flags = PRIMARIES_FIRST_HOP; batman_packet->flags = PRIMARIES_FIRST_HOP;
batman_packet->ttl = TTL; batman_packet->ttl = TTL;
...@@ -162,42 +153,42 @@ static void set_primary_if(struct bat_priv *bat_priv, ...@@ -162,42 +153,42 @@ static void set_primary_if(struct bat_priv *bat_priv,
atomic_set(&bat_priv->hna_local_changed, 1); atomic_set(&bat_priv->hna_local_changed, 1);
} }
static bool hardif_is_iface_up(struct batman_if *batman_if) static bool hardif_is_iface_up(struct hard_iface *hard_iface)
{ {
if (batman_if->net_dev->flags & IFF_UP) if (hard_iface->net_dev->flags & IFF_UP)
return true; return true;
return false; return false;
} }
static void update_mac_addresses(struct batman_if *batman_if) static void update_mac_addresses(struct hard_iface *hard_iface)
{ {
memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig, memcpy(((struct batman_packet *)(hard_iface->packet_buff))->orig,
batman_if->net_dev->dev_addr, ETH_ALEN); hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender, memcpy(((struct batman_packet *)(hard_iface->packet_buff))->prev_sender,
batman_if->net_dev->dev_addr, ETH_ALEN); hard_iface->net_dev->dev_addr, ETH_ALEN);
} }
static void check_known_mac_addr(struct net_device *net_dev) static void check_known_mac_addr(struct net_device *net_dev)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if ((batman_if->if_status != IF_ACTIVE) && if ((hard_iface->if_status != IF_ACTIVE) &&
(batman_if->if_status != IF_TO_BE_ACTIVATED)) (hard_iface->if_status != IF_TO_BE_ACTIVATED))
continue; continue;
if (batman_if->net_dev == net_dev) if (hard_iface->net_dev == net_dev)
continue; continue;
if (!compare_orig(batman_if->net_dev->dev_addr, if (!compare_eth(hard_iface->net_dev->dev_addr,
net_dev->dev_addr)) net_dev->dev_addr))
continue; continue;
pr_warning("The newly added mac address (%pM) already exists " pr_warning("The newly added mac address (%pM) already exists "
"on: %s\n", net_dev->dev_addr, "on: %s\n", net_dev->dev_addr,
batman_if->net_dev->name); hard_iface->net_dev->name);
pr_warning("It is strongly recommended to keep mac addresses " pr_warning("It is strongly recommended to keep mac addresses "
"unique to avoid problems!\n"); "unique to avoid problems!\n");
} }
...@@ -207,7 +198,7 @@ static void check_known_mac_addr(struct net_device *net_dev) ...@@ -207,7 +198,7 @@ static void check_known_mac_addr(struct net_device *net_dev)
int hardif_min_mtu(struct net_device *soft_iface) int hardif_min_mtu(struct net_device *soft_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(soft_iface); struct bat_priv *bat_priv = netdev_priv(soft_iface);
struct batman_if *batman_if; struct hard_iface *hard_iface;
/* allow big frames if all devices are capable to do so /* allow big frames if all devices are capable to do so
* (have MTU > 1500 + BAT_HEADER_LEN) */ * (have MTU > 1500 + BAT_HEADER_LEN) */
int min_mtu = ETH_DATA_LEN; int min_mtu = ETH_DATA_LEN;
...@@ -216,15 +207,15 @@ int hardif_min_mtu(struct net_device *soft_iface) ...@@ -216,15 +207,15 @@ int hardif_min_mtu(struct net_device *soft_iface)
goto out; goto out;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if ((batman_if->if_status != IF_ACTIVE) && if ((hard_iface->if_status != IF_ACTIVE) &&
(batman_if->if_status != IF_TO_BE_ACTIVATED)) (hard_iface->if_status != IF_TO_BE_ACTIVATED))
continue; continue;
if (batman_if->soft_iface != soft_iface) if (hard_iface->soft_iface != soft_iface)
continue; continue;
min_mtu = min_t(int, batman_if->net_dev->mtu - BAT_HEADER_LEN, min_mtu = min_t(int, hard_iface->net_dev->mtu - BAT_HEADER_LEN,
min_mtu); min_mtu);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -242,77 +233,95 @@ void update_min_mtu(struct net_device *soft_iface) ...@@ -242,77 +233,95 @@ void update_min_mtu(struct net_device *soft_iface)
soft_iface->mtu = min_mtu; soft_iface->mtu = min_mtu;
} }
static void hardif_activate_interface(struct batman_if *batman_if) static void hardif_activate_interface(struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
if (batman_if->if_status != IF_INACTIVE) if (hard_iface->if_status != IF_INACTIVE)
return; return;
bat_priv = netdev_priv(batman_if->soft_iface); bat_priv = netdev_priv(hard_iface->soft_iface);
update_mac_addresses(batman_if); update_mac_addresses(hard_iface);
batman_if->if_status = IF_TO_BE_ACTIVATED; hard_iface->if_status = IF_TO_BE_ACTIVATED;
/** /**
* the first active interface becomes our primary interface or * the first active interface becomes our primary interface or
* the next active interface after the old primay interface was removed * the next active interface after the old primay interface was removed
*/ */
if (!bat_priv->primary_if) if (!bat_priv->primary_if)
set_primary_if(bat_priv, batman_if); set_primary_if(bat_priv, hard_iface);
bat_info(batman_if->soft_iface, "Interface activated: %s\n", bat_info(hard_iface->soft_iface, "Interface activated: %s\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
update_min_mtu(batman_if->soft_iface); update_min_mtu(hard_iface->soft_iface);
return; return;
} }
static void hardif_deactivate_interface(struct batman_if *batman_if) static void hardif_deactivate_interface(struct hard_iface *hard_iface)
{ {
if ((batman_if->if_status != IF_ACTIVE) && if ((hard_iface->if_status != IF_ACTIVE) &&
(batman_if->if_status != IF_TO_BE_ACTIVATED)) (hard_iface->if_status != IF_TO_BE_ACTIVATED))
return; return;
batman_if->if_status = IF_INACTIVE; hard_iface->if_status = IF_INACTIVE;
bat_info(batman_if->soft_iface, "Interface deactivated: %s\n", bat_info(hard_iface->soft_iface, "Interface deactivated: %s\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
update_min_mtu(batman_if->soft_iface); update_min_mtu(hard_iface->soft_iface);
} }
int hardif_enable_interface(struct batman_if *batman_if, char *iface_name) int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name)
{ {
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
struct net_device *soft_iface;
int ret;
if (hard_iface->if_status != IF_NOT_IN_USE)
goto out;
if (batman_if->if_status != IF_NOT_IN_USE) if (!atomic_inc_not_zero(&hard_iface->refcount))
goto out; goto out;
batman_if->soft_iface = dev_get_by_name(&init_net, iface_name); soft_iface = dev_get_by_name(&init_net, iface_name);
if (!batman_if->soft_iface) { if (!soft_iface) {
batman_if->soft_iface = softif_create(iface_name); soft_iface = softif_create(iface_name);
if (!batman_if->soft_iface) if (!soft_iface) {
ret = -ENOMEM;
goto err; goto err;
}
/* dev_get_by_name() increases the reference counter for us */ /* dev_get_by_name() increases the reference counter for us */
dev_hold(batman_if->soft_iface); dev_hold(soft_iface);
} }
bat_priv = netdev_priv(batman_if->soft_iface); if (!softif_is_valid(soft_iface)) {
batman_if->packet_len = BAT_PACKET_LEN; pr_err("Can't create batman mesh interface %s: "
batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_ATOMIC); "already exists as regular interface\n",
soft_iface->name);
dev_put(soft_iface);
ret = -EINVAL;
goto err;
}
hard_iface->soft_iface = soft_iface;
bat_priv = netdev_priv(hard_iface->soft_iface);
hard_iface->packet_len = BAT_PACKET_LEN;
hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC);
if (!batman_if->packet_buff) { if (!hard_iface->packet_buff) {
bat_err(batman_if->soft_iface, "Can't add interface packet " bat_err(hard_iface->soft_iface, "Can't add interface packet "
"(%s): out of memory\n", batman_if->net_dev->name); "(%s): out of memory\n", hard_iface->net_dev->name);
ret = -ENOMEM;
goto err; goto err;
} }
batman_packet = (struct batman_packet *)(batman_if->packet_buff); batman_packet = (struct batman_packet *)(hard_iface->packet_buff);
batman_packet->packet_type = BAT_PACKET; batman_packet->packet_type = BAT_PACKET;
batman_packet->version = COMPAT_VERSION; batman_packet->version = COMPAT_VERSION;
batman_packet->flags = 0; batman_packet->flags = 0;
...@@ -320,107 +329,107 @@ int hardif_enable_interface(struct batman_if *batman_if, char *iface_name) ...@@ -320,107 +329,107 @@ int hardif_enable_interface(struct batman_if *batman_if, char *iface_name)
batman_packet->tq = TQ_MAX_VALUE; batman_packet->tq = TQ_MAX_VALUE;
batman_packet->num_hna = 0; batman_packet->num_hna = 0;
batman_if->if_num = bat_priv->num_ifaces; hard_iface->if_num = bat_priv->num_ifaces;
bat_priv->num_ifaces++; bat_priv->num_ifaces++;
batman_if->if_status = IF_INACTIVE; hard_iface->if_status = IF_INACTIVE;
orig_hash_add_if(batman_if, bat_priv->num_ifaces); orig_hash_add_if(hard_iface, bat_priv->num_ifaces);
batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN); hard_iface->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN);
batman_if->batman_adv_ptype.func = batman_skb_recv; hard_iface->batman_adv_ptype.func = batman_skb_recv;
batman_if->batman_adv_ptype.dev = batman_if->net_dev; hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
kref_get(&batman_if->refcount); dev_add_pack(&hard_iface->batman_adv_ptype);
dev_add_pack(&batman_if->batman_adv_ptype);
atomic_set(&batman_if->seqno, 1); atomic_set(&hard_iface->seqno, 1);
atomic_set(&batman_if->frag_seqno, 1); atomic_set(&hard_iface->frag_seqno, 1);
bat_info(batman_if->soft_iface, "Adding interface: %s\n", bat_info(hard_iface->soft_iface, "Adding interface: %s\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
if (atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < if (atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu <
ETH_DATA_LEN + BAT_HEADER_LEN) ETH_DATA_LEN + BAT_HEADER_LEN)
bat_info(batman_if->soft_iface, bat_info(hard_iface->soft_iface,
"The MTU of interface %s is too small (%i) to handle " "The MTU of interface %s is too small (%i) to handle "
"the transport of batman-adv packets. Packets going " "the transport of batman-adv packets. Packets going "
"over this interface will be fragmented on layer2 " "over this interface will be fragmented on layer2 "
"which could impact the performance. Setting the MTU " "which could impact the performance. Setting the MTU "
"to %zi would solve the problem.\n", "to %zi would solve the problem.\n",
batman_if->net_dev->name, batman_if->net_dev->mtu, hard_iface->net_dev->name, hard_iface->net_dev->mtu,
ETH_DATA_LEN + BAT_HEADER_LEN); ETH_DATA_LEN + BAT_HEADER_LEN);
if (!atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < if (!atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu <
ETH_DATA_LEN + BAT_HEADER_LEN) ETH_DATA_LEN + BAT_HEADER_LEN)
bat_info(batman_if->soft_iface, bat_info(hard_iface->soft_iface,
"The MTU of interface %s is too small (%i) to handle " "The MTU of interface %s is too small (%i) to handle "
"the transport of batman-adv packets. If you experience" "the transport of batman-adv packets. If you experience"
" problems getting traffic through try increasing the " " problems getting traffic through try increasing the "
"MTU to %zi.\n", "MTU to %zi.\n",
batman_if->net_dev->name, batman_if->net_dev->mtu, hard_iface->net_dev->name, hard_iface->net_dev->mtu,
ETH_DATA_LEN + BAT_HEADER_LEN); ETH_DATA_LEN + BAT_HEADER_LEN);
if (hardif_is_iface_up(batman_if)) if (hardif_is_iface_up(hard_iface))
hardif_activate_interface(batman_if); hardif_activate_interface(hard_iface);
else else
bat_err(batman_if->soft_iface, "Not using interface %s " bat_err(hard_iface->soft_iface, "Not using interface %s "
"(retrying later): interface not active\n", "(retrying later): interface not active\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
/* begin scheduling originator messages on that interface */ /* begin scheduling originator messages on that interface */
schedule_own_packet(batman_if); schedule_own_packet(hard_iface);
out: out:
return 0; return 0;
err: err:
return -ENOMEM; hardif_free_ref(hard_iface);
return ret;
} }
void hardif_disable_interface(struct batman_if *batman_if) void hardif_disable_interface(struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
if (batman_if->if_status == IF_ACTIVE) if (hard_iface->if_status == IF_ACTIVE)
hardif_deactivate_interface(batman_if); hardif_deactivate_interface(hard_iface);
if (batman_if->if_status != IF_INACTIVE) if (hard_iface->if_status != IF_INACTIVE)
return; return;
bat_info(batman_if->soft_iface, "Removing interface: %s\n", bat_info(hard_iface->soft_iface, "Removing interface: %s\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
dev_remove_pack(&batman_if->batman_adv_ptype); dev_remove_pack(&hard_iface->batman_adv_ptype);
kref_put(&batman_if->refcount, hardif_free_ref);
bat_priv->num_ifaces--; bat_priv->num_ifaces--;
orig_hash_del_if(batman_if, bat_priv->num_ifaces); orig_hash_del_if(hard_iface, bat_priv->num_ifaces);
if (batman_if == bat_priv->primary_if) { if (hard_iface == bat_priv->primary_if) {
struct batman_if *new_if; struct hard_iface *new_if;
new_if = get_active_batman_if(batman_if->soft_iface); new_if = hardif_get_active(hard_iface->soft_iface);
set_primary_if(bat_priv, new_if); set_primary_if(bat_priv, new_if);
if (new_if) if (new_if)
kref_put(&new_if->refcount, hardif_free_ref); hardif_free_ref(new_if);
} }
kfree(batman_if->packet_buff); kfree(hard_iface->packet_buff);
batman_if->packet_buff = NULL; hard_iface->packet_buff = NULL;
batman_if->if_status = IF_NOT_IN_USE; hard_iface->if_status = IF_NOT_IN_USE;
/* delete all references to this batman_if */ /* delete all references to this hard_iface */
purge_orig_ref(bat_priv); purge_orig_ref(bat_priv);
purge_outstanding_packets(bat_priv, batman_if); purge_outstanding_packets(bat_priv, hard_iface);
dev_put(batman_if->soft_iface); dev_put(hard_iface->soft_iface);
/* nobody uses this interface anymore */ /* nobody uses this interface anymore */
if (!bat_priv->num_ifaces) if (!bat_priv->num_ifaces)
softif_destroy(batman_if->soft_iface); softif_destroy(hard_iface->soft_iface);
batman_if->soft_iface = NULL; hard_iface->soft_iface = NULL;
hardif_free_ref(hard_iface);
} }
static struct batman_if *hardif_add_interface(struct net_device *net_dev) static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
int ret; int ret;
ret = is_valid_iface(net_dev); ret = is_valid_iface(net_dev);
...@@ -429,73 +438,73 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev) ...@@ -429,73 +438,73 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev)
dev_hold(net_dev); dev_hold(net_dev);
batman_if = kmalloc(sizeof(struct batman_if), GFP_ATOMIC); hard_iface = kmalloc(sizeof(struct hard_iface), GFP_ATOMIC);
if (!batman_if) { if (!hard_iface) {
pr_err("Can't add interface (%s): out of memory\n", pr_err("Can't add interface (%s): out of memory\n",
net_dev->name); net_dev->name);
goto release_dev; goto release_dev;
} }
ret = sysfs_add_hardif(&batman_if->hardif_obj, net_dev); ret = sysfs_add_hardif(&hard_iface->hardif_obj, net_dev);
if (ret) if (ret)
goto free_if; goto free_if;
batman_if->if_num = -1; hard_iface->if_num = -1;
batman_if->net_dev = net_dev; hard_iface->net_dev = net_dev;
batman_if->soft_iface = NULL; hard_iface->soft_iface = NULL;
batman_if->if_status = IF_NOT_IN_USE; hard_iface->if_status = IF_NOT_IN_USE;
INIT_LIST_HEAD(&batman_if->list); INIT_LIST_HEAD(&hard_iface->list);
kref_init(&batman_if->refcount); /* extra reference for return */
atomic_set(&hard_iface->refcount, 2);
check_known_mac_addr(batman_if->net_dev); check_known_mac_addr(hard_iface->net_dev);
spin_lock(&if_list_lock); spin_lock(&hardif_list_lock);
list_add_tail_rcu(&batman_if->list, &if_list); list_add_tail_rcu(&hard_iface->list, &hardif_list);
spin_unlock(&if_list_lock); spin_unlock(&hardif_list_lock);
/* extra reference for return */ return hard_iface;
kref_get(&batman_if->refcount);
return batman_if;
free_if: free_if:
kfree(batman_if); kfree(hard_iface);
release_dev: release_dev:
dev_put(net_dev); dev_put(net_dev);
out: out:
return NULL; return NULL;
} }
static void hardif_remove_interface(struct batman_if *batman_if) static void hardif_remove_interface(struct hard_iface *hard_iface)
{ {
/* first deactivate interface */ /* first deactivate interface */
if (batman_if->if_status != IF_NOT_IN_USE) if (hard_iface->if_status != IF_NOT_IN_USE)
hardif_disable_interface(batman_if); hardif_disable_interface(hard_iface);
if (batman_if->if_status != IF_NOT_IN_USE) if (hard_iface->if_status != IF_NOT_IN_USE)
return; return;
batman_if->if_status = IF_TO_BE_REMOVED; hard_iface->if_status = IF_TO_BE_REMOVED;
sysfs_del_hardif(&batman_if->hardif_obj); sysfs_del_hardif(&hard_iface->hardif_obj);
call_rcu(&batman_if->rcu, hardif_free_rcu); hardif_free_ref(hard_iface);
} }
void hardif_remove_interfaces(void) void hardif_remove_interfaces(void)
{ {
struct batman_if *batman_if, *batman_if_tmp; struct hard_iface *hard_iface, *hard_iface_tmp;
struct list_head if_queue; struct list_head if_queue;
INIT_LIST_HEAD(&if_queue); INIT_LIST_HEAD(&if_queue);
spin_lock(&if_list_lock); spin_lock(&hardif_list_lock);
list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) { list_for_each_entry_safe(hard_iface, hard_iface_tmp,
list_del_rcu(&batman_if->list); &hardif_list, list) {
list_add_tail(&batman_if->list, &if_queue); list_del_rcu(&hard_iface->list);
list_add_tail(&hard_iface->list, &if_queue);
} }
spin_unlock(&if_list_lock); spin_unlock(&hardif_list_lock);
rtnl_lock(); rtnl_lock();
list_for_each_entry_safe(batman_if, batman_if_tmp, &if_queue, list) { list_for_each_entry_safe(hard_iface, hard_iface_tmp, &if_queue, list) {
hardif_remove_interface(batman_if); hardif_remove_interface(hard_iface);
} }
rtnl_unlock(); rtnl_unlock();
} }
...@@ -504,43 +513,43 @@ static int hard_if_event(struct notifier_block *this, ...@@ -504,43 +513,43 @@ static int hard_if_event(struct notifier_block *this,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *net_dev = (struct net_device *)ptr; struct net_device *net_dev = (struct net_device *)ptr;
struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
if (!batman_if && event == NETDEV_REGISTER) if (!hard_iface && event == NETDEV_REGISTER)
batman_if = hardif_add_interface(net_dev); hard_iface = hardif_add_interface(net_dev);
if (!batman_if) if (!hard_iface)
goto out; goto out;
switch (event) { switch (event) {
case NETDEV_UP: case NETDEV_UP:
hardif_activate_interface(batman_if); hardif_activate_interface(hard_iface);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
case NETDEV_DOWN: case NETDEV_DOWN:
hardif_deactivate_interface(batman_if); hardif_deactivate_interface(hard_iface);
break; break;
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
spin_lock(&if_list_lock); spin_lock(&hardif_list_lock);
list_del_rcu(&batman_if->list); list_del_rcu(&hard_iface->list);
spin_unlock(&if_list_lock); spin_unlock(&hardif_list_lock);
hardif_remove_interface(batman_if); hardif_remove_interface(hard_iface);
break; break;
case NETDEV_CHANGEMTU: case NETDEV_CHANGEMTU:
if (batman_if->soft_iface) if (hard_iface->soft_iface)
update_min_mtu(batman_if->soft_iface); update_min_mtu(hard_iface->soft_iface);
break; break;
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
if (batman_if->if_status == IF_NOT_IN_USE) if (hard_iface->if_status == IF_NOT_IN_USE)
goto hardif_put; goto hardif_put;
check_known_mac_addr(batman_if->net_dev); check_known_mac_addr(hard_iface->net_dev);
update_mac_addresses(batman_if); update_mac_addresses(hard_iface);
bat_priv = netdev_priv(batman_if->soft_iface); bat_priv = netdev_priv(hard_iface->soft_iface);
if (batman_if == bat_priv->primary_if) if (hard_iface == bat_priv->primary_if)
update_primary_addr(bat_priv); update_primary_addr(bat_priv);
break; break;
default: default:
...@@ -548,7 +557,7 @@ static int hard_if_event(struct notifier_block *this, ...@@ -548,7 +557,7 @@ static int hard_if_event(struct notifier_block *this,
}; };
hardif_put: hardif_put:
kref_put(&batman_if->refcount, hardif_free_ref); hardif_free_ref(hard_iface);
out: out:
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -561,10 +570,10 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, ...@@ -561,10 +570,10 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
{ {
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
struct batman_if *batman_if; struct hard_iface *hard_iface;
int ret; int ret;
batman_if = container_of(ptype, struct batman_if, batman_adv_ptype); hard_iface = container_of(ptype, struct hard_iface, batman_adv_ptype);
skb = skb_share_check(skb, GFP_ATOMIC); skb = skb_share_check(skb, GFP_ATOMIC);
/* skb was released by skb_share_check() */ /* skb was released by skb_share_check() */
...@@ -580,16 +589,16 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, ...@@ -580,16 +589,16 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
|| !skb_mac_header(skb))) || !skb_mac_header(skb)))
goto err_free; goto err_free;
if (!batman_if->soft_iface) if (!hard_iface->soft_iface)
goto err_free; goto err_free;
bat_priv = netdev_priv(batman_if->soft_iface); bat_priv = netdev_priv(hard_iface->soft_iface);
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
goto err_free; goto err_free;
/* discard frames on not active interfaces */ /* discard frames on not active interfaces */
if (batman_if->if_status != IF_ACTIVE) if (hard_iface->if_status != IF_ACTIVE)
goto err_free; goto err_free;
batman_packet = (struct batman_packet *)skb->data; batman_packet = (struct batman_packet *)skb->data;
...@@ -607,32 +616,32 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, ...@@ -607,32 +616,32 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
switch (batman_packet->packet_type) { switch (batman_packet->packet_type) {
/* batman originator packet */ /* batman originator packet */
case BAT_PACKET: case BAT_PACKET:
ret = recv_bat_packet(skb, batman_if); ret = recv_bat_packet(skb, hard_iface);
break; break;
/* batman icmp packet */ /* batman icmp packet */
case BAT_ICMP: case BAT_ICMP:
ret = recv_icmp_packet(skb, batman_if); ret = recv_icmp_packet(skb, hard_iface);
break; break;
/* unicast packet */ /* unicast packet */
case BAT_UNICAST: case BAT_UNICAST:
ret = recv_unicast_packet(skb, batman_if); ret = recv_unicast_packet(skb, hard_iface);
break; break;
/* fragmented unicast packet */ /* fragmented unicast packet */
case BAT_UNICAST_FRAG: case BAT_UNICAST_FRAG:
ret = recv_ucast_frag_packet(skb, batman_if); ret = recv_ucast_frag_packet(skb, hard_iface);
break; break;
/* broadcast packet */ /* broadcast packet */
case BAT_BCAST: case BAT_BCAST:
ret = recv_bcast_packet(skb, batman_if); ret = recv_bcast_packet(skb, hard_iface);
break; break;
/* vis packet */ /* vis packet */
case BAT_VIS: case BAT_VIS:
ret = recv_vis_packet(skb, batman_if); ret = recv_vis_packet(skb, hard_iface);
break; break;
default: default:
ret = NET_RX_DROP; ret = NET_RX_DROP;
......
...@@ -31,19 +31,18 @@ ...@@ -31,19 +31,18 @@
extern struct notifier_block hard_if_notifier; extern struct notifier_block hard_if_notifier;
struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev); struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev);
int hardif_enable_interface(struct batman_if *batman_if, char *iface_name); int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name);
void hardif_disable_interface(struct batman_if *batman_if); void hardif_disable_interface(struct hard_iface *hard_iface);
void hardif_remove_interfaces(void); void hardif_remove_interfaces(void);
int hardif_min_mtu(struct net_device *soft_iface); int hardif_min_mtu(struct net_device *soft_iface);
void update_min_mtu(struct net_device *soft_iface); void update_min_mtu(struct net_device *soft_iface);
void hardif_free_rcu(struct rcu_head *rcu);
static inline void hardif_free_ref(struct kref *refcount) static inline void hardif_free_ref(struct hard_iface *hard_iface)
{ {
struct batman_if *batman_if; if (atomic_dec_and_test(&hard_iface->refcount))
call_rcu(&hard_iface->rcu, hardif_free_rcu);
batman_if = container_of(refcount, struct batman_if, refcount);
kfree(batman_if);
} }
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ #endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
...@@ -27,13 +27,16 @@ static void hash_init(struct hashtable_t *hash) ...@@ -27,13 +27,16 @@ static void hash_init(struct hashtable_t *hash)
{ {
int i; int i;
for (i = 0 ; i < hash->size; i++) for (i = 0 ; i < hash->size; i++) {
INIT_HLIST_HEAD(&hash->table[i]); INIT_HLIST_HEAD(&hash->table[i]);
spin_lock_init(&hash->list_locks[i]);
}
} }
/* free only the hashtable and the hash itself. */ /* free only the hashtable and the hash itself. */
void hash_destroy(struct hashtable_t *hash) void hash_destroy(struct hashtable_t *hash)
{ {
kfree(hash->list_locks);
kfree(hash->table); kfree(hash->table);
kfree(hash); kfree(hash);
} }
...@@ -43,20 +46,25 @@ struct hashtable_t *hash_new(int size) ...@@ -43,20 +46,25 @@ struct hashtable_t *hash_new(int size)
{ {
struct hashtable_t *hash; struct hashtable_t *hash;
hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC); hash = kmalloc(sizeof(struct hashtable_t), GFP_ATOMIC);
if (!hash) if (!hash)
return NULL; return NULL;
hash->size = size;
hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC);
if (!hash->table)
goto free_hash;
if (!hash->table) { hash->list_locks = kmalloc(sizeof(spinlock_t) * size, GFP_ATOMIC);
kfree(hash); if (!hash->list_locks)
return NULL; goto free_table;
}
hash->size = size;
hash_init(hash); hash_init(hash);
return hash; return hash;
free_table:
kfree(hash->table);
free_hash:
kfree(hash);
return NULL;
} }
...@@ -28,21 +28,17 @@ ...@@ -28,21 +28,17 @@
* compare 2 element datas for their keys, * compare 2 element datas for their keys,
* return 0 if same and not 0 if not * return 0 if same and not 0 if not
* same */ * same */
typedef int (*hashdata_compare_cb)(void *, void *); typedef int (*hashdata_compare_cb)(struct hlist_node *, void *);
/* the hashfunction, should return an index /* the hashfunction, should return an index
* based on the key in the data of the first * based on the key in the data of the first
* argument and the size the second */ * argument and the size the second */
typedef int (*hashdata_choose_cb)(void *, int); typedef int (*hashdata_choose_cb)(void *, int);
typedef void (*hashdata_free_cb)(void *, void *); typedef void (*hashdata_free_cb)(struct hlist_node *, void *);
struct element_t {
void *data; /* pointer to the data */
struct hlist_node hlist; /* bucket list pointer */
};
struct hashtable_t { struct hashtable_t {
struct hlist_head *table; /* the hashtable itself, with the buckets */ struct hlist_head *table; /* the hashtable itself with the buckets */
spinlock_t *list_locks; /* spinlock for each hash list entry */
int size; /* size of hashtable */ int size; /* size of hashtable */
}; };
...@@ -59,21 +55,22 @@ static inline void hash_delete(struct hashtable_t *hash, ...@@ -59,21 +55,22 @@ static inline void hash_delete(struct hashtable_t *hash,
hashdata_free_cb free_cb, void *arg) hashdata_free_cb free_cb, void *arg)
{ {
struct hlist_head *head; struct hlist_head *head;
struct hlist_node *walk, *safe; struct hlist_node *node, *node_tmp;
struct element_t *bucket; spinlock_t *list_lock; /* spinlock to protect write access */
int i; int i;
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
list_lock = &hash->list_locks[i];
hlist_for_each_safe(walk, safe, head) { spin_lock_bh(list_lock);
bucket = hlist_entry(walk, struct element_t, hlist); hlist_for_each_safe(node, node_tmp, head) {
if (free_cb) hlist_del_rcu(node);
free_cb(bucket->data, arg);
hlist_del(walk); if (free_cb)
kfree(bucket); free_cb(node, arg);
} }
spin_unlock_bh(list_lock);
} }
hash_destroy(hash); hash_destroy(hash);
...@@ -82,35 +79,41 @@ static inline void hash_delete(struct hashtable_t *hash, ...@@ -82,35 +79,41 @@ static inline void hash_delete(struct hashtable_t *hash,
/* adds data to the hashtable. returns 0 on success, -1 on error */ /* adds data to the hashtable. returns 0 on success, -1 on error */
static inline int hash_add(struct hashtable_t *hash, static inline int hash_add(struct hashtable_t *hash,
hashdata_compare_cb compare, hashdata_compare_cb compare,
hashdata_choose_cb choose, void *data) hashdata_choose_cb choose,
void *data, struct hlist_node *data_node)
{ {
int index; int index;
struct hlist_head *head; struct hlist_head *head;
struct hlist_node *walk, *safe; struct hlist_node *node;
struct element_t *bucket; spinlock_t *list_lock; /* spinlock to protect write access */
if (!hash) if (!hash)
return -1; goto err;
index = choose(data, hash->size); index = choose(data, hash->size);
head = &hash->table[index]; head = &hash->table[index];
list_lock = &hash->list_locks[index];
rcu_read_lock();
__hlist_for_each_rcu(node, head) {
if (!compare(node, data))
continue;
hlist_for_each_safe(walk, safe, head) { goto err_unlock;
bucket = hlist_entry(walk, struct element_t, hlist);
if (compare(bucket->data, data))
return -1;
} }
rcu_read_unlock();
/* no duplicate found in list, add new element */ /* no duplicate found in list, add new element */
bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); spin_lock_bh(list_lock);
hlist_add_head_rcu(data_node, head);
if (!bucket) spin_unlock_bh(list_lock);
return -1;
bucket->data = data;
hlist_add_head(&bucket->hlist, head);
return 0; return 0;
err_unlock:
rcu_read_unlock();
err:
return -1;
} }
/* removes data from hash, if found. returns pointer do data on success, so you /* removes data from hash, if found. returns pointer do data on success, so you
...@@ -122,50 +125,25 @@ static inline void *hash_remove(struct hashtable_t *hash, ...@@ -122,50 +125,25 @@ static inline void *hash_remove(struct hashtable_t *hash,
hashdata_choose_cb choose, void *data) hashdata_choose_cb choose, void *data)
{ {
size_t index; size_t index;
struct hlist_node *walk; struct hlist_node *node;
struct element_t *bucket;
struct hlist_head *head; struct hlist_head *head;
void *data_save; void *data_save = NULL;
index = choose(data, hash->size); index = choose(data, hash->size);
head = &hash->table[index]; head = &hash->table[index];
hlist_for_each_entry(bucket, walk, head, hlist) { spin_lock_bh(&hash->list_locks[index]);
if (compare(bucket->data, data)) { hlist_for_each(node, head) {
data_save = bucket->data; if (!compare(node, data))
hlist_del(walk); continue;
kfree(bucket);
return data_save;
}
}
return NULL;
}
/* finds data, based on the key in keydata. returns the found data on success,
* or NULL on error */
static inline void *hash_find(struct hashtable_t *hash,
hashdata_compare_cb compare,
hashdata_choose_cb choose, void *keydata)
{
int index;
struct hlist_head *head;
struct hlist_node *walk;
struct element_t *bucket;
if (!hash)
return NULL;
index = choose(keydata , hash->size);
head = &hash->table[index];
hlist_for_each(walk, head) { data_save = node;
bucket = hlist_entry(walk, struct element_t, hlist); hlist_del_rcu(node);
if (compare(bucket->data, keydata)) break;
return bucket->data;
} }
spin_unlock_bh(&hash->list_locks[index]);
return NULL; return data_save;
} }
#endif /* _NET_BATMAN_ADV_HASH_H_ */ #endif /* _NET_BATMAN_ADV_HASH_H_ */
...@@ -156,10 +156,9 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, ...@@ -156,10 +156,9 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
struct sk_buff *skb; struct sk_buff *skb;
struct icmp_packet_rr *icmp_packet; struct icmp_packet_rr *icmp_packet;
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct batman_if *batman_if; struct neigh_node *neigh_node = NULL;
size_t packet_len = sizeof(struct icmp_packet); size_t packet_len = sizeof(struct icmp_packet);
uint8_t dstaddr[ETH_ALEN];
if (len < sizeof(struct icmp_packet)) { if (len < sizeof(struct icmp_packet)) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
...@@ -219,47 +218,52 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, ...@@ -219,47 +218,52 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
goto dst_unreach; goto dst_unreach;
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
compare_orig, choose_orig,
icmp_packet->dst));
if (!orig_node) if (!orig_node)
goto unlock; goto unlock;
if (!orig_node->router) neigh_node = orig_node->router;
if (!neigh_node)
goto unlock; goto unlock;
batman_if = orig_node->router->if_incoming; if (!atomic_inc_not_zero(&neigh_node->refcount)) {
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); neigh_node = NULL;
goto unlock;
}
spin_unlock_bh(&bat_priv->orig_hash_lock); rcu_read_unlock();
if (!batman_if) if (!neigh_node->if_incoming)
goto dst_unreach; goto dst_unreach;
if (batman_if->if_status != IF_ACTIVE) if (neigh_node->if_incoming->if_status != IF_ACTIVE)
goto dst_unreach; goto dst_unreach;
memcpy(icmp_packet->orig, memcpy(icmp_packet->orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
if (packet_len == sizeof(struct icmp_packet_rr)) if (packet_len == sizeof(struct icmp_packet_rr))
memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN); memcpy(icmp_packet->rr,
neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN);
send_skb_packet(skb, batman_if, dstaddr);
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
goto out; goto out;
unlock: unlock:
spin_unlock_bh(&bat_priv->orig_hash_lock); rcu_read_unlock();
dst_unreach: dst_unreach:
icmp_packet->msg_type = DESTINATION_UNREACHABLE; icmp_packet->msg_type = DESTINATION_UNREACHABLE;
bat_socket_add_packet(socket_client, icmp_packet, packet_len); bat_socket_add_packet(socket_client, icmp_packet, packet_len);
free_skb: free_skb:
kfree_skb(skb); kfree_skb(skb);
out: out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return len; return len;
} }
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include "vis.h" #include "vis.h"
#include "hash.h" #include "hash.h"
struct list_head if_list; struct list_head hardif_list;
unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
...@@ -41,7 +41,7 @@ struct workqueue_struct *bat_event_workqueue; ...@@ -41,7 +41,7 @@ struct workqueue_struct *bat_event_workqueue;
static int __init batman_init(void) static int __init batman_init(void)
{ {
INIT_LIST_HEAD(&if_list); INIT_LIST_HEAD(&hardif_list);
/* the name should not be longer than 10 chars - see /* the name should not be longer than 10 chars - see
* http://lwn.net/Articles/23634/ */ * http://lwn.net/Articles/23634/ */
...@@ -79,7 +79,6 @@ int mesh_init(struct net_device *soft_iface) ...@@ -79,7 +79,6 @@ int mesh_init(struct net_device *soft_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(soft_iface); struct bat_priv *bat_priv = netdev_priv(soft_iface);
spin_lock_init(&bat_priv->orig_hash_lock);
spin_lock_init(&bat_priv->forw_bat_list_lock); spin_lock_init(&bat_priv->forw_bat_list_lock);
spin_lock_init(&bat_priv->forw_bcast_list_lock); spin_lock_init(&bat_priv->forw_bcast_list_lock);
spin_lock_init(&bat_priv->hna_lhash_lock); spin_lock_init(&bat_priv->hna_lhash_lock);
...@@ -154,14 +153,14 @@ void dec_module_count(void) ...@@ -154,14 +153,14 @@ void dec_module_count(void)
int is_my_mac(uint8_t *addr) int is_my_mac(uint8_t *addr)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->if_status != IF_ACTIVE) if (hard_iface->if_status != IF_ACTIVE)
continue; continue;
if (compare_orig(batman_if->net_dev->dev_addr, addr)) { if (compare_eth(hard_iface->net_dev->dev_addr, addr)) {
rcu_read_unlock(); rcu_read_unlock();
return 1; return 1;
} }
......
...@@ -122,7 +122,7 @@ ...@@ -122,7 +122,7 @@
#define REVISION_VERSION_STR " "REVISION_VERSION #define REVISION_VERSION_STR " "REVISION_VERSION
#endif #endif
extern struct list_head if_list; extern struct list_head hardif_list;
extern unsigned char broadcast_addr[]; extern unsigned char broadcast_addr[];
extern struct workqueue_struct *bat_event_workqueue; extern struct workqueue_struct *bat_event_workqueue;
...@@ -165,4 +165,14 @@ static inline void bat_dbg(char type __always_unused, ...@@ -165,4 +165,14 @@ static inline void bat_dbg(char type __always_unused,
pr_err("%s: " fmt, _netdev->name, ## arg); \ pr_err("%s: " fmt, _netdev->name, ## arg); \
} while (0) } while (0)
/**
* returns 1 if they are the same ethernet addr
*
* note: can't use compare_ether_addr() as it requires aligned memory
*/
static inline int compare_eth(void *data1, void *data2)
{
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
#endif /* _NET_BATMAN_ADV_MAIN_H_ */ #endif /* _NET_BATMAN_ADV_MAIN_H_ */
...@@ -44,24 +44,36 @@ int originator_init(struct bat_priv *bat_priv) ...@@ -44,24 +44,36 @@ int originator_init(struct bat_priv *bat_priv)
if (bat_priv->orig_hash) if (bat_priv->orig_hash)
return 1; return 1;
spin_lock_bh(&bat_priv->orig_hash_lock);
bat_priv->orig_hash = hash_new(1024); bat_priv->orig_hash = hash_new(1024);
if (!bat_priv->orig_hash) if (!bat_priv->orig_hash)
goto err; goto err;
spin_unlock_bh(&bat_priv->orig_hash_lock);
start_purge_timer(bat_priv); start_purge_timer(bat_priv);
return 1; return 1;
err: err:
spin_unlock_bh(&bat_priv->orig_hash_lock);
return 0; return 0;
} }
struct neigh_node * static void neigh_node_free_rcu(struct rcu_head *rcu)
create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, {
uint8_t *neigh, struct batman_if *if_incoming) struct neigh_node *neigh_node;
neigh_node = container_of(rcu, struct neigh_node, rcu);
kfree(neigh_node);
}
void neigh_node_free_ref(struct neigh_node *neigh_node)
{
if (atomic_dec_and_test(&neigh_node->refcount))
call_rcu(&neigh_node->rcu, neigh_node_free_rcu);
}
struct neigh_node *create_neighbor(struct orig_node *orig_node,
struct orig_node *orig_neigh_node,
uint8_t *neigh,
struct hard_iface *if_incoming)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct neigh_node *neigh_node; struct neigh_node *neigh_node;
...@@ -73,50 +85,94 @@ create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, ...@@ -73,50 +85,94 @@ create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node,
if (!neigh_node) if (!neigh_node)
return NULL; return NULL;
INIT_LIST_HEAD(&neigh_node->list); INIT_HLIST_NODE(&neigh_node->list);
INIT_LIST_HEAD(&neigh_node->bonding_list);
memcpy(neigh_node->addr, neigh, ETH_ALEN); memcpy(neigh_node->addr, neigh, ETH_ALEN);
neigh_node->orig_node = orig_neigh_node; neigh_node->orig_node = orig_neigh_node;
neigh_node->if_incoming = if_incoming; neigh_node->if_incoming = if_incoming;
list_add_tail(&neigh_node->list, &orig_node->neigh_list); /* extra reference for return */
atomic_set(&neigh_node->refcount, 2);
spin_lock_bh(&orig_node->neigh_list_lock);
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
spin_unlock_bh(&orig_node->neigh_list_lock);
return neigh_node; return neigh_node;
} }
static void free_orig_node(void *data, void *arg) static void orig_node_free_rcu(struct rcu_head *rcu)
{ {
struct list_head *list_pos, *list_pos_tmp; struct hlist_node *node, *node_tmp;
struct neigh_node *neigh_node; struct neigh_node *neigh_node, *tmp_neigh_node;
struct orig_node *orig_node = (struct orig_node *)data; struct orig_node *orig_node;
struct bat_priv *bat_priv = (struct bat_priv *)arg;
/* for all neighbors towards this originator ... */ orig_node = container_of(rcu, struct orig_node, rcu);
list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) {
neigh_node = list_entry(list_pos, struct neigh_node, list); spin_lock_bh(&orig_node->neigh_list_lock);
/* for all bonding members ... */
list_for_each_entry_safe(neigh_node, tmp_neigh_node,
&orig_node->bond_list, bonding_list) {
list_del_rcu(&neigh_node->bonding_list);
neigh_node_free_ref(neigh_node);
}
list_del(list_pos); /* for all neighbors towards this originator ... */
kfree(neigh_node); hlist_for_each_entry_safe(neigh_node, node, node_tmp,
&orig_node->neigh_list, list) {
hlist_del_rcu(&neigh_node->list);
neigh_node_free_ref(neigh_node);
} }
spin_unlock_bh(&orig_node->neigh_list_lock);
frag_list_free(&orig_node->frag_list); frag_list_free(&orig_node->frag_list);
hna_global_del_orig(bat_priv, orig_node, "originator timed out"); hna_global_del_orig(orig_node->bat_priv, orig_node,
"originator timed out");
kfree(orig_node->bcast_own); kfree(orig_node->bcast_own);
kfree(orig_node->bcast_own_sum); kfree(orig_node->bcast_own_sum);
kfree(orig_node); kfree(orig_node);
} }
void orig_node_free_ref(struct orig_node *orig_node)
{
if (atomic_dec_and_test(&orig_node->refcount))
call_rcu(&orig_node->rcu, orig_node_free_rcu);
}
void originator_free(struct bat_priv *bat_priv) void originator_free(struct bat_priv *bat_priv)
{ {
if (!bat_priv->orig_hash) struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *node, *node_tmp;
struct hlist_head *head;
spinlock_t *list_lock; /* spinlock to protect write access */
struct orig_node *orig_node;
int i;
if (!hash)
return; return;
cancel_delayed_work_sync(&bat_priv->orig_work); cancel_delayed_work_sync(&bat_priv->orig_work);
spin_lock_bh(&bat_priv->orig_hash_lock);
hash_delete(bat_priv->orig_hash, free_orig_node, bat_priv);
bat_priv->orig_hash = NULL; bat_priv->orig_hash = NULL;
spin_unlock_bh(&bat_priv->orig_hash_lock);
for (i = 0; i < hash->size; i++) {
head = &hash->table[i];
list_lock = &hash->list_locks[i];
spin_lock_bh(list_lock);
hlist_for_each_entry_safe(orig_node, node, node_tmp,
head, hash_entry) {
hlist_del_rcu(node);
orig_node_free_ref(orig_node);
}
spin_unlock_bh(list_lock);
}
hash_destroy(hash);
} }
/* this function finds or creates an originator entry for the given /* this function finds or creates an originator entry for the given
...@@ -127,10 +183,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) ...@@ -127,10 +183,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
int size; int size;
int hash_added; int hash_added;
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, orig_node = orig_hash_find(bat_priv, addr);
compare_orig, choose_orig,
addr));
if (orig_node) if (orig_node)
return orig_node; return orig_node;
...@@ -141,8 +194,16 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) ...@@ -141,8 +194,16 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
if (!orig_node) if (!orig_node)
return NULL; return NULL;
INIT_LIST_HEAD(&orig_node->neigh_list); INIT_HLIST_HEAD(&orig_node->neigh_list);
INIT_LIST_HEAD(&orig_node->bond_list);
spin_lock_init(&orig_node->ogm_cnt_lock);
spin_lock_init(&orig_node->bcast_seqno_lock);
spin_lock_init(&orig_node->neigh_list_lock);
/* extra reference for return */
atomic_set(&orig_node->refcount, 2);
orig_node->bat_priv = bat_priv;
memcpy(orig_node->orig, addr, ETH_ALEN); memcpy(orig_node->orig, addr, ETH_ALEN);
orig_node->router = NULL; orig_node->router = NULL;
orig_node->hna_buff = NULL; orig_node->hna_buff = NULL;
...@@ -151,6 +212,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) ...@@ -151,6 +212,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
orig_node->batman_seqno_reset = jiffies - 1 orig_node->batman_seqno_reset = jiffies - 1
- msecs_to_jiffies(RESET_PROTECTION_MS); - msecs_to_jiffies(RESET_PROTECTION_MS);
atomic_set(&orig_node->bond_candidates, 0);
size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS; size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS;
orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
...@@ -166,8 +229,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) ...@@ -166,8 +229,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
if (!orig_node->bcast_own_sum) if (!orig_node->bcast_own_sum)
goto free_bcast_own; goto free_bcast_own;
hash_added = hash_add(bat_priv->orig_hash, compare_orig, choose_orig, hash_added = hash_add(bat_priv->orig_hash, compare_orig,
orig_node); choose_orig, orig_node, &orig_node->hash_entry);
if (hash_added < 0) if (hash_added < 0)
goto free_bcast_own_sum; goto free_bcast_own_sum;
...@@ -185,23 +248,30 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv, ...@@ -185,23 +248,30 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv,
struct orig_node *orig_node, struct orig_node *orig_node,
struct neigh_node **best_neigh_node) struct neigh_node **best_neigh_node)
{ {
struct list_head *list_pos, *list_pos_tmp; struct hlist_node *node, *node_tmp;
struct neigh_node *neigh_node; struct neigh_node *neigh_node;
bool neigh_purged = false; bool neigh_purged = false;
*best_neigh_node = NULL; *best_neigh_node = NULL;
spin_lock_bh(&orig_node->neigh_list_lock);
/* for all neighbors towards this originator ... */ /* for all neighbors towards this originator ... */
list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { hlist_for_each_entry_safe(neigh_node, node, node_tmp,
neigh_node = list_entry(list_pos, struct neigh_node, list); &orig_node->neigh_list, list) {
if ((time_after(jiffies, if ((time_after(jiffies,
neigh_node->last_valid + PURGE_TIMEOUT * HZ)) || neigh_node->last_valid + PURGE_TIMEOUT * HZ)) ||
(neigh_node->if_incoming->if_status == IF_INACTIVE) || (neigh_node->if_incoming->if_status == IF_INACTIVE) ||
(neigh_node->if_incoming->if_status == IF_NOT_IN_USE) ||
(neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) { (neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) {
if (neigh_node->if_incoming->if_status == if ((neigh_node->if_incoming->if_status ==
IF_TO_BE_REMOVED) IF_INACTIVE) ||
(neigh_node->if_incoming->if_status ==
IF_NOT_IN_USE) ||
(neigh_node->if_incoming->if_status ==
IF_TO_BE_REMOVED))
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"neighbor purge: originator %pM, " "neighbor purge: originator %pM, "
"neighbor: %pM, iface: %s\n", "neighbor: %pM, iface: %s\n",
...@@ -215,14 +285,18 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv, ...@@ -215,14 +285,18 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv,
(neigh_node->last_valid / HZ)); (neigh_node->last_valid / HZ));
neigh_purged = true; neigh_purged = true;
list_del(list_pos);
kfree(neigh_node); hlist_del_rcu(&neigh_node->list);
bonding_candidate_del(orig_node, neigh_node);
neigh_node_free_ref(neigh_node);
} else { } else {
if ((!*best_neigh_node) || if ((!*best_neigh_node) ||
(neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) (neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
*best_neigh_node = neigh_node; *best_neigh_node = neigh_node;
} }
} }
spin_unlock_bh(&orig_node->neigh_list_lock);
return neigh_purged; return neigh_purged;
} }
...@@ -245,9 +319,6 @@ static bool purge_orig_node(struct bat_priv *bat_priv, ...@@ -245,9 +319,6 @@ static bool purge_orig_node(struct bat_priv *bat_priv,
best_neigh_node, best_neigh_node,
orig_node->hna_buff, orig_node->hna_buff,
orig_node->hna_buff_len); orig_node->hna_buff_len);
/* update bonding candidates, we could have lost
* some candidates. */
update_bonding_candidates(orig_node);
} }
} }
...@@ -257,40 +328,38 @@ static bool purge_orig_node(struct bat_priv *bat_priv, ...@@ -257,40 +328,38 @@ static bool purge_orig_node(struct bat_priv *bat_priv,
static void _purge_orig(struct bat_priv *bat_priv) static void _purge_orig(struct bat_priv *bat_priv)
{ {
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk, *safe; struct hlist_node *node, *node_tmp;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket; spinlock_t *list_lock; /* spinlock to protect write access */
struct orig_node *orig_node; struct orig_node *orig_node;
int i; int i;
if (!hash) if (!hash)
return; return;
spin_lock_bh(&bat_priv->orig_hash_lock);
/* for all origins... */ /* for all origins... */
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
list_lock = &hash->list_locks[i];
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { spin_lock_bh(list_lock);
orig_node = bucket->data; hlist_for_each_entry_safe(orig_node, node, node_tmp,
head, hash_entry) {
if (purge_orig_node(bat_priv, orig_node)) { if (purge_orig_node(bat_priv, orig_node)) {
if (orig_node->gw_flags) if (orig_node->gw_flags)
gw_node_delete(bat_priv, orig_node); gw_node_delete(bat_priv, orig_node);
hlist_del(walk); hlist_del_rcu(node);
kfree(bucket); orig_node_free_ref(orig_node);
free_orig_node(orig_node, bat_priv); continue;
} }
if (time_after(jiffies, orig_node->last_frag_packet + if (time_after(jiffies, orig_node->last_frag_packet +
msecs_to_jiffies(FRAG_TIMEOUT))) msecs_to_jiffies(FRAG_TIMEOUT)))
frag_list_free(&orig_node->frag_list); frag_list_free(&orig_node->frag_list);
} }
spin_unlock_bh(list_lock);
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
gw_node_purge(bat_priv); gw_node_purge(bat_priv);
gw_election(bat_priv); gw_election(bat_priv);
...@@ -318,9 +387,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) ...@@ -318,9 +387,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
struct net_device *net_dev = (struct net_device *)seq->private; struct net_device *net_dev = (struct net_device *)seq->private;
struct bat_priv *bat_priv = netdev_priv(net_dev); struct bat_priv *bat_priv = netdev_priv(net_dev);
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node, *node_tmp;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
struct neigh_node *neigh_node; struct neigh_node *neigh_node;
int batman_count = 0; int batman_count = 0;
...@@ -348,14 +416,11 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) ...@@ -348,14 +416,11 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
"Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
"outgoingIF", "Potential nexthops"); "outgoingIF", "Potential nexthops");
spin_lock_bh(&bat_priv->orig_hash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
if (!orig_node->router) if (!orig_node->router)
continue; continue;
...@@ -374,8 +439,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) ...@@ -374,8 +439,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
neigh_node->addr, neigh_node->addr,
neigh_node->if_incoming->net_dev->name); neigh_node->if_incoming->net_dev->name);
list_for_each_entry(neigh_node, &orig_node->neigh_list, hlist_for_each_entry_rcu(neigh_node, node_tmp,
list) { &orig_node->neigh_list, list) {
seq_printf(seq, " %pM (%3i)", neigh_node->addr, seq_printf(seq, " %pM (%3i)", neigh_node->addr,
neigh_node->tq_avg); neigh_node->tq_avg);
} }
...@@ -383,10 +448,9 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) ...@@ -383,10 +448,9 @@ int orig_seq_print_text(struct seq_file *seq, void *offset)
seq_printf(seq, "\n"); seq_printf(seq, "\n");
batman_count++; batman_count++;
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
if ((batman_count == 0)) if ((batman_count == 0))
seq_printf(seq, "No batman nodes in range ...\n"); seq_printf(seq, "No batman nodes in range ...\n");
...@@ -423,36 +487,36 @@ static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) ...@@ -423,36 +487,36 @@ static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
return 0; return 0;
} }
int orig_hash_add_if(struct batman_if *batman_if, int max_if_num) int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
int i; int i, ret;
/* resize all orig nodes because orig_node->bcast_own(_sum) depend on /* resize all orig nodes because orig_node->bcast_own(_sum) depend on
* if_num */ * if_num */
spin_lock_bh(&bat_priv->orig_hash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
spin_lock_bh(&orig_node->ogm_cnt_lock);
ret = orig_node_add_if(orig_node, max_if_num);
spin_unlock_bh(&orig_node->ogm_cnt_lock);
if (orig_node_add_if(orig_node, max_if_num) == -1) if (ret == -1)
goto err; goto err;
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
return 0; return 0;
err: err:
spin_unlock_bh(&bat_priv->orig_hash_lock); rcu_read_unlock();
return -ENOMEM; return -ENOMEM;
} }
...@@ -508,57 +572,55 @@ static int orig_node_del_if(struct orig_node *orig_node, ...@@ -508,57 +572,55 @@ static int orig_node_del_if(struct orig_node *orig_node,
return 0; return 0;
} }
int orig_hash_del_if(struct batman_if *batman_if, int max_if_num) int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket; struct hard_iface *hard_iface_tmp;
struct batman_if *batman_if_tmp;
struct orig_node *orig_node; struct orig_node *orig_node;
int i, ret; int i, ret;
/* resize all orig nodes because orig_node->bcast_own(_sum) depend on /* resize all orig nodes because orig_node->bcast_own(_sum) depend on
* if_num */ * if_num */
spin_lock_bh(&bat_priv->orig_hash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
spin_lock_bh(&orig_node->ogm_cnt_lock);
ret = orig_node_del_if(orig_node, max_if_num, ret = orig_node_del_if(orig_node, max_if_num,
batman_if->if_num); hard_iface->if_num);
spin_unlock_bh(&orig_node->ogm_cnt_lock);
if (ret == -1) if (ret == -1)
goto err; goto err;
} }
rcu_read_unlock();
} }
/* renumber remaining batman interfaces _inside_ of orig_hash_lock */ /* renumber remaining batman interfaces _inside_ of orig_hash_lock */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if_tmp, &if_list, list) { list_for_each_entry_rcu(hard_iface_tmp, &hardif_list, list) {
if (batman_if_tmp->if_status == IF_NOT_IN_USE) if (hard_iface_tmp->if_status == IF_NOT_IN_USE)
continue; continue;
if (batman_if == batman_if_tmp) if (hard_iface == hard_iface_tmp)
continue; continue;
if (batman_if->soft_iface != batman_if_tmp->soft_iface) if (hard_iface->soft_iface != hard_iface_tmp->soft_iface)
continue; continue;
if (batman_if_tmp->if_num > batman_if->if_num) if (hard_iface_tmp->if_num > hard_iface->if_num)
batman_if_tmp->if_num--; hard_iface_tmp->if_num--;
} }
rcu_read_unlock(); rcu_read_unlock();
batman_if->if_num = -1; hard_iface->if_num = -1;
spin_unlock_bh(&bat_priv->orig_hash_lock);
return 0; return 0;
err: err:
spin_unlock_bh(&bat_priv->orig_hash_lock); rcu_read_unlock();
return -ENOMEM; return -ENOMEM;
} }
...@@ -22,21 +22,28 @@ ...@@ -22,21 +22,28 @@
#ifndef _NET_BATMAN_ADV_ORIGINATOR_H_ #ifndef _NET_BATMAN_ADV_ORIGINATOR_H_
#define _NET_BATMAN_ADV_ORIGINATOR_H_ #define _NET_BATMAN_ADV_ORIGINATOR_H_
#include "hash.h"
int originator_init(struct bat_priv *bat_priv); int originator_init(struct bat_priv *bat_priv);
void originator_free(struct bat_priv *bat_priv); void originator_free(struct bat_priv *bat_priv);
void purge_orig_ref(struct bat_priv *bat_priv); void purge_orig_ref(struct bat_priv *bat_priv);
void orig_node_free_ref(struct orig_node *orig_node);
struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr);
struct neigh_node * struct neigh_node *create_neighbor(struct orig_node *orig_node,
create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, struct orig_node *orig_neigh_node,
uint8_t *neigh, struct batman_if *if_incoming); uint8_t *neigh,
struct hard_iface *if_incoming);
void neigh_node_free_ref(struct neigh_node *neigh_node);
int orig_seq_print_text(struct seq_file *seq, void *offset); int orig_seq_print_text(struct seq_file *seq, void *offset);
int orig_hash_add_if(struct batman_if *batman_if, int max_if_num); int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num);
int orig_hash_del_if(struct batman_if *batman_if, int max_if_num); int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num);
/* returns 1 if they are the same originator */ /* returns 1 if they are the same originator */
static inline int compare_orig(void *data1, void *data2) static inline int compare_orig(struct hlist_node *node, void *data2)
{ {
void *data1 = container_of(node, struct orig_node, hash_entry);
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
} }
...@@ -61,4 +68,35 @@ static inline int choose_orig(void *data, int32_t size) ...@@ -61,4 +68,35 @@ static inline int choose_orig(void *data, int32_t size)
return hash % size; return hash % size;
} }
static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv,
void *data)
{
struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_head *head;
struct hlist_node *node;
struct orig_node *orig_node, *orig_node_tmp = NULL;
int index;
if (!hash)
return NULL;
index = choose_orig(data, hash->size);
head = &hash->table[index];
rcu_read_lock();
hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
if (!compare_eth(orig_node, data))
continue;
if (!atomic_inc_not_zero(&orig_node->refcount))
continue;
orig_node_tmp = orig_node;
break;
}
rcu_read_unlock();
return orig_node_tmp;
}
#endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */ #endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */
...@@ -35,35 +35,33 @@ ...@@ -35,35 +35,33 @@
#include "gateway_client.h" #include "gateway_client.h"
#include "unicast.h" #include "unicast.h"
void slide_own_bcast_window(struct batman_if *batman_if) void slide_own_bcast_window(struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
unsigned long *word; unsigned long *word;
int i; int i;
size_t word_index; size_t word_index;
spin_lock_bh(&bat_priv->orig_hash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
word_index = batman_if->if_num * NUM_WORDS; spin_lock_bh(&orig_node->ogm_cnt_lock);
word_index = hard_iface->if_num * NUM_WORDS;
word = &(orig_node->bcast_own[word_index]); word = &(orig_node->bcast_own[word_index]);
bit_get_packet(bat_priv, word, 1, 0); bit_get_packet(bat_priv, word, 1, 0);
orig_node->bcast_own_sum[batman_if->if_num] = orig_node->bcast_own_sum[hard_iface->if_num] =
bit_packet_count(word); bit_packet_count(word);
spin_unlock_bh(&orig_node->ogm_cnt_lock);
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
} }
static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node,
...@@ -89,6 +87,8 @@ static void update_route(struct bat_priv *bat_priv, ...@@ -89,6 +87,8 @@ static void update_route(struct bat_priv *bat_priv,
struct neigh_node *neigh_node, struct neigh_node *neigh_node,
unsigned char *hna_buff, int hna_buff_len) unsigned char *hna_buff, int hna_buff_len)
{ {
struct neigh_node *neigh_node_tmp;
/* route deleted */ /* route deleted */
if ((orig_node->router) && (!neigh_node)) { if ((orig_node->router) && (!neigh_node)) {
...@@ -115,7 +115,12 @@ static void update_route(struct bat_priv *bat_priv, ...@@ -115,7 +115,12 @@ static void update_route(struct bat_priv *bat_priv,
orig_node->router->addr); orig_node->router->addr);
} }
if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount))
neigh_node = NULL;
neigh_node_tmp = orig_node->router;
orig_node->router = neigh_node; orig_node->router = neigh_node;
if (neigh_node_tmp)
neigh_node_free_ref(neigh_node_tmp);
} }
...@@ -138,73 +143,93 @@ void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, ...@@ -138,73 +143,93 @@ void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node,
static int is_bidirectional_neigh(struct orig_node *orig_node, static int is_bidirectional_neigh(struct orig_node *orig_node,
struct orig_node *orig_neigh_node, struct orig_node *orig_neigh_node,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
struct batman_if *if_incoming) struct hard_iface *if_incoming)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; struct neigh_node *neigh_node = NULL, *tmp_neigh_node;
struct hlist_node *node;
unsigned char total_count; unsigned char total_count;
uint8_t orig_eq_count, neigh_rq_count, tq_own;
int tq_asym_penalty, ret = 0;
if (orig_node == orig_neigh_node) { if (orig_node == orig_neigh_node) {
list_for_each_entry(tmp_neigh_node, rcu_read_lock();
&orig_node->neigh_list, hlist_for_each_entry_rcu(tmp_neigh_node, node,
list) { &orig_node->neigh_list, list) {
if (compare_orig(tmp_neigh_node->addr, if (!compare_eth(tmp_neigh_node->addr,
orig_neigh_node->orig) && orig_neigh_node->orig))
(tmp_neigh_node->if_incoming == if_incoming)) continue;
neigh_node = tmp_neigh_node;
if (tmp_neigh_node->if_incoming != if_incoming)
continue;
if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
continue;
neigh_node = tmp_neigh_node;
} }
rcu_read_unlock();
if (!neigh_node) if (!neigh_node)
neigh_node = create_neighbor(orig_node, neigh_node = create_neighbor(orig_node,
orig_neigh_node, orig_neigh_node,
orig_neigh_node->orig, orig_neigh_node->orig,
if_incoming); if_incoming);
/* create_neighbor failed, return 0 */
if (!neigh_node) if (!neigh_node)
return 0; goto out;
neigh_node->last_valid = jiffies; neigh_node->last_valid = jiffies;
} else { } else {
/* find packet count of corresponding one hop neighbor */ /* find packet count of corresponding one hop neighbor */
list_for_each_entry(tmp_neigh_node, rcu_read_lock();
&orig_neigh_node->neigh_list, list) { hlist_for_each_entry_rcu(tmp_neigh_node, node,
&orig_neigh_node->neigh_list, list) {
if (!compare_eth(tmp_neigh_node->addr,
orig_neigh_node->orig))
continue;
if (tmp_neigh_node->if_incoming != if_incoming)
continue;
if (compare_orig(tmp_neigh_node->addr, if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
orig_neigh_node->orig) && continue;
(tmp_neigh_node->if_incoming == if_incoming))
neigh_node = tmp_neigh_node; neigh_node = tmp_neigh_node;
} }
rcu_read_unlock();
if (!neigh_node) if (!neigh_node)
neigh_node = create_neighbor(orig_neigh_node, neigh_node = create_neighbor(orig_neigh_node,
orig_neigh_node, orig_neigh_node,
orig_neigh_node->orig, orig_neigh_node->orig,
if_incoming); if_incoming);
/* create_neighbor failed, return 0 */
if (!neigh_node) if (!neigh_node)
return 0; goto out;
} }
orig_node->last_valid = jiffies; orig_node->last_valid = jiffies;
spin_lock_bh(&orig_node->ogm_cnt_lock);
orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];
neigh_rq_count = neigh_node->real_packet_count;
spin_unlock_bh(&orig_node->ogm_cnt_lock);
/* pay attention to not get a value bigger than 100 % */ /* pay attention to not get a value bigger than 100 % */
total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > total_count = (orig_eq_count > neigh_rq_count ?
neigh_node->real_packet_count ? neigh_rq_count : orig_eq_count);
neigh_node->real_packet_count :
orig_neigh_node->bcast_own_sum[if_incoming->if_num]);
/* if we have too few packets (too less data) we set tq_own to zero */ /* if we have too few packets (too less data) we set tq_own to zero */
/* if we receive too few packets it is not considered bidirectional */ /* if we receive too few packets it is not considered bidirectional */
if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) ||
(neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) (neigh_rq_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM))
orig_neigh_node->tq_own = 0; tq_own = 0;
else else
/* neigh_node->real_packet_count is never zero as we /* neigh_node->real_packet_count is never zero as we
* only purge old information when getting new * only purge old information when getting new
* information */ * information */
orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / tq_own = (TQ_MAX_VALUE * total_count) / neigh_rq_count;
neigh_node->real_packet_count;
/* /*
* 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does
...@@ -212,20 +237,16 @@ static int is_bidirectional_neigh(struct orig_node *orig_node, ...@@ -212,20 +237,16 @@ static int is_bidirectional_neigh(struct orig_node *orig_node,
* punishes asymmetric links more. This will give a value * punishes asymmetric links more. This will give a value
* between 0 and TQ_MAX_VALUE * between 0 and TQ_MAX_VALUE
*/ */
orig_neigh_node->tq_asym_penalty = tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE *
TQ_MAX_VALUE - (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) *
(TQ_MAX_VALUE * (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) *
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count)) /
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * (TQ_LOCAL_WINDOW_SIZE *
(TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / TQ_LOCAL_WINDOW_SIZE *
(TQ_LOCAL_WINDOW_SIZE * TQ_LOCAL_WINDOW_SIZE);
TQ_LOCAL_WINDOW_SIZE *
TQ_LOCAL_WINDOW_SIZE); batman_packet->tq = ((batman_packet->tq * tq_own * tq_asym_penalty) /
(TQ_MAX_VALUE * TQ_MAX_VALUE));
batman_packet->tq = ((batman_packet->tq *
orig_neigh_node->tq_own *
orig_neigh_node->tq_asym_penalty) /
(TQ_MAX_VALUE * TQ_MAX_VALUE));
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"bidirectional: " "bidirectional: "
...@@ -233,34 +254,141 @@ static int is_bidirectional_neigh(struct orig_node *orig_node, ...@@ -233,34 +254,141 @@ static int is_bidirectional_neigh(struct orig_node *orig_node,
"real recv = %2i, local tq: %3i, asym_penalty: %3i, " "real recv = %2i, local tq: %3i, asym_penalty: %3i, "
"total tq: %3i\n", "total tq: %3i\n",
orig_node->orig, orig_neigh_node->orig, total_count, orig_node->orig, orig_neigh_node->orig, total_count,
neigh_node->real_packet_count, orig_neigh_node->tq_own, neigh_rq_count, tq_own, tq_asym_penalty, batman_packet->tq);
orig_neigh_node->tq_asym_penalty, batman_packet->tq);
/* if link has the minimum required transmission quality /* if link has the minimum required transmission quality
* consider it bidirectional */ * consider it bidirectional */
if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT)
return 1; ret = 1;
return 0; out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
return ret;
}
/* caller must hold the neigh_list_lock */
void bonding_candidate_del(struct orig_node *orig_node,
struct neigh_node *neigh_node)
{
/* this neighbor is not part of our candidate list */
if (list_empty(&neigh_node->bonding_list))
goto out;
list_del_rcu(&neigh_node->bonding_list);
INIT_LIST_HEAD(&neigh_node->bonding_list);
neigh_node_free_ref(neigh_node);
atomic_dec(&orig_node->bond_candidates);
out:
return;
}
static void bonding_candidate_add(struct orig_node *orig_node,
struct neigh_node *neigh_node)
{
struct hlist_node *node;
struct neigh_node *tmp_neigh_node;
uint8_t best_tq, interference_candidate = 0;
spin_lock_bh(&orig_node->neigh_list_lock);
/* only consider if it has the same primary address ... */
if (!compare_eth(orig_node->orig,
neigh_node->orig_node->primary_addr))
goto candidate_del;
if (!orig_node->router)
goto candidate_del;
best_tq = orig_node->router->tq_avg;
/* ... and is good enough to be considered */
if (neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD)
goto candidate_del;
/**
* check if we have another candidate with the same mac address or
* interface. If we do, we won't select this candidate because of
* possible interference.
*/
hlist_for_each_entry_rcu(tmp_neigh_node, node,
&orig_node->neigh_list, list) {
if (tmp_neigh_node == neigh_node)
continue;
/* we only care if the other candidate is even
* considered as candidate. */
if (list_empty(&tmp_neigh_node->bonding_list))
continue;
if ((neigh_node->if_incoming == tmp_neigh_node->if_incoming) ||
(compare_eth(neigh_node->addr, tmp_neigh_node->addr))) {
interference_candidate = 1;
break;
}
}
/* don't care further if it is an interference candidate */
if (interference_candidate)
goto candidate_del;
/* this neighbor already is part of our candidate list */
if (!list_empty(&neigh_node->bonding_list))
goto out;
if (!atomic_inc_not_zero(&neigh_node->refcount))
goto out;
list_add_rcu(&neigh_node->bonding_list, &orig_node->bond_list);
atomic_inc(&orig_node->bond_candidates);
goto out;
candidate_del:
bonding_candidate_del(orig_node, neigh_node);
out:
spin_unlock_bh(&orig_node->neigh_list_lock);
return;
}
/* copy primary address for bonding */
static void bonding_save_primary(struct orig_node *orig_node,
struct orig_node *orig_neigh_node,
struct batman_packet *batman_packet)
{
if (!(batman_packet->flags & PRIMARIES_FIRST_HOP))
return;
memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN);
} }
static void update_orig(struct bat_priv *bat_priv, static void update_orig(struct bat_priv *bat_priv,
struct orig_node *orig_node, struct orig_node *orig_node,
struct ethhdr *ethhdr, struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
struct batman_if *if_incoming, struct hard_iface *if_incoming,
unsigned char *hna_buff, int hna_buff_len, unsigned char *hna_buff, int hna_buff_len,
char is_duplicate) char is_duplicate)
{ {
struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
struct orig_node *orig_node_tmp;
struct hlist_node *node;
int tmp_hna_buff_len; int tmp_hna_buff_len;
uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;
bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): "
"Searching and updating originator entry of received packet\n"); "Searching and updating originator entry of received packet\n");
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { rcu_read_lock();
if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && hlist_for_each_entry_rcu(tmp_neigh_node, node,
(tmp_neigh_node->if_incoming == if_incoming)) { &orig_node->neigh_list, list) {
if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) &&
(tmp_neigh_node->if_incoming == if_incoming) &&
atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
if (neigh_node)
neigh_node_free_ref(neigh_node);
neigh_node = tmp_neigh_node; neigh_node = tmp_neigh_node;
continue; continue;
} }
...@@ -279,16 +407,20 @@ static void update_orig(struct bat_priv *bat_priv, ...@@ -279,16 +407,20 @@ static void update_orig(struct bat_priv *bat_priv,
orig_tmp = get_orig_node(bat_priv, ethhdr->h_source); orig_tmp = get_orig_node(bat_priv, ethhdr->h_source);
if (!orig_tmp) if (!orig_tmp)
return; goto unlock;
neigh_node = create_neighbor(orig_node, orig_tmp, neigh_node = create_neighbor(orig_node, orig_tmp,
ethhdr->h_source, if_incoming); ethhdr->h_source, if_incoming);
orig_node_free_ref(orig_tmp);
if (!neigh_node) if (!neigh_node)
return; goto unlock;
} else } else
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Updating existing last-hop neighbor of originator\n"); "Updating existing last-hop neighbor of originator\n");
rcu_read_unlock();
orig_node->flags = batman_packet->flags; orig_node->flags = batman_packet->flags;
neigh_node->last_valid = jiffies; neigh_node->last_valid = jiffies;
...@@ -302,6 +434,8 @@ static void update_orig(struct bat_priv *bat_priv, ...@@ -302,6 +434,8 @@ static void update_orig(struct bat_priv *bat_priv,
neigh_node->last_ttl = batman_packet->ttl; neigh_node->last_ttl = batman_packet->ttl;
} }
bonding_candidate_add(orig_node, neigh_node);
tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ?
batman_packet->num_hna * ETH_ALEN : hna_buff_len); batman_packet->num_hna * ETH_ALEN : hna_buff_len);
...@@ -318,10 +452,22 @@ static void update_orig(struct bat_priv *bat_priv, ...@@ -318,10 +452,22 @@ static void update_orig(struct bat_priv *bat_priv,
/* if the TQ is the same and the link not more symetric we /* if the TQ is the same and the link not more symetric we
* won't consider it either */ * won't consider it either */
if ((orig_node->router) && if ((orig_node->router) &&
((neigh_node->tq_avg == orig_node->router->tq_avg) && (neigh_node->tq_avg == orig_node->router->tq_avg)) {
(orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] orig_node_tmp = orig_node->router->orig_node;
>= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
goto update_hna; bcast_own_sum_orig =
orig_node_tmp->bcast_own_sum[if_incoming->if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
orig_node_tmp = neigh_node->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
bcast_own_sum_neigh =
orig_node_tmp->bcast_own_sum[if_incoming->if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
if (bcast_own_sum_orig >= bcast_own_sum_neigh)
goto update_hna;
}
update_routes(bat_priv, orig_node, neigh_node, update_routes(bat_priv, orig_node, neigh_node,
hna_buff, tmp_hna_buff_len); hna_buff, tmp_hna_buff_len);
...@@ -342,6 +488,14 @@ static void update_orig(struct bat_priv *bat_priv, ...@@ -342,6 +488,14 @@ static void update_orig(struct bat_priv *bat_priv,
(atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) && (atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) &&
(atomic_read(&bat_priv->gw_sel_class) > 2)) (atomic_read(&bat_priv->gw_sel_class) > 2))
gw_check_election(bat_priv, orig_node); gw_check_election(bat_priv, orig_node);
goto out;
unlock:
rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
} }
/* checks whether the host restarted and is in the protection time. /* checks whether the host restarted and is in the protection time.
...@@ -379,34 +533,38 @@ static int window_protected(struct bat_priv *bat_priv, ...@@ -379,34 +533,38 @@ static int window_protected(struct bat_priv *bat_priv,
*/ */
static char count_real_packets(struct ethhdr *ethhdr, static char count_real_packets(struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
struct batman_if *if_incoming) struct hard_iface *if_incoming)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct orig_node *orig_node; struct orig_node *orig_node;
struct neigh_node *tmp_neigh_node; struct neigh_node *tmp_neigh_node;
struct hlist_node *node;
char is_duplicate = 0; char is_duplicate = 0;
int32_t seq_diff; int32_t seq_diff;
int need_update = 0; int need_update = 0;
int set_mark; int set_mark, ret = -1;
orig_node = get_orig_node(bat_priv, batman_packet->orig); orig_node = get_orig_node(bat_priv, batman_packet->orig);
if (!orig_node) if (!orig_node)
return 0; return 0;
spin_lock_bh(&orig_node->ogm_cnt_lock);
seq_diff = batman_packet->seqno - orig_node->last_real_seqno; seq_diff = batman_packet->seqno - orig_node->last_real_seqno;
/* signalize caller that the packet is to be dropped. */ /* signalize caller that the packet is to be dropped. */
if (window_protected(bat_priv, seq_diff, if (window_protected(bat_priv, seq_diff,
&orig_node->batman_seqno_reset)) &orig_node->batman_seqno_reset))
return -1; goto out;
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { rcu_read_lock();
hlist_for_each_entry_rcu(tmp_neigh_node, node,
&orig_node->neigh_list, list) {
is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, is_duplicate |= get_bit_status(tmp_neigh_node->real_bits,
orig_node->last_real_seqno, orig_node->last_real_seqno,
batman_packet->seqno); batman_packet->seqno);
if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) &&
(tmp_neigh_node->if_incoming == if_incoming)) (tmp_neigh_node->if_incoming == if_incoming))
set_mark = 1; set_mark = 1;
else else
...@@ -420,6 +578,7 @@ static char count_real_packets(struct ethhdr *ethhdr, ...@@ -420,6 +578,7 @@ static char count_real_packets(struct ethhdr *ethhdr,
tmp_neigh_node->real_packet_count = tmp_neigh_node->real_packet_count =
bit_packet_count(tmp_neigh_node->real_bits); bit_packet_count(tmp_neigh_node->real_bits);
} }
rcu_read_unlock();
if (need_update) { if (need_update) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
...@@ -428,121 +587,21 @@ static char count_real_packets(struct ethhdr *ethhdr, ...@@ -428,121 +587,21 @@ static char count_real_packets(struct ethhdr *ethhdr,
orig_node->last_real_seqno = batman_packet->seqno; orig_node->last_real_seqno = batman_packet->seqno;
} }
return is_duplicate; ret = is_duplicate;
}
/* copy primary address for bonding */
static void mark_bonding_address(struct orig_node *orig_node,
struct orig_node *orig_neigh_node,
struct batman_packet *batman_packet)
{
if (batman_packet->flags & PRIMARIES_FIRST_HOP)
memcpy(orig_neigh_node->primary_addr,
orig_node->orig, ETH_ALEN);
return;
}
/* mark possible bond.candidates in the neighbor list */
void update_bonding_candidates(struct orig_node *orig_node)
{
int candidates;
int interference_candidate;
int best_tq;
struct neigh_node *tmp_neigh_node, *tmp_neigh_node2;
struct neigh_node *first_candidate, *last_candidate;
/* update the candidates for this originator */
if (!orig_node->router) {
orig_node->bond.candidates = 0;
return;
}
best_tq = orig_node->router->tq_avg;
/* update bond.candidates */
candidates = 0;
/* mark other nodes which also received "PRIMARIES FIRST HOP" packets
* as "bonding partner" */
/* first, zero the list */
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
tmp_neigh_node->next_bond_candidate = NULL;
}
first_candidate = NULL;
last_candidate = NULL;
list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) {
/* only consider if it has the same primary address ... */
if (memcmp(orig_node->orig,
tmp_neigh_node->orig_node->primary_addr,
ETH_ALEN) != 0)
continue;
/* ... and is good enough to be considered */
if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD)
continue;
/* check if we have another candidate with the same
* mac address or interface. If we do, we won't
* select this candidate because of possible interference. */
interference_candidate = 0;
list_for_each_entry(tmp_neigh_node2,
&orig_node->neigh_list, list) {
if (tmp_neigh_node2 == tmp_neigh_node)
continue;
/* we only care if the other candidate is even
* considered as candidate. */
if (!tmp_neigh_node2->next_bond_candidate)
continue;
if ((tmp_neigh_node->if_incoming ==
tmp_neigh_node2->if_incoming)
|| (memcmp(tmp_neigh_node->addr,
tmp_neigh_node2->addr, ETH_ALEN) == 0)) {
interference_candidate = 1;
break;
}
}
/* don't care further if it is an interference candidate */
if (interference_candidate)
continue;
if (!first_candidate) {
first_candidate = tmp_neigh_node;
tmp_neigh_node->next_bond_candidate = first_candidate;
} else
tmp_neigh_node->next_bond_candidate = last_candidate;
last_candidate = tmp_neigh_node;
candidates++;
}
if (candidates > 0) {
first_candidate->next_bond_candidate = last_candidate;
orig_node->bond.selected = first_candidate;
}
orig_node->bond.candidates = candidates; out:
spin_unlock_bh(&orig_node->ogm_cnt_lock);
orig_node_free_ref(orig_node);
return ret;
} }
void receive_bat_packet(struct ethhdr *ethhdr, void receive_bat_packet(struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
unsigned char *hna_buff, int hna_buff_len, unsigned char *hna_buff, int hna_buff_len,
struct batman_if *if_incoming) struct hard_iface *if_incoming)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
struct batman_if *batman_if; struct hard_iface *hard_iface;
struct orig_node *orig_neigh_node, *orig_node; struct orig_node *orig_neigh_node, *orig_node;
char has_directlink_flag; char has_directlink_flag;
char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;
...@@ -570,8 +629,8 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -570,8 +629,8 @@ void receive_bat_packet(struct ethhdr *ethhdr,
has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0);
is_single_hop_neigh = (compare_orig(ethhdr->h_source, is_single_hop_neigh = (compare_eth(ethhdr->h_source,
batman_packet->orig) ? 1 : 0); batman_packet->orig) ? 1 : 0);
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Received BATMAN packet via NB: %pM, IF: %s [%pM] " "Received BATMAN packet via NB: %pM, IF: %s [%pM] "
...@@ -584,26 +643,26 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -584,26 +643,26 @@ void receive_bat_packet(struct ethhdr *ethhdr,
has_directlink_flag); has_directlink_flag);
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->if_status != IF_ACTIVE) if (hard_iface->if_status != IF_ACTIVE)
continue; continue;
if (batman_if->soft_iface != if_incoming->soft_iface) if (hard_iface->soft_iface != if_incoming->soft_iface)
continue; continue;
if (compare_orig(ethhdr->h_source, if (compare_eth(ethhdr->h_source,
batman_if->net_dev->dev_addr)) hard_iface->net_dev->dev_addr))
is_my_addr = 1; is_my_addr = 1;
if (compare_orig(batman_packet->orig, if (compare_eth(batman_packet->orig,
batman_if->net_dev->dev_addr)) hard_iface->net_dev->dev_addr))
is_my_orig = 1; is_my_orig = 1;
if (compare_orig(batman_packet->prev_sender, if (compare_eth(batman_packet->prev_sender,
batman_if->net_dev->dev_addr)) hard_iface->net_dev->dev_addr))
is_my_oldorig = 1; is_my_oldorig = 1;
if (compare_orig(ethhdr->h_source, broadcast_addr)) if (compare_eth(ethhdr->h_source, broadcast_addr))
is_broadcast = 1; is_broadcast = 1;
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -635,7 +694,6 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -635,7 +694,6 @@ void receive_bat_packet(struct ethhdr *ethhdr,
int offset; int offset;
orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source); orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source);
if (!orig_neigh_node) if (!orig_neigh_node)
return; return;
...@@ -644,18 +702,22 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -644,18 +702,22 @@ void receive_bat_packet(struct ethhdr *ethhdr,
/* if received seqno equals last send seqno save new /* if received seqno equals last send seqno save new
* seqno for bidirectional check */ * seqno for bidirectional check */
if (has_directlink_flag && if (has_directlink_flag &&
compare_orig(if_incoming->net_dev->dev_addr, compare_eth(if_incoming->net_dev->dev_addr,
batman_packet->orig) && batman_packet->orig) &&
(batman_packet->seqno - if_incoming_seqno + 2 == 0)) { (batman_packet->seqno - if_incoming_seqno + 2 == 0)) {
offset = if_incoming->if_num * NUM_WORDS; offset = if_incoming->if_num * NUM_WORDS;
spin_lock_bh(&orig_neigh_node->ogm_cnt_lock);
word = &(orig_neigh_node->bcast_own[offset]); word = &(orig_neigh_node->bcast_own[offset]);
bit_mark(word, 0); bit_mark(word, 0);
orig_neigh_node->bcast_own_sum[if_incoming->if_num] = orig_neigh_node->bcast_own_sum[if_incoming->if_num] =
bit_packet_count(word); bit_packet_count(word);
spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock);
} }
bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: "
"originator packet from myself (via neighbor)\n"); "originator packet from myself (via neighbor)\n");
orig_node_free_ref(orig_neigh_node);
return; return;
} }
...@@ -676,27 +738,27 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -676,27 +738,27 @@ void receive_bat_packet(struct ethhdr *ethhdr,
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: packet within seqno protection time " "Drop packet: packet within seqno protection time "
"(sender: %pM)\n", ethhdr->h_source); "(sender: %pM)\n", ethhdr->h_source);
return; goto out;
} }
if (batman_packet->tq == 0) { if (batman_packet->tq == 0) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: originator packet with tq equal 0\n"); "Drop packet: originator packet with tq equal 0\n");
return; goto out;
} }
/* avoid temporary routing loops */ /* avoid temporary routing loops */
if ((orig_node->router) && if ((orig_node->router) &&
(orig_node->router->orig_node->router) && (orig_node->router->orig_node->router) &&
(compare_orig(orig_node->router->addr, (compare_eth(orig_node->router->addr,
batman_packet->prev_sender)) && batman_packet->prev_sender)) &&
!(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && !(compare_eth(batman_packet->orig, batman_packet->prev_sender)) &&
(compare_orig(orig_node->router->addr, (compare_eth(orig_node->router->addr,
orig_node->router->orig_node->router->addr))) { orig_node->router->orig_node->router->addr))) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: ignoring all rebroadcast packets that " "Drop packet: ignoring all rebroadcast packets that "
"may make me loop (sender: %pM)\n", ethhdr->h_source); "may make me loop (sender: %pM)\n", ethhdr->h_source);
return; goto out;
} }
/* if sender is a direct neighbor the sender mac equals /* if sender is a direct neighbor the sender mac equals
...@@ -705,19 +767,21 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -705,19 +767,21 @@ void receive_bat_packet(struct ethhdr *ethhdr,
orig_node : orig_node :
get_orig_node(bat_priv, ethhdr->h_source)); get_orig_node(bat_priv, ethhdr->h_source));
if (!orig_neigh_node) if (!orig_neigh_node)
return; goto out;
/* drop packet if sender is not a direct neighbor and if we /* drop packet if sender is not a direct neighbor and if we
* don't route towards it */ * don't route towards it */
if (!is_single_hop_neigh && (!orig_neigh_node->router)) { if (!is_single_hop_neigh && (!orig_neigh_node->router)) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: OGM via unknown neighbor!\n"); "Drop packet: OGM via unknown neighbor!\n");
return; goto out_neigh;
} }
is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node,
batman_packet, if_incoming); batman_packet, if_incoming);
bonding_save_primary(orig_node, orig_neigh_node, batman_packet);
/* update ranking if it is not a duplicate or has the same /* update ranking if it is not a duplicate or has the same
* seqno and similar ttl as the non-duplicate */ * seqno and similar ttl as the non-duplicate */
if (is_bidirectional && if (is_bidirectional &&
...@@ -727,9 +791,6 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -727,9 +791,6 @@ void receive_bat_packet(struct ethhdr *ethhdr,
update_orig(bat_priv, orig_node, ethhdr, batman_packet, update_orig(bat_priv, orig_node, ethhdr, batman_packet,
if_incoming, hna_buff, hna_buff_len, is_duplicate); if_incoming, hna_buff, hna_buff_len, is_duplicate);
mark_bonding_address(orig_node, orig_neigh_node, batman_packet);
update_bonding_candidates(orig_node);
/* is single hop (direct) neighbor */ /* is single hop (direct) neighbor */
if (is_single_hop_neigh) { if (is_single_hop_neigh) {
...@@ -739,31 +800,36 @@ void receive_bat_packet(struct ethhdr *ethhdr, ...@@ -739,31 +800,36 @@ void receive_bat_packet(struct ethhdr *ethhdr,
bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: "
"rebroadcast neighbor packet with direct link flag\n"); "rebroadcast neighbor packet with direct link flag\n");
return; goto out_neigh;
} }
/* multihop originator */ /* multihop originator */
if (!is_bidirectional) { if (!is_bidirectional) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: not received via bidirectional link\n"); "Drop packet: not received via bidirectional link\n");
return; goto out_neigh;
} }
if (is_duplicate) { if (is_duplicate) {
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Drop packet: duplicate packet received\n"); "Drop packet: duplicate packet received\n");
return; goto out_neigh;
} }
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"Forwarding packet: rebroadcast originator packet\n"); "Forwarding packet: rebroadcast originator packet\n");
schedule_forward_packet(orig_node, ethhdr, batman_packet, schedule_forward_packet(orig_node, ethhdr, batman_packet,
0, hna_buff_len, if_incoming); 0, hna_buff_len, if_incoming);
out_neigh:
if ((orig_neigh_node) && (!is_single_hop_neigh))
orig_node_free_ref(orig_neigh_node);
out:
orig_node_free_ref(orig_node);
} }
int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface);
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
/* drop packet if it has not necessary minimum size */ /* drop packet if it has not necessary minimum size */
...@@ -790,12 +856,10 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) ...@@ -790,12 +856,10 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if)
ethhdr = (struct ethhdr *)skb_mac_header(skb); ethhdr = (struct ethhdr *)skb_mac_header(skb);
spin_lock_bh(&bat_priv->orig_hash_lock);
receive_aggr_bat_packet(ethhdr, receive_aggr_bat_packet(ethhdr,
skb->data, skb->data,
skb_headlen(skb), skb_headlen(skb),
batman_if); hard_iface);
spin_unlock_bh(&bat_priv->orig_hash_lock);
kfree_skb(skb); kfree_skb(skb);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
...@@ -804,68 +868,75 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) ...@@ -804,68 +868,75 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if)
static int recv_my_icmp_packet(struct bat_priv *bat_priv, static int recv_my_icmp_packet(struct bat_priv *bat_priv,
struct sk_buff *skb, size_t icmp_len) struct sk_buff *skb, size_t icmp_len)
{ {
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct neigh_node *neigh_node = NULL;
struct icmp_packet_rr *icmp_packet; struct icmp_packet_rr *icmp_packet;
struct batman_if *batman_if; int ret = NET_RX_DROP;
int ret;
uint8_t dstaddr[ETH_ALEN];
icmp_packet = (struct icmp_packet_rr *)skb->data; icmp_packet = (struct icmp_packet_rr *)skb->data;
/* add data to device queue */ /* add data to device queue */
if (icmp_packet->msg_type != ECHO_REQUEST) { if (icmp_packet->msg_type != ECHO_REQUEST) {
bat_socket_receive_packet(icmp_packet, icmp_len); bat_socket_receive_packet(icmp_packet, icmp_len);
return NET_RX_DROP; goto out;
} }
if (!bat_priv->primary_if) if (!bat_priv->primary_if)
return NET_RX_DROP; goto out;
/* answer echo request (ping) */ /* answer echo request (ping) */
/* get routing information */ /* get routing information */
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, orig_node = orig_hash_find(bat_priv, icmp_packet->orig);
compare_orig, choose_orig,
icmp_packet->orig)); if (!orig_node)
ret = NET_RX_DROP; goto unlock;
if ((orig_node) && (orig_node->router)) {
/* don't lock while sending the packets ... we therefore
* copy the required data before sending */
batman_if = orig_node->router->if_incoming;
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
spin_unlock_bh(&bat_priv->orig_hash_lock);
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
return NET_RX_DROP;
icmp_packet = (struct icmp_packet_rr *)skb->data; neigh_node = orig_node->router;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); if (!neigh_node)
memcpy(icmp_packet->orig, goto unlock;
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = ECHO_REPLY;
icmp_packet->ttl = TTL;
send_skb_packet(skb, batman_if, dstaddr); if (!atomic_inc_not_zero(&neigh_node->refcount)) {
ret = NET_RX_SUCCESS; neigh_node = NULL;
goto unlock;
}
} else rcu_read_unlock();
spin_unlock_bh(&bat_priv->orig_hash_lock);
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
goto out;
icmp_packet = (struct icmp_packet_rr *)skb->data;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = ECHO_REPLY;
icmp_packet->ttl = TTL;
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
ret = NET_RX_SUCCESS;
goto out;
unlock:
rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return ret; return ret;
} }
static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct neigh_node *neigh_node = NULL;
struct icmp_packet *icmp_packet; struct icmp_packet *icmp_packet;
struct batman_if *batman_if; int ret = NET_RX_DROP;
int ret;
uint8_t dstaddr[ETH_ALEN];
icmp_packet = (struct icmp_packet *)skb->data; icmp_packet = (struct icmp_packet *)skb->data;
...@@ -874,59 +945,67 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, ...@@ -874,59 +945,67 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
pr_debug("Warning - can't forward icmp packet from %pM to " pr_debug("Warning - can't forward icmp packet from %pM to "
"%pM: ttl exceeded\n", icmp_packet->orig, "%pM: ttl exceeded\n", icmp_packet->orig,
icmp_packet->dst); icmp_packet->dst);
return NET_RX_DROP; goto out;
} }
if (!bat_priv->primary_if) if (!bat_priv->primary_if)
return NET_RX_DROP; goto out;
/* get routing information */ /* get routing information */
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *) orig_node = orig_hash_find(bat_priv, icmp_packet->orig);
hash_find(bat_priv->orig_hash, compare_orig, choose_orig,
icmp_packet->orig));
ret = NET_RX_DROP;
if ((orig_node) && (orig_node->router)) {
/* don't lock while sending the packets ... we therefore
* copy the required data before sending */
batman_if = orig_node->router->if_incoming;
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
spin_unlock_bh(&bat_priv->orig_hash_lock);
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
return NET_RX_DROP;
icmp_packet = (struct icmp_packet *) skb->data; if (!orig_node)
goto unlock;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); neigh_node = orig_node->router;
memcpy(icmp_packet->orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = TTL_EXCEEDED;
icmp_packet->ttl = TTL;
send_skb_packet(skb, batman_if, dstaddr); if (!neigh_node)
ret = NET_RX_SUCCESS; goto unlock;
} else if (!atomic_inc_not_zero(&neigh_node->refcount)) {
spin_unlock_bh(&bat_priv->orig_hash_lock); neigh_node = NULL;
goto unlock;
}
rcu_read_unlock();
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
goto out;
icmp_packet = (struct icmp_packet *)skb->data;
memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
memcpy(icmp_packet->orig,
bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
icmp_packet->msg_type = TTL_EXCEEDED;
icmp_packet->ttl = TTL;
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
ret = NET_RX_SUCCESS;
goto out;
unlock:
rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return ret; return ret;
} }
int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{ {
struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct icmp_packet_rr *icmp_packet; struct icmp_packet_rr *icmp_packet;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct batman_if *batman_if; struct neigh_node *neigh_node = NULL;
int hdr_size = sizeof(struct icmp_packet); int hdr_size = sizeof(struct icmp_packet);
int ret; int ret = NET_RX_DROP;
uint8_t dstaddr[ETH_ALEN];
/** /**
* we truncate all incoming icmp packets if they don't match our size * we truncate all incoming icmp packets if they don't match our size
...@@ -936,21 +1015,21 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) ...@@ -936,21 +1015,21 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if)
/* drop packet if it has not necessary minimum size */ /* drop packet if it has not necessary minimum size */
if (unlikely(!pskb_may_pull(skb, hdr_size))) if (unlikely(!pskb_may_pull(skb, hdr_size)))
return NET_RX_DROP; goto out;
ethhdr = (struct ethhdr *)skb_mac_header(skb); ethhdr = (struct ethhdr *)skb_mac_header(skb);
/* packet with unicast indication but broadcast recipient */ /* packet with unicast indication but broadcast recipient */
if (is_broadcast_ether_addr(ethhdr->h_dest)) if (is_broadcast_ether_addr(ethhdr->h_dest))
return NET_RX_DROP; goto out;
/* packet with broadcast sender address */ /* packet with broadcast sender address */
if (is_broadcast_ether_addr(ethhdr->h_source)) if (is_broadcast_ether_addr(ethhdr->h_source))
return NET_RX_DROP; goto out;
/* not for me */ /* not for me */
if (!is_my_mac(ethhdr->h_dest)) if (!is_my_mac(ethhdr->h_dest))
return NET_RX_DROP; goto out;
icmp_packet = (struct icmp_packet_rr *)skb->data; icmp_packet = (struct icmp_packet_rr *)skb->data;
...@@ -970,50 +1049,59 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) ...@@ -970,50 +1049,59 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if)
if (icmp_packet->ttl < 2) if (icmp_packet->ttl < 2)
return recv_icmp_ttl_exceeded(bat_priv, skb); return recv_icmp_ttl_exceeded(bat_priv, skb);
ret = NET_RX_DROP;
/* get routing information */ /* get routing information */
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *) orig_node = orig_hash_find(bat_priv, icmp_packet->dst);
hash_find(bat_priv->orig_hash, compare_orig, choose_orig,
icmp_packet->dst));
if ((orig_node) && (orig_node->router)) { if (!orig_node)
goto unlock;
/* don't lock while sending the packets ... we therefore neigh_node = orig_node->router;
* copy the required data before sending */
batman_if = orig_node->router->if_incoming;
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
spin_unlock_bh(&bat_priv->orig_hash_lock);
/* create a copy of the skb, if needed, to modify it. */ if (!neigh_node)
if (skb_cow(skb, sizeof(struct ethhdr)) < 0) goto unlock;
return NET_RX_DROP;
icmp_packet = (struct icmp_packet_rr *)skb->data; if (!atomic_inc_not_zero(&neigh_node->refcount)) {
neigh_node = NULL;
goto unlock;
}
/* decrement ttl */ rcu_read_unlock();
icmp_packet->ttl--;
/* route it */ /* create a copy of the skb, if needed, to modify it. */
send_skb_packet(skb, batman_if, dstaddr); if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
ret = NET_RX_SUCCESS; goto out;
} else icmp_packet = (struct icmp_packet_rr *)skb->data;
spin_unlock_bh(&bat_priv->orig_hash_lock);
/* decrement ttl */
icmp_packet->ttl--;
/* route it */
send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
ret = NET_RX_SUCCESS;
goto out;
unlock:
rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return ret; return ret;
} }
/* find a suitable router for this originator, and use /* find a suitable router for this originator, and use
* bonding if possible. */ * bonding if possible. increases the found neighbors
* refcount.*/
struct neigh_node *find_router(struct bat_priv *bat_priv, struct neigh_node *find_router(struct bat_priv *bat_priv,
struct orig_node *orig_node, struct orig_node *orig_node,
struct batman_if *recv_if) struct hard_iface *recv_if)
{ {
struct orig_node *primary_orig_node; struct orig_node *primary_orig_node;
struct orig_node *router_orig; struct orig_node *router_orig;
struct neigh_node *router, *first_candidate, *best_router; struct neigh_node *router, *first_candidate, *tmp_neigh_node;
static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
int bonding_enabled; int bonding_enabled;
...@@ -1025,78 +1113,128 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, ...@@ -1025,78 +1113,128 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,
/* without bonding, the first node should /* without bonding, the first node should
* always choose the default router. */ * always choose the default router. */
bonding_enabled = atomic_read(&bat_priv->bonding); bonding_enabled = atomic_read(&bat_priv->bonding);
if ((!recv_if) && (!bonding_enabled)) rcu_read_lock();
return orig_node->router; /* select default router to output */
router = orig_node->router;
router_orig = orig_node->router->orig_node; router_orig = orig_node->router->orig_node;
if (!router_orig || !atomic_inc_not_zero(&router->refcount)) {
rcu_read_unlock();
return NULL;
}
if ((!recv_if) && (!bonding_enabled))
goto return_router;
/* if we have something in the primary_addr, we can search /* if we have something in the primary_addr, we can search
* for a potential bonding candidate. */ * for a potential bonding candidate. */
if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) if (compare_eth(router_orig->primary_addr, zero_mac))
return orig_node->router; goto return_router;
/* find the orig_node which has the primary interface. might /* find the orig_node which has the primary interface. might
* even be the same as our router_orig in many cases */ * even be the same as our router_orig in many cases */
if (memcmp(router_orig->primary_addr, if (compare_eth(router_orig->primary_addr, router_orig->orig)) {
router_orig->orig, ETH_ALEN) == 0) {
primary_orig_node = router_orig; primary_orig_node = router_orig;
} else { } else {
primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, primary_orig_node = orig_hash_find(bat_priv,
choose_orig, router_orig->primary_addr);
router_orig->primary_addr);
if (!primary_orig_node) if (!primary_orig_node)
return orig_node->router; goto return_router;
orig_node_free_ref(primary_orig_node);
} }
/* with less than 2 candidates, we can't do any /* with less than 2 candidates, we can't do any
* bonding and prefer the original router. */ * bonding and prefer the original router. */
if (atomic_read(&primary_orig_node->bond_candidates) < 2)
if (primary_orig_node->bond.candidates < 2) goto return_router;
return orig_node->router;
/* all nodes between should choose a candidate which /* all nodes between should choose a candidate which
* is is not on the interface where the packet came * is is not on the interface where the packet came
* in. */ * in. */
first_candidate = primary_orig_node->bond.selected;
router = first_candidate; neigh_node_free_ref(router);
first_candidate = NULL;
router = NULL;
if (bonding_enabled) { if (bonding_enabled) {
/* in the bonding case, send the packets in a round /* in the bonding case, send the packets in a round
* robin fashion over the remaining interfaces. */ * robin fashion over the remaining interfaces. */
do {
list_for_each_entry_rcu(tmp_neigh_node,
&primary_orig_node->bond_list, bonding_list) {
if (!first_candidate)
first_candidate = tmp_neigh_node;
/* recv_if == NULL on the first node. */ /* recv_if == NULL on the first node. */
if (router->if_incoming != recv_if) if (tmp_neigh_node->if_incoming != recv_if &&
atomic_inc_not_zero(&tmp_neigh_node->refcount)) {
router = tmp_neigh_node;
break; break;
}
}
router = router->next_bond_candidate; /* use the first candidate if nothing was found. */
} while (router != first_candidate); if (!router && first_candidate &&
atomic_inc_not_zero(&first_candidate->refcount))
router = first_candidate;
primary_orig_node->bond.selected = router->next_bond_candidate; if (!router) {
rcu_read_unlock();
return NULL;
}
/* selected should point to the next element
* after the current router */
spin_lock_bh(&primary_orig_node->neigh_list_lock);
/* this is a list_move(), which unfortunately
* does not exist as rcu version */
list_del_rcu(&primary_orig_node->bond_list);
list_add_rcu(&primary_orig_node->bond_list,
&router->bonding_list);
spin_unlock_bh(&primary_orig_node->neigh_list_lock);
} else { } else {
/* if bonding is disabled, use the best of the /* if bonding is disabled, use the best of the
* remaining candidates which are not using * remaining candidates which are not using
* this interface. */ * this interface. */
best_router = first_candidate; list_for_each_entry_rcu(tmp_neigh_node,
&primary_orig_node->bond_list, bonding_list) {
if (!first_candidate)
first_candidate = tmp_neigh_node;
do {
/* recv_if == NULL on the first node. */ /* recv_if == NULL on the first node. */
if ((router->if_incoming != recv_if) && if (tmp_neigh_node->if_incoming == recv_if)
(router->tq_avg > best_router->tq_avg)) continue;
best_router = router;
router = router->next_bond_candidate; if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
} while (router != first_candidate); continue;
router = best_router; /* if we don't have a router yet
} * or this one is better, choose it. */
if ((!router) ||
(tmp_neigh_node->tq_avg > router->tq_avg)) {
/* decrement refcount of
* previously selected router */
if (router)
neigh_node_free_ref(router);
router = tmp_neigh_node;
atomic_inc_not_zero(&router->refcount);
}
neigh_node_free_ref(tmp_neigh_node);
}
/* use the first candidate if nothing was found. */
if (!router && first_candidate &&
atomic_inc_not_zero(&first_candidate->refcount))
router = first_candidate;
}
return_router:
rcu_read_unlock();
return router; return router;
} }
...@@ -1125,17 +1263,14 @@ static int check_unicast_packet(struct sk_buff *skb, int hdr_size) ...@@ -1125,17 +1263,14 @@ static int check_unicast_packet(struct sk_buff *skb, int hdr_size)
return 0; return 0;
} }
int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
int hdr_size)
{ {
struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct neigh_node *router; struct neigh_node *neigh_node = NULL;
struct batman_if *batman_if;
uint8_t dstaddr[ETH_ALEN];
struct unicast_packet *unicast_packet; struct unicast_packet *unicast_packet;
struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
int ret; int ret = NET_RX_DROP;
struct sk_buff *new_skb; struct sk_buff *new_skb;
unicast_packet = (struct unicast_packet *)skb->data; unicast_packet = (struct unicast_packet *)skb->data;
...@@ -1145,53 +1280,51 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, ...@@ -1145,53 +1280,51 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if,
pr_debug("Warning - can't forward unicast packet from %pM to " pr_debug("Warning - can't forward unicast packet from %pM to "
"%pM: ttl exceeded\n", ethhdr->h_source, "%pM: ttl exceeded\n", ethhdr->h_source,
unicast_packet->dest); unicast_packet->dest);
return NET_RX_DROP; goto out;
} }
/* get routing information */ /* get routing information */
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *) orig_node = orig_hash_find(bat_priv, unicast_packet->dest);
hash_find(bat_priv->orig_hash, compare_orig, choose_orig,
unicast_packet->dest));
router = find_router(bat_priv, orig_node, recv_if);
if (!router) { if (!orig_node)
spin_unlock_bh(&bat_priv->orig_hash_lock); goto unlock;
return NET_RX_DROP;
}
/* don't lock while sending the packets ... we therefore rcu_read_unlock();
* copy the required data before sending */
batman_if = router->if_incoming; /* find_router() increases neigh_nodes refcount if found. */
memcpy(dstaddr, router->addr, ETH_ALEN); neigh_node = find_router(bat_priv, orig_node, recv_if);
spin_unlock_bh(&bat_priv->orig_hash_lock); if (!neigh_node)
goto out;
/* create a copy of the skb, if needed, to modify it. */ /* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, sizeof(struct ethhdr)) < 0) if (skb_cow(skb, sizeof(struct ethhdr)) < 0)
return NET_RX_DROP; goto out;
unicast_packet = (struct unicast_packet *)skb->data; unicast_packet = (struct unicast_packet *)skb->data;
if (unicast_packet->packet_type == BAT_UNICAST && if (unicast_packet->packet_type == BAT_UNICAST &&
atomic_read(&bat_priv->fragmentation) && atomic_read(&bat_priv->fragmentation) &&
skb->len > batman_if->net_dev->mtu) skb->len > neigh_node->if_incoming->net_dev->mtu) {
return frag_send_skb(skb, bat_priv, batman_if, ret = frag_send_skb(skb, bat_priv,
dstaddr); neigh_node->if_incoming, neigh_node->addr);
goto out;
}
if (unicast_packet->packet_type == BAT_UNICAST_FRAG && if (unicast_packet->packet_type == BAT_UNICAST_FRAG &&
frag_can_reassemble(skb, batman_if->net_dev->mtu)) { frag_can_reassemble(skb, neigh_node->if_incoming->net_dev->mtu)) {
ret = frag_reassemble_skb(skb, bat_priv, &new_skb); ret = frag_reassemble_skb(skb, bat_priv, &new_skb);
if (ret == NET_RX_DROP) if (ret == NET_RX_DROP)
return NET_RX_DROP; goto out;
/* packet was buffered for late merge */ /* packet was buffered for late merge */
if (!new_skb) if (!new_skb) {
return NET_RX_SUCCESS; ret = NET_RX_SUCCESS;
goto out;
}
skb = new_skb; skb = new_skb;
unicast_packet = (struct unicast_packet *)skb->data; unicast_packet = (struct unicast_packet *)skb->data;
...@@ -1201,12 +1334,21 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, ...@@ -1201,12 +1334,21 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if,
unicast_packet->ttl--; unicast_packet->ttl--;
/* route it */ /* route it */
send_skb_packet(skb, batman_if, dstaddr); send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
ret = NET_RX_SUCCESS;
goto out;
return NET_RX_SUCCESS; unlock:
rcu_read_unlock();
out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return ret;
} }
int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{ {
struct unicast_packet *unicast_packet; struct unicast_packet *unicast_packet;
int hdr_size = sizeof(struct unicast_packet); int hdr_size = sizeof(struct unicast_packet);
...@@ -1222,10 +1364,10 @@ int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) ...@@ -1222,10 +1364,10 @@ int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if)
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
} }
return route_unicast_packet(skb, recv_if, hdr_size); return route_unicast_packet(skb, recv_if);
} }
int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{ {
struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct unicast_frag_packet *unicast_packet; struct unicast_frag_packet *unicast_packet;
...@@ -1255,89 +1397,96 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) ...@@ -1255,89 +1397,96 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if)
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
} }
return route_unicast_packet(skb, recv_if, hdr_size); return route_unicast_packet(skb, recv_if);
} }
int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if) int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{ {
struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct orig_node *orig_node; struct orig_node *orig_node = NULL;
struct bcast_packet *bcast_packet; struct bcast_packet *bcast_packet;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
int hdr_size = sizeof(struct bcast_packet); int hdr_size = sizeof(struct bcast_packet);
int ret = NET_RX_DROP;
int32_t seq_diff; int32_t seq_diff;
/* drop packet if it has not necessary minimum size */ /* drop packet if it has not necessary minimum size */
if (unlikely(!pskb_may_pull(skb, hdr_size))) if (unlikely(!pskb_may_pull(skb, hdr_size)))
return NET_RX_DROP; goto out;
ethhdr = (struct ethhdr *)skb_mac_header(skb); ethhdr = (struct ethhdr *)skb_mac_header(skb);
/* packet with broadcast indication but unicast recipient */ /* packet with broadcast indication but unicast recipient */
if (!is_broadcast_ether_addr(ethhdr->h_dest)) if (!is_broadcast_ether_addr(ethhdr->h_dest))
return NET_RX_DROP; goto out;
/* packet with broadcast sender address */ /* packet with broadcast sender address */
if (is_broadcast_ether_addr(ethhdr->h_source)) if (is_broadcast_ether_addr(ethhdr->h_source))
return NET_RX_DROP; goto out;
/* ignore broadcasts sent by myself */ /* ignore broadcasts sent by myself */
if (is_my_mac(ethhdr->h_source)) if (is_my_mac(ethhdr->h_source))
return NET_RX_DROP; goto out;
bcast_packet = (struct bcast_packet *)skb->data; bcast_packet = (struct bcast_packet *)skb->data;
/* ignore broadcasts originated by myself */ /* ignore broadcasts originated by myself */
if (is_my_mac(bcast_packet->orig)) if (is_my_mac(bcast_packet->orig))
return NET_RX_DROP; goto out;
if (bcast_packet->ttl < 2) if (bcast_packet->ttl < 2)
return NET_RX_DROP; goto out;
spin_lock_bh(&bat_priv->orig_hash_lock); rcu_read_lock();
orig_node = ((struct orig_node *) orig_node = orig_hash_find(bat_priv, bcast_packet->orig);
hash_find(bat_priv->orig_hash, compare_orig, choose_orig,
bcast_packet->orig));
if (!orig_node) { if (!orig_node)
spin_unlock_bh(&bat_priv->orig_hash_lock); goto rcu_unlock;
return NET_RX_DROP;
} rcu_read_unlock();
spin_lock_bh(&orig_node->bcast_seqno_lock);
/* check whether the packet is a duplicate */ /* check whether the packet is a duplicate */
if (get_bit_status(orig_node->bcast_bits, if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno,
orig_node->last_bcast_seqno, ntohl(bcast_packet->seqno)))
ntohl(bcast_packet->seqno))) { goto spin_unlock;
spin_unlock_bh(&bat_priv->orig_hash_lock);
return NET_RX_DROP;
}
seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno;
/* check whether the packet is old and the host just restarted. */ /* check whether the packet is old and the host just restarted. */
if (window_protected(bat_priv, seq_diff, if (window_protected(bat_priv, seq_diff,
&orig_node->bcast_seqno_reset)) { &orig_node->bcast_seqno_reset))
spin_unlock_bh(&bat_priv->orig_hash_lock); goto spin_unlock;
return NET_RX_DROP;
}
/* mark broadcast in flood history, update window position /* mark broadcast in flood history, update window position
* if required. */ * if required. */
if (bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) if (bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1))
orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno);
spin_unlock_bh(&bat_priv->orig_hash_lock); spin_unlock_bh(&orig_node->bcast_seqno_lock);
/* rebroadcast packet */ /* rebroadcast packet */
add_bcast_packet_to_list(bat_priv, skb); add_bcast_packet_to_list(bat_priv, skb);
/* broadcast for me */ /* broadcast for me */
interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size);
ret = NET_RX_SUCCESS;
goto out;
return NET_RX_SUCCESS; rcu_unlock:
rcu_read_unlock();
goto out;
spin_unlock:
spin_unlock_bh(&orig_node->bcast_seqno_lock);
out:
if (orig_node)
orig_node_free_ref(orig_node);
return ret;
} }
int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if) int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if)
{ {
struct vis_packet *vis_packet; struct vis_packet *vis_packet;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
......
...@@ -22,24 +22,25 @@ ...@@ -22,24 +22,25 @@
#ifndef _NET_BATMAN_ADV_ROUTING_H_ #ifndef _NET_BATMAN_ADV_ROUTING_H_
#define _NET_BATMAN_ADV_ROUTING_H_ #define _NET_BATMAN_ADV_ROUTING_H_
void slide_own_bcast_window(struct batman_if *batman_if); void slide_own_bcast_window(struct hard_iface *hard_iface);
void receive_bat_packet(struct ethhdr *ethhdr, void receive_bat_packet(struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
unsigned char *hna_buff, int hna_buff_len, unsigned char *hna_buff, int hna_buff_len,
struct batman_if *if_incoming); struct hard_iface *if_incoming);
void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node,
struct neigh_node *neigh_node, unsigned char *hna_buff, struct neigh_node *neigh_node, unsigned char *hna_buff,
int hna_buff_len); int hna_buff_len);
int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int hdr_size); int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if); int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if);
int recv_bat_packet(struct sk_buff *skb, struct batman_if *recv_if);
struct neigh_node *find_router(struct bat_priv *bat_priv, struct neigh_node *find_router(struct bat_priv *bat_priv,
struct orig_node *orig_node, struct batman_if *recv_if); struct orig_node *orig_node,
void update_bonding_candidates(struct orig_node *orig_node); struct hard_iface *recv_if);
void bonding_candidate_del(struct orig_node *orig_node,
struct neigh_node *neigh_node);
#endif /* _NET_BATMAN_ADV_ROUTING_H_ */ #endif /* _NET_BATMAN_ADV_ROUTING_H_ */
...@@ -56,20 +56,20 @@ static unsigned long forward_send_time(void) ...@@ -56,20 +56,20 @@ static unsigned long forward_send_time(void)
/* send out an already prepared packet to the given address via the /* send out an already prepared packet to the given address via the
* specified batman interface */ * specified batman interface */
int send_skb_packet(struct sk_buff *skb, int send_skb_packet(struct sk_buff *skb,
struct batman_if *batman_if, struct hard_iface *hard_iface,
uint8_t *dst_addr) uint8_t *dst_addr)
{ {
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
if (batman_if->if_status != IF_ACTIVE) if (hard_iface->if_status != IF_ACTIVE)
goto send_skb_err; goto send_skb_err;
if (unlikely(!batman_if->net_dev)) if (unlikely(!hard_iface->net_dev))
goto send_skb_err; goto send_skb_err;
if (!(batman_if->net_dev->flags & IFF_UP)) { if (!(hard_iface->net_dev->flags & IFF_UP)) {
pr_warning("Interface %s is not up - can't send packet via " pr_warning("Interface %s is not up - can't send packet via "
"that interface!\n", batman_if->net_dev->name); "that interface!\n", hard_iface->net_dev->name);
goto send_skb_err; goto send_skb_err;
} }
...@@ -80,7 +80,7 @@ int send_skb_packet(struct sk_buff *skb, ...@@ -80,7 +80,7 @@ int send_skb_packet(struct sk_buff *skb,
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
ethhdr = (struct ethhdr *) skb_mac_header(skb); ethhdr = (struct ethhdr *) skb_mac_header(skb);
memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
...@@ -88,7 +88,7 @@ int send_skb_packet(struct sk_buff *skb, ...@@ -88,7 +88,7 @@ int send_skb_packet(struct sk_buff *skb,
skb->priority = TC_PRIO_CONTROL; skb->priority = TC_PRIO_CONTROL;
skb->protocol = __constant_htons(ETH_P_BATMAN); skb->protocol = __constant_htons(ETH_P_BATMAN);
skb->dev = batman_if->net_dev; skb->dev = hard_iface->net_dev;
/* dev_queue_xmit() returns a negative result on error. However on /* dev_queue_xmit() returns a negative result on error. However on
* congestion and traffic shaping, it drops and returns NET_XMIT_DROP * congestion and traffic shaping, it drops and returns NET_XMIT_DROP
...@@ -102,16 +102,16 @@ int send_skb_packet(struct sk_buff *skb, ...@@ -102,16 +102,16 @@ int send_skb_packet(struct sk_buff *skb,
/* Send a packet to a given interface */ /* Send a packet to a given interface */
static void send_packet_to_if(struct forw_packet *forw_packet, static void send_packet_to_if(struct forw_packet *forw_packet,
struct batman_if *batman_if) struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
char *fwd_str; char *fwd_str;
uint8_t packet_num; uint8_t packet_num;
int16_t buff_pos; int16_t buff_pos;
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
struct sk_buff *skb; struct sk_buff *skb;
if (batman_if->if_status != IF_ACTIVE) if (hard_iface->if_status != IF_ACTIVE)
return; return;
packet_num = 0; packet_num = 0;
...@@ -126,7 +126,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet, ...@@ -126,7 +126,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
/* we might have aggregated direct link packets with an /* we might have aggregated direct link packets with an
* ordinary base packet */ * ordinary base packet */
if ((forw_packet->direct_link_flags & (1 << packet_num)) && if ((forw_packet->direct_link_flags & (1 << packet_num)) &&
(forw_packet->if_incoming == batman_if)) (forw_packet->if_incoming == hard_iface))
batman_packet->flags |= DIRECTLINK; batman_packet->flags |= DIRECTLINK;
else else
batman_packet->flags &= ~DIRECTLINK; batman_packet->flags &= ~DIRECTLINK;
...@@ -142,7 +142,8 @@ static void send_packet_to_if(struct forw_packet *forw_packet, ...@@ -142,7 +142,8 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
batman_packet->tq, batman_packet->ttl, batman_packet->tq, batman_packet->ttl,
(batman_packet->flags & DIRECTLINK ? (batman_packet->flags & DIRECTLINK ?
"on" : "off"), "on" : "off"),
batman_if->net_dev->name, batman_if->net_dev->dev_addr); hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
buff_pos += sizeof(struct batman_packet) + buff_pos += sizeof(struct batman_packet) +
(batman_packet->num_hna * ETH_ALEN); (batman_packet->num_hna * ETH_ALEN);
...@@ -154,13 +155,13 @@ static void send_packet_to_if(struct forw_packet *forw_packet, ...@@ -154,13 +155,13 @@ static void send_packet_to_if(struct forw_packet *forw_packet,
/* create clone because function is called more than once */ /* create clone because function is called more than once */
skb = skb_clone(forw_packet->skb, GFP_ATOMIC); skb = skb_clone(forw_packet->skb, GFP_ATOMIC);
if (skb) if (skb)
send_skb_packet(skb, batman_if, broadcast_addr); send_skb_packet(skb, hard_iface, broadcast_addr);
} }
/* send a batman packet */ /* send a batman packet */
static void send_packet(struct forw_packet *forw_packet) static void send_packet(struct forw_packet *forw_packet)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
struct net_device *soft_iface; struct net_device *soft_iface;
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
struct batman_packet *batman_packet = struct batman_packet *batman_packet =
...@@ -204,17 +205,17 @@ static void send_packet(struct forw_packet *forw_packet) ...@@ -204,17 +205,17 @@ static void send_packet(struct forw_packet *forw_packet)
/* broadcast on every interface */ /* broadcast on every interface */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->soft_iface != soft_iface) if (hard_iface->soft_iface != soft_iface)
continue; continue;
send_packet_to_if(forw_packet, batman_if); send_packet_to_if(forw_packet, hard_iface);
} }
rcu_read_unlock(); rcu_read_unlock();
} }
static void rebuild_batman_packet(struct bat_priv *bat_priv, static void rebuild_batman_packet(struct bat_priv *bat_priv,
struct batman_if *batman_if) struct hard_iface *hard_iface)
{ {
int new_len; int new_len;
unsigned char *new_buff; unsigned char *new_buff;
...@@ -226,7 +227,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, ...@@ -226,7 +227,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv,
/* keep old buffer if kmalloc should fail */ /* keep old buffer if kmalloc should fail */
if (new_buff) { if (new_buff) {
memcpy(new_buff, batman_if->packet_buff, memcpy(new_buff, hard_iface->packet_buff,
sizeof(struct batman_packet)); sizeof(struct batman_packet));
batman_packet = (struct batman_packet *)new_buff; batman_packet = (struct batman_packet *)new_buff;
...@@ -234,21 +235,21 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, ...@@ -234,21 +235,21 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv,
new_buff + sizeof(struct batman_packet), new_buff + sizeof(struct batman_packet),
new_len - sizeof(struct batman_packet)); new_len - sizeof(struct batman_packet));
kfree(batman_if->packet_buff); kfree(hard_iface->packet_buff);
batman_if->packet_buff = new_buff; hard_iface->packet_buff = new_buff;
batman_if->packet_len = new_len; hard_iface->packet_len = new_len;
} }
} }
void schedule_own_packet(struct batman_if *batman_if) void schedule_own_packet(struct hard_iface *hard_iface)
{ {
struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
unsigned long send_time; unsigned long send_time;
struct batman_packet *batman_packet; struct batman_packet *batman_packet;
int vis_server; int vis_server;
if ((batman_if->if_status == IF_NOT_IN_USE) || if ((hard_iface->if_status == IF_NOT_IN_USE) ||
(batman_if->if_status == IF_TO_BE_REMOVED)) (hard_iface->if_status == IF_TO_BE_REMOVED))
return; return;
vis_server = atomic_read(&bat_priv->vis_mode); vis_server = atomic_read(&bat_priv->vis_mode);
...@@ -260,51 +261,51 @@ void schedule_own_packet(struct batman_if *batman_if) ...@@ -260,51 +261,51 @@ void schedule_own_packet(struct batman_if *batman_if)
* outdated packets (especially uninitialized mac addresses) in the * outdated packets (especially uninitialized mac addresses) in the
* packet queue * packet queue
*/ */
if (batman_if->if_status == IF_TO_BE_ACTIVATED) if (hard_iface->if_status == IF_TO_BE_ACTIVATED)
batman_if->if_status = IF_ACTIVE; hard_iface->if_status = IF_ACTIVE;
/* if local hna has changed and interface is a primary interface */ /* if local hna has changed and interface is a primary interface */
if ((atomic_read(&bat_priv->hna_local_changed)) && if ((atomic_read(&bat_priv->hna_local_changed)) &&
(batman_if == bat_priv->primary_if)) (hard_iface == bat_priv->primary_if))
rebuild_batman_packet(bat_priv, batman_if); rebuild_batman_packet(bat_priv, hard_iface);
/** /**
* NOTE: packet_buff might just have been re-allocated in * NOTE: packet_buff might just have been re-allocated in
* rebuild_batman_packet() * rebuild_batman_packet()
*/ */
batman_packet = (struct batman_packet *)batman_if->packet_buff; batman_packet = (struct batman_packet *)hard_iface->packet_buff;
/* change sequence number to network order */ /* change sequence number to network order */
batman_packet->seqno = batman_packet->seqno =
htonl((uint32_t)atomic_read(&batman_if->seqno)); htonl((uint32_t)atomic_read(&hard_iface->seqno));
if (vis_server == VIS_TYPE_SERVER_SYNC) if (vis_server == VIS_TYPE_SERVER_SYNC)
batman_packet->flags |= VIS_SERVER; batman_packet->flags |= VIS_SERVER;
else else
batman_packet->flags &= ~VIS_SERVER; batman_packet->flags &= ~VIS_SERVER;
if ((batman_if == bat_priv->primary_if) && if ((hard_iface == bat_priv->primary_if) &&
(atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)) (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER))
batman_packet->gw_flags = batman_packet->gw_flags =
(uint8_t)atomic_read(&bat_priv->gw_bandwidth); (uint8_t)atomic_read(&bat_priv->gw_bandwidth);
else else
batman_packet->gw_flags = 0; batman_packet->gw_flags = 0;
atomic_inc(&batman_if->seqno); atomic_inc(&hard_iface->seqno);
slide_own_bcast_window(batman_if); slide_own_bcast_window(hard_iface);
send_time = own_send_time(bat_priv); send_time = own_send_time(bat_priv);
add_bat_packet_to_list(bat_priv, add_bat_packet_to_list(bat_priv,
batman_if->packet_buff, hard_iface->packet_buff,
batman_if->packet_len, hard_iface->packet_len,
batman_if, 1, send_time); hard_iface, 1, send_time);
} }
void schedule_forward_packet(struct orig_node *orig_node, void schedule_forward_packet(struct orig_node *orig_node,
struct ethhdr *ethhdr, struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
uint8_t directlink, int hna_buff_len, uint8_t directlink, int hna_buff_len,
struct batman_if *if_incoming) struct hard_iface *if_incoming)
{ {
struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
unsigned char in_tq, in_ttl, tq_avg = 0; unsigned char in_tq, in_ttl, tq_avg = 0;
...@@ -326,7 +327,7 @@ void schedule_forward_packet(struct orig_node *orig_node, ...@@ -326,7 +327,7 @@ void schedule_forward_packet(struct orig_node *orig_node,
if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { if ((orig_node->router) && (orig_node->router->tq_avg != 0)) {
/* rebroadcast ogm of best ranking neighbor as is */ /* rebroadcast ogm of best ranking neighbor as is */
if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { if (!compare_eth(orig_node->router->addr, ethhdr->h_source)) {
batman_packet->tq = orig_node->router->tq_avg; batman_packet->tq = orig_node->router->tq_avg;
if (orig_node->router->last_ttl) if (orig_node->router->last_ttl)
...@@ -443,7 +444,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb) ...@@ -443,7 +444,7 @@ int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
static void send_outstanding_bcast_packet(struct work_struct *work) static void send_outstanding_bcast_packet(struct work_struct *work)
{ {
struct batman_if *batman_if; struct hard_iface *hard_iface;
struct delayed_work *delayed_work = struct delayed_work *delayed_work =
container_of(work, struct delayed_work, work); container_of(work, struct delayed_work, work);
struct forw_packet *forw_packet = struct forw_packet *forw_packet =
...@@ -461,14 +462,14 @@ static void send_outstanding_bcast_packet(struct work_struct *work) ...@@ -461,14 +462,14 @@ static void send_outstanding_bcast_packet(struct work_struct *work)
/* rebroadcast packet */ /* rebroadcast packet */
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(batman_if, &if_list, list) { list_for_each_entry_rcu(hard_iface, &hardif_list, list) {
if (batman_if->soft_iface != soft_iface) if (hard_iface->soft_iface != soft_iface)
continue; continue;
/* send a copy of the saved skb */ /* send a copy of the saved skb */
skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC); skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC);
if (skb1) if (skb1)
send_skb_packet(skb1, batman_if, broadcast_addr); send_skb_packet(skb1, hard_iface, broadcast_addr);
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -521,15 +522,15 @@ void send_outstanding_bat_packet(struct work_struct *work) ...@@ -521,15 +522,15 @@ void send_outstanding_bat_packet(struct work_struct *work)
} }
void purge_outstanding_packets(struct bat_priv *bat_priv, void purge_outstanding_packets(struct bat_priv *bat_priv,
struct batman_if *batman_if) struct hard_iface *hard_iface)
{ {
struct forw_packet *forw_packet; struct forw_packet *forw_packet;
struct hlist_node *tmp_node, *safe_tmp_node; struct hlist_node *tmp_node, *safe_tmp_node;
if (batman_if) if (hard_iface)
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"purge_outstanding_packets(): %s\n", "purge_outstanding_packets(): %s\n",
batman_if->net_dev->name); hard_iface->net_dev->name);
else else
bat_dbg(DBG_BATMAN, bat_priv, bat_dbg(DBG_BATMAN, bat_priv,
"purge_outstanding_packets()\n"); "purge_outstanding_packets()\n");
...@@ -543,8 +544,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, ...@@ -543,8 +544,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
* if purge_outstanding_packets() was called with an argmument * if purge_outstanding_packets() was called with an argmument
* we delete only packets belonging to the given interface * we delete only packets belonging to the given interface
*/ */
if ((batman_if) && if ((hard_iface) &&
(forw_packet->if_incoming != batman_if)) (forw_packet->if_incoming != hard_iface))
continue; continue;
spin_unlock_bh(&bat_priv->forw_bcast_list_lock); spin_unlock_bh(&bat_priv->forw_bcast_list_lock);
...@@ -567,8 +568,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, ...@@ -567,8 +568,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv,
* if purge_outstanding_packets() was called with an argmument * if purge_outstanding_packets() was called with an argmument
* we delete only packets belonging to the given interface * we delete only packets belonging to the given interface
*/ */
if ((batman_if) && if ((hard_iface) &&
(forw_packet->if_incoming != batman_if)) (forw_packet->if_incoming != hard_iface))
continue; continue;
spin_unlock_bh(&bat_priv->forw_bat_list_lock); spin_unlock_bh(&bat_priv->forw_bat_list_lock);
......
...@@ -23,17 +23,17 @@ ...@@ -23,17 +23,17 @@
#define _NET_BATMAN_ADV_SEND_H_ #define _NET_BATMAN_ADV_SEND_H_
int send_skb_packet(struct sk_buff *skb, int send_skb_packet(struct sk_buff *skb,
struct batman_if *batman_if, struct hard_iface *hard_iface,
uint8_t *dst_addr); uint8_t *dst_addr);
void schedule_own_packet(struct batman_if *batman_if); void schedule_own_packet(struct hard_iface *hard_iface);
void schedule_forward_packet(struct orig_node *orig_node, void schedule_forward_packet(struct orig_node *orig_node,
struct ethhdr *ethhdr, struct ethhdr *ethhdr,
struct batman_packet *batman_packet, struct batman_packet *batman_packet,
uint8_t directlink, int hna_buff_len, uint8_t directlink, int hna_buff_len,
struct batman_if *if_outgoing); struct hard_iface *if_outgoing);
int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb); int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb);
void send_outstanding_bat_packet(struct work_struct *work); void send_outstanding_bat_packet(struct work_struct *work);
void purge_outstanding_packets(struct bat_priv *bat_priv, void purge_outstanding_packets(struct bat_priv *bat_priv,
struct batman_if *batman_if); struct hard_iface *hard_iface);
#endif /* _NET_BATMAN_ADV_SEND_H_ */ #endif /* _NET_BATMAN_ADV_SEND_H_ */
...@@ -29,14 +29,12 @@ ...@@ -29,14 +29,12 @@
#include "hash.h" #include "hash.h"
#include "gateway_common.h" #include "gateway_common.h"
#include "gateway_client.h" #include "gateway_client.h"
#include "send.h"
#include "bat_sysfs.h" #include "bat_sysfs.h"
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include "unicast.h" #include "unicast.h"
#include "routing.h"
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
...@@ -78,20 +76,18 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len) ...@@ -78,20 +76,18 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len)
return 0; return 0;
} }
static void softif_neigh_free_ref(struct kref *refcount) static void softif_neigh_free_rcu(struct rcu_head *rcu)
{ {
struct softif_neigh *softif_neigh; struct softif_neigh *softif_neigh;
softif_neigh = container_of(refcount, struct softif_neigh, refcount); softif_neigh = container_of(rcu, struct softif_neigh, rcu);
kfree(softif_neigh); kfree(softif_neigh);
} }
static void softif_neigh_free_rcu(struct rcu_head *rcu) static void softif_neigh_free_ref(struct softif_neigh *softif_neigh)
{ {
struct softif_neigh *softif_neigh; if (atomic_dec_and_test(&softif_neigh->refcount))
call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu);
softif_neigh = container_of(rcu, struct softif_neigh, rcu);
kref_put(&softif_neigh->refcount, softif_neigh_free_ref);
} }
void softif_neigh_purge(struct bat_priv *bat_priv) void softif_neigh_purge(struct bat_priv *bat_priv)
...@@ -118,11 +114,10 @@ void softif_neigh_purge(struct bat_priv *bat_priv) ...@@ -118,11 +114,10 @@ void softif_neigh_purge(struct bat_priv *bat_priv)
softif_neigh->addr, softif_neigh->vid); softif_neigh->addr, softif_neigh->vid);
softif_neigh_tmp = bat_priv->softif_neigh; softif_neigh_tmp = bat_priv->softif_neigh;
bat_priv->softif_neigh = NULL; bat_priv->softif_neigh = NULL;
kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref(softif_neigh_tmp);
softif_neigh_free_ref);
} }
call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); softif_neigh_free_ref(softif_neigh);
} }
spin_unlock_bh(&bat_priv->softif_neigh_lock); spin_unlock_bh(&bat_priv->softif_neigh_lock);
...@@ -137,14 +132,17 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, ...@@ -137,14 +132,17 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(softif_neigh, node, hlist_for_each_entry_rcu(softif_neigh, node,
&bat_priv->softif_neigh_list, list) { &bat_priv->softif_neigh_list, list) {
if (memcmp(softif_neigh->addr, addr, ETH_ALEN) != 0) if (!compare_eth(softif_neigh->addr, addr))
continue; continue;
if (softif_neigh->vid != vid) if (softif_neigh->vid != vid)
continue; continue;
if (!atomic_inc_not_zero(&softif_neigh->refcount))
continue;
softif_neigh->last_seen = jiffies; softif_neigh->last_seen = jiffies;
goto found; goto out;
} }
softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC);
...@@ -154,15 +152,14 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, ...@@ -154,15 +152,14 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
memcpy(softif_neigh->addr, addr, ETH_ALEN); memcpy(softif_neigh->addr, addr, ETH_ALEN);
softif_neigh->vid = vid; softif_neigh->vid = vid;
softif_neigh->last_seen = jiffies; softif_neigh->last_seen = jiffies;
kref_init(&softif_neigh->refcount); /* initialize with 2 - caller decrements counter by one */
atomic_set(&softif_neigh->refcount, 2);
INIT_HLIST_NODE(&softif_neigh->list); INIT_HLIST_NODE(&softif_neigh->list);
spin_lock_bh(&bat_priv->softif_neigh_lock); spin_lock_bh(&bat_priv->softif_neigh_lock);
hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list);
spin_unlock_bh(&bat_priv->softif_neigh_lock); spin_unlock_bh(&bat_priv->softif_neigh_lock);
found:
kref_get(&softif_neigh->refcount);
out: out:
rcu_read_unlock(); rcu_read_unlock();
return softif_neigh; return softif_neigh;
...@@ -174,8 +171,6 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) ...@@ -174,8 +171,6 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
struct bat_priv *bat_priv = netdev_priv(net_dev); struct bat_priv *bat_priv = netdev_priv(net_dev);
struct softif_neigh *softif_neigh; struct softif_neigh *softif_neigh;
struct hlist_node *node; struct hlist_node *node;
size_t buf_size, pos;
char *buff;
if (!bat_priv->primary_if) { if (!bat_priv->primary_if) {
return seq_printf(seq, "BATMAN mesh %s disabled - " return seq_printf(seq, "BATMAN mesh %s disabled - "
...@@ -185,33 +180,15 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) ...@@ -185,33 +180,15 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name);
buf_size = 1;
/* Estimate length for: " xx:xx:xx:xx:xx:xx\n" */
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(softif_neigh, node, hlist_for_each_entry_rcu(softif_neigh, node,
&bat_priv->softif_neigh_list, list) &bat_priv->softif_neigh_list, list)
buf_size += 30; seq_printf(seq, "%s %pM (vid: %d)\n",
rcu_read_unlock();
buff = kmalloc(buf_size, GFP_ATOMIC);
if (!buff)
return -ENOMEM;
buff[0] = '\0';
pos = 0;
rcu_read_lock();
hlist_for_each_entry_rcu(softif_neigh, node,
&bat_priv->softif_neigh_list, list) {
pos += snprintf(buff + pos, 31, "%s %pM (vid: %d)\n",
bat_priv->softif_neigh == softif_neigh bat_priv->softif_neigh == softif_neigh
? "=>" : " ", softif_neigh->addr, ? "=>" : " ", softif_neigh->addr,
softif_neigh->vid); softif_neigh->vid);
}
rcu_read_unlock(); rcu_read_unlock();
seq_printf(seq, "%s", buff);
kfree(buff);
return 0; return 0;
} }
...@@ -266,7 +243,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, ...@@ -266,7 +243,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
softif_neigh->addr, softif_neigh->vid); softif_neigh->addr, softif_neigh->vid);
softif_neigh_tmp = bat_priv->softif_neigh; softif_neigh_tmp = bat_priv->softif_neigh;
bat_priv->softif_neigh = softif_neigh; bat_priv->softif_neigh = softif_neigh;
kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref); softif_neigh_free_ref(softif_neigh_tmp);
/* we need to hold the additional reference */ /* we need to hold the additional reference */
goto err; goto err;
} }
...@@ -284,7 +261,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, ...@@ -284,7 +261,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
} }
out: out:
kref_put(&softif_neigh->refcount, softif_neigh_free_ref); softif_neigh_free_ref(softif_neigh);
err: err:
kfree_skb(skb); kfree_skb(skb);
return; return;
...@@ -437,7 +414,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) ...@@ -437,7 +414,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
} }
void interface_rx(struct net_device *soft_iface, void interface_rx(struct net_device *soft_iface,
struct sk_buff *skb, struct batman_if *recv_if, struct sk_buff *skb, struct hard_iface *recv_if,
int hdr_size) int hdr_size)
{ {
struct bat_priv *bat_priv = netdev_priv(soft_iface); struct bat_priv *bat_priv = netdev_priv(soft_iface);
...@@ -485,7 +462,7 @@ void interface_rx(struct net_device *soft_iface, ...@@ -485,7 +462,7 @@ void interface_rx(struct net_device *soft_iface,
memcpy(unicast_packet->dest, memcpy(unicast_packet->dest,
bat_priv->softif_neigh->addr, ETH_ALEN); bat_priv->softif_neigh->addr, ETH_ALEN);
ret = route_unicast_packet(skb, recv_if, hdr_size); ret = route_unicast_packet(skb, recv_if);
if (ret == NET_RX_DROP) if (ret == NET_RX_DROP)
goto dropped; goto dropped;
...@@ -645,6 +622,19 @@ void softif_destroy(struct net_device *soft_iface) ...@@ -645,6 +622,19 @@ void softif_destroy(struct net_device *soft_iface)
unregister_netdevice(soft_iface); unregister_netdevice(soft_iface);
} }
int softif_is_valid(struct net_device *net_dev)
{
#ifdef HAVE_NET_DEVICE_OPS
if (net_dev->netdev_ops->ndo_start_xmit == interface_tx)
return 1;
#else
if (net_dev->hard_start_xmit == interface_tx)
return 1;
#endif
return 0;
}
/* ethtool */ /* ethtool */
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
......
...@@ -27,9 +27,10 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset); ...@@ -27,9 +27,10 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset);
void softif_neigh_purge(struct bat_priv *bat_priv); void softif_neigh_purge(struct bat_priv *bat_priv);
int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); int interface_tx(struct sk_buff *skb, struct net_device *soft_iface);
void interface_rx(struct net_device *soft_iface, void interface_rx(struct net_device *soft_iface,
struct sk_buff *skb, struct batman_if *recv_if, struct sk_buff *skb, struct hard_iface *recv_if,
int hdr_size); int hdr_size);
struct net_device *softif_create(char *name); struct net_device *softif_create(char *name);
void softif_destroy(struct net_device *soft_iface); void softif_destroy(struct net_device *soft_iface);
int softif_is_valid(struct net_device *net_dev);
#endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */
...@@ -30,12 +30,85 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv, ...@@ -30,12 +30,85 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv,
struct hna_global_entry *hna_global_entry, struct hna_global_entry *hna_global_entry,
char *message); char *message);
/* returns 1 if they are the same mac addr */
static int compare_lhna(struct hlist_node *node, void *data2)
{
void *data1 = container_of(node, struct hna_local_entry, hash_entry);
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
/* returns 1 if they are the same mac addr */
static int compare_ghna(struct hlist_node *node, void *data2)
{
void *data1 = container_of(node, struct hna_global_entry, hash_entry);
return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
}
static void hna_local_start_timer(struct bat_priv *bat_priv) static void hna_local_start_timer(struct bat_priv *bat_priv)
{ {
INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge); INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge);
queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ); queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ);
} }
static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv,
void *data)
{
struct hashtable_t *hash = bat_priv->hna_local_hash;
struct hlist_head *head;
struct hlist_node *node;
struct hna_local_entry *hna_local_entry, *hna_local_entry_tmp = NULL;
int index;
if (!hash)
return NULL;
index = choose_orig(data, hash->size);
head = &hash->table[index];
rcu_read_lock();
hlist_for_each_entry_rcu(hna_local_entry, node, head, hash_entry) {
if (!compare_eth(hna_local_entry, data))
continue;
hna_local_entry_tmp = hna_local_entry;
break;
}
rcu_read_unlock();
return hna_local_entry_tmp;
}
static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv,
void *data)
{
struct hashtable_t *hash = bat_priv->hna_global_hash;
struct hlist_head *head;
struct hlist_node *node;
struct hna_global_entry *hna_global_entry;
struct hna_global_entry *hna_global_entry_tmp = NULL;
int index;
if (!hash)
return NULL;
index = choose_orig(data, hash->size);
head = &hash->table[index];
rcu_read_lock();
hlist_for_each_entry_rcu(hna_global_entry, node, head, hash_entry) {
if (!compare_eth(hna_global_entry, data))
continue;
hna_global_entry_tmp = hna_global_entry;
break;
}
rcu_read_unlock();
return hna_global_entry_tmp;
}
int hna_local_init(struct bat_priv *bat_priv) int hna_local_init(struct bat_priv *bat_priv)
{ {
if (bat_priv->hna_local_hash) if (bat_priv->hna_local_hash)
...@@ -60,10 +133,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) ...@@ -60,10 +133,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr)
int required_bytes; int required_bytes;
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
hna_local_entry = hna_local_entry = hna_local_hash_find(bat_priv, addr);
((struct hna_local_entry *)hash_find(bat_priv->hna_local_hash,
compare_orig, choose_orig,
addr));
spin_unlock_bh(&bat_priv->hna_lhash_lock); spin_unlock_bh(&bat_priv->hna_lhash_lock);
if (hna_local_entry) { if (hna_local_entry) {
...@@ -99,15 +169,15 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) ...@@ -99,15 +169,15 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr)
hna_local_entry->last_seen = jiffies; hna_local_entry->last_seen = jiffies;
/* the batman interface mac address should never be purged */ /* the batman interface mac address should never be purged */
if (compare_orig(addr, soft_iface->dev_addr)) if (compare_eth(addr, soft_iface->dev_addr))
hna_local_entry->never_purge = 1; hna_local_entry->never_purge = 1;
else else
hna_local_entry->never_purge = 0; hna_local_entry->never_purge = 0;
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
hash_add(bat_priv->hna_local_hash, compare_orig, choose_orig, hash_add(bat_priv->hna_local_hash, compare_lhna, choose_orig,
hna_local_entry); hna_local_entry, &hna_local_entry->hash_entry);
bat_priv->num_local_hna++; bat_priv->num_local_hna++;
atomic_set(&bat_priv->hna_local_changed, 1); atomic_set(&bat_priv->hna_local_changed, 1);
...@@ -116,9 +186,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) ...@@ -116,9 +186,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr)
/* remove address from global hash if present */ /* remove address from global hash if present */
spin_lock_bh(&bat_priv->hna_ghash_lock); spin_lock_bh(&bat_priv->hna_ghash_lock);
hna_global_entry = ((struct hna_global_entry *) hna_global_entry = hna_global_hash_find(bat_priv, addr);
hash_find(bat_priv->hna_global_hash,
compare_orig, choose_orig, addr));
if (hna_global_entry) if (hna_global_entry)
_hna_global_del_orig(bat_priv, hna_global_entry, _hna_global_del_orig(bat_priv, hna_global_entry,
...@@ -132,28 +200,27 @@ int hna_local_fill_buffer(struct bat_priv *bat_priv, ...@@ -132,28 +200,27 @@ int hna_local_fill_buffer(struct bat_priv *bat_priv,
{ {
struct hashtable_t *hash = bat_priv->hna_local_hash; struct hashtable_t *hash = bat_priv->hna_local_hash;
struct hna_local_entry *hna_local_entry; struct hna_local_entry *hna_local_entry;
struct element_t *bucket; struct hlist_node *node;
int i;
struct hlist_node *walk;
struct hlist_head *head; struct hlist_head *head;
int count = 0; int i, count = 0;
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
hlist_for_each_entry_rcu(hna_local_entry, node,
head, hash_entry) {
if (buff_len < (count + 1) * ETH_ALEN) if (buff_len < (count + 1) * ETH_ALEN)
break; break;
hna_local_entry = bucket->data;
memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr, memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr,
ETH_ALEN); ETH_ALEN);
count++; count++;
} }
rcu_read_unlock();
} }
/* if we did not get all new local hnas see you next time ;-) */ /* if we did not get all new local hnas see you next time ;-) */
...@@ -170,12 +237,11 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -170,12 +237,11 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
struct bat_priv *bat_priv = netdev_priv(net_dev); struct bat_priv *bat_priv = netdev_priv(net_dev);
struct hashtable_t *hash = bat_priv->hna_local_hash; struct hashtable_t *hash = bat_priv->hna_local_hash;
struct hna_local_entry *hna_local_entry; struct hna_local_entry *hna_local_entry;
int i; struct hlist_node *node;
struct hlist_node *walk;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
size_t buf_size, pos; size_t buf_size, pos;
char *buff; char *buff;
int i;
if (!bat_priv->primary_if) { if (!bat_priv->primary_if) {
return seq_printf(seq, "BATMAN mesh %s disabled - " return seq_printf(seq, "BATMAN mesh %s disabled - "
...@@ -194,8 +260,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -194,8 +260,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each(walk, head) rcu_read_lock();
__hlist_for_each_rcu(node, head)
buf_size += 21; buf_size += 21;
rcu_read_unlock();
} }
buff = kmalloc(buf_size, GFP_ATOMIC); buff = kmalloc(buf_size, GFP_ATOMIC);
...@@ -203,18 +271,20 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -203,18 +271,20 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
spin_unlock_bh(&bat_priv->hna_lhash_lock); spin_unlock_bh(&bat_priv->hna_lhash_lock);
return -ENOMEM; return -ENOMEM;
} }
buff[0] = '\0'; buff[0] = '\0';
pos = 0; pos = 0;
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
hna_local_entry = bucket->data; hlist_for_each_entry_rcu(hna_local_entry, node,
head, hash_entry) {
pos += snprintf(buff + pos, 22, " * %pM\n", pos += snprintf(buff + pos, 22, " * %pM\n",
hna_local_entry->addr); hna_local_entry->addr);
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->hna_lhash_lock); spin_unlock_bh(&bat_priv->hna_lhash_lock);
...@@ -224,9 +294,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) ...@@ -224,9 +294,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset)
return 0; return 0;
} }
static void _hna_local_del(void *data, void *arg) static void _hna_local_del(struct hlist_node *node, void *arg)
{ {
struct bat_priv *bat_priv = (struct bat_priv *)arg; struct bat_priv *bat_priv = (struct bat_priv *)arg;
void *data = container_of(node, struct hna_local_entry, hash_entry);
kfree(data); kfree(data);
bat_priv->num_local_hna--; bat_priv->num_local_hna--;
...@@ -240,9 +311,9 @@ static void hna_local_del(struct bat_priv *bat_priv, ...@@ -240,9 +311,9 @@ static void hna_local_del(struct bat_priv *bat_priv,
bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n", bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n",
hna_local_entry->addr, message); hna_local_entry->addr, message);
hash_remove(bat_priv->hna_local_hash, compare_orig, choose_orig, hash_remove(bat_priv->hna_local_hash, compare_lhna, choose_orig,
hna_local_entry->addr); hna_local_entry->addr);
_hna_local_del(hna_local_entry, bat_priv); _hna_local_del(&hna_local_entry->hash_entry, bat_priv);
} }
void hna_local_remove(struct bat_priv *bat_priv, void hna_local_remove(struct bat_priv *bat_priv,
...@@ -252,9 +323,7 @@ void hna_local_remove(struct bat_priv *bat_priv, ...@@ -252,9 +323,7 @@ void hna_local_remove(struct bat_priv *bat_priv,
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
hna_local_entry = (struct hna_local_entry *) hna_local_entry = hna_local_hash_find(bat_priv, addr);
hash_find(bat_priv->hna_local_hash, compare_orig, choose_orig,
addr);
if (hna_local_entry) if (hna_local_entry)
hna_local_del(bat_priv, hna_local_entry, message); hna_local_del(bat_priv, hna_local_entry, message);
...@@ -270,27 +339,29 @@ static void hna_local_purge(struct work_struct *work) ...@@ -270,27 +339,29 @@ static void hna_local_purge(struct work_struct *work)
container_of(delayed_work, struct bat_priv, hna_work); container_of(delayed_work, struct bat_priv, hna_work);
struct hashtable_t *hash = bat_priv->hna_local_hash; struct hashtable_t *hash = bat_priv->hna_local_hash;
struct hna_local_entry *hna_local_entry; struct hna_local_entry *hna_local_entry;
int i; struct hlist_node *node, *node_tmp;
struct hlist_node *walk, *safe;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
unsigned long timeout; unsigned long timeout;
int i;
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { hlist_for_each_entry_safe(hna_local_entry, node, node_tmp,
hna_local_entry = bucket->data; head, hash_entry) {
if (hna_local_entry->never_purge)
continue;
timeout = hna_local_entry->last_seen; timeout = hna_local_entry->last_seen;
timeout += LOCAL_HNA_TIMEOUT * HZ; timeout += LOCAL_HNA_TIMEOUT * HZ;
if ((!hna_local_entry->never_purge) && if (time_before(jiffies, timeout))
time_after(jiffies, timeout)) continue;
hna_local_del(bat_priv, hna_local_entry,
"address timed out"); hna_local_del(bat_priv, hna_local_entry,
"address timed out");
} }
} }
...@@ -334,9 +405,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv, ...@@ -334,9 +405,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv,
spin_lock_bh(&bat_priv->hna_ghash_lock); spin_lock_bh(&bat_priv->hna_ghash_lock);
hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
hna_global_entry = (struct hna_global_entry *) hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr);
hash_find(bat_priv->hna_global_hash, compare_orig,
choose_orig, hna_ptr);
if (!hna_global_entry) { if (!hna_global_entry) {
spin_unlock_bh(&bat_priv->hna_ghash_lock); spin_unlock_bh(&bat_priv->hna_ghash_lock);
...@@ -356,8 +425,9 @@ void hna_global_add_orig(struct bat_priv *bat_priv, ...@@ -356,8 +425,9 @@ void hna_global_add_orig(struct bat_priv *bat_priv,
hna_global_entry->addr, orig_node->orig); hna_global_entry->addr, orig_node->orig);
spin_lock_bh(&bat_priv->hna_ghash_lock); spin_lock_bh(&bat_priv->hna_ghash_lock);
hash_add(bat_priv->hna_global_hash, compare_orig, hash_add(bat_priv->hna_global_hash, compare_ghna,
choose_orig, hna_global_entry); choose_orig, hna_global_entry,
&hna_global_entry->hash_entry);
} }
...@@ -368,9 +438,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv, ...@@ -368,9 +438,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv,
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
hna_local_entry = (struct hna_local_entry *) hna_local_entry = hna_local_hash_find(bat_priv, hna_ptr);
hash_find(bat_priv->hna_local_hash, compare_orig,
choose_orig, hna_ptr);
if (hna_local_entry) if (hna_local_entry)
hna_local_del(bat_priv, hna_local_entry, hna_local_del(bat_priv, hna_local_entry,
...@@ -400,12 +468,11 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) ...@@ -400,12 +468,11 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset)
struct bat_priv *bat_priv = netdev_priv(net_dev); struct bat_priv *bat_priv = netdev_priv(net_dev);
struct hashtable_t *hash = bat_priv->hna_global_hash; struct hashtable_t *hash = bat_priv->hna_global_hash;
struct hna_global_entry *hna_global_entry; struct hna_global_entry *hna_global_entry;
int i; struct hlist_node *node;
struct hlist_node *walk;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
size_t buf_size, pos; size_t buf_size, pos;
char *buff; char *buff;
int i;
if (!bat_priv->primary_if) { if (!bat_priv->primary_if) {
return seq_printf(seq, "BATMAN mesh %s disabled - " return seq_printf(seq, "BATMAN mesh %s disabled - "
...@@ -423,8 +490,10 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) ...@@ -423,8 +490,10 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each(walk, head) rcu_read_lock();
__hlist_for_each_rcu(node, head)
buf_size += 43; buf_size += 43;
rcu_read_unlock();
} }
buff = kmalloc(buf_size, GFP_ATOMIC); buff = kmalloc(buf_size, GFP_ATOMIC);
...@@ -438,14 +507,15 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) ...@@ -438,14 +507,15 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
hna_global_entry = bucket->data; hlist_for_each_entry_rcu(hna_global_entry, node,
head, hash_entry) {
pos += snprintf(buff + pos, 44, pos += snprintf(buff + pos, 44,
" * %pM via %pM\n", " * %pM via %pM\n",
hna_global_entry->addr, hna_global_entry->addr,
hna_global_entry->orig_node->orig); hna_global_entry->orig_node->orig);
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->hna_ghash_lock); spin_unlock_bh(&bat_priv->hna_ghash_lock);
...@@ -464,7 +534,7 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv, ...@@ -464,7 +534,7 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv,
hna_global_entry->addr, hna_global_entry->orig_node->orig, hna_global_entry->addr, hna_global_entry->orig_node->orig,
message); message);
hash_remove(bat_priv->hna_global_hash, compare_orig, choose_orig, hash_remove(bat_priv->hna_global_hash, compare_ghna, choose_orig,
hna_global_entry->addr); hna_global_entry->addr);
kfree(hna_global_entry); kfree(hna_global_entry);
} }
...@@ -483,9 +553,7 @@ void hna_global_del_orig(struct bat_priv *bat_priv, ...@@ -483,9 +553,7 @@ void hna_global_del_orig(struct bat_priv *bat_priv,
while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) {
hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN);
hna_global_entry = (struct hna_global_entry *) hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr);
hash_find(bat_priv->hna_global_hash, compare_orig,
choose_orig, hna_ptr);
if ((hna_global_entry) && if ((hna_global_entry) &&
(hna_global_entry->orig_node == orig_node)) (hna_global_entry->orig_node == orig_node))
...@@ -502,8 +570,10 @@ void hna_global_del_orig(struct bat_priv *bat_priv, ...@@ -502,8 +570,10 @@ void hna_global_del_orig(struct bat_priv *bat_priv,
orig_node->hna_buff = NULL; orig_node->hna_buff = NULL;
} }
static void hna_global_del(void *data, void *arg) static void hna_global_del(struct hlist_node *node, void *arg)
{ {
void *data = container_of(node, struct hna_global_entry, hash_entry);
kfree(data); kfree(data);
} }
...@@ -519,15 +589,20 @@ void hna_global_free(struct bat_priv *bat_priv) ...@@ -519,15 +589,20 @@ void hna_global_free(struct bat_priv *bat_priv)
struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)
{ {
struct hna_global_entry *hna_global_entry; struct hna_global_entry *hna_global_entry;
struct orig_node *orig_node = NULL;
spin_lock_bh(&bat_priv->hna_ghash_lock); spin_lock_bh(&bat_priv->hna_ghash_lock);
hna_global_entry = (struct hna_global_entry *) hna_global_entry = hna_global_hash_find(bat_priv, addr);
hash_find(bat_priv->hna_global_hash,
compare_orig, choose_orig, addr);
spin_unlock_bh(&bat_priv->hna_ghash_lock);
if (!hna_global_entry) if (!hna_global_entry)
return NULL; goto out;
return hna_global_entry->orig_node; if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount))
goto out;
orig_node = hna_global_entry->orig_node;
out:
spin_unlock_bh(&bat_priv->hna_ghash_lock);
return orig_node;
} }
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
sizeof(struct bcast_packet)))) sizeof(struct bcast_packet))))
struct batman_if { struct hard_iface {
struct list_head list; struct list_head list;
int16_t if_num; int16_t if_num;
char if_status; char if_status;
...@@ -43,7 +43,7 @@ struct batman_if { ...@@ -43,7 +43,7 @@ struct batman_if {
unsigned char *packet_buff; unsigned char *packet_buff;
int packet_len; int packet_len;
struct kobject *hardif_obj; struct kobject *hardif_obj;
struct kref refcount; atomic_t refcount;
struct packet_type batman_adv_ptype; struct packet_type batman_adv_ptype;
struct net_device *soft_iface; struct net_device *soft_iface;
struct rcu_head rcu; struct rcu_head rcu;
...@@ -70,8 +70,6 @@ struct orig_node { ...@@ -70,8 +70,6 @@ struct orig_node {
struct neigh_node *router; struct neigh_node *router;
unsigned long *bcast_own; unsigned long *bcast_own;
uint8_t *bcast_own_sum; uint8_t *bcast_own_sum;
uint8_t tq_own;
int tq_asym_penalty;
unsigned long last_valid; unsigned long last_valid;
unsigned long bcast_seqno_reset; unsigned long bcast_seqno_reset;
unsigned long batman_seqno_reset; unsigned long batman_seqno_reset;
...@@ -83,20 +81,28 @@ struct orig_node { ...@@ -83,20 +81,28 @@ struct orig_node {
uint8_t last_ttl; uint8_t last_ttl;
unsigned long bcast_bits[NUM_WORDS]; unsigned long bcast_bits[NUM_WORDS];
uint32_t last_bcast_seqno; uint32_t last_bcast_seqno;
struct list_head neigh_list; struct hlist_head neigh_list;
struct list_head frag_list; struct list_head frag_list;
spinlock_t neigh_list_lock; /* protects neighbor list */
atomic_t refcount;
struct rcu_head rcu;
struct hlist_node hash_entry;
struct bat_priv *bat_priv;
unsigned long last_frag_packet; unsigned long last_frag_packet;
struct { spinlock_t ogm_cnt_lock; /* protects: bcast_own, bcast_own_sum,
uint8_t candidates; * neigh_node->real_bits,
struct neigh_node *selected; * neigh_node->real_packet_count */
} bond; spinlock_t bcast_seqno_lock; /* protects bcast_bits,
* last_bcast_seqno */
atomic_t bond_candidates;
struct list_head bond_list;
}; };
struct gw_node { struct gw_node {
struct hlist_node list; struct hlist_node list;
struct orig_node *orig_node; struct orig_node *orig_node;
unsigned long deleted; unsigned long deleted;
struct kref refcount; atomic_t refcount;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -105,18 +111,20 @@ struct gw_node { ...@@ -105,18 +111,20 @@ struct gw_node {
* @last_valid: when last packet via this neighbor was received * @last_valid: when last packet via this neighbor was received
*/ */
struct neigh_node { struct neigh_node {
struct list_head list; struct hlist_node list;
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
uint8_t real_packet_count; uint8_t real_packet_count;
uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE]; uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE];
uint8_t tq_index; uint8_t tq_index;
uint8_t tq_avg; uint8_t tq_avg;
uint8_t last_ttl; uint8_t last_ttl;
struct neigh_node *next_bond_candidate; struct list_head bonding_list;
unsigned long last_valid; unsigned long last_valid;
unsigned long real_bits[NUM_WORDS]; unsigned long real_bits[NUM_WORDS];
atomic_t refcount;
struct rcu_head rcu;
struct orig_node *orig_node; struct orig_node *orig_node;
struct batman_if *if_incoming; struct hard_iface *if_incoming;
}; };
...@@ -140,7 +148,7 @@ struct bat_priv { ...@@ -140,7 +148,7 @@ struct bat_priv {
struct hlist_head softif_neigh_list; struct hlist_head softif_neigh_list;
struct softif_neigh *softif_neigh; struct softif_neigh *softif_neigh;
struct debug_log *debug_log; struct debug_log *debug_log;
struct batman_if *primary_if; struct hard_iface *primary_if;
struct kobject *mesh_obj; struct kobject *mesh_obj;
struct dentry *debug_dir; struct dentry *debug_dir;
struct hlist_head forw_bat_list; struct hlist_head forw_bat_list;
...@@ -151,12 +159,11 @@ struct bat_priv { ...@@ -151,12 +159,11 @@ struct bat_priv {
struct hashtable_t *hna_local_hash; struct hashtable_t *hna_local_hash;
struct hashtable_t *hna_global_hash; struct hashtable_t *hna_global_hash;
struct hashtable_t *vis_hash; struct hashtable_t *vis_hash;
spinlock_t orig_hash_lock; /* protects orig_hash */
spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
spinlock_t forw_bcast_list_lock; /* protects */ spinlock_t forw_bcast_list_lock; /* protects */
spinlock_t hna_lhash_lock; /* protects hna_local_hash */ spinlock_t hna_lhash_lock; /* protects hna_local_hash */
spinlock_t hna_ghash_lock; /* protects hna_global_hash */ spinlock_t hna_ghash_lock; /* protects hna_global_hash */
spinlock_t gw_list_lock; /* protects gw_list */ spinlock_t gw_list_lock; /* protects gw_list and curr_gw */
spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_hash_lock; /* protects vis_hash */
spinlock_t vis_list_lock; /* protects vis_info::recv_list */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */
spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */
...@@ -165,7 +172,7 @@ struct bat_priv { ...@@ -165,7 +172,7 @@ struct bat_priv {
struct delayed_work hna_work; struct delayed_work hna_work;
struct delayed_work orig_work; struct delayed_work orig_work;
struct delayed_work vis_work; struct delayed_work vis_work;
struct gw_node *curr_gw; struct gw_node __rcu *curr_gw; /* rcu protected pointer */
struct vis_info *my_vis_info; struct vis_info *my_vis_info;
}; };
...@@ -188,11 +195,13 @@ struct hna_local_entry { ...@@ -188,11 +195,13 @@ struct hna_local_entry {
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
unsigned long last_seen; unsigned long last_seen;
char never_purge; char never_purge;
struct hlist_node hash_entry;
}; };
struct hna_global_entry { struct hna_global_entry {
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
struct orig_node *orig_node; struct orig_node *orig_node;
struct hlist_node hash_entry;
}; };
/** /**
...@@ -208,7 +217,7 @@ struct forw_packet { ...@@ -208,7 +217,7 @@ struct forw_packet {
uint32_t direct_link_flags; uint32_t direct_link_flags;
uint8_t num_packets; uint8_t num_packets;
struct delayed_work delayed_work; struct delayed_work delayed_work;
struct batman_if *if_incoming; struct hard_iface *if_incoming;
}; };
/* While scanning for vis-entries of a particular vis-originator /* While scanning for vis-entries of a particular vis-originator
...@@ -242,6 +251,7 @@ struct vis_info { ...@@ -242,6 +251,7 @@ struct vis_info {
* from. we should not reply to them. */ * from. we should not reply to them. */
struct list_head send_list; struct list_head send_list;
struct kref refcount; struct kref refcount;
struct hlist_node hash_entry;
struct bat_priv *bat_priv; struct bat_priv *bat_priv;
/* this packet might be part of the vis send queue. */ /* this packet might be part of the vis send queue. */
struct sk_buff *skb_packet; struct sk_buff *skb_packet;
...@@ -264,7 +274,7 @@ struct softif_neigh { ...@@ -264,7 +274,7 @@ struct softif_neigh {
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
unsigned long last_seen; unsigned long last_seen;
short vid; short vid;
struct kref refcount; atomic_t refcount;
struct rcu_head rcu; struct rcu_head rcu;
}; };
......
...@@ -183,15 +183,10 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, ...@@ -183,15 +183,10 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
(struct unicast_frag_packet *)skb->data; (struct unicast_frag_packet *)skb->data;
*new_skb = NULL; *new_skb = NULL;
spin_lock_bh(&bat_priv->orig_hash_lock);
orig_node = ((struct orig_node *)
hash_find(bat_priv->orig_hash, compare_orig, choose_orig,
unicast_packet->orig));
if (!orig_node) { orig_node = orig_hash_find(bat_priv, unicast_packet->orig);
pr_debug("couldn't find originator in orig_hash\n"); if (!orig_node)
goto out; goto out;
}
orig_node->last_frag_packet = jiffies; orig_node->last_frag_packet = jiffies;
...@@ -215,14 +210,15 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, ...@@ -215,14 +210,15 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
/* if not, merge failed */ /* if not, merge failed */
if (*new_skb) if (*new_skb)
ret = NET_RX_SUCCESS; ret = NET_RX_SUCCESS;
out:
spin_unlock_bh(&bat_priv->orig_hash_lock);
out:
if (orig_node)
orig_node_free_ref(orig_node);
return ret; return ret;
} }
int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
struct batman_if *batman_if, uint8_t dstaddr[]) struct hard_iface *hard_iface, uint8_t dstaddr[])
{ {
struct unicast_packet tmp_uc, *unicast_packet; struct unicast_packet tmp_uc, *unicast_packet;
struct sk_buff *frag_skb; struct sk_buff *frag_skb;
...@@ -267,12 +263,12 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, ...@@ -267,12 +263,12 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
frag1->flags = UNI_FRAG_HEAD | large_tail; frag1->flags = UNI_FRAG_HEAD | large_tail;
frag2->flags = large_tail; frag2->flags = large_tail;
seqno = atomic_add_return(2, &batman_if->frag_seqno); seqno = atomic_add_return(2, &hard_iface->frag_seqno);
frag1->seqno = htons(seqno - 1); frag1->seqno = htons(seqno - 1);
frag2->seqno = htons(seqno); frag2->seqno = htons(seqno);
send_skb_packet(skb, batman_if, dstaddr); send_skb_packet(skb, hard_iface, dstaddr);
send_skb_packet(frag_skb, batman_if, dstaddr); send_skb_packet(frag_skb, hard_iface, dstaddr);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
drop_frag: drop_frag:
...@@ -286,40 +282,37 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) ...@@ -286,40 +282,37 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
{ {
struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
struct unicast_packet *unicast_packet; struct unicast_packet *unicast_packet;
struct orig_node *orig_node = NULL; struct orig_node *orig_node;
struct batman_if *batman_if; struct neigh_node *neigh_node;
struct neigh_node *router;
int data_len = skb->len; int data_len = skb->len;
uint8_t dstaddr[6]; int ret = 1;
spin_lock_bh(&bat_priv->orig_hash_lock);
/* get routing information */ /* get routing information */
if (is_multicast_ether_addr(ethhdr->h_dest)) if (is_multicast_ether_addr(ethhdr->h_dest)) {
orig_node = (struct orig_node *)gw_get_selected(bat_priv); orig_node = (struct orig_node *)gw_get_selected(bat_priv);
if (orig_node)
goto find_router;
}
/* check for hna host */ /* check for hna host - increases orig_node refcount */
if (!orig_node) orig_node = transtable_search(bat_priv, ethhdr->h_dest);
orig_node = transtable_search(bat_priv, ethhdr->h_dest);
router = find_router(bat_priv, orig_node, NULL);
if (!router)
goto unlock;
/* don't lock while sending the packets ... we therefore
* copy the required data before sending */
batman_if = router->if_incoming; find_router:
memcpy(dstaddr, router->addr, ETH_ALEN); /**
* find_router():
* - if orig_node is NULL it returns NULL
* - increases neigh_nodes refcount if found.
*/
neigh_node = find_router(bat_priv, orig_node, NULL);
spin_unlock_bh(&bat_priv->orig_hash_lock); if (!neigh_node)
goto out;
if (batman_if->if_status != IF_ACTIVE) if (neigh_node->if_incoming->if_status != IF_ACTIVE)
goto dropped; goto out;
if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0) if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0)
goto dropped; goto out;
unicast_packet = (struct unicast_packet *)skb->data; unicast_packet = (struct unicast_packet *)skb->data;
...@@ -333,18 +326,24 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) ...@@ -333,18 +326,24 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
if (atomic_read(&bat_priv->fragmentation) && if (atomic_read(&bat_priv->fragmentation) &&
data_len + sizeof(struct unicast_packet) > data_len + sizeof(struct unicast_packet) >
batman_if->net_dev->mtu) { neigh_node->if_incoming->net_dev->mtu) {
/* send frag skb decreases ttl */ /* send frag skb decreases ttl */
unicast_packet->ttl++; unicast_packet->ttl++;
return frag_send_skb(skb, bat_priv, batman_if, ret = frag_send_skb(skb, bat_priv,
dstaddr); neigh_node->if_incoming, neigh_node->addr);
goto out;
} }
send_skb_packet(skb, batman_if, dstaddr);
return 0;
unlock: send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
spin_unlock_bh(&bat_priv->orig_hash_lock); ret = 0;
dropped: goto out;
kfree_skb(skb);
return 1; out:
if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
if (ret == 1)
kfree_skb(skb);
return ret;
} }
...@@ -32,7 +32,7 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, ...@@ -32,7 +32,7 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
void frag_list_free(struct list_head *head); void frag_list_free(struct list_head *head);
int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv);
int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
struct batman_if *batman_if, uint8_t dstaddr[]); struct hard_iface *hard_iface, uint8_t dstaddr[]);
static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) static inline int frag_can_reassemble(struct sk_buff *skb, int mtu)
{ {
......
...@@ -68,15 +68,16 @@ static void free_info(struct kref *ref) ...@@ -68,15 +68,16 @@ static void free_info(struct kref *ref)
} }
/* Compare two vis packets, used by the hashing algorithm */ /* Compare two vis packets, used by the hashing algorithm */
static int vis_info_cmp(void *data1, void *data2) static int vis_info_cmp(struct hlist_node *node, void *data2)
{ {
struct vis_info *d1, *d2; struct vis_info *d1, *d2;
struct vis_packet *p1, *p2; struct vis_packet *p1, *p2;
d1 = data1;
d1 = container_of(node, struct vis_info, hash_entry);
d2 = data2; d2 = data2;
p1 = (struct vis_packet *)d1->skb_packet->data; p1 = (struct vis_packet *)d1->skb_packet->data;
p2 = (struct vis_packet *)d2->skb_packet->data; p2 = (struct vis_packet *)d2->skb_packet->data;
return compare_orig(p1->vis_orig, p2->vis_orig); return compare_eth(p1->vis_orig, p2->vis_orig);
} }
/* hash function to choose an entry in a hash table of given size */ /* hash function to choose an entry in a hash table of given size */
...@@ -104,6 +105,34 @@ static int vis_info_choose(void *data, int size) ...@@ -104,6 +105,34 @@ static int vis_info_choose(void *data, int size)
return hash % size; return hash % size;
} }
static struct vis_info *vis_hash_find(struct bat_priv *bat_priv,
void *data)
{
struct hashtable_t *hash = bat_priv->vis_hash;
struct hlist_head *head;
struct hlist_node *node;
struct vis_info *vis_info, *vis_info_tmp = NULL;
int index;
if (!hash)
return NULL;
index = vis_info_choose(data, hash->size);
head = &hash->table[index];
rcu_read_lock();
hlist_for_each_entry_rcu(vis_info, node, head, hash_entry) {
if (!vis_info_cmp(node, data))
continue;
vis_info_tmp = vis_info;
break;
}
rcu_read_unlock();
return vis_info_tmp;
}
/* insert interface to the list of interfaces of one originator, if it /* insert interface to the list of interfaces of one originator, if it
* does not already exist in the list */ * does not already exist in the list */
static void vis_data_insert_interface(const uint8_t *interface, static void vis_data_insert_interface(const uint8_t *interface,
...@@ -114,7 +143,7 @@ static void vis_data_insert_interface(const uint8_t *interface, ...@@ -114,7 +143,7 @@ static void vis_data_insert_interface(const uint8_t *interface,
struct hlist_node *pos; struct hlist_node *pos;
hlist_for_each_entry(entry, pos, if_list, list) { hlist_for_each_entry(entry, pos, if_list, list) {
if (compare_orig(entry->addr, (void *)interface)) if (compare_eth(entry->addr, (void *)interface))
return; return;
} }
...@@ -166,7 +195,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, ...@@ -166,7 +195,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry,
/* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */
if (primary && entry->quality == 0) if (primary && entry->quality == 0)
return sprintf(buff, "HNA %pM, ", entry->dest); return sprintf(buff, "HNA %pM, ", entry->dest);
else if (compare_orig(entry->src, src)) else if (compare_eth(entry->src, src))
return sprintf(buff, "TQ %pM %d, ", entry->dest, return sprintf(buff, "TQ %pM %d, ", entry->dest,
entry->quality); entry->quality);
...@@ -175,9 +204,8 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, ...@@ -175,9 +204,8 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry,
int vis_seq_print_text(struct seq_file *seq, void *offset) int vis_seq_print_text(struct seq_file *seq, void *offset)
{ {
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct vis_info *info; struct vis_info *info;
struct vis_packet *packet; struct vis_packet *packet;
struct vis_info_entry *entries; struct vis_info_entry *entries;
...@@ -203,8 +231,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -203,8 +231,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
info = bucket->data; hlist_for_each_entry_rcu(info, node, head, hash_entry) {
packet = (struct vis_packet *)info->skb_packet->data; packet = (struct vis_packet *)info->skb_packet->data;
entries = (struct vis_info_entry *) entries = (struct vis_info_entry *)
((char *)packet + sizeof(struct vis_packet)); ((char *)packet + sizeof(struct vis_packet));
...@@ -213,7 +241,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -213,7 +241,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
if (entries[j].quality == 0) if (entries[j].quality == 0)
continue; continue;
compare = compare =
compare_orig(entries[j].src, packet->vis_orig); compare_eth(entries[j].src, packet->vis_orig);
vis_data_insert_interface(entries[j].src, vis_data_insert_interface(entries[j].src,
&vis_if_list, &vis_if_list,
compare); compare);
...@@ -223,7 +251,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -223,7 +251,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
buf_size += 18 + 26 * packet->entries; buf_size += 18 + 26 * packet->entries;
/* add primary/secondary records */ /* add primary/secondary records */
if (compare_orig(entry->addr, packet->vis_orig)) if (compare_eth(entry->addr, packet->vis_orig))
buf_size += buf_size +=
vis_data_count_prim_sec(&vis_if_list); vis_data_count_prim_sec(&vis_if_list);
...@@ -236,6 +264,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -236,6 +264,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
kfree(entry); kfree(entry);
} }
} }
rcu_read_unlock();
} }
buff = kmalloc(buf_size, GFP_ATOMIC); buff = kmalloc(buf_size, GFP_ATOMIC);
...@@ -249,8 +278,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -249,8 +278,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
info = bucket->data; hlist_for_each_entry_rcu(info, node, head, hash_entry) {
packet = (struct vis_packet *)info->skb_packet->data; packet = (struct vis_packet *)info->skb_packet->data;
entries = (struct vis_info_entry *) entries = (struct vis_info_entry *)
((char *)packet + sizeof(struct vis_packet)); ((char *)packet + sizeof(struct vis_packet));
...@@ -259,7 +288,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -259,7 +288,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
if (entries[j].quality == 0) if (entries[j].quality == 0)
continue; continue;
compare = compare =
compare_orig(entries[j].src, packet->vis_orig); compare_eth(entries[j].src, packet->vis_orig);
vis_data_insert_interface(entries[j].src, vis_data_insert_interface(entries[j].src,
&vis_if_list, &vis_if_list,
compare); compare);
...@@ -277,7 +306,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -277,7 +306,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
entry->primary); entry->primary);
/* add primary/secondary records */ /* add primary/secondary records */
if (compare_orig(entry->addr, packet->vis_orig)) if (compare_eth(entry->addr, packet->vis_orig))
buff_pos += buff_pos +=
vis_data_read_prim_sec(buff + buff_pos, vis_data_read_prim_sec(buff + buff_pos,
&vis_if_list); &vis_if_list);
...@@ -291,6 +320,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) ...@@ -291,6 +320,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
kfree(entry); kfree(entry);
} }
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->vis_hash_lock); spin_unlock_bh(&bat_priv->vis_hash_lock);
...@@ -345,7 +375,7 @@ static int recv_list_is_in(struct bat_priv *bat_priv, ...@@ -345,7 +375,7 @@ static int recv_list_is_in(struct bat_priv *bat_priv,
spin_lock_bh(&bat_priv->vis_list_lock); spin_lock_bh(&bat_priv->vis_list_lock);
list_for_each_entry(entry, recv_list, list) { list_for_each_entry(entry, recv_list, list) {
if (memcmp(entry->mac, mac, ETH_ALEN) == 0) { if (compare_eth(entry->mac, mac)) {
spin_unlock_bh(&bat_priv->vis_list_lock); spin_unlock_bh(&bat_priv->vis_list_lock);
return 1; return 1;
} }
...@@ -381,8 +411,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, ...@@ -381,8 +411,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
sizeof(struct vis_packet)); sizeof(struct vis_packet));
memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN);
old_info = hash_find(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, old_info = vis_hash_find(bat_priv, &search_elem);
&search_elem);
kfree_skb(search_elem.skb_packet); kfree_skb(search_elem.skb_packet);
if (old_info) { if (old_info) {
...@@ -442,7 +471,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, ...@@ -442,7 +471,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
/* try to add it */ /* try to add it */
hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose,
info); info, &info->hash_entry);
if (hash_added < 0) { if (hash_added < 0) {
/* did not work (for some reason) */ /* did not work (for some reason) */
kref_put(&info->refcount, free_info); kref_put(&info->refcount, free_info);
...@@ -529,9 +558,8 @@ static int find_best_vis_server(struct bat_priv *bat_priv, ...@@ -529,9 +558,8 @@ static int find_best_vis_server(struct bat_priv *bat_priv,
struct vis_info *info) struct vis_info *info)
{ {
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
struct vis_packet *packet; struct vis_packet *packet;
int best_tq = -1, i; int best_tq = -1, i;
...@@ -541,16 +569,17 @@ static int find_best_vis_server(struct bat_priv *bat_priv, ...@@ -541,16 +569,17 @@ static int find_best_vis_server(struct bat_priv *bat_priv,
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
if ((orig_node) && (orig_node->router) && if ((orig_node) && (orig_node->router) &&
(orig_node->flags & VIS_SERVER) && (orig_node->flags & VIS_SERVER) &&
(orig_node->router->tq_avg > best_tq)) { (orig_node->router->tq_avg > best_tq)) {
best_tq = orig_node->router->tq_avg; best_tq = orig_node->router->tq_avg;
memcpy(packet->target_orig, orig_node->orig, memcpy(packet->target_orig, orig_node->orig,
ETH_ALEN); ETH_ALEN);
} }
} }
rcu_read_unlock();
} }
return best_tq; return best_tq;
...@@ -573,9 +602,8 @@ static bool vis_packet_full(struct vis_info *info) ...@@ -573,9 +602,8 @@ static bool vis_packet_full(struct vis_info *info)
static int generate_vis_packet(struct bat_priv *bat_priv) static int generate_vis_packet(struct bat_priv *bat_priv)
{ {
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
struct neigh_node *neigh_node; struct neigh_node *neigh_node;
struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info;
...@@ -587,7 +615,6 @@ static int generate_vis_packet(struct bat_priv *bat_priv) ...@@ -587,7 +615,6 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
info->first_seen = jiffies; info->first_seen = jiffies;
packet->vis_type = atomic_read(&bat_priv->vis_mode); packet->vis_type = atomic_read(&bat_priv->vis_mode);
spin_lock_bh(&bat_priv->orig_hash_lock);
memcpy(packet->target_orig, broadcast_addr, ETH_ALEN); memcpy(packet->target_orig, broadcast_addr, ETH_ALEN);
packet->ttl = TTL; packet->ttl = TTL;
packet->seqno = htonl(ntohl(packet->seqno) + 1); packet->seqno = htonl(ntohl(packet->seqno) + 1);
...@@ -597,23 +624,21 @@ static int generate_vis_packet(struct bat_priv *bat_priv) ...@@ -597,23 +624,21 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) { if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) {
best_tq = find_best_vis_server(bat_priv, info); best_tq = find_best_vis_server(bat_priv, info);
if (best_tq < 0) { if (best_tq < 0)
spin_unlock_bh(&bat_priv->orig_hash_lock);
return -1; return -1;
}
} }
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
neigh_node = orig_node->router; neigh_node = orig_node->router;
if (!neigh_node) if (!neigh_node)
continue; continue;
if (!compare_orig(neigh_node->addr, orig_node->orig)) if (!compare_eth(neigh_node->addr, orig_node->orig))
continue; continue;
if (neigh_node->if_incoming->if_status != IF_ACTIVE) if (neigh_node->if_incoming->if_status != IF_ACTIVE)
...@@ -632,23 +657,19 @@ static int generate_vis_packet(struct bat_priv *bat_priv) ...@@ -632,23 +657,19 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
entry->quality = neigh_node->tq_avg; entry->quality = neigh_node->tq_avg;
packet->entries++; packet->entries++;
if (vis_packet_full(info)) { if (vis_packet_full(info))
spin_unlock_bh(&bat_priv->orig_hash_lock); goto unlock;
return 0;
}
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
hash = bat_priv->hna_local_hash; hash = bat_priv->hna_local_hash;
spin_lock_bh(&bat_priv->hna_lhash_lock); spin_lock_bh(&bat_priv->hna_lhash_lock);
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { hlist_for_each_entry(hna_local_entry, node, head, hash_entry) {
hna_local_entry = bucket->data;
entry = (struct vis_info_entry *) entry = (struct vis_info_entry *)
skb_put(info->skb_packet, skb_put(info->skb_packet,
sizeof(*entry)); sizeof(*entry));
...@@ -666,6 +687,10 @@ static int generate_vis_packet(struct bat_priv *bat_priv) ...@@ -666,6 +687,10 @@ static int generate_vis_packet(struct bat_priv *bat_priv)
spin_unlock_bh(&bat_priv->hna_lhash_lock); spin_unlock_bh(&bat_priv->hna_lhash_lock);
return 0; return 0;
unlock:
rcu_read_unlock();
return 0;
} }
/* free old vis packets. Must be called with this vis_hash_lock /* free old vis packets. Must be called with this vis_hash_lock
...@@ -674,25 +699,22 @@ static void purge_vis_packets(struct bat_priv *bat_priv) ...@@ -674,25 +699,22 @@ static void purge_vis_packets(struct bat_priv *bat_priv)
{ {
int i; int i;
struct hashtable_t *hash = bat_priv->vis_hash; struct hashtable_t *hash = bat_priv->vis_hash;
struct hlist_node *walk, *safe; struct hlist_node *node, *node_tmp;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct vis_info *info; struct vis_info *info;
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { hlist_for_each_entry_safe(info, node, node_tmp,
info = bucket->data; head, hash_entry) {
/* never purge own data. */ /* never purge own data. */
if (info == bat_priv->my_vis_info) if (info == bat_priv->my_vis_info)
continue; continue;
if (time_after(jiffies, if (time_after(jiffies,
info->first_seen + VIS_TIMEOUT * HZ)) { info->first_seen + VIS_TIMEOUT * HZ)) {
hlist_del(walk); hlist_del(node);
kfree(bucket);
send_list_del(info); send_list_del(info);
kref_put(&info->refcount, free_info); kref_put(&info->refcount, free_info);
} }
...@@ -704,27 +726,24 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv, ...@@ -704,27 +726,24 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv,
struct vis_info *info) struct vis_info *info)
{ {
struct hashtable_t *hash = bat_priv->orig_hash; struct hashtable_t *hash = bat_priv->orig_hash;
struct hlist_node *walk; struct hlist_node *node;
struct hlist_head *head; struct hlist_head *head;
struct element_t *bucket;
struct orig_node *orig_node; struct orig_node *orig_node;
struct vis_packet *packet; struct vis_packet *packet;
struct sk_buff *skb; struct sk_buff *skb;
struct batman_if *batman_if; struct hard_iface *hard_iface;
uint8_t dstaddr[ETH_ALEN]; uint8_t dstaddr[ETH_ALEN];
int i; int i;
spin_lock_bh(&bat_priv->orig_hash_lock);
packet = (struct vis_packet *)info->skb_packet->data; packet = (struct vis_packet *)info->skb_packet->data;
/* send to all routers in range. */ /* send to all routers in range. */
for (i = 0; i < hash->size; i++) { for (i = 0; i < hash->size; i++) {
head = &hash->table[i]; head = &hash->table[i];
hlist_for_each_entry(bucket, walk, head, hlist) { rcu_read_lock();
orig_node = bucket->data; hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
/* if it's a vis server and reachable, send it. */ /* if it's a vis server and reachable, send it. */
if ((!orig_node) || (!orig_node->router)) if ((!orig_node) || (!orig_node->router))
continue; continue;
...@@ -737,54 +756,61 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv, ...@@ -737,54 +756,61 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv,
continue; continue;
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
batman_if = orig_node->router->if_incoming; hard_iface = orig_node->router->if_incoming;
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
spin_unlock_bh(&bat_priv->orig_hash_lock);
skb = skb_clone(info->skb_packet, GFP_ATOMIC); skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (skb) if (skb)
send_skb_packet(skb, batman_if, dstaddr); send_skb_packet(skb, hard_iface, dstaddr);
spin_lock_bh(&bat_priv->orig_hash_lock);
} }
rcu_read_unlock();
} }
spin_unlock_bh(&bat_priv->orig_hash_lock);
} }
static void unicast_vis_packet(struct bat_priv *bat_priv, static void unicast_vis_packet(struct bat_priv *bat_priv,
struct vis_info *info) struct vis_info *info)
{ {
struct orig_node *orig_node; struct orig_node *orig_node;
struct neigh_node *neigh_node = NULL;
struct sk_buff *skb; struct sk_buff *skb;
struct vis_packet *packet; struct vis_packet *packet;
struct batman_if *batman_if;
uint8_t dstaddr[ETH_ALEN];
spin_lock_bh(&bat_priv->orig_hash_lock);
packet = (struct vis_packet *)info->skb_packet->data; packet = (struct vis_packet *)info->skb_packet->data;
orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
compare_orig, choose_orig,
packet->target_orig));
if ((!orig_node) || (!orig_node->router)) rcu_read_lock();
goto out; orig_node = orig_hash_find(bat_priv, packet->target_orig);
/* don't lock while sending the packets ... we therefore if (!orig_node)
* copy the required data before sending */ goto unlock;
batman_if = orig_node->router->if_incoming;
memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); neigh_node = orig_node->router;
spin_unlock_bh(&bat_priv->orig_hash_lock);
if (!neigh_node)
goto unlock;
if (!atomic_inc_not_zero(&neigh_node->refcount)) {
neigh_node = NULL;
goto unlock;
}
rcu_read_unlock();
skb = skb_clone(info->skb_packet, GFP_ATOMIC); skb = skb_clone(info->skb_packet, GFP_ATOMIC);
if (skb) if (skb)
send_skb_packet(skb, batman_if, dstaddr); send_skb_packet(skb, neigh_node->if_incoming,
neigh_node->addr);
return; goto out;
unlock:
rcu_read_unlock();
out: out:
spin_unlock_bh(&bat_priv->orig_hash_lock); if (neigh_node)
neigh_node_free_ref(neigh_node);
if (orig_node)
orig_node_free_ref(orig_node);
return;
} }
/* only send one vis packet. called from send_vis_packets() */ /* only send one vis packet. called from send_vis_packets() */
...@@ -896,7 +922,8 @@ int vis_init(struct bat_priv *bat_priv) ...@@ -896,7 +922,8 @@ int vis_init(struct bat_priv *bat_priv)
INIT_LIST_HEAD(&bat_priv->vis_send_list); INIT_LIST_HEAD(&bat_priv->vis_send_list);
hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose,
bat_priv->my_vis_info); bat_priv->my_vis_info,
&bat_priv->my_vis_info->hash_entry);
if (hash_added < 0) { if (hash_added < 0) {
pr_err("Can't add own vis packet into hash\n"); pr_err("Can't add own vis packet into hash\n");
/* not in hash, need to remove it manually. */ /* not in hash, need to remove it manually. */
...@@ -918,10 +945,11 @@ int vis_init(struct bat_priv *bat_priv) ...@@ -918,10 +945,11 @@ int vis_init(struct bat_priv *bat_priv)
} }
/* Decrease the reference count on a hash item info */ /* Decrease the reference count on a hash item info */
static void free_info_ref(void *data, void *arg) static void free_info_ref(struct hlist_node *node, void *arg)
{ {
struct vis_info *info = data; struct vis_info *info;
info = container_of(node, struct vis_info, hash_entry);
send_list_del(info); send_list_del(info);
kref_put(&info->refcount, free_info); kref_put(&info->refcount, free_info);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment