/* These are the offsets of the "hw type" and "hw address length" in the dhcp * packet starting at the beginning of the dhcp header
*/ #define BATADV_DHCP_HTYPE_OFFSET 1 #define BATADV_DHCP_HLEN_OFFSET 2 /* Value of htype representing Ethernet */ #define BATADV_DHCP_HTYPE_ETHERNET 0x01 /* This is the offset of the "chaddr" field in the dhcp packet starting at the * beginning of the dhcp header
*/ #define BATADV_DHCP_CHADDR_OFFSET 28
/** * batadv_gw_node_release() - release gw_node from lists and queue for free * after rcu grace period * @ref: kref pointer of the gw_node
*/ void batadv_gw_node_release(struct kref *ref)
{ struct batadv_gw_node *gw_node;
/** * batadv_gw_get_selected_gw_node() - Get currently selected gateway * @bat_priv: the bat priv with all the mesh interface information * * Return: selected gateway (with increased refcnt), NULL on errors
*/ struct batadv_gw_node *
batadv_gw_get_selected_gw_node(struct batadv_priv *bat_priv)
{ struct batadv_gw_node *gw_node;
rcu_read_lock();
gw_node = rcu_dereference(bat_priv->gw.curr_gw); if (!gw_node) goto out;
if (!kref_get_unless_zero(&gw_node->refcount))
gw_node = NULL;
out:
rcu_read_unlock(); return gw_node;
}
/** * batadv_gw_get_selected_orig() - Get originator of currently selected gateway * @bat_priv: the bat priv with all the mesh interface information * * Return: orig_node of selected gateway (with increased refcnt), NULL on errors
*/ struct batadv_orig_node *
batadv_gw_get_selected_orig(struct batadv_priv *bat_priv)
{ struct batadv_gw_node *gw_node; struct batadv_orig_node *orig_node = NULL;
gw_node = batadv_gw_get_selected_gw_node(bat_priv); if (!gw_node) goto out;
rcu_read_lock();
orig_node = gw_node->orig_node; if (!orig_node) goto unlock;
if (!kref_get_unless_zero(&orig_node->refcount))
orig_node = NULL;
/** * batadv_gw_reselect() - force a gateway reselection * @bat_priv: the bat priv with all the mesh interface information * * Set a flag to remind the GW component to perform a new gateway reselection. * However this function does not ensure that the current gateway is going to be * deselected. The reselection mechanism may elect the same gateway once again. * * This means that invoking batadv_gw_reselect() does not guarantee a gateway * change and therefore a uevent is not necessarily expected.
*/ void batadv_gw_reselect(struct batadv_priv *bat_priv)
{
atomic_set(&bat_priv->gw.reselect, 1);
}
/** * batadv_gw_check_client_stop() - check if client mode has been switched off * @bat_priv: the bat priv with all the mesh interface information * * This function assumes the caller has checked that the gw state *is actually * changing*. This function is not supposed to be called when there is no state * change.
*/ void batadv_gw_check_client_stop(struct batadv_priv *bat_priv)
{ struct batadv_gw_node *curr_gw;
if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) return;
curr_gw = batadv_gw_get_selected_gw_node(bat_priv); if (!curr_gw) return;
/* deselect the current gateway so that next time that client mode is * enabled a proper GW_ADD event can be sent
*/
batadv_gw_select(bat_priv, NULL);
/* if batman-adv is switching the gw client mode off and a gateway was * already selected, send a DEL uevent
*/
batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_DEL, NULL);
batadv_gw_node_put(curr_gw);
}
/** * batadv_gw_election() - Elect the best gateway * @bat_priv: the bat priv with all the mesh interface information
*/ void batadv_gw_election(struct batadv_priv *bat_priv)
{ struct batadv_gw_node *curr_gw = NULL; struct batadv_gw_node *next_gw = NULL; struct batadv_neigh_node *router = NULL; struct batadv_neigh_ifinfo *router_ifinfo = NULL; char gw_addr[18] = { '\0' };
if (atomic_read(&bat_priv->gw.mode) != BATADV_GW_MODE_CLIENT) goto out;
if (!bat_priv->algo_ops->gw.get_best_gw_node) goto out;
if (!batadv_atomic_dec_not_zero(&bat_priv->gw.reselect) && curr_gw) goto out;
/* if gw.reselect is set to 1 it means that a previous call to * gw.is_eligible() said that we have a new best GW, therefore it can * now be picked from the list and selected
*/
next_gw = bat_priv->algo_ops->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);
/** * batadv_gw_check_election() - Elect orig node as best gateway when eligible * @bat_priv: the bat priv with all the mesh interface information * @orig_node: orig node which is to be checked
*/ void batadv_gw_check_election(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node)
{ struct batadv_orig_node *curr_gw_orig;
/* abort immediately if the routing algorithm does not support gateway * election
*/ if (!bat_priv->algo_ops->gw.is_eligible) return;
curr_gw_orig = batadv_gw_get_selected_orig(bat_priv); if (!curr_gw_orig) goto reselect;
/* this node already is the gateway */ if (curr_gw_orig == orig_node) goto out;
if (!bat_priv->algo_ops->gw.is_eligible(bat_priv, curr_gw_orig,
orig_node)) goto out;
/** * batadv_gw_node_add() - add gateway node to list of available gateways * @bat_priv: the bat priv with all the mesh interface information * @orig_node: originator announcing gateway capabilities * @gateway: announced bandwidth information * * Has to be called with the appropriate locks being acquired * (gw.list_lock).
*/ staticvoid batadv_gw_node_add(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, struct batadv_tvlv_gateway_data *gateway)
{ struct batadv_gw_node *gw_node;
lockdep_assert_held(&bat_priv->gw.list_lock);
if (gateway->bandwidth_down == 0) return;
gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); if (!gw_node) return;
/* don't return reference to new gw_node */
batadv_gw_node_put(gw_node);
}
/** * batadv_gw_node_get() - retrieve gateway node from list of available gateways * @bat_priv: the bat priv with all the mesh interface information * @orig_node: originator announcing gateway capabilities * * Return: gateway node if found or NULL otherwise.
*/ struct batadv_gw_node *batadv_gw_node_get(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node)
{ struct batadv_gw_node *gw_node_tmp, *gw_node = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.gateway_list,
list) { if (gw_node_tmp->orig_node != orig_node) continue;
if (!kref_get_unless_zero(&gw_node_tmp->refcount)) continue;
/** * batadv_gw_node_update() - update list of available gateways with changed * bandwidth information * @bat_priv: the bat priv with all the mesh interface information * @orig_node: originator announcing gateway capabilities * @gateway: announced bandwidth information
*/ void batadv_gw_node_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, struct batadv_tvlv_gateway_data *gateway)
{ struct batadv_gw_node *gw_node, *curr_gw = NULL;
if (ntohl(gateway->bandwidth_down) == 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Gateway %pM removed from gateway list\n",
orig_node->orig);
/* Note: We don't need a NULL check here, since curr_gw never * gets dereferenced.
*/
spin_lock_bh(&bat_priv->gw.list_lock); if (!hlist_unhashed(&gw_node->list)) {
hlist_del_init_rcu(&gw_node->list);
batadv_gw_node_put(gw_node);
bat_priv->gw.generation++;
}
spin_unlock_bh(&bat_priv->gw.list_lock);
curr_gw = batadv_gw_get_selected_gw_node(bat_priv); if (gw_node == curr_gw)
batadv_gw_reselect(bat_priv);
batadv_gw_node_put(curr_gw);
}
out:
batadv_gw_node_put(gw_node);
}
/** * batadv_gw_node_delete() - Remove orig_node from gateway list * @bat_priv: the bat priv with all the mesh interface information * @orig_node: orig node which is currently in process of being removed
*/ void batadv_gw_node_delete(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node)
{ struct batadv_tvlv_gateway_data gateway;
/** * batadv_gw_node_free() - Free gateway information from mesh interface * @bat_priv: the bat priv with all the mesh interface information
*/ void batadv_gw_node_free(struct batadv_priv *bat_priv)
{ struct batadv_gw_node *gw_node; struct hlist_node *node_tmp;
/** * batadv_gw_dhcp_recipient_get() - check if a packet is a DHCP message * @skb: the packet to check * @header_len: a pointer to the batman-adv header size * @chaddr: buffer where the client address will be stored. Valid * only if the function returns BATADV_DHCP_TO_CLIENT * * This function may re-allocate the data buffer of the skb passed as argument. * * Return: * - BATADV_DHCP_NO if the packet is not a dhcp message or if there was an error * while parsing it * - BATADV_DHCP_TO_SERVER if this is a message going to the DHCP server * - BATADV_DHCP_TO_CLIENT if this is a message going to a DHCP client
*/ enum batadv_dhcp_recipient
batadv_gw_dhcp_recipient_get(struct sk_buff *skb, unsignedint *header_len,
u8 *chaddr)
{ enum batadv_dhcp_recipient ret = BATADV_DHCP_NO; struct ethhdr *ethhdr; struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; struct vlan_ethhdr *vhdr; int chaddr_offset;
__be16 proto;
u8 *p;
/* check for ethernet header */ if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) return BATADV_DHCP_NO;
ethhdr = eth_hdr(skb);
proto = ethhdr->h_proto;
*header_len += ETH_HLEN;
/* check for initial vlan header */ if (proto == htons(ETH_P_8021Q)) { if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) return BATADV_DHCP_NO;
vhdr = vlan_eth_hdr(skb);
proto = vhdr->h_vlan_encapsulated_proto;
*header_len += VLAN_HLEN;
}
/* check for ip header */ switch (proto) { case htons(ETH_P_IP): if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) return BATADV_DHCP_NO;
/* check for bootp port */ switch (proto) { case htons(ETH_P_IP): if (udphdr->dest == htons(67))
ret = BATADV_DHCP_TO_SERVER; elseif (udphdr->source == htons(67))
ret = BATADV_DHCP_TO_CLIENT; break; case htons(ETH_P_IPV6): if (udphdr->dest == htons(547))
ret = BATADV_DHCP_TO_SERVER; elseif (udphdr->source == htons(547))
ret = BATADV_DHCP_TO_CLIENT; break;
}
chaddr_offset = *header_len + BATADV_DHCP_CHADDR_OFFSET; /* store the client address if the message is going to a client */ if (ret == BATADV_DHCP_TO_CLIENT) { if (!pskb_may_pull(skb, chaddr_offset + ETH_ALEN)) return BATADV_DHCP_NO;
/* check if the DHCP packet carries an Ethernet DHCP */
p = skb->data + *header_len + BATADV_DHCP_HTYPE_OFFSET; if (*p != BATADV_DHCP_HTYPE_ETHERNET) return BATADV_DHCP_NO;
/* check if the DHCP packet carries a valid Ethernet address */
p = skb->data + *header_len + BATADV_DHCP_HLEN_OFFSET; if (*p != ETH_ALEN) return BATADV_DHCP_NO;
/** * batadv_gw_out_of_range() - check if the dhcp request destination is the best * gateway * @bat_priv: the bat priv with all the mesh interface information * @skb: the outgoing packet * * Check if the skb is a DHCP request and if it is sent to the current best GW * server. Due to topology changes it may be the case that the GW server * previously selected is not the best one anymore. * * This call might reallocate skb data. * Must be invoked only when the DHCP packet is going TO a DHCP SERVER. * * Return: true if the packet destination is unicast and it is not the best gw, * false otherwise.
*/ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb)
{ struct batadv_neigh_node *neigh_curr = NULL; struct batadv_neigh_node *neigh_old = NULL; struct batadv_orig_node *orig_dst_node = NULL; struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; struct ethhdr *ethhdr = (struct ethhdr *)skb->data; bool out_of_range = false;
u8 curr_tq_avg; unsignedshort vid;
vid = batadv_get_vid(skb, 0);
if (is_multicast_ether_addr(ethhdr->h_dest)) goto out;
orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source,
ethhdr->h_dest, vid); if (!orig_dst_node) goto out;
gw_node = batadv_gw_node_get(bat_priv, orig_dst_node); if (!gw_node) goto out;
switch (atomic_read(&bat_priv->gw.mode)) { case BATADV_GW_MODE_SERVER: /* If we are a GW then we are our best GW. We can artificially * set the tq towards ourself as the maximum value
*/
curr_tq_avg = BATADV_TQ_MAX_VALUE; break; case BATADV_GW_MODE_CLIENT:
curr_gw = batadv_gw_get_selected_gw_node(bat_priv); if (!curr_gw) goto out;
/* packet is going to our gateway */ if (curr_gw->orig_node == orig_dst_node) goto out;
/* 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 = batadv_find_router(bat_priv, curr_gw->orig_node,
NULL); if (!neigh_curr) goto out;
curr_ifinfo = batadv_neigh_ifinfo_get(neigh_curr,
BATADV_IF_DEFAULT); if (!curr_ifinfo) goto out;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.