Add method to get station/mpath information
authorJiwan Kim <ji-wan.kim@samsung.com>
Mon, 13 Mar 2017 05:58:20 +0000 (14:58 +0900)
committersaerome.kim <saerome.kim@samsung.com>
Mon, 17 Jul 2017 02:35:36 +0000 (11:35 +0900)
include/mesh-netlink.h
include/mesh-request.h
include/mesh.h
introspection/mesh.xml
src/mesh-netlink.c
src/mesh-request.c
src/mesh-service-interface.c

index b9f657d..07f93ab 100644 (file)
@@ -34,4 +34,7 @@ int mesh_netlink_get_scan_result(const char* mesh_if_name, GList **scan_list);
 int mesh_netlink_set_mesh_parameter(const char* mesh_if_name,
                const char* param_name, unsigned int value);
 
+int mesh_netlink_get_station_info(const char* mesh_if_name, GList **station_list);
+int mesh_netlink_get_mpath_info(const char* mesh_if_name, GList **mpath_list);
+
 #endif /* __MESH_NETLINK_H__ */
index 23e97b4..8e82968 100644 (file)
@@ -67,6 +67,10 @@ int mesh_request_enable_softap(
 int mesh_request_disable_softap(
                const char* bridge_interface, const char* softap_interface);
 
+/* Mesh Station & path */
+int mesh_request_get_station_info(const char* mesh_interface, GList **station_list);
+int mesh_request_get_mpath_info(const char* mesh_interface, GList **mpath_list);
+
 /* Notifications */
 void mesh_notify_scan_done();
 
index c7caa2c..a08567b 100644 (file)
@@ -62,6 +62,62 @@ typedef struct {
        gint data_rate; /**< Data rate */
 } mesh_scan_result_s;
 
+/**< Connected mesh network station */
+typedef struct {
+       gchar *bssid; /**< BSSID */
+       guint inactive_time;
+       guint64 rx_bytes; /**< RX bytes */
+       guint rx_packets; /**< RX packets */
+       guint64 tx_bytes; /**< TX bytes */
+       guint tx_packets; /**< TX packets */
+       guint tx_retries;
+       guint tx_failed;
+       guint beacon_loss;
+       guint64 beacon_rx;
+       guint64 rx_drop_misc;
+       gint rssi; /**< RSSI */
+       gint rssi_avg; /**< RSSI */
+       guint beacon_signal_avg;
+       guint64 t_offset;
+       guint tx_bitrate; /**< TX bit rate */
+       guint rx_bitrate; /**< TX bit rate */
+       guint64 rx_duration;
+       gushort llid;
+       gushort plid;
+       guchar mesh_plink; /**< Mesh plink */
+       guint local_ps_mode;
+       guint peer_ps_mode;
+       guint non_peer_ps_mode;
+       gboolean authorized;
+       gboolean authenticated;
+       gboolean associated;
+       gboolean preamble;
+       gboolean wme;
+       gboolean mfp;
+       gboolean tdls_peer;
+       guchar dtim_period;
+       gushort beacon_interval;
+       gboolean cts_protection;
+       gboolean short_preamble;
+       gboolean short_slot_time;
+       guint connected_time;
+} mesh_station_info_s;
+
+/**< Mesh path information structure */
+typedef struct {
+       gchar *dest_addr; /**< Destination address */
+       gchar *next_hop; /**< Next Hop */
+       gchar *interface; /**< Interface name */
+       guint sn; /**< SN */
+       guint metric; /**< Metric */
+       guint qlen; /**< QLEN */
+       guint exptime; /**< Expired time */
+       guint discovery_timeout; /**< Discovery timeout */
+       guchar discovery_retries; /**< Discovery retries */
+       guchar flags; /**< Flags */
+} mesh_mpath_info_s;
+
+
 /**< mesh service structure */
 typedef struct _mesh_service {
        GMainLoop *main_loop; /**< Service main-loop */
@@ -72,6 +128,9 @@ typedef struct _mesh_service {
        GList *saved_mesh_network; /**< Saved mesh network list */
        GList *scanned_mesh_network; /**< Scanned mesh network list */
        mesh_network_info_s *joined_network; /**< Joined network info */
+
+       GList *station_list; /**< Mesh station list */
+       GList *mpath_list; /**< MPath list */
 } mesh_service;
 
  #endif /* __MESH_H__ */
index 89a44d6..935b690 100644 (file)
                        <arg type="u" name="result" direction="out"/>\r
                </method>\r
                <method name="get_station_info">\r
-                       <arg type="a(a{sv})" name="station" direction="out"/>\r
+                       <arg type="aa{sv}" name="station" direction="out"/>\r
                        <arg type="u" name="result" direction="out"/>\r
                </method>\r
                <method name="get_mpath_info">\r
-                       <arg type="a(a{sv})" name="mpath" direction="out"/>\r
+                       <arg type="aa{sv}" name="mpath" direction="out"/>\r
                        <arg type="u" name="result" direction="out"/>\r
                </method>\r
 \r
index 4a57563..8729446 100644 (file)
@@ -52,6 +52,8 @@
 #define MESH_ELEMENT_ID           114
 #define MAX_MAC_ADDR_LEN          18
 
+#define BIT(x) (1ULL<<(x))
+
 typedef enum {
        MESH_NL_CALLBACK_FINISHED = 0,
        MESH_NL_CALLBACK_TRYING,
@@ -71,6 +73,8 @@ typedef struct {
        gchar* scanning_interface;
        bool error_occured;
        GList **scan_list;
+       GList **station_list;
+       GList **mpath_list;
 } mesh_nl_state;
 
 typedef struct {
@@ -78,6 +82,16 @@ typedef struct {
        int id;
 } multicast_group_id_args;
 
+enum plink_state {
+       LISTEN,
+       OPN_SNT,
+       OPN_RCVD,
+       CNF_RCVD,
+       ESTAB,
+       HOLDING,
+       BLOCKED
+};
+
 static bool scan_in_progress = FALSE;
 
 #if 0
@@ -904,6 +918,164 @@ static char* _get_meshid(const uint8_t len, const uint8_t *data)
        return result;
 }
 
+static char *get_chain_signal(struct nlattr *attr_list)
+{
+       struct nlattr *attr;
+       static char buf[64];
+       char *cur = buf;
+       int i = 0, rem;
+       const char *prefix;
+
+       if (!attr_list)
+               return (char *)"";
+
+       nla_for_each_nested(attr, attr_list, rem) {
+               if (i++ > 0)
+                       prefix = ", ";
+               else
+                       prefix = "[";
+
+               cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
+                               (int8_t) nla_get_u8(attr));
+       }
+
+       if (i)
+               snprintf(cur, sizeof(buf) - (cur - buf), "] ");
+
+       return buf;
+}
+
+static int parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
+{
+       int rate = 0;
+       char *pos = buf;
+       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+       };
+
+       if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+                            bitrate_attr, rate_policy)) {
+               snprintf(buf, buflen, "failed to parse nested rate attributes!");
+               return 0;
+       }
+
+       if (rinfo[NL80211_RATE_INFO_BITRATE32])
+               rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
+       else if (rinfo[NL80211_RATE_INFO_BITRATE])
+               rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
+       if (rate > 0)
+               pos += snprintf(pos, buflen - (pos - buf),
+                               "%d.%d MBit/s", rate / 10, rate % 10);
+
+       if (rinfo[NL80211_RATE_INFO_MCS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
+       if (rinfo[NL80211_RATE_INFO_VHT_MCS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
+       if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
+       if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
+       if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
+       if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
+               pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
+       if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+               pos += snprintf(pos, buflen - (pos - buf), " short GI");
+       if (rinfo[NL80211_RATE_INFO_VHT_NSS])
+               pos += snprintf(pos, buflen - (pos - buf),
+                               " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
+
+       return rate;
+}
+
+static void parse_bss_param(struct nlattr *bss_param_attr, mesh_station_info_s *station_info)
+{
+       struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info;
+       static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = {
+               [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
+               [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
+               [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
+               [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
+               [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
+       };
+
+       if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX,
+                            bss_param_attr, bss_poilcy)) {
+               MESH_LOGE("failed to parse nested bss param attributes!");
+       }
+
+       info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD];
+       if (info) {
+               station_info->dtim_period = nla_get_u8(info);
+               MESH_LOGD("    DTIM period:\t%u", station_info->dtim_period);
+       }
+       info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL];
+       if (info) {
+               station_info->beacon_interval = nla_get_u16(info);
+               MESH_LOGD("    beacon interval:%u", station_info->beacon_interval);
+       }
+       info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT];
+       if (info) {
+               MESH_LOGD("    CTS protection:");
+               if (nla_get_u16(info)) {
+                       MESH_LOGD("    yes");
+                       station_info->cts_protection = TRUE;
+               } else {
+                       MESH_LOGD("    no");
+                       station_info->cts_protection = FALSE;
+               }
+       }
+       info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE];
+       if (info) {
+               MESH_LOGD("    short preamble:");
+               if (nla_get_u16(info)) {
+                       MESH_LOGD("    yes");
+                       station_info->short_preamble = TRUE;
+               } else {
+                       MESH_LOGD("    no");
+                       station_info->short_preamble = FALSE;
+               }
+       }
+       info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME];
+       if (info) {
+               MESH_LOGD("    short slot time:");
+               if (nla_get_u16(info)) {
+                       MESH_LOGD("    yes");
+                       station_info->short_slot_time = TRUE;
+               } else {
+                       MESH_LOGD("    no");
+                       station_info->short_slot_time = FALSE;
+               }
+       }
+}
+
+static void print_power_mode(struct nlattr *a)
+{
+       enum nl80211_mesh_power_mode pm = nla_get_u32(a);
+
+       switch (pm) {
+       case NL80211_MESH_POWER_ACTIVE:
+               MESH_LOGD("ACTIVE");
+               break;
+       case NL80211_MESH_POWER_LIGHT_SLEEP:
+               MESH_LOGD("LIGHT SLEEP");
+               break;
+       case NL80211_MESH_POWER_DEEP_SLEEP:
+               MESH_LOGD("DEEP SLEEP");
+               break;
+       default:
+               MESH_LOGD("UNKNOWN");
+               break;
+       }
+}
+
 static int _on_receive_bss_information(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1020,6 +1192,435 @@ FINISH:
        return NL_SKIP;
 }
 
+static int _on_receive_station_info(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+       struct nl80211_sta_flag_update *sta_flags;
+       static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+               [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
+               [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_BEACON_RX] = { .type = NLA_U64},
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_T_OFFSET] = { .type = NLA_U64 },
+               [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_RX_BITRATE] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
+               [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
+               [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_TX_RETRIES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_BEACON_LOSS] = { .type = NLA_U32},
+               [NL80211_STA_INFO_RX_DROP_MISC] = { .type = NLA_U64},
+               [NL80211_STA_INFO_STA_FLAGS] =
+                       { .minlen = sizeof(struct nl80211_sta_flag_update) },
+               [NL80211_STA_INFO_LOCAL_PM] = { .type = NLA_U32},
+               [NL80211_STA_INFO_PEER_PM] = { .type = NLA_U32},
+               [NL80211_STA_INFO_NONPEER_PM] = { .type = NLA_U32},
+               [NL80211_STA_INFO_CHAIN_SIGNAL] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_CHAIN_SIGNAL_AVG] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_TID_STATS] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_BSS_PARAM] = { .type = NLA_NESTED },
+               [NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
+       };
+       char mac_addr[20], state_name[10], dev[20];
+       char *chain;
+       mesh_nl_state *state = (mesh_nl_state *)arg;
+       mesh_station_info_s *station_info = NULL;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), NULL);
+
+       /*
+        * TODO: validate the interface and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending station notifications.
+        */
+
+       if (!tb[NL80211_ATTR_STA_INFO]) {
+               MESH_LOGE("missing station stats !");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+                               tb[NL80211_ATTR_STA_INFO],
+                               stats_policy)) {
+               MESH_LOGE("failed to parse nested attributes!");
+               return NL_SKIP;
+       }
+
+       station_info = g_try_new0(mesh_station_info_s, 1);
+       if (NULL == station_info) {
+               MESH_LOGE("Failed to allocate station info !");
+               return NL_SKIP;
+       }
+
+       mac_addr_n2a(mac_addr, nla_data(tb[NL80211_ATTR_MAC]));
+       if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+       MESH_LOGD("Station %s (on %s)", mac_addr, dev);
+       station_info->bssid = g_strdup(mac_addr);
+
+       if (sinfo[NL80211_STA_INFO_INACTIVE_TIME]) {
+               station_info->inactive_time =
+                       nla_get_u32(sinfo[NL80211_STA_INFO_INACTIVE_TIME]);
+               MESH_LOGE("    inactive time:\t%u ms", station_info->inactive_time);
+       }
+
+       if (sinfo[NL80211_STA_INFO_RX_BYTES64]) {
+               station_info->rx_bytes =
+                       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_BYTES64]);
+               MESH_LOGE("    rx bytes:\t%llu", station_info->rx_bytes);
+       }
+       else if (sinfo[NL80211_STA_INFO_RX_BYTES]) {
+               station_info->rx_bytes =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]);
+               MESH_LOGD("    rx bytes:\t%u", station_info->rx_bytes);
+       }
+       if (sinfo[NL80211_STA_INFO_RX_PACKETS]) {
+               station_info->rx_packets =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_RX_PACKETS]);
+               MESH_LOGD("    rx packets:\t%u", station_info->rx_packets);
+       }
+
+       if (sinfo[NL80211_STA_INFO_TX_BYTES64]) {
+               station_info->tx_bytes =
+                               (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_TX_BYTES64]);
+               MESH_LOGD("    tx bytes:\t%llu", station_info->rx_packets);
+       }
+       else if (sinfo[NL80211_STA_INFO_TX_BYTES]) {
+               station_info->tx_bytes =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]);
+               MESH_LOGD("    tx bytes:\t%u", station_info->tx_bytes);
+       }
+       if (sinfo[NL80211_STA_INFO_TX_PACKETS]) {
+               station_info->tx_packets =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_TX_PACKETS]);
+               MESH_LOGD("    tx packets:\t%u", station_info->tx_packets);
+       }
+       if (sinfo[NL80211_STA_INFO_TX_RETRIES]) {
+               station_info->tx_retries =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_TX_RETRIES]);
+               MESH_LOGD("    tx retries:\t%u", station_info->tx_retries);
+       }
+       if (sinfo[NL80211_STA_INFO_TX_FAILED]) {
+               station_info->tx_failed =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_TX_FAILED]);
+               MESH_LOGD("    tx failed:\t%u", station_info->tx_failed);
+       }
+       if (sinfo[NL80211_STA_INFO_BEACON_LOSS]) {
+               station_info->beacon_loss =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_BEACON_LOSS]);
+               MESH_LOGD("    beacon loss:\t%u", station_info->beacon_loss);
+       }
+       if (sinfo[NL80211_STA_INFO_BEACON_RX]) {
+               station_info->beacon_rx =
+                               (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_BEACON_RX]);
+               MESH_LOGD("    beacon rx:\t%llu", station_info->beacon_rx);
+       }
+       if (sinfo[NL80211_STA_INFO_RX_DROP_MISC]) {
+               station_info->rx_drop_misc =
+                       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DROP_MISC]);
+               MESH_LOGD("    rx drop misc:\t%llu", station_info->rx_drop_misc);
+       }
+
+       chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL]);
+       if (sinfo[NL80211_STA_INFO_SIGNAL]) {
+               station_info->rssi =
+                               (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+               MESH_LOGD("    signal:  \t%d %sdBm", station_info->rssi, chain);
+       }
+
+       chain = get_chain_signal(sinfo[NL80211_STA_INFO_CHAIN_SIGNAL_AVG]);
+       if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) {
+               station_info->rssi_avg =
+                               (int8_t)nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
+               MESH_LOGD("    signal avg:\t%d %sdBm", station_info->rssi_avg, chain);
+       }
+
+       if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]) {
+               station_info->beacon_signal_avg =
+                               nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+               MESH_LOGD("    beacon signal avg:\t%d dBm", station_info->beacon_signal_avg);
+       }
+       if (sinfo[NL80211_STA_INFO_T_OFFSET]) {
+               station_info->t_offset =
+                       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]);
+               MESH_LOGD("    Toffset:\t%llu us", station_info->t_offset);
+       }
+
+       if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+               char buf[100];
+               station_info->tx_bitrate =
+                       parse_bitrate(sinfo[NL80211_STA_INFO_TX_BITRATE], buf, sizeof(buf));
+               MESH_LOGD("    tx bitrate:\t%s", buf);
+       }
+
+       if (sinfo[NL80211_STA_INFO_RX_BITRATE]) {
+               char buf[100];
+               station_info->rx_bitrate =
+                       parse_bitrate(sinfo[NL80211_STA_INFO_RX_BITRATE], buf, sizeof(buf));
+               MESH_LOGD("    rx bitrate:\t%s", buf);
+       }
+
+       if (sinfo[NL80211_STA_INFO_RX_DURATION]) {
+               station_info->rx_duration =
+                       (unsigned long long)nla_get_u64(sinfo[NL80211_STA_INFO_RX_DURATION]);
+               MESH_LOGD("    rx duration:\t%lld us", station_info->rx_duration);
+       }
+
+       if (sinfo[NL80211_STA_INFO_LLID]) {
+               station_info->llid = nla_get_u16(sinfo[NL80211_STA_INFO_LLID]);
+               MESH_LOGD("    mesh llid:\t%d", station_info->llid);
+       }
+       if (sinfo[NL80211_STA_INFO_PLID]) {
+               station_info->llid = nla_get_u16(sinfo[NL80211_STA_INFO_PLID]);
+               MESH_LOGD("    mesh plid:\t%d", station_info->plid);
+       }
+       if (sinfo[NL80211_STA_INFO_PLINK_STATE]) {
+               station_info->mesh_plink =
+                               nla_get_u8(sinfo[NL80211_STA_INFO_PLINK_STATE]);
+               switch (station_info->mesh_plink) {
+               case LISTEN:
+                       strcpy(state_name, "LISTEN");
+                       break;
+               case OPN_SNT:
+                       strcpy(state_name, "OPN_SNT");
+                       break;
+               case OPN_RCVD:
+                       strcpy(state_name, "OPN_RCVD");
+                       break;
+               case CNF_RCVD:
+                       strcpy(state_name, "CNF_RCVD");
+                       break;
+               case ESTAB:
+                       strcpy(state_name, "ESTAB");
+                       break;
+               case HOLDING:
+                       strcpy(state_name, "HOLDING");
+                       break;
+               case BLOCKED:
+                       strcpy(state_name, "BLOCKED");
+                       break;
+               default:
+                       strcpy(state_name, "UNKNOWN");
+                       break;
+               }
+               MESH_LOGD("    mesh plink:\t%s", state_name);
+       }
+       if (sinfo[NL80211_STA_INFO_LOCAL_PM]) {
+               station_info->local_ps_mode =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_LOCAL_PM]);
+               MESH_LOGD("    mesh local PS mode:\t");
+               print_power_mode(sinfo[NL80211_STA_INFO_LOCAL_PM]);
+       }
+       if (sinfo[NL80211_STA_INFO_PEER_PM]) {
+               station_info->peer_ps_mode =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_PEER_PM]);
+               MESH_LOGD("    mesh peer PS mode:\t");
+               print_power_mode(sinfo[NL80211_STA_INFO_PEER_PM]);
+       }
+       if (sinfo[NL80211_STA_INFO_NONPEER_PM]) {
+               station_info->non_peer_ps_mode =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_NONPEER_PM]);
+               MESH_LOGD("    mesh non-peer PS mode:\t");
+               print_power_mode(sinfo[NL80211_STA_INFO_NONPEER_PM]);
+       }
+
+       if (sinfo[NL80211_STA_INFO_STA_FLAGS]) {
+               sta_flags = (struct nl80211_sta_flag_update *)
+                           nla_data(sinfo[NL80211_STA_INFO_STA_FLAGS]);
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+                       MESH_LOGD("    authorized:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
+                               MESH_LOGD("        yes");
+                               station_info->authorized = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->authorized = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
+                       MESH_LOGD("    authenticated:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) {
+                               MESH_LOGD("        yes");
+                               station_info->authenticated = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->authenticated = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
+                       MESH_LOGD("    associated:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
+                               MESH_LOGD("        yes");
+                               station_info->associated = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->associated = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
+                       MESH_LOGD("    preamble:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
+                               MESH_LOGD("        short");
+                               station_info->preamble = TRUE;
+                       } else {
+                               MESH_LOGD("        long");
+                               station_info->preamble = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_WME)) {
+                       MESH_LOGD("    WMM/WME:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_WME)) {
+                               MESH_LOGD("        yes");
+                               station_info->wme = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->wme = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_MFP)) {
+                       MESH_LOGD("    MFP:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_MFP)) {
+                               MESH_LOGD("        yes");
+                               station_info->mfp = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->mfp = FALSE;
+                       }
+               }
+
+               if (sta_flags->mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+                       MESH_LOGD("    TDLS peer:");
+                       if (sta_flags->set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+                               MESH_LOGD("        yes");
+                               station_info->tdls_peer = TRUE;
+                       } else {
+                               MESH_LOGD("        no");
+                               station_info->tdls_peer = FALSE;
+                       }
+               }
+       }
+
+       if (sinfo[NL80211_STA_INFO_BSS_PARAM])
+               parse_bss_param(sinfo[NL80211_STA_INFO_BSS_PARAM], station_info);
+       if (sinfo[NL80211_STA_INFO_CONNECTED_TIME]) {
+               station_info->connected_time =
+                               nla_get_u32(sinfo[NL80211_STA_INFO_CONNECTED_TIME]);
+               MESH_LOGD("    connected time:\t%u seconds", station_info->connected_time);
+       }
+
+       MESH_LOGD("");
+       *(state->station_list) = g_list_prepend(*(state->station_list), station_info);
+
+       return NL_SKIP;
+}
+
+static int _on_receive_mpath_info(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *pinfo[NL80211_MPATH_INFO_MAX + 1];
+       char dst[20], next_hop[20], dev[20];
+       static struct nla_policy mpath_policy[NL80211_MPATH_INFO_MAX + 1] = {
+               [NL80211_MPATH_INFO_FRAME_QLEN] = { .type = NLA_U32 },
+               [NL80211_MPATH_INFO_SN] = { .type = NLA_U32 },
+               [NL80211_MPATH_INFO_METRIC] = { .type = NLA_U32 },
+               [NL80211_MPATH_INFO_EXPTIME] = { .type = NLA_U32 },
+               [NL80211_MPATH_INFO_DISCOVERY_TIMEOUT] = { .type = NLA_U32 },
+               [NL80211_MPATH_INFO_DISCOVERY_RETRIES] = { .type = NLA_U8 },
+               [NL80211_MPATH_INFO_FLAGS] = { .type = NLA_U8 },
+       };
+
+       mesh_nl_state *state = (mesh_nl_state *)arg;
+       mesh_mpath_info_s *mpath_info = NULL;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       /*
+        * TODO: validate the interface and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending mpath notifications.
+        */
+
+       if (!tb[NL80211_ATTR_MPATH_INFO]) {
+               MESH_LOGE("missing mesh path info!");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(pinfo, NL80211_MPATH_INFO_MAX,
+                            tb[NL80211_ATTR_MPATH_INFO],
+                            mpath_policy)) {
+               MESH_LOGE("failed to parse nested attributes!");
+               return NL_SKIP;
+       }
+
+       mpath_info = g_try_new0(mesh_mpath_info_s, 1);
+       if (NULL == mpath_info) {
+               MESH_LOGE("Failed to allocate mesh path info !");
+               return NL_SKIP;
+       }
+
+       mac_addr_n2a(dst, nla_data(tb[NL80211_ATTR_MAC]));
+       mpath_info->dest_addr = g_strdup(dst);
+       MESH_LOGD("Destination Address : %s", mpath_info->dest_addr);
+
+       mac_addr_n2a(next_hop, nla_data(tb[NL80211_ATTR_MPATH_NEXT_HOP]));
+       mpath_info->next_hop = g_strdup(next_hop);
+       MESH_LOGD("Next hop Address : %s", mpath_info->next_hop);
+
+       if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), dev);
+       mpath_info->interface = g_strdup(dev);
+       MESH_LOGD("Interface : %s", mpath_info->interface);
+       
+       if (pinfo[NL80211_MPATH_INFO_SN]) {
+               mpath_info->sn = nla_get_u32(pinfo[NL80211_MPATH_INFO_SN]);
+               MESH_LOGD("SN : %u", mpath_info->sn);
+       }
+       if (pinfo[NL80211_MPATH_INFO_METRIC]) {
+               mpath_info->metric = nla_get_u32(pinfo[NL80211_MPATH_INFO_METRIC]);
+               MESH_LOGD("Metric : %u", mpath_info->metric);
+       }
+       if (pinfo[NL80211_MPATH_INFO_FRAME_QLEN]) {
+               mpath_info->qlen = nla_get_u32(pinfo[NL80211_MPATH_INFO_FRAME_QLEN]);
+               MESH_LOGD("QLEN : %u", mpath_info->qlen);
+       }
+       if (pinfo[NL80211_MPATH_INFO_EXPTIME]) {
+               mpath_info->exptime = nla_get_u32(pinfo[NL80211_MPATH_INFO_EXPTIME]);
+               MESH_LOGD("ExpTime : %u", mpath_info->exptime);
+       }
+       if (pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT]) {
+               mpath_info->discovery_timeout =
+                               nla_get_u32(pinfo[NL80211_MPATH_INFO_DISCOVERY_TIMEOUT]);
+               MESH_LOGD("Discovery Timeout : %u", mpath_info->discovery_timeout);
+       }
+       if (pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES]) {
+               mpath_info->discovery_retries =
+                               nla_get_u8(pinfo[NL80211_MPATH_INFO_DISCOVERY_RETRIES]);
+               MESH_LOGD("Discovery Retries : %u", mpath_info->discovery_retries);
+       }
+       if (pinfo[NL80211_MPATH_INFO_FLAGS]) {
+               mpath_info->flags = nla_get_u8(pinfo[NL80211_MPATH_INFO_FLAGS]);
+               MESH_LOGD("Flags : 0x%x", mpath_info->flags);
+       }
+
+       MESH_LOGD("");
+       *(state->mpath_list) = g_list_prepend(*(state->mpath_list), mpath_info);
+
+       return NL_SKIP;
+}
+
 static int _send_nl_trigger_full_scan(const char* mesh_if_name)
 {
        /* Nested message */
@@ -1708,6 +2309,164 @@ nla_put_failure:
        return MESHD_ERROR_OPERATION_FAILED;
 }
 
