#include <linux/atomic.h>
 #include <linux/bug.h>
 #include <linux/cache.h>
+#include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/jiffies.h>
+#include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include "bat_algo.h"
 #include "bat_v_elp.h"
 #include "bat_v_ogm.h"
+#include "gateway_client.h"
+#include "gateway_common.h"
 #include "hard-interface.h"
 #include "hash.h"
 #include "originator.h"
        return ret;
 }
 
+static ssize_t batadv_v_store_sel_class(struct batadv_priv *bat_priv,
+                                       char *buff, size_t count)
+{
+       u32 old_class, class;
+
+       if (!batadv_parse_throughput(bat_priv->soft_iface, buff,
+                                    "B.A.T.M.A.N. V GW selection class",
+                                    &class))
+               return -EINVAL;
+
+       old_class = atomic_read(&bat_priv->gw.sel_class);
+       atomic_set(&bat_priv->gw.sel_class, class);
+
+       if (old_class != class)
+               batadv_gw_reselect(bat_priv);
+
+       return count;
+}
+
+static ssize_t batadv_v_show_sel_class(struct batadv_priv *bat_priv, char *buff)
+{
+       u32 class = atomic_read(&bat_priv->gw.sel_class);
+
+       return sprintf(buff, "%u.%u MBit\n", class / 10, class % 10);
+}
+
 static struct batadv_algo_ops batadv_batman_v __read_mostly = {
        .name = "BATMAN_V",
        .iface = {
        .orig = {
                .print = batadv_v_orig_print,
        },
+       .gw = {
+               .store_sel_class = batadv_v_store_sel_class,
+               .show_sel_class = batadv_v_show_sel_class,
+       },
 };
 
 /**
 
        return count;
 }
 
+static ssize_t batadv_show_gw_sel_class(struct kobject *kobj,
+                                       struct attribute *attr, char *buff)
+{
+       struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
+
+       if (bat_priv->algo_ops->gw.show_sel_class)
+               return bat_priv->algo_ops->gw.show_sel_class(bat_priv, buff);
+
+       return sprintf(buff, "%i\n", atomic_read(&bat_priv->gw.sel_class));
+}
+
+static ssize_t batadv_store_gw_sel_class(struct kobject *kobj,
+                                        struct attribute *attr, char *buff,
+                                        size_t count)
+{
+       struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj);
+
+       if (buff[count - 1] == '\n')
+               buff[count - 1] = '\0';
+
+       if (bat_priv->algo_ops->gw.store_sel_class)
+               return bat_priv->algo_ops->gw.store_sel_class(bat_priv, buff,
+                                                             count);
+
+       return __batadv_store_uint_attr(buff, count, 1, BATADV_TQ_MAX_VALUE,
+                                       batadv_post_gw_reselect, attr,
+                                       &bat_priv->gw.sel_class,
+                                       bat_priv->soft_iface);
+}
+
 static ssize_t batadv_show_gw_bwidth(struct kobject *kobj,
                                     struct attribute *attr, char *buff)
 {
                     2 * BATADV_JITTER, INT_MAX, NULL);
 BATADV_ATTR_SIF_UINT(hop_penalty, hop_penalty, S_IRUGO | S_IWUSR, 0,
                     BATADV_TQ_MAX_VALUE, NULL);
-BATADV_ATTR_SIF_UINT(gw_sel_class, gw.sel_class, S_IRUGO | S_IWUSR, 1,
-                    BATADV_TQ_MAX_VALUE, batadv_post_gw_reselect);
+static BATADV_ATTR(gw_sel_class, S_IRUGO | S_IWUSR, batadv_show_gw_sel_class,
+                  batadv_store_gw_sel_class);
 static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth,
                   batadv_store_gw_bwidth);
 #ifdef CONFIG_BATMAN_ADV_MCAST
 
                      struct batadv_hard_iface *hard_iface);
 };
 
+/**
+ * struct batadv_algo_gw_ops - mesh algorithm callbacks (GW specific)
+ * @store_sel_class: parse and stores a new GW selection class (optional)
+ * @show_sel_class: prints the current GW selection class (optional)
+ */
+struct batadv_algo_gw_ops {
+       ssize_t (*store_sel_class)(struct batadv_priv *bat_priv, char *buff,
+                                  size_t count);
+       ssize_t (*show_sel_class)(struct batadv_priv *bat_priv, char *buff);
+};
+
 /**
  * struct batadv_algo_ops - mesh algorithm callbacks
  * @list: list node for the batadv_algo_list
  * @iface: callbacks related to interface handling
  * @neigh: callbacks related to neighbors handling
  * @orig: callbacks related to originators handling
+ * @gw: callbacks related to GW mode
  */
 struct batadv_algo_ops {
        struct hlist_node list;
        struct batadv_algo_iface_ops iface;
        struct batadv_algo_neigh_ops neigh;
        struct batadv_algo_orig_ops orig;
+       struct batadv_algo_gw_ops gw;
 };
 
 /**