Three new features have been added: 1) generic uevent support 2) uevent triggering for gw state changes 3) gw propagation for clients
Patch summary:
- 1/3: a wrapper function that permits to trigger uevents has been added. The function takes a "type", an "action" and a "data" as parameters which will be used as uevent attributes. In particular they will respectively be BATTYPE, BATACTION and BATDATA in the uevent environment. The "type" and the "action" field are managed using two enum definitions and two char arrais. Currently the supported "type" is 'gw' only and the supported "actions" are 'add', 'change' and 'del'. The "data" parameter is a free field which can be filled by the programmer to send any useful data to the userspace. Uevents can be seen in userspace with the following command: "$ udevadm monitor --property"
- 2/3: on a gateway add/change/del event a corresponding uevent of type 'gw' is triggered. In particular, in case of setting up a gw for the first time a uevent with action 'add' is triggered. In case of changing the best gateway a uevent with action 'change' is triggered. In case of deselection of any gateway a uevent with action DEL is triggered.
- 3/3: a gw propagation feature for clients has been provided. The main target of this patch is to make clients able to change L3 gateway as soon as the mesh node currently serving them changes its default one at the batman layer. For this target a passive approach has been followed: whenever a unicast DHCPREQUEST for renewal is sent from a client, the node will inspect it and check if it is directed to the current default gateway or not. If it is not the case then the packet is dropped. In this way the client will get a timeout and a new DHCPREQUEST for discover will be sent giving the possibility to the node to select the best gw to forward this packet to (current gw selection mode).
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
A triggered uevent has three properties: - type: the event class. Who generates the event (only 'gw' is currently defined). Corresponds to the BATTYPE uevent variable. - action: the associated action with the event ('add'/'change'/'del' are currently defined). Corresponds to the BAACTION uevent variable. - data: any useful data for the userspace. Corresponds to the BATDATA uevent variable.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- bat_sysfs.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_sysfs.h | 4 +++ main.h | 11 +++++++++ 3 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/bat_sysfs.c b/bat_sysfs.c index 497a070..83c0980 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -32,6 +32,16 @@ #define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) #define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj))
+static char *uev_action_str[] = { + "add", + "del", + "change" +}; + +static char *uev_type_str[] = { + "gw" +}; + /* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ struct bat_attribute bat_attr_##_name = { \ @@ -594,3 +604,62 @@ void sysfs_del_hardif(struct kobject **hardif_obj) kobject_put(*hardif_obj); *hardif_obj = NULL; } + +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data) +{ + int ret = -1; + struct hard_iface *primary_if = NULL; + struct kobject *bat_kobj; + char *uevent_env[4] = { NULL, NULL, NULL, NULL }; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + bat_kobj = &primary_if->soft_iface->dev.kobj; + + uevent_env[0] = kmalloc(strlen("BATTYPE=") + + strlen(uev_type_str[type]) + 1, + GFP_ATOMIC); + if (!uevent_env[0]) + goto out; + + sprintf(uevent_env[0], "BATTYPE=%s", uev_type_str[type]); + + uevent_env[1] = kmalloc(strlen("BATACTION=") + + strlen(uev_action_str[action]) + 1, + GFP_ATOMIC); + if (!uevent_env[1]) + goto out; + + sprintf(uevent_env[1], "BATACTION=%s", uev_action_str[action]); + + /* If the event is DEL, ignore the data field */ + if (action == UEV_DEL) + goto throw; + + uevent_env[2] = kmalloc(strlen("BATDATA=") + + strlen(data) + 1, GFP_ATOMIC); + if (!uevent_env[2]) + goto out; + + sprintf(uevent_env[2], "BATDATA=%s", data); + +throw: + ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env); +out: + kfree(uevent_env[0]); + kfree(uevent_env[1]); + kfree(uevent_env[2]); + + if (primary_if) + hardif_free_ref(primary_if); + + if (ret) + bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send " + "uevent for (%s,%s,%s) event\n", + uev_type_str[type], uev_action_str[action], + (action == UEV_DEL ? "NULL" : data)); + return ret; +} diff --git a/bat_sysfs.h b/bat_sysfs.h index 02f1fa7..2b1a1d0 100644 --- a/bat_sysfs.h +++ b/bat_sysfs.h @@ -26,6 +26,8 @@ #define SYSFS_IF_MESH_SUBDIR "mesh" #define SYSFS_IF_BAT_SUBDIR "batman_adv"
+struct bat_priv; + struct bat_attribute { struct attribute attr; ssize_t (*show)(struct kobject *kobj, struct attribute *attr, @@ -38,5 +40,7 @@ int sysfs_add_meshif(struct net_device *dev); void sysfs_del_meshif(struct net_device *dev); int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void sysfs_del_hardif(struct kobject **hardif_obj); +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data);
#endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --git a/main.h b/main.h index 101d9dc..0dfb46e 100644 --- a/main.h +++ b/main.h @@ -78,6 +78,17 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
+ +enum uev_action { + UEV_ADD = 0, + UEV_DEL, + UEV_CHANGE +}; + +enum uev_type { + UEV_GW = 0 +}; + /* * Debug Messages */
On Thu, May 05, 2011 at 09:13:07AM +0200, Antonio Quartulli wrote:
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
A triggered uevent has three properties:
- type: the event class. Who generates the event (only 'gw' is currently defined). Corresponds to the BATTYPE uevent variable.
- action: the associated action with the event ('add'/'change'/'del' are currently defined). Corresponds to the BAACTION uevent variable.
- data: any useful data for the userspace. Corresponds to the BATDATA uevent variable.
Signed-off-by: Antonio Quartulli ordex@autistici.org
bat_sysfs.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_sysfs.h | 4 +++ main.h | 11 +++++++++ 3 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/bat_sysfs.c b/bat_sysfs.c index 497a070..83c0980 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -32,6 +32,16 @@ #define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) #define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj))
+static char *uev_action_str[] = {
- "add",
- "del",
- "change"
+};
+static char *uev_type_str[] = {
- "gw"
+};
/* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ struct bat_attribute bat_attr_##_name = { \ @@ -594,3 +604,62 @@ void sysfs_del_hardif(struct kobject **hardif_obj) kobject_put(*hardif_obj); *hardif_obj = NULL; }
+int throw_uevent(struct bat_priv *bat_priv, enum uev_type type,
enum uev_action action, char *data)
+{
- int ret = -1;
- struct hard_iface *primary_if = NULL;
- struct kobject *bat_kobj;
- char *uevent_env[4] = { NULL, NULL, NULL, NULL };
- primary_if = primary_if_get_selected(bat_priv);
- if (!primary_if)
goto out;
- bat_kobj = &primary_if->soft_iface->dev.kobj;
- uevent_env[0] = kmalloc(strlen("BATTYPE=") +
strlen(uev_type_str[type]) + 1,
GFP_ATOMIC);
- if (!uevent_env[0])
goto out;
- sprintf(uevent_env[0], "BATTYPE=%s", uev_type_str[type]);
Hi Antonio
I don't particularly like having BATTYPE= twice, once in the kmalloc and a second time in the sprintf. Maybe somebody will decide that BATUTYPE is a better name, change the sprintf, forget about the kmalloc, and overflow the allocated memory by one byte. snprintf will prevent the corruption. I've made this sort of stupid error myself, and it takes longer to debug than to write a bit more defensive code which prevents the error.
- uevent_env[1] = kmalloc(strlen("BATACTION=") +
strlen(uev_action_str[action]) + 1,
GFP_ATOMIC);
- if (!uevent_env[1])
goto out;
- sprintf(uevent_env[1], "BATACTION=%s", uev_action_str[action]);
- /* If the event is DEL, ignore the data field */
- if (action == UEV_DEL)
goto throw;
I would replace this goto with a plain if statement.
- uevent_env[2] = kmalloc(strlen("BATDATA=") +
strlen(data) + 1, GFP_ATOMIC);
- if (!uevent_env[2])
goto out;
- sprintf(uevent_env[2], "BATDATA=%s", data);
+throw:
- ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env);
+out:
- kfree(uevent_env[0]);
- kfree(uevent_env[1]);
- kfree(uevent_env[2]);
- if (primary_if)
hardif_free_ref(primary_if);
- if (ret)
bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send "
"uevent for (%s,%s,%s) event\n",
uev_type_str[type], uev_action_str[action],
(action == UEV_DEL ? "NULL" : data));
The value of ret could be interesting here, especially if kobject_uevent_env() failed.
- return ret;
+}
Andrew
On gio, mag 05, 2011 at 03:34:24 +0200, Andrew Lunn wrote:
- uevent_env[0] = kmalloc(strlen("BATTYPE=") +
strlen(uev_type_str[type]) + 1,
GFP_ATOMIC);
- if (!uevent_env[0])
goto out;
- sprintf(uevent_env[0], "BATTYPE=%s", uev_type_str[type]);
Hi Antonio
Hi Andrew,
I don't particularly like having BATTYPE= twice, once in the kmalloc and a second time in the sprintf. Maybe somebody will decide that BATUTYPE is a better name, change the sprintf, forget about the kmalloc, and overflow the allocated memory by one byte. snprintf will prevent the corruption. I've made this sort of stupid error myself, and it takes longer to debug than to write a bit more defensive code which prevents the error.
I definitely agree. I thin that using a define like #define UEV_TYPE_VAR "BATTYPE=" would be more elegant. In this case I can avoid to use snprintf. Do you agree on this?
- /* If the event is DEL, ignore the data field */
- if (action == UEV_DEL)
goto throw;
I would replace this goto with a plain if statement.
Mh..ok. I abused of this if->goto style even if not really needed
- if (ret)
bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send "
"uevent for (%s,%s,%s) event\n",
uev_type_str[type], uev_action_str[action],
(action == UEV_DEL ? "NULL" : data));
The value of ret could be interesting here, especially if kobject_uevent_env() failed.
Mh, Ok. I can print it into the message. Is there a function in the kernel to transform it in a proper string?
Thank you very much.
Regards,
I definitely agree. I thin that using a define like #define UEV_TYPE_VAR "BATTYPE=" would be more elegant. In this case I can avoid to use snprintf. Do you agree on this?
Yes, that is safer.
- if (ret)
bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send "
"uevent for (%s,%s,%s) event\n",
uev_type_str[type], uev_action_str[action],
(action == UEV_DEL ? "NULL" : data));
The value of ret could be interesting here, especially if kobject_uevent_env() failed.
Mh, Ok. I can print it into the message. Is there a function in the kernel to transform it in a proper string?
I don't think so. At least i cannot find a kstrerror(). It is messy, since each architecture can define its own numbers for the symbols. Most don't and use asm-generic/errno.h, but Sparc for example has an errno.h which is compatible to SunOS.
Just print the decimal value. Anybody who is competent enough to debug the code should be able to map a value back to a symbol.
Andrew
On dom, mag 08, 2011 at 10:11:48 +0200, Andrew Lunn wrote:
I definitely agree. I thin that using a define like #define UEV_TYPE_VAR "BATTYPE=" would be more elegant. In this case I can avoid to use snprintf. Do you agree on this?
Yes, that is safer.
- if (ret)
bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send "
"uevent for (%s,%s,%s) event\n",
uev_type_str[type], uev_action_str[action],
(action == UEV_DEL ? "NULL" : data));
The value of ret could be interesting here, especially if kobject_uevent_env() failed.
Mh, Ok. I can print it into the message. Is there a function in the kernel to transform it in a proper string?
I don't think so. At least i cannot find a kstrerror(). It is messy, since each architecture can define its own numbers for the symbols. Most don't and use asm-generic/errno.h, but Sparc for example has an errno.h which is compatible to SunOS.
Just print the decimal value. Anybody who is competent enough to debug the code should be able to map a value back to a symbol.
Ok!
Thanks again,
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- gateway_client.c | 31 ++++++++++++++++++++++++------- main.c | 1 + types.h | 1 + 3 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 65f3953..8a7090c 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" @@ -105,17 +106,18 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); }
void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; - struct neigh_node *router; + struct neigh_node *router = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; + char gw_addr[18] = { '\0' };
/** * The batman daemon checks here if we already passed a full originator @@ -127,7 +129,7 @@ void gw_election(struct bat_priv *bat_priv) return;
curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) goto out;
rcu_read_lock(); @@ -186,9 +188,8 @@ void gw_election(struct bat_priv *bat_priv) }
if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) - goto unlock; + if (curr_gw_tmp) + router = orig_node_get_router(curr_gw_tmp->orig_node);
if ((curr_gw) && (!curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, @@ -209,8 +210,24 @@ void gw_election(struct bat_priv *bat_priv) curr_gw_tmp->orig_node->gw_flags, router->tq_avg);
- neigh_node_free_ref(router); + if (router) + neigh_node_free_ref(router); gw_select(bat_priv, curr_gw_tmp); + rcu_read_unlock(); + + /* Throw the gateway event to the userspace */ + if (!curr_gw_tmp) { + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + goto out; + } + + sprintf(gw_addr, "%pM", curr_gw_tmp->orig_node->orig); + + if (curr_gw) + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + else + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + goto out; }
unlock: diff --git a/main.c b/main.c index 7edf8d7..792f682 100644 --- a/main.c +++ b/main.c @@ -111,6 +111,7 @@ int mesh_init(struct net_device *soft_iface) if (vis_init(bat_priv) < 1) goto err;
+ atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end;
diff --git a/types.h b/types.h index 9ae507a..e1f22b0 100644 --- a/types.h +++ b/types.h @@ -173,6 +173,7 @@ struct bat_priv { struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; };
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org --- gateway_client.c | 28 ++++++++++++++++++++++++++-- gateway_client.h | 3 ++- main.h | 3 ++- soft-interface.c | 10 ++++++++-- 4 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 8a7090c..6b3b775 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,6 +25,7 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> @@ -505,14 +506,17 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr, *neigh_old; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -580,7 +584,27 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point it can only be a renewal packet (because it is the + * only dhcp client message sent as unicast) and we have to decide + * whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + /* If the dhcp packet has been sent to a different gw, we have + * to evaluate whether the old gw is still enough reliable */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < GW_THRESHOLD) + ret = -1; +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); + } + if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 0dfb46e..b59bfad 100644 --- a/main.h +++ b/main.h @@ -78,7 +78,6 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -89,6 +88,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 25 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index 8023c4e..79d4012 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -569,6 +570,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -603,8 +605,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* TODO: check this for locks */ hna_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -612,6 +616,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret == 0) do_bcast = true; } + if (orig_node) + orig_node_free_ref(orig_node);
/* ethernet packet should be broadcasted */ if (do_bcast) {
On Thu, May 05, 2011 at 09:13:09AM +0200, Antonio Quartulli wrote:
- /* If old_gw != NULL then this packet is unicast.
* So, at this point it can only be a renewal packet (because it is the
* only dhcp client message sent as unicast) and we have to decide
* whether to drop it or not */
What about the release message? Is that not also unicast?
- if (old_gw && curr_gw->orig_node != old_gw) {
/* If the dhcp packet has been sent to a different gw, we have
* to evaluate whether the old gw is still enough reliable */
still reliable enough.
Andrew
On gio, mag 05, 2011 at 03:46:49 +0200, Andrew Lunn wrote:
On Thu, May 05, 2011 at 09:13:09AM +0200, Antonio Quartulli wrote:
- /* If old_gw != NULL then this packet is unicast.
* So, at this point it can only be a renewal packet (because it is the
* only dhcp client message sent as unicast) and we have to decide
* whether to drop it or not */
What about the release message? Is that not also unicast?
You are right (dhcpdecline is unicast too). I have to inspect the dhcp header and look at the message type option.
- if (old_gw && curr_gw->orig_node != old_gw) {
/* If the dhcp packet has been sent to a different gw, we have
* to evaluate whether the old gw is still enough reliable */
still reliable enough.
Thanks!
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
A triggered uevent has three properties: - type: the event class. Who generates the event (only 'gw' is currently defined). Corresponds to the BATTYPE uevent variable. - action: the associated action with the event ('add'/'change'/'del' are currently defined). Corresponds to the BAACTION uevent variable. - data: any useful data for the userspace. Corresponds to the BATDATA uevent variable.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- 1) BAT*= strings have been substituted by macros. 2) the return value of kobject_uevent is now included in the error message
bat_sysfs.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_sysfs.h | 2 + main.h | 11 +++++++++ 3 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/bat_sysfs.c b/bat_sysfs.c index 497a070..5bb9b34 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -32,6 +32,20 @@ #define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) #define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj))
+#define UEV_TYPE_VAR "BATTYPE=" +#define UEV_ACTION_VAR "BATACTION=" +#define UEV_DATA_VAR "BATDATA=" + +static char *uev_action_str[] = { + "add", + "del", + "change" +}; + +static char *uev_type_str[] = { + "gw" +}; + /* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ struct bat_attribute bat_attr_##_name = { \ @@ -594,3 +608,60 @@ void sysfs_del_hardif(struct kobject **hardif_obj) kobject_put(*hardif_obj); *hardif_obj = NULL; } + +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data) +{ + int ret = -1; + struct hard_iface *primary_if = NULL; + struct kobject *bat_kobj; + char *uevent_env[4] = { NULL, NULL, NULL, NULL }; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + bat_kobj = &primary_if->soft_iface->dev.kobj; + + uevent_env[0] = kmalloc(strlen(UEV_TYPE_VAR) + + strlen(uev_type_str[type]) + 1, + GFP_ATOMIC); + if (!uevent_env[0]) + goto out; + + sprintf(uevent_env[0], "%s%s", UEV_TYPE_VAR, uev_type_str[type]); + + uevent_env[1] = kmalloc(strlen(UEV_ACTION_VAR) + + strlen(uev_action_str[action]) + 1, + GFP_ATOMIC); + if (!uevent_env[1]) + goto out; + + sprintf(uevent_env[1], "%s%s", UEV_ACTION_VAR, uev_action_str[action]); + + /* If the event is DEL, ignore the data field */ + if (action != UEV_DEL) { + uevent_env[2] = kmalloc(strlen(UEV_DATA_VAR) + + strlen(data) + 1, GFP_ATOMIC); + if (!uevent_env[2]) + goto out; + + sprintf(uevent_env[2], "%s%s", UEV_DATA_VAR, data); + } + + ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env); +out: + kfree(uevent_env[0]); + kfree(uevent_env[1]); + kfree(uevent_env[2]); + + if (primary_if) + hardif_free_ref(primary_if); + + if (ret) + bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send " + "uevent for (%s,%s,%s) event (err: %d)\n", + uev_type_str[type], uev_action_str[action], + (action == UEV_DEL ? "NULL" : data), ret); + return ret; +} diff --git a/bat_sysfs.h b/bat_sysfs.h index 02f1fa7..e48ed9a 100644 --- a/bat_sysfs.h +++ b/bat_sysfs.h @@ -38,5 +38,7 @@ int sysfs_add_meshif(struct net_device *dev); void sysfs_del_meshif(struct net_device *dev); int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void sysfs_del_hardif(struct kobject **hardif_obj); +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data);
#endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --git a/main.h b/main.h index 3ca3941..4f9991d 100644 --- a/main.h +++ b/main.h @@ -79,6 +79,17 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
+ +enum uev_action { + UEV_ADD = 0, + UEV_DEL, + UEV_CHANGE +}; + +enum uev_type { + UEV_GW = 0 +}; + /* * Debug Messages */
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- gateway_client.c | 31 ++++++++++++++++++++++++------- main.c | 1 + types.h | 1 + 3 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 65f3953..8a7090c 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" @@ -105,17 +106,18 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); }
void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; - struct neigh_node *router; + struct neigh_node *router = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; + char gw_addr[18] = { '\0' };
/** * The batman daemon checks here if we already passed a full originator @@ -127,7 +129,7 @@ void gw_election(struct bat_priv *bat_priv) return;
curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) goto out;
rcu_read_lock(); @@ -186,9 +188,8 @@ void gw_election(struct bat_priv *bat_priv) }
if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) - goto unlock; + if (curr_gw_tmp) + router = orig_node_get_router(curr_gw_tmp->orig_node);
if ((curr_gw) && (!curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, @@ -209,8 +210,24 @@ void gw_election(struct bat_priv *bat_priv) curr_gw_tmp->orig_node->gw_flags, router->tq_avg);
- neigh_node_free_ref(router); + if (router) + neigh_node_free_ref(router); gw_select(bat_priv, curr_gw_tmp); + rcu_read_unlock(); + + /* Throw the gateway event to the userspace */ + if (!curr_gw_tmp) { + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + goto out; + } + + sprintf(gw_addr, "%pM", curr_gw_tmp->orig_node->orig); + + if (curr_gw) + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + else + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + goto out; }
unlock: diff --git a/main.c b/main.c index 0a7cee0..ca060a9 100644 --- a/main.c +++ b/main.c @@ -111,6 +111,7 @@ int mesh_init(struct net_device *soft_iface) if (vis_init(bat_priv) < 1) goto err;
+ atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end;
diff --git a/types.h b/types.h index fab70e8..87b890e 100644 --- a/types.h +++ b/types.h @@ -173,6 +173,7 @@ struct bat_priv { struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; };
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org --- 1) Following Andrew's suggestion, the dhcp unicast packet type is now checked to ensure it is a DHCPREQUEST.
2) Comments corrections
Thanks
gateway_client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 8a7090c..da2bc8d 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; @@ -505,14 +511,75 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octect 1: option type + * - octect 2: option data len (only if type != 255 and 0) + * - octect 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octect: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octect and go to the data octect */ + if (pkt_len < 2) + goto out; + pkt_len -= 2; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -580,7 +647,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 4f9991d..fbd7d37 100644 --- a/main.h +++ b/main.h @@ -79,7 +79,6 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -90,6 +89,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 25 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index c76a33e..6d02d92 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -569,6 +570,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -603,8 +605,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* TODO: check this for locks */ tt_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -612,6 +616,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret == 0) do_bcast = true; } + if (orig_node) + orig_node_free_ref(orig_node);
/* ethernet packet should be broadcasted */ if (do_bcast) {
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
A triggered uevent has three properties: - type: the event class. Who generates the event (only 'gw' is currently defined). Corresponds to the BATTYPE uevent variable. - action: the associated action with the event ('add'/'change'/'del' are currently defined). Corresponds to the BAACTION uevent variable. - data: any useful data for the userspace. Corresponds to the BATDATA uevent variable.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- bat_sysfs.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_sysfs.h | 2 + main.h | 11 +++++++++ 3 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/bat_sysfs.c b/bat_sysfs.c index 497a070..5bb9b34 100644 --- a/bat_sysfs.c +++ b/bat_sysfs.c @@ -32,6 +32,20 @@ #define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) #define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj))
+#define UEV_TYPE_VAR "BATTYPE=" +#define UEV_ACTION_VAR "BATACTION=" +#define UEV_DATA_VAR "BATDATA=" + +static char *uev_action_str[] = { + "add", + "del", + "change" +}; + +static char *uev_type_str[] = { + "gw" +}; + /* Use this, if you have customized show and store functions */ #define BAT_ATTR(_name, _mode, _show, _store) \ struct bat_attribute bat_attr_##_name = { \ @@ -594,3 +608,60 @@ void sysfs_del_hardif(struct kobject **hardif_obj) kobject_put(*hardif_obj); *hardif_obj = NULL; } + +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data) +{ + int ret = -1; + struct hard_iface *primary_if = NULL; + struct kobject *bat_kobj; + char *uevent_env[4] = { NULL, NULL, NULL, NULL }; + + primary_if = primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + bat_kobj = &primary_if->soft_iface->dev.kobj; + + uevent_env[0] = kmalloc(strlen(UEV_TYPE_VAR) + + strlen(uev_type_str[type]) + 1, + GFP_ATOMIC); + if (!uevent_env[0]) + goto out; + + sprintf(uevent_env[0], "%s%s", UEV_TYPE_VAR, uev_type_str[type]); + + uevent_env[1] = kmalloc(strlen(UEV_ACTION_VAR) + + strlen(uev_action_str[action]) + 1, + GFP_ATOMIC); + if (!uevent_env[1]) + goto out; + + sprintf(uevent_env[1], "%s%s", UEV_ACTION_VAR, uev_action_str[action]); + + /* If the event is DEL, ignore the data field */ + if (action != UEV_DEL) { + uevent_env[2] = kmalloc(strlen(UEV_DATA_VAR) + + strlen(data) + 1, GFP_ATOMIC); + if (!uevent_env[2]) + goto out; + + sprintf(uevent_env[2], "%s%s", UEV_DATA_VAR, data); + } + + ret = kobject_uevent_env(bat_kobj, KOBJ_CHANGE, uevent_env); +out: + kfree(uevent_env[0]); + kfree(uevent_env[1]); + kfree(uevent_env[2]); + + if (primary_if) + hardif_free_ref(primary_if); + + if (ret) + bat_dbg(DBG_BATMAN, bat_priv, "Impossible to send " + "uevent for (%s,%s,%s) event (err: %d)\n", + uev_type_str[type], uev_action_str[action], + (action == UEV_DEL ? "NULL" : data), ret); + return ret; +} diff --git a/bat_sysfs.h b/bat_sysfs.h index 02f1fa7..e48ed9a 100644 --- a/bat_sysfs.h +++ b/bat_sysfs.h @@ -38,5 +38,7 @@ int sysfs_add_meshif(struct net_device *dev); void sysfs_del_meshif(struct net_device *dev); int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void sysfs_del_hardif(struct kobject **hardif_obj); +int throw_uevent(struct bat_priv *bat_priv, enum uev_type type, + enum uev_action action, char *data);
#endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --git a/main.h b/main.h index 3ca3941..4f9991d 100644 --- a/main.h +++ b/main.h @@ -79,6 +79,17 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
+ +enum uev_action { + UEV_ADD = 0, + UEV_DEL, + UEV_CHANGE +}; + +enum uev_type { + UEV_GW = 0 +}; + /* * Debug Messages */
On Mon, May 09, 2011 at 03:02:28PM +0200, Antonio Quartulli wrote:
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
Hi Antonio
Since this is part of sysfs you should add a description to Documentation/networking/batman-adv.txt.
Andrew
On mar, mag 10, 2011 at 07:08:03 +0200, Andrew Lunn wrote:
On Mon, May 09, 2011 at 03:02:28PM +0200, Antonio Quartulli wrote:
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
Hi Antonio
Since this is part of sysfs you should add a description to Documentation/networking/batman-adv.txt.
Ah ok, I didn't know this. I think Marek already planned to instruct me on this part. Thank you for reminding me!
On Tuesday, May 10, 2011 08:29:28 AM Antonio Quartulli wrote:
Since this is part of sysfs you should add a description to Documentation/networking/batman-adv.txt.
Ah ok, I didn't know this. I think Marek already planned to instruct me on this part. Thank you for reminding me!
I believe we still lack some documentation here or did I overlook something ?
Regards, Marek
On Sat, Jun 11, 2011 at 12:10:24PM +0200, Marek Lindner wrote:
On Tuesday, May 10, 2011 08:29:28 AM Antonio Quartulli wrote:
Since this is part of sysfs you should add a description to Documentation/networking/batman-adv.txt.
Ah ok, I didn't know this. I think Marek already planned to instruct me on this part. Thank you for reminding me!
I believe we still lack some documentation here or did I overlook something ?
Yes, we still lack documentation. I can send some text in the next days... :)
Thanks,
On Monday, May 09, 2011 03:02:28 PM Antonio Quartulli wrote:
Using throw_uevent() is now possible to trigger uevent signal that can be recognised in userspace. Uevents will be triggered through the /devices/virtual/net/{MESH_IFACE} kobject.
A triggered uevent has three properties:
- type: the event class. Who generates the event (only 'gw' is currently defined). Corresponds to the BATTYPE uevent variable.
- action: the associated action with the event ('add'/'change'/'del' are currently defined). Corresponds to the BAACTION uevent variable.
- data: any useful data for the userspace. Corresponds to the BATDATA uevent variable.
Applied in revision d2586d6.
Thanks, Marek
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Signed-off-by: Antonio Quartulli ordex@autistici.org --- a NULL check on "router" in gw_election() has been added
gateway_client.c | 33 +++++++++++++++++++++++++++------ main.c | 1 + types.h | 1 + 3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 65f3953..761dcfb 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" @@ -105,17 +106,18 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); }
void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; - struct neigh_node *router; + struct neigh_node *router = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; + char gw_addr[18] = { '\0' };
/** * The batman daemon checks here if we already passed a full originator @@ -127,7 +129,7 @@ void gw_election(struct bat_priv *bat_priv) return;
curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) goto out;
rcu_read_lock(); @@ -186,9 +188,12 @@ void gw_election(struct bat_priv *bat_priv) }
if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) + if (curr_gw_tmp) + router = orig_node_get_router(curr_gw_tmp->orig_node); + if (!router) { + gw_deselect(bat_priv); goto unlock; + }
if ((curr_gw) && (!curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, @@ -209,8 +214,24 @@ void gw_election(struct bat_priv *bat_priv) curr_gw_tmp->orig_node->gw_flags, router->tq_avg);
- neigh_node_free_ref(router); + if (router) + neigh_node_free_ref(router); gw_select(bat_priv, curr_gw_tmp); + rcu_read_unlock(); + + /* Throw the gateway event to the userspace */ + if (!curr_gw_tmp) { + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + goto out; + } + + sprintf(gw_addr, "%pM", curr_gw_tmp->orig_node->orig); + + if (curr_gw) + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + else + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + goto out; }
unlock: diff --git a/main.c b/main.c index 0a7cee0..ca060a9 100644 --- a/main.c +++ b/main.c @@ -111,6 +111,7 @@ int mesh_init(struct net_device *soft_iface) if (vis_init(bat_priv) < 1) goto err;
+ atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end;
diff --git a/types.h b/types.h index fab70e8..87b890e 100644 --- a/types.h +++ b/types.h @@ -173,6 +173,7 @@ struct bat_priv { struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; };
From: Antonio Quartulli ordex@autistici.org
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Signed-off-by: Antonio Quartulli ordex@autistici.org Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- * gw_election() code refactored to increase readability * bug fixed: if curr_gw_tmp is NULL the uevent would never be triggered because the variable "router" would also be NULL
gateway_client.c | 143 ++++++++++++++++++++++++++++++++--------------------- main.c | 1 + types.h | 1 + 3 files changed, 88 insertions(+), 57 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 24aee56..964a27e 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" @@ -97,40 +98,19 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); }
-void gw_election(struct bat_priv *bat_priv) +static struct gw_node *gw_get_best_gw_node(struct bat_priv *bat_priv) { - struct hlist_node *node; - struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; struct neigh_node *router; - uint8_t max_tq = 0; + struct hlist_node *node; + struct gw_node *gw_node, *curr_gw = NULL; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; + uint8_t max_tq = 0; int down, up;
- /** - * The batman daemon checks here if we already passed a full originator - * cycle in order to make sure we don't choose the first gateway we - * hear about. This check is based on the daemon's uptime which we - * don't have. - **/ - if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) - return; - - curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) - goto out; - rcu_read_lock(); - if (hlist_empty(&bat_priv->gw_list)) { - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - gw_deselect(bat_priv); - goto unlock; - } - hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { if (gw_node->deleted) continue; @@ -139,6 +119,9 @@ void gw_election(struct bat_priv *bat_priv) if (!router) continue;
+ if (!atomic_inc_not_zero(&gw_node->refcount)) + goto next; + switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, @@ -151,8 +134,12 @@ void gw_election(struct bat_priv *bat_priv)
if ((tmp_gw_factor > max_gw_factor) || ((tmp_gw_factor == max_gw_factor) && - (router->tq_avg > max_tq))) - curr_gw_tmp = gw_node; + (router->tq_avg > max_tq))) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break;
default: /** @@ -163,8 +150,12 @@ void gw_election(struct bat_priv *bat_priv) * soon as a better gateway appears which has * $routing_class more tq points) **/ - if (router->tq_avg > max_tq) - curr_gw_tmp = gw_node; + if (router->tq_avg > max_tq) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break; }
@@ -174,42 +165,80 @@ void gw_election(struct bat_priv *bat_priv) if (tmp_gw_factor > max_gw_factor) max_gw_factor = tmp_gw_factor;
+next: neigh_node_free_ref(router); + gw_node_free_ref(gw_node); } + rcu_read_unlock();
- if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) - goto unlock; + return curr_gw; +}
- if ((curr_gw) && (!curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - else if ((!curr_gw) && (curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Adding route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); - else - bat_dbg(DBG_BATMAN, bat_priv, - "Changing route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); +void gw_election(struct bat_priv *bat_priv) +{ + struct gw_node *curr_gw = NULL, *next_gw = NULL; + struct neigh_node *router = NULL; + char gw_addr[18] = { '\0' };
- neigh_node_free_ref(router); - gw_select(bat_priv, curr_gw_tmp); + /** + * The batman daemon checks here if we already passed a full originator + * cycle in order to make sure we don't choose the first gateway we + * hear about. This check is based on the daemon's uptime which we + * don't have. + **/ + if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) + goto out; + + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) + goto out; + + curr_gw = gw_get_selected_gw_node(bat_priv); + + next_gw = gw_get_best_gw_node(bat_priv); + + if (curr_gw == next_gw) + goto out; + + if (next_gw) { + sprintf(gw_addr, "%pM", next_gw->orig_node->orig); + + router = orig_node_get_router(next_gw->orig_node); + if (!router) { + gw_deselect(bat_priv); + goto out; + } }
-unlock: - rcu_read_unlock(); + if ((curr_gw) && (!next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Removing selected gateway - no gateway in range\n"); + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + } else if ((!curr_gw) && (next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + } else { + bat_dbg(DBG_BATMAN, bat_priv, + "Changing route to gateway %pM " + "(gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + } + + gw_select(bat_priv, next_gw); + out: if (curr_gw) gw_node_free_ref(curr_gw); + if (next_gw) + gw_node_free_ref(next_gw); + if (router) + neigh_node_free_ref(router); }
void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) diff --git a/main.c b/main.c index c2b06b7..e367e69 100644 --- a/main.c +++ b/main.c @@ -113,6 +113,7 @@ int mesh_init(struct net_device *soft_iface) if (vis_init(bat_priv) < 1) goto err;
+ atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end;
diff --git a/types.h b/types.h index 11e8569..85cf122 100644 --- a/types.h +++ b/types.h @@ -201,6 +201,7 @@ struct bat_priv { struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; };
From: Antonio Quartulli ordex@autistici.org
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Signed-off-by: Antonio Quartulli ordex@autistici.org Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- * gw_get_best_gw_node() should not call gw_node_free_ref() if atomic_inc_not_zero() failed
gateway_client.c | 148 ++++++++++++++++++++++++++++++++--------------------- main.c | 1 + types.h | 1 + 3 files changed, 91 insertions(+), 59 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 24aee56..64505ee 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -20,6 +20,7 @@ */
#include "main.h" +#include "bat_sysfs.h" #include "gateway_client.h" #include "gateway_common.h" #include "hard-interface.h" @@ -97,40 +98,19 @@ static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node)
void gw_deselect(struct bat_priv *bat_priv) { - gw_select(bat_priv, NULL); + atomic_set(&bat_priv->gw_reselect, 1); }
-void gw_election(struct bat_priv *bat_priv) +static struct gw_node *gw_get_best_gw_node(struct bat_priv *bat_priv) { - struct hlist_node *node; - struct gw_node *gw_node, *curr_gw = NULL, *curr_gw_tmp = NULL; struct neigh_node *router; - uint8_t max_tq = 0; + struct hlist_node *node; + struct gw_node *gw_node, *curr_gw = NULL; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; + uint8_t max_tq = 0; int down, up;
- /** - * The batman daemon checks here if we already passed a full originator - * cycle in order to make sure we don't choose the first gateway we - * hear about. This check is based on the daemon's uptime which we - * don't have. - **/ - if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) - return; - - curr_gw = gw_get_selected_gw_node(bat_priv); - if (curr_gw) - goto out; - rcu_read_lock(); - if (hlist_empty(&bat_priv->gw_list)) { - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - gw_deselect(bat_priv); - goto unlock; - } - hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { if (gw_node->deleted) continue; @@ -139,6 +119,9 @@ void gw_election(struct bat_priv *bat_priv) if (!router) continue;
+ if (!atomic_inc_not_zero(&gw_node->refcount)) + goto next; + switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, @@ -151,8 +134,12 @@ void gw_election(struct bat_priv *bat_priv)
if ((tmp_gw_factor > max_gw_factor) || ((tmp_gw_factor == max_gw_factor) && - (router->tq_avg > max_tq))) - curr_gw_tmp = gw_node; + (router->tq_avg > max_tq))) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break;
default: /** @@ -163,8 +150,12 @@ void gw_election(struct bat_priv *bat_priv) * soon as a better gateway appears which has * $routing_class more tq points) **/ - if (router->tq_avg > max_tq) - curr_gw_tmp = gw_node; + if (router->tq_avg > max_tq) { + if (curr_gw) + gw_node_free_ref(curr_gw); + curr_gw = gw_node; + atomic_inc(&curr_gw->refcount); + } break; }
@@ -174,42 +165,81 @@ void gw_election(struct bat_priv *bat_priv) if (tmp_gw_factor > max_gw_factor) max_gw_factor = tmp_gw_factor;
- neigh_node_free_ref(router); - } - - if (curr_gw != curr_gw_tmp) { - router = orig_node_get_router(curr_gw_tmp->orig_node); - if (!router) - goto unlock; - - if ((curr_gw) && (!curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Removing selected gateway - " - "no gateway in range\n"); - else if ((!curr_gw) && (curr_gw_tmp)) - bat_dbg(DBG_BATMAN, bat_priv, - "Adding route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); - else - bat_dbg(DBG_BATMAN, bat_priv, - "Changing route to gateway %pM " - "(gw_flags: %i, tq: %i)\n", - curr_gw_tmp->orig_node->orig, - curr_gw_tmp->orig_node->gw_flags, - router->tq_avg); + gw_node_free_ref(gw_node);
+next: neigh_node_free_ref(router); - gw_select(bat_priv, curr_gw_tmp); } - -unlock: rcu_read_unlock(); + + return curr_gw; +} + +void gw_election(struct bat_priv *bat_priv) +{ + struct gw_node *curr_gw = NULL, *next_gw = NULL; + struct neigh_node *router = NULL; + char gw_addr[18] = { '\0' }; + + /** + * The batman daemon checks here if we already passed a full originator + * cycle in order to make sure we don't choose the first gateway we + * hear about. This check is based on the daemon's uptime which we + * don't have. + **/ + if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) + goto out; + + if (!atomic_dec_not_zero(&bat_priv->gw_reselect)) + goto out; + + curr_gw = gw_get_selected_gw_node(bat_priv); + + next_gw = gw_get_best_gw_node(bat_priv); + + if (curr_gw == next_gw) + goto out; + + if (next_gw) { + sprintf(gw_addr, "%pM", next_gw->orig_node->orig); + + router = orig_node_get_router(next_gw->orig_node); + if (!router) { + gw_deselect(bat_priv); + goto out; + } + } + + if ((curr_gw) && (!next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Removing selected gateway - no gateway in range\n"); + throw_uevent(bat_priv, UEV_GW, UEV_DEL, NULL); + } else if ((!curr_gw) && (next_gw)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_ADD, gw_addr); + } else { + bat_dbg(DBG_BATMAN, bat_priv, + "Changing route to gateway %pM " + "(gw_flags: %i, tq: %i)\n", + next_gw->orig_node->orig, + next_gw->orig_node->gw_flags, + router->tq_avg); + throw_uevent(bat_priv, UEV_GW, UEV_CHANGE, gw_addr); + } + + gw_select(bat_priv, next_gw); + out: if (curr_gw) gw_node_free_ref(curr_gw); + if (next_gw) + gw_node_free_ref(next_gw); + if (router) + neigh_node_free_ref(router); }
void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) diff --git a/main.c b/main.c index c2b06b7..e367e69 100644 --- a/main.c +++ b/main.c @@ -113,6 +113,7 @@ int mesh_init(struct net_device *soft_iface) if (vis_init(bat_priv) < 1) goto err;
+ atomic_set(&bat_priv->gw_reselect, 0); atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); goto end;
diff --git a/types.h b/types.h index 11e8569..85cf122 100644 --- a/types.h +++ b/types.h @@ -201,6 +201,7 @@ struct bat_priv { struct delayed_work orig_work; struct delayed_work vis_work; struct gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t gw_reselect; struct hard_iface __rcu *primary_if; /* rcu protected pointer */ struct vis_info *my_vis_info; };
On Saturday, June 11, 2011 12:21:28 PM Marek Lindner wrote:
In case of new default gw, changing the default gw or deleting the default gw a uevent is triggered with type=gw, action=add/change/del and data={GW_ORIG_ADDRESS} (if any).
The gateway election mechanism has been a little revised. Now the gw_election is trigered by an atomic_t flag (gw_reselect) which is is set to 1 in case of election needed, avoding to set curr_gw to NULL.
Sven suggested splitting this patch, therefore I applied this in revision 6a17ecc and c792264.
Thanks, Marek
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org --- gateway_client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 761dcfb..33bceee 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; @@ -509,14 +515,75 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octect 1: option type + * - octect 2: option data len (only if type != 255 and 0) + * - octect 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octect: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octect and go to the data octect */ + if (pkt_len < 2) + goto out; + pkt_len -= 2; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -584,7 +651,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 4f9991d..fbd7d37 100644 --- a/main.h +++ b/main.h @@ -79,7 +79,6 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -90,6 +89,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 25 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index c76a33e..6d02d92 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -569,6 +570,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -603,8 +605,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* TODO: check this for locks */ tt_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -612,6 +616,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret == 0) do_bcast = true; } + if (orig_node) + orig_node_free_ref(orig_node);
/* ethernet packet should be broadcasted */ if (do_bcast) {
On Mon, May 09, 2011 at 03:02:30PM +0200, Antonio Quartulli wrote:
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org
gateway_client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 761dcfb..33bceee 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at
- the beginning of the dhcp header */
+#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3
static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; @@ -509,14 +515,75 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{
- int ret = false;
- unsigned char *p;
- int pkt_len;
- if (skb_linearize(skb) < 0)
goto out;
- pkt_len = skb_headlen(skb);
- if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1)
goto out;
- p = skb->data + header_len + DHCP_OPTIONS_OFFSET;
- pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1;
- /* Access the dhcp option lists. Each entry is made up by:
* - octect 1: option type
* - octect 2: option data len (only if type != 255 and 0)
* - octect 3: option data */
- while (*p != 255 && !ret) {
/* p now points to the first octect: option type */
if (*p == 53) {
/* type 53 is the message type option.
* Jump the len octect and go to the data octect */
if (pkt_len < 2)
goto out;
pkt_len -= 2;
p += 2;
/* check if the message type is what we need */
if (*p == DHCP_REQUEST)
ret = true;
Why do you continue parsing the options if it is not a DHCP_REQUEST? Do you think there will be a second, third, fourth option information element 53?
Andrew
Hi Andrew, hi everyone,
On lun, mag 09, 2011 at 04:48:12 +0200, Andrew Lunn wrote:
- while (*p != 255 && !ret) {
/* p now points to the first octect: option type */
if (*p == 53) {
/* type 53 is the message type option.
* Jump the len octect and go to the data octect */
if (pkt_len < 2)
goto out;
pkt_len -= 2;
p += 2;
/* check if the message type is what we need */
if (*p == DHCP_REQUEST)
ret = true;
Why do you continue parsing the options if it is not a DHCP_REQUEST? Do you think there will be a second, third, fourth option information element 53?
No. There is no reason to continue parsing the options. I'll modify the if so that I can exit the while loop as soon as I find the option 53.
Thanks for finding this detail!
Regards,
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org --- Corrected while loop in is_type_dhcprequest(): if we find option 53, we exit the loop without going through the rest of the option list.
gateway_client.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 105 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 761dcfb..616d43c 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; @@ -509,14 +515,76 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octect 1: option type + * - octect 2: option data len (only if type != 255 and 0) + * - octect 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octect: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octect and go to the data octect */ + if (pkt_len < 2) + goto out; + pkt_len -= 2; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + break; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -584,7 +652,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 4f9991d..5b7ad0a 100644 --- a/main.h +++ b/main.h @@ -79,7 +79,6 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -90,6 +89,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 50 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index c76a33e..6d02d92 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -569,6 +570,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -603,8 +605,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* TODO: check this for locks */ tt_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -612,6 +616,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret == 0) do_bcast = true; } + if (orig_node) + orig_node_free_ref(orig_node);
/* ethernet packet should be broadcasted */ if (do_bcast) {
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org --- Removed a useless decrement in is_type_dhcprequest()
gateway_client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 761dcfb..3b43781 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; @@ -509,14 +515,75 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octect 1: option type + * - octect 2: option data len (only if type != 255 and 0) + * - octect 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octect: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octect and go to the data octect */ + if (pkt_len < 2) + goto out; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + break; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -584,7 +651,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 4f9991d..5b7ad0a 100644 --- a/main.h +++ b/main.h @@ -79,7 +79,6 @@ #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -90,6 +89,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 50 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index c76a33e..6d02d92 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -569,6 +570,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -603,8 +605,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* TODO: check this for locks */ tt_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -612,6 +616,8 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret == 0) do_bcast = true; } + if (orig_node) + orig_node_free_ref(orig_node);
/* ethernet packet should be broadcasted */ if (do_bcast) {
From: Antonio Quartulli ordex@autistici.org
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Signed-off-by: Antonio Quartulli ordex@autistici.org Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- * if gw_is_target() returns a value smaller than zero in interface_tx() the code does not decrement orig_node's refcount
gateway_client.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- gateway_client.h | 3 +- main.h | 3 +- soft-interface.c | 10 ++++- 4 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/gateway_client.c b/gateway_client.c index 64505ee..d90463a 100644 --- a/gateway_client.c +++ b/gateway_client.c @@ -25,11 +25,17 @@ #include "gateway_common.h" #include "hard-interface.h" #include "originator.h" +#include "routing.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/udp.h> #include <linux/if_vlan.h>
+/* This is the offset of the options field in a dhcp packet starting at + * the beginning of the dhcp header */ +#define DHCP_OPTIONS_OFFSET 240 +#define DHCP_REQUEST 3 + static void gw_node_free_ref(struct gw_node *gw_node) { if (atomic_dec_and_test(&gw_node->refcount)) @@ -509,14 +515,75 @@ out: return ret; }
-int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +static bool is_type_dhcprequest(struct sk_buff *skb, int header_len) +{ + int ret = false; + unsigned char *p; + int pkt_len; + + if (skb_linearize(skb) < 0) + goto out; + + pkt_len = skb_headlen(skb); + + if (pkt_len < header_len + DHCP_OPTIONS_OFFSET + 1) + goto out; + + p = skb->data + header_len + DHCP_OPTIONS_OFFSET; + pkt_len -= header_len + DHCP_OPTIONS_OFFSET + 1; + + /* Access the dhcp option lists. Each entry is made up by: + * - octect 1: option type + * - octect 2: option data len (only if type != 255 and 0) + * - octect 3: option data */ + while (*p != 255 && !ret) { + /* p now points to the first octect: option type */ + if (*p == 53) { + /* type 53 is the message type option. + * Jump the len octect and go to the data octect */ + if (pkt_len < 2) + goto out; + p += 2; + + /* check if the message type is what we need */ + if (*p == DHCP_REQUEST) + ret = true; + break; + } else if (*p == 0) { + /* option type 0 (padding), just go forward */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + } else { + /* This is any other option. So we get the length... */ + if (pkt_len < 1) + goto out; + pkt_len--; + p++; + + /* ...and then we jump over the data */ + if (pkt_len < *p) + goto out; + pkt_len -= *p; + p += (*p); + } + } +out: + return ret; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw) { struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct gw_node *curr_gw; + struct neigh_node *neigh_curr = NULL, *neigh_old = NULL; unsigned int header_len = 0; + int ret = 1;
if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) return 0; @@ -584,7 +651,30 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (!curr_gw) return 0;
+ /* If old_gw != NULL then this packet is unicast. + * So, at this point we have to check the message type: if it is a + * DHCPREQUEST we have to decide whether to drop it or not */ + if (old_gw && curr_gw->orig_node != old_gw) { + if (is_type_dhcprequest(skb, header_len)) { + /* If the dhcp packet has been sent to a different gw, + * we have to evaluate whether the old gw is still + * reliable enough */ + neigh_curr = find_router(bat_priv, curr_gw->orig_node, + NULL); + neigh_old = find_router(bat_priv, old_gw, NULL); + if (!neigh_curr || !neigh_old) + goto free_neigh; + if (neigh_curr->tq_avg - neigh_old->tq_avg < + GW_THRESHOLD) + ret = -1; + } + } +free_neigh: + if (neigh_old) + neigh_node_free_ref(neigh_old); + if (neigh_curr) + neigh_node_free_ref(neigh_curr); if (curr_gw) gw_node_free_ref(curr_gw); - return 1; + return ret; } diff --git a/gateway_client.h b/gateway_client.h index 1ce8c60..b9b983c 100644 --- a/gateway_client.h +++ b/gateway_client.h @@ -31,6 +31,7 @@ void gw_node_update(struct bat_priv *bat_priv, void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); void gw_node_purge(struct bat_priv *bat_priv); int gw_client_seq_print_text(struct seq_file *seq, void *offset); -int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb, + struct orig_node *old_gw);
#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/main.h b/main.h index 7f3768e..059866c 100644 --- a/main.h +++ b/main.h @@ -91,7 +91,6 @@ enum mesh_state { #define BCAST_QUEUE_LEN 256 #define BATMAN_QUEUE_LEN 256
- enum uev_action { UEV_ADD = 0, UEV_DEL, @@ -102,6 +101,8 @@ enum uev_type { UEV_GW = 0 };
+#define GW_THRESHOLD 50 + /* * Debug Messages */ diff --git a/soft-interface.c b/soft-interface.c index b268f85..7aeed0d 100644 --- a/soft-interface.c +++ b/soft-interface.c @@ -30,6 +30,7 @@ #include "gateway_common.h" #include "gateway_client.h" #include "bat_sysfs.h" +#include "originator.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> @@ -561,6 +562,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; struct softif_neigh *curr_softif_neigh = NULL; + struct orig_node *orig_node = NULL; int data_len = skb->len, ret; short vid = -1; bool do_bcast = false; @@ -595,8 +597,10 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) /* Register the client MAC in the transtable */ tt_local_add(soft_iface, ethhdr->h_source);
- if (is_multicast_ether_addr(ethhdr->h_dest)) { - ret = gw_is_target(bat_priv, skb); + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + if (is_multicast_ether_addr(ethhdr->h_dest) || + (orig_node && orig_node->gw_flags)) { + ret = gw_is_target(bat_priv, skb, orig_node);
if (ret < 0) goto dropped; @@ -656,6 +660,8 @@ end: softif_neigh_free_ref(curr_softif_neigh); if (primary_if) hardif_free_ref(primary_if); + if (orig_node) + orig_node_free_ref(orig_node); return NETDEV_TX_OK; }
On Saturday, June 11, 2011 12:25:58 PM Marek Lindner wrote:
From: Antonio Quartulli ordex@autistici.org
If a client issues a DHCPREQUEST for renewal, the packet is dropped if the old destination (the old gateway for the client) TQ is smaller than the current best gateway TQ less GW_THRESHOLD
Applied in revision 17852fa.
Thanks, Marek
b.a.t.m.a.n@lists.open-mesh.org