+static int _send_nl_get_station_info(const char* if_name, GList **station_list)
+{
+       mesh_nl_state state = {
+               .nl80211_id = -1,
+               .callback_state = MESH_NL_CALLBACK_TRYING,
+               .event_source = 0,
+               .nl_socket = NULL,
+               .msg = NULL,
+               .cb = NULL,
+               .s_cb = NULL,
+               .scanning_interface = (char*)if_name,
+               .station_list = station_list
+       };
+       int err = MESHD_ERROR_NONE;
+       int device_index = 0;
+       int ret;
+       int test = 0;
+
+       ret = __initialize_nl80211(&state);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to initialize nl80211");
+               return ret;
+       }
+
+       ret = __initialize_netlink_message(&state);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to initialize netlink message");
+               goto DESTROY;
+       }
+
+       /* Set command into message */
+       genlmsg_put(state.msg, 0, 0, state.nl80211_id, 0,
+                   NLM_F_DUMP, NL80211_CMD_GET_STATION, 0);
+
+       /* Add attributes into message */
+       MESH_LOGD("Dump station list with interface [%s]", if_name);
+       ret = __get_device_index_from_string(if_name, &device_index);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to get mesh interface device index");
+               err = ret;
+               goto DESTROY;
+       }
+       NLA_PUT_U32(state.msg, NL80211_ATTR_IFINDEX, device_index);
+
+       /* Register valid callback to dump result */
+       nl_cb_set(state.cb, NL_CB_VALID, NL_CB_CUSTOM,
+                       _on_receive_station_info, &state);
+
+       /* Send message into kernel */
+       ret = nl_send_auto(state.nl_socket, state.msg);
+       if (ret < 0) {
+               MESH_LOGE("Failed to nl_send_auto() [%s](%d)",
+                               nl_geterror(ret), ret);
+               err = MESHD_ERROR_OPERATION_FAILED;
+               goto DESTROY;
+       }
+
+       /* sync response */
+       state.callback_state = MESH_NL_CALLBACK_TRYING;
+       while (state.callback_state == MESH_NL_CALLBACK_TRYING) {
+               MESH_LOGD("  count [%02d]", ++test);
+               nl_recvmsgs(state.nl_socket, state.cb);
+       }
+       MESH_LOGD("Finished");
+
+DESTROY:
+       __clean_netlink_message(&state);
+       __clean_nl80211(&state);
+
+       return err;
+
+nla_put_failure:
+       MESH_LOGE("Failed to message build");
+       __clean_netlink_message(&state);
+       __clean_nl80211(&state);
+
+       return MESHD_ERROR_OPERATION_FAILED;
+}
+
+static int _send_nl_get_mpath_info(const char* if_name, GList **mpath_list)
+{
+       mesh_nl_state state = {
+               .nl80211_id = -1,
+               .callback_state = MESH_NL_CALLBACK_TRYING,
+               .event_source = 0,
+               .nl_socket = NULL,
+               .msg = NULL,
+               .cb = NULL,
+               .s_cb = NULL,
+               .scanning_interface = (char*)if_name,
+               .mpath_list = mpath_list
+       };
+       int err = MESHD_ERROR_NONE;
+       int device_index = 0;
+       int ret;
+       int test = 0;
+
+       ret = __initialize_nl80211(&state);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to initialize nl80211");
+               return ret;
+       }
+
+       ret = __initialize_netlink_message(&state);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to initialize netlink message");
+               goto DESTROY;
+       }
+
+       /* Set command into message */
+       genlmsg_put(state.msg, 0, 0, state.nl80211_id, 0,
+                   NLM_F_DUMP, NL80211_CMD_GET_MPATH, 0);
+
+       /* Add attributes into message */
+       MESH_LOGD("Dump station list with interface [%s]", if_name);
+       ret = __get_device_index_from_string(if_name, &device_index);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to get mesh interface device index");
+               err = ret;
+               goto DESTROY;
+       }
+       NLA_PUT_U32(state.msg, NL80211_ATTR_IFINDEX, device_index);
+
+       /* Register valid callback to dump result */
+       nl_cb_set(state.cb, NL_CB_VALID, NL_CB_CUSTOM,
+                       _on_receive_mpath_info, &state);
+
+       /* Send message into kernel */
+       ret = nl_send_auto(state.nl_socket, state.msg);
+       if (ret < 0) {
+               MESH_LOGE("Failed to nl_send_auto() [%s](%d)",
+                               nl_geterror(ret), ret);
+               err = MESHD_ERROR_OPERATION_FAILED;
+               goto DESTROY;
+       }
+
+       /* sync response */
+       state.callback_state = MESH_NL_CALLBACK_TRYING;
+       while (state.callback_state == MESH_NL_CALLBACK_TRYING) {
+               MESH_LOGD("  count [%02d]", ++test);
+               nl_recvmsgs(state.nl_socket, state.cb);
+       }
+       MESH_LOGD("Finished");
+
+DESTROY:
+       __clean_netlink_message(&state);
+       __clean_nl80211(&state);
+
+       return err;
+
+nla_put_failure:
+       MESH_LOGE("Failed to message build");
+       __clean_netlink_message(&state);
+       __clean_nl80211(&state);
+
+       return MESHD_ERROR_OPERATION_FAILED;
+}
+
 int mesh_netlink_set_type_managed(const char* if_name)
 {
        int ret = MESHD_ERROR_NONE;
@@ -1851,7 +2610,7 @@ int mesh_netlink_get_scan_result(const char* mesh_if_name, GList **scan_list)
        }
        if (NULL == scan_list) {
                MESH_LOGE("Invalid parameter [%p]", scan_list);
-               return MESHD_ERROR_INVALID_PARAMETER;   
+               return MESHD_ERROR_INVALID_PARAMETER;
        }
 
        MESH_LOGD("Get scan result on [%s]", mesh_if_name);
