examples/ubuntu: add netlink network provider

This report the network state using netlink kernel interface thru
libnl-3.0 and allow us to remove the dependencies on network manager
command line tools.

Bug: 24466635
Change-Id: Icebc38a68522ad24a6f7cdad6e22cf39ce098ffa
Reviewed-on: https://weave-review.googlesource.com/1246
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
Reviewed-by: Alex Vakulenko <avakulenko@google.com>
diff --git a/libweave/examples/ubuntu/netlink_network.cc b/libweave/examples/ubuntu/netlink_network.cc
new file mode 100644
index 0000000..a76655a
--- /dev/null
+++ b/libweave/examples/ubuntu/netlink_network.cc
@@ -0,0 +1,115 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "examples/ubuntu/netlink_network.h"
+
+#include <weave/provider/task_runner.h>
+#include <base/bind.h>
+#include <netlink/route/link.h>
+
+#include "examples/ubuntu/ssl_stream.h"
+
+namespace weave {
+namespace examples {
+
+void NetlinkNetworkImpl::Deleter::operator()(nl_sock* s) {
+  nl_close(s);
+  nl_socket_free(s);
+}
+
+void NetlinkNetworkImpl::Deleter::operator()(nl_cache* c) {
+  nl_cache_put(c);
+}
+
+void NetlinkNetworkImpl::Deleter::operator()(rtnl_link* l) {
+  rtnl_link_put(l);
+}
+
+NetlinkNetworkImpl::NetlinkNetworkImpl(weave::provider::TaskRunner* task_runner)
+    : task_runner_(task_runner) {
+  nl_sock_.reset(nl_socket_alloc());
+  CHECK(nl_sock_);
+  CHECK_EQ(nl_connect(nl_sock_.get(), NETLINK_ROUTE), 0);
+  nl_cache* nl_cache;
+  CHECK_EQ(rtnl_link_alloc_cache(nl_sock_.get(), AF_UNSPEC, &nl_cache), 0);
+  nl_cache_.reset(nl_cache);
+  UpdateNetworkState();
+}
+
+void NetlinkNetworkImpl::AddConnectionChangedCallback(
+    const ConnectionChangedCallback& callback) {
+  callbacks_.push_back(callback);
+}
+
+void NetlinkNetworkImpl::UpdateNetworkState() {
+  // TODO(proppy): pick up interface with the default route instead of index 0.
+  // TODO(proppy): test actual internet connection.
+  // TODO(proppy): subscribe to interface changes instead of polling.
+  network_state_ = GetInterfaceState(0);
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&NetlinkNetworkImpl::UpdateNetworkState,
+                            weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(10));
+  for (const auto& cb : callbacks_)
+    cb.Run();
+}
+
+weave::provider::Network::State NetlinkNetworkImpl::GetInterfaceState(
+    int if_index) {
+  auto refill_result = nl_cache_refill(nl_sock_.get(), nl_cache_.get());
+  if (refill_result < 0) {
+    LOG(ERROR) << "failed to refresh netlink cache: " << refill_result;
+    return Network::State::kFailure;
+  }
+  std::unique_ptr<rtnl_link, Deleter> nl_link{
+      rtnl_link_get(nl_cache_.get(), if_index)};
+  if (!nl_link) {
+    LOG(ERROR) << "failed to get interface 0";
+    return Network::State::kFailure;
+  }
+
+  int state = rtnl_link_get_operstate(nl_link.get());
+  switch (state) {
+    case IF_OPER_DOWN:
+    case IF_OPER_LOWERLAYERDOWN:
+      return Network::State::kOffline;
+    case IF_OPER_DORMANT:
+      return Network::State::kConnecting;
+    case IF_OPER_UP:
+      return Network::State::kConnected;
+    case IF_OPER_TESTING:
+    case IF_OPER_NOTPRESENT:
+    case IF_OPER_UNKNOWN:
+    default:
+      LOG(ERROR) << "unknown interface state: " << state;
+      return Network::State::kFailure;
+  }
+}
+
+weave::provider::Network::State NetlinkNetworkImpl::GetConnectionState() const {
+  return network_state_;
+}
+
+void NetlinkNetworkImpl::OpenSslSocket(
+    const std::string& host,
+    uint16_t port,
+    const OpenSslSocketSuccessCallback& success_callback,
+    const ErrorCallback& error_callback) {
+  // Connect to SSL port instead of upgrading to TLS.
+  std::unique_ptr<SSLStream> tls_stream{new SSLStream{task_runner_}};
+
+  if (tls_stream->Init(host, port)) {
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(success_callback, base::Passed(&tls_stream)), {});
+  } else {
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "tls", "tls_init_failed",
+                 "Failed to initialize TLS stream.");
+    task_runner_->PostDelayedTask(FROM_HERE,
+                                  base::Bind(error_callback, error.get()), {});
+  }
+}
+
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/ubuntu/netlink_network.h b/libweave/examples/ubuntu/netlink_network.h
new file mode 100644
index 0000000..3f5f51a
--- /dev/null
+++ b/libweave/examples/ubuntu/netlink_network.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_EXAMPLES_UBUNTU_NETLINK_MANAGER_H_
+#define LIBWEAVE_EXAMPLES_UBUNTU_NETLINK_MANAGER_H_
+
+#include <weave/provider/network.h>
+
+#include <base/memory/weak_ptr.h>
+
+struct nl_sock;
+struct nl_cache;
+struct rtnl_link;
+
+namespace weave {
+
+namespace provider {
+class TaskRunner;
+}
+
+namespace examples {
+
+class NetlinkNetworkImpl : public weave::provider::Network {
+ public:
+  explicit NetlinkNetworkImpl(weave::provider::TaskRunner* task_runner_);
+  void AddConnectionChangedCallback(
+      const ConnectionChangedCallback& callback) override;
+  State GetConnectionState() const override;
+  void OpenSslSocket(const std::string& host,
+                     uint16_t port,
+                     const OpenSslSocketSuccessCallback& success_callback,
+                     const ErrorCallback& error_callback) override;
+
+ private:
+  class Deleter {
+   public:
+    void operator()(nl_sock* s);
+    void operator()(nl_cache* c);
+    void operator()(rtnl_link* l);
+  };
+  void UpdateNetworkState();
+  State GetInterfaceState(int if_index);
+  provider::TaskRunner* task_runner_{nullptr};
+  std::vector<ConnectionChangedCallback> callbacks_;
+  provider::Network::State network_state_{provider::Network::State::kOffline};
+  std::unique_ptr<nl_sock, Deleter> nl_sock_;
+  std::unique_ptr<nl_cache, Deleter> nl_cache_;
+  base::WeakPtrFactory<NetlinkNetworkImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace examples
+}  // namespace weave
+
+#endif  // LIBWEAVE_EXAMPLES_UBUNTU_NETLINK_MANAGER_H_
diff --git a/libweave/examples/ubuntu/prerequisites.sh b/libweave/examples/ubuntu/prerequisites.sh
index f64f06b..6af38b9 100755
--- a/libweave/examples/ubuntu/prerequisites.sh
+++ b/libweave/examples/ubuntu/prerequisites.sh
@@ -15,6 +15,8 @@
   libavahi-client-dev \
   libcurl4-openssl-dev \
   libexpat1-dev \
+  libnl-3-dev \
+  libnl-route-3-dev \
   libtool \
   ninja-build \
   || exit 1
diff --git a/libweave/examples/ubuntu/weave.gyp b/libweave/examples/ubuntu/weave.gyp
index a4df01d..81b492b 100644
--- a/libweave/examples/ubuntu/weave.gyp
+++ b/libweave/examples/ubuntu/weave.gyp
@@ -3,7 +3,7 @@
     {
       'target_name': 'weave',
       'type': 'executable',
-      'cflags': ['-pthread'],
+      'cflags': ['-pthread', '-I/usr/include/libnl3'],
       'sources': [
         'avahi_client.cc',
         'bluez_client.cc',
@@ -12,6 +12,7 @@
         'event_task_runner.cc',
         'file_config_store.cc',
         'main.cc',
+        'netlink_network.cc',
         'network_manager.cc',
         'ssl_stream.cc',
       ],
@@ -28,6 +29,8 @@
         '-lavahi-common',
         '-lavahi-client',
         '-levent_openssl',
+        '-lnl-3',
+        '-lnl-route-3',
       ]
     }
   ]