Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (C) B.A.T.M.A.N. contributors:
0003  *
0004  * Linus Lüssing, Marek Lindner
0005  */
0006 
0007 #include "bat_v.h"
0008 #include "main.h"
0009 
0010 #include <linux/atomic.h>
0011 #include <linux/cache.h>
0012 #include <linux/errno.h>
0013 #include <linux/if_ether.h>
0014 #include <linux/init.h>
0015 #include <linux/jiffies.h>
0016 #include <linux/kref.h>
0017 #include <linux/list.h>
0018 #include <linux/minmax.h>
0019 #include <linux/netdevice.h>
0020 #include <linux/netlink.h>
0021 #include <linux/rculist.h>
0022 #include <linux/rcupdate.h>
0023 #include <linux/skbuff.h>
0024 #include <linux/spinlock.h>
0025 #include <linux/stddef.h>
0026 #include <linux/types.h>
0027 #include <linux/workqueue.h>
0028 #include <net/genetlink.h>
0029 #include <net/netlink.h>
0030 #include <uapi/linux/batadv_packet.h>
0031 #include <uapi/linux/batman_adv.h>
0032 
0033 #include "bat_algo.h"
0034 #include "bat_v_elp.h"
0035 #include "bat_v_ogm.h"
0036 #include "gateway_client.h"
0037 #include "gateway_common.h"
0038 #include "hard-interface.h"
0039 #include "hash.h"
0040 #include "log.h"
0041 #include "netlink.h"
0042 #include "originator.h"
0043 
0044 static void batadv_v_iface_activate(struct batadv_hard_iface *hard_iface)
0045 {
0046     struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
0047     struct batadv_hard_iface *primary_if;
0048 
0049     primary_if = batadv_primary_if_get_selected(bat_priv);
0050 
0051     if (primary_if) {
0052         batadv_v_elp_iface_activate(primary_if, hard_iface);
0053         batadv_hardif_put(primary_if);
0054     }
0055 
0056     /* B.A.T.M.A.N. V does not use any queuing mechanism, therefore it can
0057      * set the interface as ACTIVE right away, without any risk of race
0058      * condition
0059      */
0060     if (hard_iface->if_status == BATADV_IF_TO_BE_ACTIVATED)
0061         hard_iface->if_status = BATADV_IF_ACTIVE;
0062 }
0063 
0064 static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface)
0065 {
0066     int ret;
0067 
0068     ret = batadv_v_elp_iface_enable(hard_iface);
0069     if (ret < 0)
0070         return ret;
0071 
0072     ret = batadv_v_ogm_iface_enable(hard_iface);
0073     if (ret < 0)
0074         batadv_v_elp_iface_disable(hard_iface);
0075 
0076     return ret;
0077 }
0078 
0079 static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface)
0080 {
0081     batadv_v_ogm_iface_disable(hard_iface);
0082     batadv_v_elp_iface_disable(hard_iface);
0083 }
0084 
0085 static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface)
0086 {
0087     batadv_v_elp_primary_iface_set(hard_iface);
0088     batadv_v_ogm_primary_iface_set(hard_iface);
0089 }
0090 
0091 /**
0092  * batadv_v_iface_update_mac() - react to hard-interface MAC address change
0093  * @hard_iface: the modified interface
0094  *
0095  * If the modified interface is the primary one, update the originator
0096  * address in the ELP and OGM messages to reflect the new MAC address.
0097  */
0098 static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface)
0099 {
0100     struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
0101     struct batadv_hard_iface *primary_if;
0102 
0103     primary_if = batadv_primary_if_get_selected(bat_priv);
0104     if (primary_if != hard_iface)
0105         goto out;
0106 
0107     batadv_v_primary_iface_set(hard_iface);
0108 out:
0109     batadv_hardif_put(primary_if);
0110 }
0111 
0112 static void
0113 batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
0114 {
0115     ewma_throughput_init(&hardif_neigh->bat_v.throughput);
0116     INIT_WORK(&hardif_neigh->bat_v.metric_work,
0117           batadv_v_elp_throughput_metric_update);
0118 }
0119 
0120 /**
0121  * batadv_v_neigh_dump_neigh() - Dump a neighbour into a message
0122  * @msg: Netlink message to dump into
0123  * @portid: Port making netlink request
0124  * @seq: Sequence number of netlink message
0125  * @hardif_neigh: Neighbour to dump
0126  *
0127  * Return: Error code, or 0 on success
0128  */
0129 static int
0130 batadv_v_neigh_dump_neigh(struct sk_buff *msg, u32 portid, u32 seq,
0131               struct batadv_hardif_neigh_node *hardif_neigh)
0132 {
0133     void *hdr;
0134     unsigned int last_seen_msecs;
0135     u32 throughput;
0136 
0137     last_seen_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen);
0138     throughput = ewma_throughput_read(&hardif_neigh->bat_v.throughput);
0139     throughput = throughput * 100;
0140 
0141     hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
0142               BATADV_CMD_GET_NEIGHBORS);
0143     if (!hdr)
0144         return -ENOBUFS;
0145 
0146     if (nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
0147             hardif_neigh->addr) ||
0148         nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
0149                hardif_neigh->if_incoming->net_dev->name) ||
0150         nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
0151             hardif_neigh->if_incoming->net_dev->ifindex) ||
0152         nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
0153             last_seen_msecs) ||
0154         nla_put_u32(msg, BATADV_ATTR_THROUGHPUT, throughput))
0155         goto nla_put_failure;
0156 
0157     genlmsg_end(msg, hdr);
0158     return 0;
0159 
0160  nla_put_failure:
0161     genlmsg_cancel(msg, hdr);
0162     return -EMSGSIZE;
0163 }
0164 
0165 /**
0166  * batadv_v_neigh_dump_hardif() - Dump the  neighbours of a hard interface into
0167  *  a message
0168  * @msg: Netlink message to dump into
0169  * @portid: Port making netlink request
0170  * @seq: Sequence number of netlink message
0171  * @bat_priv: The bat priv with all the soft interface information
0172  * @hard_iface: The hard interface to be dumped
0173  * @idx_s: Entries to be skipped
0174  *
0175  * This function assumes the caller holds rcu_read_lock().
0176  *
0177  * Return: Error code, or 0 on success
0178  */
0179 static int
0180 batadv_v_neigh_dump_hardif(struct sk_buff *msg, u32 portid, u32 seq,
0181                struct batadv_priv *bat_priv,
0182                struct batadv_hard_iface *hard_iface,
0183                int *idx_s)
0184 {
0185     struct batadv_hardif_neigh_node *hardif_neigh;
0186     int idx = 0;
0187 
0188     hlist_for_each_entry_rcu(hardif_neigh,
0189                  &hard_iface->neigh_list, list) {
0190         if (idx++ < *idx_s)
0191             continue;
0192 
0193         if (batadv_v_neigh_dump_neigh(msg, portid, seq, hardif_neigh)) {
0194             *idx_s = idx - 1;
0195             return -EMSGSIZE;
0196         }
0197     }
0198 
0199     *idx_s = 0;
0200     return 0;
0201 }
0202 
0203 /**
0204  * batadv_v_neigh_dump() - Dump the neighbours of a hard interface  into a
0205  *  message
0206  * @msg: Netlink message to dump into
0207  * @cb: Control block containing additional options
0208  * @bat_priv: The bat priv with all the soft interface information
0209  * @single_hardif: Limit dumping to this hard interface
0210  */
0211 static void
0212 batadv_v_neigh_dump(struct sk_buff *msg, struct netlink_callback *cb,
0213             struct batadv_priv *bat_priv,
0214             struct batadv_hard_iface *single_hardif)
0215 {
0216     struct batadv_hard_iface *hard_iface;
0217     int i_hardif = 0;
0218     int i_hardif_s = cb->args[0];
0219     int idx = cb->args[1];
0220     int portid = NETLINK_CB(cb->skb).portid;
0221 
0222     rcu_read_lock();
0223     if (single_hardif) {
0224         if (i_hardif_s == 0) {
0225             if (batadv_v_neigh_dump_hardif(msg, portid,
0226                                cb->nlh->nlmsg_seq,
0227                                bat_priv, single_hardif,
0228                                &idx) == 0)
0229                 i_hardif++;
0230         }
0231     } else {
0232         list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
0233             if (hard_iface->soft_iface != bat_priv->soft_iface)
0234                 continue;
0235 
0236             if (i_hardif++ < i_hardif_s)
0237                 continue;
0238 
0239             if (batadv_v_neigh_dump_hardif(msg, portid,
0240                                cb->nlh->nlmsg_seq,
0241                                bat_priv, hard_iface,
0242                                &idx)) {
0243                 i_hardif--;
0244                 break;
0245             }
0246         }
0247     }
0248     rcu_read_unlock();
0249 
0250     cb->args[0] = i_hardif;
0251     cb->args[1] = idx;
0252 }
0253 
0254 /**
0255  * batadv_v_orig_dump_subentry() - Dump an originator subentry into a message
0256  * @msg: Netlink message to dump into
0257  * @portid: Port making netlink request
0258  * @seq: Sequence number of netlink message
0259  * @bat_priv: The bat priv with all the soft interface information
0260  * @if_outgoing: Limit dump to entries with this outgoing interface
0261  * @orig_node: Originator to dump
0262  * @neigh_node: Single hops neighbour
0263  * @best: Is the best originator
0264  *
0265  * Return: Error code, or 0 on success
0266  */
0267 static int
0268 batadv_v_orig_dump_subentry(struct sk_buff *msg, u32 portid, u32 seq,
0269                 struct batadv_priv *bat_priv,
0270                 struct batadv_hard_iface *if_outgoing,
0271                 struct batadv_orig_node *orig_node,
0272                 struct batadv_neigh_node *neigh_node,
0273                 bool best)
0274 {
0275     struct batadv_neigh_ifinfo *n_ifinfo;
0276     unsigned int last_seen_msecs;
0277     u32 throughput;
0278     void *hdr;
0279 
0280     n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing);
0281     if (!n_ifinfo)
0282         return 0;
0283 
0284     throughput = n_ifinfo->bat_v.throughput * 100;
0285 
0286     batadv_neigh_ifinfo_put(n_ifinfo);
0287 
0288     last_seen_msecs = jiffies_to_msecs(jiffies - orig_node->last_seen);
0289 
0290     if (if_outgoing != BATADV_IF_DEFAULT &&
0291         if_outgoing != neigh_node->if_incoming)
0292         return 0;
0293 
0294     hdr = genlmsg_put(msg, portid, seq, &batadv_netlink_family, NLM_F_MULTI,
0295               BATADV_CMD_GET_ORIGINATORS);
0296     if (!hdr)
0297         return -ENOBUFS;
0298 
0299     if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN, orig_node->orig) ||
0300         nla_put(msg, BATADV_ATTR_NEIGH_ADDRESS, ETH_ALEN,
0301             neigh_node->addr) ||
0302         nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
0303                neigh_node->if_incoming->net_dev->name) ||
0304         nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
0305             neigh_node->if_incoming->net_dev->ifindex) ||
0306         nla_put_u32(msg, BATADV_ATTR_THROUGHPUT, throughput) ||
0307         nla_put_u32(msg, BATADV_ATTR_LAST_SEEN_MSECS,
0308             last_seen_msecs))
0309         goto nla_put_failure;
0310 
0311     if (best && nla_put_flag(msg, BATADV_ATTR_FLAG_BEST))
0312         goto nla_put_failure;
0313 
0314     genlmsg_end(msg, hdr);
0315     return 0;
0316 
0317  nla_put_failure:
0318     genlmsg_cancel(msg, hdr);
0319     return -EMSGSIZE;
0320 }
0321 
0322 /**
0323  * batadv_v_orig_dump_entry() - Dump an originator entry into a message
0324  * @msg: Netlink message to dump into
0325  * @portid: Port making netlink request
0326  * @seq: Sequence number of netlink message
0327  * @bat_priv: The bat priv with all the soft interface information
0328  * @if_outgoing: Limit dump to entries with this outgoing interface
0329  * @orig_node: Originator to dump
0330  * @sub_s: Number of sub entries to skip
0331  *
0332  * This function assumes the caller holds rcu_read_lock().
0333  *
0334  * Return: Error code, or 0 on success
0335  */
0336 static int
0337 batadv_v_orig_dump_entry(struct sk_buff *msg, u32 portid, u32 seq,
0338              struct batadv_priv *bat_priv,
0339              struct batadv_hard_iface *if_outgoing,
0340              struct batadv_orig_node *orig_node, int *sub_s)
0341 {
0342     struct batadv_neigh_node *neigh_node_best;
0343     struct batadv_neigh_node *neigh_node;
0344     int sub = 0;
0345     bool best;
0346 
0347     neigh_node_best = batadv_orig_router_get(orig_node, if_outgoing);
0348     if (!neigh_node_best)
0349         goto out;
0350 
0351     hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) {
0352         if (sub++ < *sub_s)
0353             continue;
0354 
0355         best = (neigh_node == neigh_node_best);
0356 
0357         if (batadv_v_orig_dump_subentry(msg, portid, seq, bat_priv,
0358                         if_outgoing, orig_node,
0359                         neigh_node, best)) {
0360             batadv_neigh_node_put(neigh_node_best);
0361 
0362             *sub_s = sub - 1;
0363             return -EMSGSIZE;
0364         }
0365     }
0366 
0367  out:
0368     batadv_neigh_node_put(neigh_node_best);
0369 
0370     *sub_s = 0;
0371     return 0;
0372 }
0373 
0374 /**
0375  * batadv_v_orig_dump_bucket() - Dump an originator bucket into a message
0376  * @msg: Netlink message to dump into
0377  * @portid: Port making netlink request
0378  * @seq: Sequence number of netlink message
0379  * @bat_priv: The bat priv with all the soft interface information
0380  * @if_outgoing: Limit dump to entries with this outgoing interface
0381  * @head: Bucket to be dumped
0382  * @idx_s: Number of entries to be skipped
0383  * @sub: Number of sub entries to be skipped
0384  *
0385  * Return: Error code, or 0 on success
0386  */
0387 static int
0388 batadv_v_orig_dump_bucket(struct sk_buff *msg, u32 portid, u32 seq,
0389               struct batadv_priv *bat_priv,
0390               struct batadv_hard_iface *if_outgoing,
0391               struct hlist_head *head, int *idx_s, int *sub)
0392 {
0393     struct batadv_orig_node *orig_node;
0394     int idx = 0;
0395 
0396     rcu_read_lock();
0397     hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
0398         if (idx++ < *idx_s)
0399             continue;
0400 
0401         if (batadv_v_orig_dump_entry(msg, portid, seq, bat_priv,
0402                          if_outgoing, orig_node, sub)) {
0403             rcu_read_unlock();
0404             *idx_s = idx - 1;
0405             return -EMSGSIZE;
0406         }
0407     }
0408     rcu_read_unlock();
0409 
0410     *idx_s = 0;
0411     *sub = 0;
0412     return 0;
0413 }
0414 
0415 /**
0416  * batadv_v_orig_dump() - Dump the originators into a message
0417  * @msg: Netlink message to dump into
0418  * @cb: Control block containing additional options
0419  * @bat_priv: The bat priv with all the soft interface information
0420  * @if_outgoing: Limit dump to entries with this outgoing interface
0421  */
0422 static void
0423 batadv_v_orig_dump(struct sk_buff *msg, struct netlink_callback *cb,
0424            struct batadv_priv *bat_priv,
0425            struct batadv_hard_iface *if_outgoing)
0426 {
0427     struct batadv_hashtable *hash = bat_priv->orig_hash;
0428     struct hlist_head *head;
0429     int bucket = cb->args[0];
0430     int idx = cb->args[1];
0431     int sub = cb->args[2];
0432     int portid = NETLINK_CB(cb->skb).portid;
0433 
0434     while (bucket < hash->size) {
0435         head = &hash->table[bucket];
0436 
0437         if (batadv_v_orig_dump_bucket(msg, portid,
0438                           cb->nlh->nlmsg_seq,
0439                           bat_priv, if_outgoing, head, &idx,
0440                           &sub))
0441             break;
0442 
0443         bucket++;
0444     }
0445 
0446     cb->args[0] = bucket;
0447     cb->args[1] = idx;
0448     cb->args[2] = sub;
0449 }
0450 
0451 static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1,
0452                   struct batadv_hard_iface *if_outgoing1,
0453                   struct batadv_neigh_node *neigh2,
0454                   struct batadv_hard_iface *if_outgoing2)
0455 {
0456     struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
0457     int ret = 0;
0458 
0459     ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
0460     if (!ifinfo1)
0461         goto err_ifinfo1;
0462 
0463     ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
0464     if (!ifinfo2)
0465         goto err_ifinfo2;
0466 
0467     ret = ifinfo1->bat_v.throughput - ifinfo2->bat_v.throughput;
0468 
0469     batadv_neigh_ifinfo_put(ifinfo2);
0470 err_ifinfo2:
0471     batadv_neigh_ifinfo_put(ifinfo1);
0472 err_ifinfo1:
0473     return ret;
0474 }
0475 
0476 static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
0477                   struct batadv_hard_iface *if_outgoing1,
0478                   struct batadv_neigh_node *neigh2,
0479                   struct batadv_hard_iface *if_outgoing2)
0480 {
0481     struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2;
0482     u32 threshold;
0483     bool ret = false;
0484 
0485     ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
0486     if (!ifinfo1)
0487         goto err_ifinfo1;
0488 
0489     ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
0490     if (!ifinfo2)
0491         goto err_ifinfo2;
0492 
0493     threshold = ifinfo1->bat_v.throughput / 4;
0494     threshold = ifinfo1->bat_v.throughput - threshold;
0495 
0496     ret = ifinfo2->bat_v.throughput > threshold;
0497 
0498     batadv_neigh_ifinfo_put(ifinfo2);
0499 err_ifinfo2:
0500     batadv_neigh_ifinfo_put(ifinfo1);
0501 err_ifinfo1:
0502     return ret;
0503 }
0504 
0505 /**
0506  * batadv_v_init_sel_class() - initialize GW selection class
0507  * @bat_priv: the bat priv with all the soft interface information
0508  */
0509 static void batadv_v_init_sel_class(struct batadv_priv *bat_priv)
0510 {
0511     /* set default throughput difference threshold to 5Mbps */
0512     atomic_set(&bat_priv->gw.sel_class, 50);
0513 }
0514 
0515 static ssize_t batadv_v_store_sel_class(struct batadv_priv *bat_priv,
0516                     char *buff, size_t count)
0517 {
0518     u32 old_class, class;
0519 
0520     if (!batadv_parse_throughput(bat_priv->soft_iface, buff,
0521                      "B.A.T.M.A.N. V GW selection class",
0522                      &class))
0523         return -EINVAL;
0524 
0525     old_class = atomic_read(&bat_priv->gw.sel_class);
0526     atomic_set(&bat_priv->gw.sel_class, class);
0527 
0528     if (old_class != class)
0529         batadv_gw_reselect(bat_priv);
0530 
0531     return count;
0532 }
0533 
0534 /**
0535  * batadv_v_gw_throughput_get() - retrieve the GW-bandwidth for a given GW
0536  * @gw_node: the GW to retrieve the metric for
0537  * @bw: the pointer where the metric will be stored. The metric is computed as
0538  *  the minimum between the GW advertised throughput and the path throughput to
0539  *  it in the mesh
0540  *
0541  * Return: 0 on success, -1 on failure
0542  */
0543 static int batadv_v_gw_throughput_get(struct batadv_gw_node *gw_node, u32 *bw)
0544 {
0545     struct batadv_neigh_ifinfo *router_ifinfo = NULL;
0546     struct batadv_orig_node *orig_node;
0547     struct batadv_neigh_node *router;
0548     int ret = -1;
0549 
0550     orig_node = gw_node->orig_node;
0551     router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
0552     if (!router)
0553         goto out;
0554 
0555     router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
0556     if (!router_ifinfo)
0557         goto out;
0558 
0559     /* the GW metric is computed as the minimum between the path throughput
0560      * to reach the GW itself and the advertised bandwidth.
0561      * This gives us an approximation of the effective throughput that the
0562      * client can expect via this particular GW node
0563      */
0564     *bw = router_ifinfo->bat_v.throughput;
0565     *bw = min_t(u32, *bw, gw_node->bandwidth_down);
0566 
0567     ret = 0;
0568 out:
0569     batadv_neigh_node_put(router);
0570     batadv_neigh_ifinfo_put(router_ifinfo);
0571 
0572     return ret;
0573 }
0574 
0575 /**
0576  * batadv_v_gw_get_best_gw_node() - retrieve the best GW node
0577  * @bat_priv: the bat priv with all the soft interface information
0578  *
0579  * Return: the GW node having the best GW-metric, NULL if no GW is known
0580  */
0581 static struct batadv_gw_node *
0582 batadv_v_gw_get_best_gw_node(struct batadv_priv *bat_priv)
0583 {
0584     struct batadv_gw_node *gw_node, *curr_gw = NULL;
0585     u32 max_bw = 0, bw;
0586 
0587     rcu_read_lock();
0588     hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.gateway_list, list) {
0589         if (!kref_get_unless_zero(&gw_node->refcount))
0590             continue;
0591 
0592         if (batadv_v_gw_throughput_get(gw_node, &bw) < 0)
0593             goto next;
0594 
0595         if (curr_gw && bw <= max_bw)
0596             goto next;
0597 
0598         batadv_gw_node_put(curr_gw);
0599 
0600         curr_gw = gw_node;
0601         kref_get(&curr_gw->refcount);
0602         max_bw = bw;
0603 
0604 next:
0605         batadv_gw_node_put(gw_node);
0606     }
0607     rcu_read_unlock();
0608 
0609     return curr_gw;
0610 }
0611 
0612 /**
0613  * batadv_v_gw_is_eligible() - check if a originator would be selected as GW
0614  * @bat_priv: the bat priv with all the soft interface information
0615  * @curr_gw_orig: originator representing the currently selected GW
0616  * @orig_node: the originator representing the new candidate
0617  *
0618  * Return: true if orig_node can be selected as current GW, false otherwise
0619  */
0620 static bool batadv_v_gw_is_eligible(struct batadv_priv *bat_priv,
0621                     struct batadv_orig_node *curr_gw_orig,
0622                     struct batadv_orig_node *orig_node)
0623 {
0624     struct batadv_gw_node *curr_gw, *orig_gw = NULL;
0625     u32 gw_throughput, orig_throughput, threshold;
0626     bool ret = false;
0627 
0628     threshold = atomic_read(&bat_priv->gw.sel_class);
0629 
0630     curr_gw = batadv_gw_node_get(bat_priv, curr_gw_orig);
0631     if (!curr_gw) {
0632         ret = true;
0633         goto out;
0634     }
0635 
0636     if (batadv_v_gw_throughput_get(curr_gw, &gw_throughput) < 0) {
0637         ret = true;
0638         goto out;
0639     }
0640 
0641     orig_gw = batadv_gw_node_get(bat_priv, orig_node);
0642     if (!orig_gw)
0643         goto out;
0644 
0645     if (batadv_v_gw_throughput_get(orig_gw, &orig_throughput) < 0)
0646         goto out;
0647 
0648     if (orig_throughput < gw_throughput)
0649         goto out;
0650 
0651     if ((orig_throughput - gw_throughput) < threshold)
0652         goto out;
0653 
0654     batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
0655            "Restarting gateway selection: better gateway found (throughput curr: %u, throughput new: %u)\n",
0656            gw_throughput, orig_throughput);
0657 
0658     ret = true;
0659 out:
0660     batadv_gw_node_put(curr_gw);
0661     batadv_gw_node_put(orig_gw);
0662 
0663     return ret;
0664 }
0665 
0666 /**
0667  * batadv_v_gw_dump_entry() - Dump a gateway into a message
0668  * @msg: Netlink message to dump into
0669  * @portid: Port making netlink request
0670  * @cb: Control block containing additional options
0671  * @bat_priv: The bat priv with all the soft interface information
0672  * @gw_node: Gateway to be dumped
0673  *
0674  * Return: Error code, or 0 on success
0675  */
0676 static int batadv_v_gw_dump_entry(struct sk_buff *msg, u32 portid,
0677                   struct netlink_callback *cb,
0678                   struct batadv_priv *bat_priv,
0679                   struct batadv_gw_node *gw_node)
0680 {
0681     struct batadv_neigh_ifinfo *router_ifinfo = NULL;
0682     struct batadv_neigh_node *router;
0683     struct batadv_gw_node *curr_gw = NULL;
0684     int ret = 0;
0685     void *hdr;
0686 
0687     router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
0688     if (!router)
0689         goto out;
0690 
0691     router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
0692     if (!router_ifinfo)
0693         goto out;
0694 
0695     curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
0696 
0697     hdr = genlmsg_put(msg, portid, cb->nlh->nlmsg_seq,
0698               &batadv_netlink_family, NLM_F_MULTI,
0699               BATADV_CMD_GET_GATEWAYS);
0700     if (!hdr) {
0701         ret = -ENOBUFS;
0702         goto out;
0703     }
0704 
0705     genl_dump_check_consistent(cb, hdr);
0706 
0707     ret = -EMSGSIZE;
0708 
0709     if (curr_gw == gw_node) {
0710         if (nla_put_flag(msg, BATADV_ATTR_FLAG_BEST)) {
0711             genlmsg_cancel(msg, hdr);
0712             goto out;
0713         }
0714     }
0715 
0716     if (nla_put(msg, BATADV_ATTR_ORIG_ADDRESS, ETH_ALEN,
0717             gw_node->orig_node->orig)) {
0718         genlmsg_cancel(msg, hdr);
0719         goto out;
0720     }
0721 
0722     if (nla_put_u32(msg, BATADV_ATTR_THROUGHPUT,
0723             router_ifinfo->bat_v.throughput)) {
0724         genlmsg_cancel(msg, hdr);
0725         goto out;
0726     }
0727 
0728     if (nla_put(msg, BATADV_ATTR_ROUTER, ETH_ALEN, router->addr)) {
0729         genlmsg_cancel(msg, hdr);
0730         goto out;
0731     }
0732 
0733     if (nla_put_string(msg, BATADV_ATTR_HARD_IFNAME,
0734                router->if_incoming->net_dev->name)) {
0735         genlmsg_cancel(msg, hdr);
0736         goto out;
0737     }
0738 
0739     if (nla_put_u32(msg, BATADV_ATTR_HARD_IFINDEX,
0740             router->if_incoming->net_dev->ifindex)) {
0741         genlmsg_cancel(msg, hdr);
0742         goto out;
0743     }
0744 
0745     if (nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_DOWN,
0746             gw_node->bandwidth_down)) {
0747         genlmsg_cancel(msg, hdr);
0748         goto out;
0749     }
0750 
0751     if (nla_put_u32(msg, BATADV_ATTR_BANDWIDTH_UP, gw_node->bandwidth_up)) {
0752         genlmsg_cancel(msg, hdr);
0753         goto out;
0754     }
0755 
0756     genlmsg_end(msg, hdr);
0757     ret = 0;
0758 
0759 out:
0760     batadv_gw_node_put(curr_gw);
0761     batadv_neigh_ifinfo_put(router_ifinfo);
0762     batadv_neigh_node_put(router);
0763     return ret;
0764 }
0765 
0766 /**
0767  * batadv_v_gw_dump() - Dump gateways into a message
0768  * @msg: Netlink message to dump into
0769  * @cb: Control block containing additional options
0770  * @bat_priv: The bat priv with all the soft interface information
0771  */
0772 static void batadv_v_gw_dump(struct sk_buff *msg, struct netlink_callback *cb,
0773                  struct batadv_priv *bat_priv)
0774 {
0775     int portid = NETLINK_CB(cb->skb).portid;
0776     struct batadv_gw_node *gw_node;
0777     int idx_skip = cb->args[0];
0778     int idx = 0;
0779 
0780     spin_lock_bh(&bat_priv->gw.list_lock);
0781     cb->seq = bat_priv->gw.generation << 1 | 1;
0782 
0783     hlist_for_each_entry(gw_node, &bat_priv->gw.gateway_list, list) {
0784         if (idx++ < idx_skip)
0785             continue;
0786 
0787         if (batadv_v_gw_dump_entry(msg, portid, cb, bat_priv,
0788                        gw_node)) {
0789             idx_skip = idx - 1;
0790             goto unlock;
0791         }
0792     }
0793 
0794     idx_skip = idx;
0795 unlock:
0796     spin_unlock_bh(&bat_priv->gw.list_lock);
0797 
0798     cb->args[0] = idx_skip;
0799 }
0800 
0801 static struct batadv_algo_ops batadv_batman_v __read_mostly = {
0802     .name = "BATMAN_V",
0803     .iface = {
0804         .activate = batadv_v_iface_activate,
0805         .enable = batadv_v_iface_enable,
0806         .disable = batadv_v_iface_disable,
0807         .update_mac = batadv_v_iface_update_mac,
0808         .primary_set = batadv_v_primary_iface_set,
0809     },
0810     .neigh = {
0811         .hardif_init = batadv_v_hardif_neigh_init,
0812         .cmp = batadv_v_neigh_cmp,
0813         .is_similar_or_better = batadv_v_neigh_is_sob,
0814         .dump = batadv_v_neigh_dump,
0815     },
0816     .orig = {
0817         .dump = batadv_v_orig_dump,
0818     },
0819     .gw = {
0820         .init_sel_class = batadv_v_init_sel_class,
0821         .store_sel_class = batadv_v_store_sel_class,
0822         .get_best_gw_node = batadv_v_gw_get_best_gw_node,
0823         .is_eligible = batadv_v_gw_is_eligible,
0824         .dump = batadv_v_gw_dump,
0825     },
0826 };
0827 
0828 /**
0829  * batadv_v_hardif_init() - initialize the algorithm specific fields in the
0830  *  hard-interface object
0831  * @hard_iface: the hard-interface to initialize
0832  */
0833 void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
0834 {
0835     /* enable link throughput auto-detection by setting the throughput
0836      * override to zero
0837      */
0838     atomic_set(&hard_iface->bat_v.throughput_override, 0);
0839     atomic_set(&hard_iface->bat_v.elp_interval, 500);
0840 
0841     hard_iface->bat_v.aggr_len = 0;
0842     skb_queue_head_init(&hard_iface->bat_v.aggr_list);
0843     INIT_DELAYED_WORK(&hard_iface->bat_v.aggr_wq,
0844               batadv_v_ogm_aggr_work);
0845 }
0846 
0847 /**
0848  * batadv_v_mesh_init() - initialize the B.A.T.M.A.N. V private resources for a
0849  *  mesh
0850  * @bat_priv: the object representing the mesh interface to initialise
0851  *
0852  * Return: 0 on success or a negative error code otherwise
0853  */
0854 int batadv_v_mesh_init(struct batadv_priv *bat_priv)
0855 {
0856     int ret = 0;
0857 
0858     ret = batadv_v_ogm_init(bat_priv);
0859     if (ret < 0)
0860         return ret;
0861 
0862     return 0;
0863 }
0864 
0865 /**
0866  * batadv_v_mesh_free() - free the B.A.T.M.A.N. V private resources for a mesh
0867  * @bat_priv: the object representing the mesh interface to free
0868  */
0869 void batadv_v_mesh_free(struct batadv_priv *bat_priv)
0870 {
0871     batadv_v_ogm_free(bat_priv);
0872 }
0873 
0874 /**
0875  * batadv_v_init() - B.A.T.M.A.N. V initialization function
0876  *
0877  * Description: Takes care of initializing all the subcomponents.
0878  * It is invoked upon module load only.
0879  *
0880  * Return: 0 on success or a negative error code otherwise
0881  */
0882 int __init batadv_v_init(void)
0883 {
0884     int ret;
0885 
0886     /* B.A.T.M.A.N. V echo location protocol packet  */
0887     ret = batadv_recv_handler_register(BATADV_ELP,
0888                        batadv_v_elp_packet_recv);
0889     if (ret < 0)
0890         return ret;
0891 
0892     ret = batadv_recv_handler_register(BATADV_OGM2,
0893                        batadv_v_ogm_packet_recv);
0894     if (ret < 0)
0895         goto elp_unregister;
0896 
0897     ret = batadv_algo_register(&batadv_batman_v);
0898     if (ret < 0)
0899         goto ogm_unregister;
0900 
0901     return ret;
0902 
0903 ogm_unregister:
0904     batadv_recv_handler_unregister(BATADV_OGM2);
0905 
0906 elp_unregister:
0907     batadv_recv_handler_unregister(BATADV_ELP);
0908 
0909     return ret;
0910 }