@@ -1882,3 +2641,41 @@ int mesh_netlink_set_mesh_parameter(const char* mesh_if_name,
 
        return ret;
 }
+
+int mesh_netlink_get_station_info(const char* mesh_if_name, GList **station_list)
+{
+       int ret = MESHD_ERROR_NONE;
+
+       if (NULL == mesh_if_name || strlen(mesh_if_name) > IFNAMSIZ) {
+               MESH_LOGE("Invalid parameter [%p]", mesh_if_name);
+               return MESHD_ERROR_INVALID_PARAMETER;
+       }
+       if (NULL == station_list) {
+               MESH_LOGE("Invalid parameter [%p]", station_list);
+               return MESHD_ERROR_INVALID_PARAMETER;
+       }
+
+       MESH_LOGD("Get connected stations");
+       ret = _send_nl_get_station_info(mesh_if_name, station_list);
+
+       return ret;
+}
+
+int mesh_netlink_get_mpath_info(const char* mesh_if_name, GList **mpath_list)
+{
+       int ret = MESHD_ERROR_NONE;
+
+       if (NULL == mesh_if_name || strlen(mesh_if_name) > IFNAMSIZ) {
+               MESH_LOGE("Invalid parameter [%p]", mesh_if_name);
+               return MESHD_ERROR_INVALID_PARAMETER;
+       }
+       if (NULL == mpath_list) {
+               MESH_LOGE("Invalid parameter [%p]", mpath_list);
+               return MESHD_ERROR_INVALID_PARAMETER;
+       }
+
+       MESH_LOGD("Get current mpath info");
+       ret = _send_nl_get_mpath_info(mesh_if_name, mpath_list);
+
+       return ret;
+}
index fc08fc0..f59ea2c 100644 (file)
@@ -589,6 +589,36 @@ int mesh_request_disable_softap(
        return ret;     
 }
 
+int mesh_request_get_station_info(const char* mesh_interface, GList **station_list)
+{
+       int ret = MESHD_ERROR_NONE;
+
+       MESH_LOGD("Request to get station info");
+
+       /* Get station info */
+       ret = mesh_netlink_get_station_info(mesh_interface, station_list);
+       if (MESHD_ERROR_NONE != ret) {
+               return ret;
+       }
+
+       return ret;     
+}
+
+int mesh_request_get_mpath_info(const char* mesh_interface, GList **mpath_list)
+{
+       int ret = MESHD_ERROR_NONE;
+
+       MESH_LOGD("Request to get mpath info");
+
+       /* Get MPath info */
+       ret = mesh_netlink_get_mpath_info(mesh_interface, mpath_list);
+       if (MESHD_ERROR_NONE != ret) {
+               return ret;
+       }
+
+       return ret;     
+}
+
 /* Notifications */
 void mesh_notify_scan_done()
 {
index 9930c80..0929da1 100644 (file)
@@ -294,6 +294,28 @@ static void _on_scan_result_destroy(gpointer data)
        }
 }
 
+static void _on_station_list_destroy(gpointer data)
+{
+       mesh_station_info_s *info = (mesh_station_info_s*)data;
+
+       if (info) {
+               g_free(info->bssid);
+               g_free(info);
+       }
+}
+
+static void _on_mpath_list_destroy(gpointer data)
+{
+       mesh_mpath_info_s *info = (mesh_mpath_info_s*)data;
+
+       if (info) {
+               g_free(info->dest_addr);
+               g_free(info->next_hop);
+               g_free(info->interface);
+               g_free(info);
+       }
+}
+
 static gboolean _meshd_dbus_handle_get_found_mesh_networks(NetMesh *object,
                GDBusMethodInvocation *invocation,
                gpointer user_data)
@@ -681,17 +703,30 @@ static gboolean _meshd_dbus_handle_set_interfaces(NetMesh *object,
 }
 
 static gboolean _meshd_dbus_handle_get_station_info(NetMesh *object,
-               GDBusMethodInvocation *invocation)
+               GDBusMethodInvocation *invocation,
+               gpointer user_data)
 {
        int ret = MESHD_ERROR_NONE;
 
        GVariantBuilder builder;
        GVariant* station;
+       GList *iter = NULL;
 
-       MESH_LOGD("Not implemented yet !");
+       mesh_service *service = (mesh_service *)user_data;
+       mesh_interface_s *info = service->interface_info;
+
+       /* Clear mesh station list */
+       g_list_free_full(service->station_list, _on_station_list_destroy);
+       service->station_list = NULL;
 
-       /* TODO: Get station infomation and make variant data */
+       ret = mesh_request_get_station_info(
+                               info->mesh_interface, &service->station_list);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to mesh_request_get_station_info");
 
+               g_dbus_method_invocation_return_error(invocation,
+                               G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Request Failed");
+       } else {
        /*
         * sh-3.2#  iw mesh0 station dump
         * Station 7c:dd:90:62:37:cf (on mesh0)
@@ -725,6 +760,84 @@ static gboolean _meshd_dbus_handle_get_station_info(NetMesh *object,
         * short slot time:yes
         * connected time:      256 seconds
         */
+               /* Get station information and make variant data */
+               g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
+
+               iter = service->station_list;
+               while (iter != NULL) {
+                       mesh_station_info_s *item = (mesh_station_info_s*)iter->data;
+
+                       g_variant_builder_open(&builder, G_VARIANT_TYPE_VARDICT);
+                       g_variant_builder_add(&builder, "{sv}", "bssid",
+                                               g_variant_new_string(item->bssid));
+                       g_variant_builder_add(&builder, "{sv}", "inactive_time",
+                                               g_variant_new_uint32(item->inactive_time));
+                       g_variant_builder_add(&builder, "{sv}", "rx_bytes",
+                                               g_variant_new_uint64(item->rx_bytes));
+                       g_variant_builder_add(&builder, "{sv}", "rx_packets",
+                                               g_variant_new_uint32(item->rx_packets));
+                       g_variant_builder_add(&builder, "{sv}", "tx_bytes",
+                                               g_variant_new_uint64(item->tx_bytes));
+                       g_variant_builder_add(&builder, "{sv}", "tx_packets",
+                                               g_variant_new_uint32(item->tx_packets));
+                       g_variant_builder_add(&builder, "{sv}", "tx_retries",
+                                               g_variant_new_uint32(item->tx_retries));
+                       g_variant_builder_add(&builder, "{sv}", "tx_failed",
+                                               g_variant_new_uint32(item->tx_failed));
+                       g_variant_builder_add(&builder, "{sv}", "beacon_loss",
+                                               g_variant_new_uint32(item->beacon_loss));
+                       g_variant_builder_add(&builder, "{sv}", "signal",
+                                               g_variant_new_int32(item->rssi));
+                       g_variant_builder_add(&builder, "{sv}", "signal_avg",
+                                               g_variant_new_int32(item->rssi_avg));
+                       g_variant_builder_add(&builder, "{sv}", "tx_bitrate",
+                                               g_variant_new_uint32(item->tx_bitrate)); /* 10 times */
+                       g_variant_builder_add(&builder, "{sv}", "rx_bitrate",
+                                               g_variant_new_uint32(item->rx_bitrate)); /* 10 times */
+                       g_variant_builder_add(&builder, "{sv}", "mesh_llid",
+                                               g_variant_new_uint16(item->llid));
+                       g_variant_builder_add(&builder, "{sv}", "mesh_plid",
+                                               g_variant_new_uint16(item->plid));
+                       g_variant_builder_add(&builder, "{sv}", "mesh_plink",
+                                               g_variant_new_byte(item->mesh_plink)); /* 0 : DISCON, 1 : ESTAB */
+                       g_variant_builder_add(&builder, "{sv}", "local_ps_mode",
+                                               g_variant_new_uint32(item->local_ps_mode)); /* 0 : INACTIVE, 1 : ACTIVE */
+                       g_variant_builder_add(&builder, "{sv}", "peer_ps_mode",
+                                               g_variant_new_uint32(item->peer_ps_mode)); /* 0 : INACTIVE, 1 : ACTIVE */
+                       g_variant_builder_add(&builder, "{sv}", "non_peer_ps_mode",
+                                               g_variant_new_uint32(item->non_peer_ps_mode)); /* 0 : INACTIVE, 1 : ACTIVE */
+                       g_variant_builder_add(&builder, "{sv}", "authorized",
+                                               g_variant_new_boolean(item->authorized));
+                       g_variant_builder_add(&builder, "{sv}", "associated",
+                                               g_variant_new_boolean(item->associated));
+                       g_variant_builder_add(&builder, "{sv}", "preamble",
+                                               g_variant_new_boolean(item->preamble));
+                       g_variant_builder_add(&builder, "{sv}", "WMM_WME",
+                                               g_variant_new_boolean(item->wme));
+                       g_variant_builder_add(&builder, "{sv}", "MFP",
+                                               g_variant_new_boolean(item->mfp));
+                       g_variant_builder_add(&builder, "{sv}", "TDLS_peer",
+                                               g_variant_new_boolean(item->tdls_peer));
+                       g_variant_builder_add(&builder, "{sv}", "DTIM_period",
+                                               g_variant_new_byte(item->dtim_period));
+                       g_variant_builder_add(&builder, "{sv}", "beacon_interval",
+                                               g_variant_new_uint16(item->beacon_interval));
+                       g_variant_builder_add(&builder, "{sv}", "short_slot_time",
+                                               g_variant_new_boolean(item->short_slot_time));
+                       g_variant_builder_add(&builder, "{sv}", "connected_time",
+                                               g_variant_new_uint32(item->connected_time));
+                       g_variant_builder_close(&builder);
+
+                       iter = g_list_next(iter);
+               }
+
+               station = g_variant_builder_end(&builder);
+               net_mesh_complete_get_saved_mesh_network(object, invocation, station, ret);
+
+               g_object_unref(station);
+       }
+
+#if 0
        g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
        g_variant_builder_add(&builder, "{sv}", "station", g_variant_new_string("7c:dd:90:62:37:cf"));
        g_variant_builder_add(&builder, "{sv}", "inactive_time", g_variant_new_uint32(1685));
@@ -737,15 +850,15 @@ static gboolean _meshd_dbus_handle_get_station_info(NetMesh *object,
        g_variant_builder_add(&builder, "{sv}", "beacon_loss", g_variant_new_uint32(0));
        g_variant_builder_add(&builder, "{sv}", "signal", g_variant_new_int32(-64));
        g_variant_builder_add(&builder, "{sv}", "signal_avg", g_variant_new_int32(-63));
-       g_variant_builder_add(&builder, "{sv}", "tx_birrate", g_variant_new_uint32(540)); /* 10 times */
-       g_variant_builder_add(&builder, "{sv}", "rx_birrate", g_variant_new_uint32(55)); /* 10 times */
+       g_variant_builder_add(&builder, "{sv}", "tx_bitrate", g_variant_new_uint32(540)); /* 10 times */
+       g_variant_builder_add(&builder, "{sv}", "rx_bitrate", g_variant_new_uint32(55)); /* 10 times */
        g_variant_builder_add(&builder, "{sv}", "mesh_llid", g_variant_new_uint32(51731));
        g_variant_builder_add(&builder, "{sv}", "mesh_plid", g_variant_new_uint32(35432));
        g_variant_builder_add(&builder, "{sv}", "mesh_plink", g_variant_new_uint32(1)); /* 0 : DISCON, 1 : ESTAB */
        g_variant_builder_add(&builder, "{sv}", "mesh_local_PS_mode", g_variant_new_uint32(1)); /* 0 : INACTIVE, 1 : ACTIVE */
        g_variant_builder_add(&builder, "{sv}", "mesh_peer_PS_mode", g_variant_new_uint32(1)); /* 0 : INACTIVE, 1 : ACTIVE */
        g_variant_builder_add(&builder, "{sv}", "mesh_none_peer_PS_mode", g_variant_new_uint32(1)); /* 0 : INACTIVE, 1 : ACTIVE */
-       g_variant_builder_add(&builder, "{sv}", "authroized", g_variant_new_boolean(TRUE));
+       g_variant_builder_add(&builder, "{sv}", "authorized", g_variant_new_boolean(TRUE));
        g_variant_builder_add(&builder, "{sv}", "associated", g_variant_new_boolean(TRUE));
        g_variant_builder_add(&builder, "{sv}", "preamble",g_variant_new_string("long"));
        g_variant_builder_add(&builder, "{sv}", "WMM_WME", g_variant_new_boolean(TRUE));
@@ -760,55 +873,77 @@ static gboolean _meshd_dbus_handle_get_station_info(NetMesh *object,
        net_mesh_complete_get_station_info(object, invocation, station, ret);
 
        g_object_unref(station);
-
+#endif
        return TRUE;
 }
 
 static gboolean _meshd_dbus_handle_get_mpath_info(NetMesh *object,
-               GDBusMethodInvocation *invocation)
+               GDBusMethodInvocation *invocation,
+               gpointer user_data)
 {
        int ret = MESHD_ERROR_NONE;
        GVariantBuilder builder;
-       GVariant* dump_data;
+       GVariant* mpath_data;
+       GList *iter = NULL;
 
-       MESH_LOGD("Not implemented yet !");
+       mesh_service *service = (mesh_service *)user_data;
+       mesh_interface_s *info = service->interface_info;
 
-       /* TODO: Get mesh path infomation and make variant data */
+       /* Clear mesh path list */
+       g_list_free_full(service->mpath_list, _on_mpath_list_destroy);
+       service->mpath_list = NULL;
 
-       // ret = _meshd_get_mpath_dump();
+       ret = mesh_request_get_mpath_info(
+                               info->mesh_interface, &service->mpath_list);
+       if (MESHD_ERROR_NONE != ret) {
+               MESH_LOGE("Failed to mesh_request_get_mpath_info");
 
+               g_dbus_method_invocation_return_error(invocation,
+                               G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Request Failed");
+       } else {
        /*
         * Example) sh-3.2# iw mesh0 mpath dump
         * DEST ADDR         NEXT HOP          IFACE    SN      METRIC  QLEN    EXPTIME         DTIM    DRET    FLAGS
         * 7c:dd:90:62:37:cf 7c:dd:90:62:37:cf mesh0    221     152             0               10                      100             0               0x5
         */
-       g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
-       g_variant_builder_add(&builder, "{sv}", "DEST_ADDR", g_variant_new_string("7c:dd:90:62:37:cf"));
-       g_variant_builder_add(&builder, "{sv}", "NEXT_HOP", g_variant_new_string("7c:dd:90:62:37:cf"));
-       g_variant_builder_add(&builder, "{sv}", "IFACE", g_variant_new_string("mesh0"));
-       g_variant_builder_add(&builder, "{sv}", "SN", g_variant_new_uint32(221));
-       g_variant_builder_add(&builder, "{sv}", "METRIC", g_variant_new_uint32(152));
-       g_variant_builder_add(&builder, "{sv}", "QLEN", g_variant_new_uint32(0));
-       g_variant_builder_add(&builder, "{sv}", "EXPTIME", g_variant_new_uint32(10));
-       g_variant_builder_add(&builder, "{sv}", "DTIM", g_variant_new_uint32(100));
-       g_variant_builder_add(&builder, "{sv}", "DRET", g_variant_new_uint32(0));
-       g_variant_builder_add(&builder, "{sv}", "FLAGS", g_variant_new_uint32(0x05));
-
-       g_variant_builder_add(&builder, "{sv}", "DEST_ADDR", g_variant_new_string("7c:dd:90:63:21:34"));
-       g_variant_builder_add(&builder, "{sv}", "NEXT_HOP", g_variant_new_string("7c:dd:90:62:37:cf"));
-       g_variant_builder_add(&builder, "{sv}", "IFACE", g_variant_new_string("mesh0"));
-       g_variant_builder_add(&builder, "{sv}", "SN", g_variant_new_uint32(221));
-       g_variant_builder_add(&builder, "{sv}", "METRIC", g_variant_new_uint32(152));
-       g_variant_builder_add(&builder, "{sv}", "QLEN", g_variant_new_uint32(0));
-       g_variant_builder_add(&builder, "{sv}", "EXPTIME", g_variant_new_uint32(10));
-       g_variant_builder_add(&builder, "{sv}", "DTIM", g_variant_new_uint32(100));
-       g_variant_builder_add(&builder, "{sv}", "DRET", g_variant_new_uint32(0));
-       g_variant_builder_add(&builder, "{sv}", "FLAGS", g_variant_new_uint32(0x05));
-       dump_data = g_variant_builder_end(&builder);
-
-       net_mesh_complete_get_mpath_info(object, invocation, dump_data, ret);
-
-       g_object_unref(dump_data);
+               /* Get mesh path information and make variant data */
+               g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
+       
+               iter = service->mpath_list;
+               while (iter != NULL) {
+                       mesh_mpath_info_s *item = (mesh_mpath_info_s*)iter->data;
+
+                       g_variant_builder_open(&builder, G_VARIANT_TYPE_VARDICT);
+                       g_variant_builder_add(&builder, "{sv}", "DEST_ADDR",
+                                               g_variant_new_string(item->dest_addr));
+                       g_variant_builder_add(&builder, "{sv}", "NEXT_HOP",
+                                               g_variant_new_string(item->next_hop));
+                       g_variant_builder_add(&builder, "{sv}", "IFACE",
+                                               g_variant_new_string(item->interface));
+                       g_variant_builder_add(&builder, "{sv}", "SN",
+                                               g_variant_new_uint32(item->sn));
+                       g_variant_builder_add(&builder, "{sv}", "METRIC",
+                                               g_variant_new_uint32(item->metric));
+                       g_variant_builder_add(&builder, "{sv}", "QLEN",
+                                               g_variant_new_uint32(item->qlen));
+                       g_variant_builder_add(&builder, "{sv}", "EXPTIME",
+                                               g_variant_new_uint32(item->exptime));
+                       g_variant_builder_add(&builder, "{sv}", "DTIM",
+                                               g_variant_new_uint32(item->discovery_timeout));
+                       g_variant_builder_add(&builder, "{sv}", "DRET",
+                                               g_variant_new_byte(item->discovery_retries));
+                       g_variant_builder_add(&builder, "{sv}", "FLAGS",
+                                               g_variant_new_byte(item->flags));
+                       g_variant_builder_close(&builder);
+
+                       iter = g_list_next(iter);
+               }
+
+               mpath_data = g_variant_builder_end(&builder);
+               net_mesh_complete_get_mpath_info(object, invocation, mpath_data, ret);
+
+               g_object_unref(mpath_data);
+       }
 
        return TRUE;
 }