Implement creating VLAN interfaces

Also, minor improvements for TCU:
- added existsAndIsUp convenience function
- added useCanSockets configuration flag

Bug: 156783614
Test: manual
Change-Id: Ia3d48655067792b712519e25ed48ae0cb657347b
diff --git a/automotive/can/1.0/default/libnetdevice/Android.bp b/automotive/can/1.0/default/libnetdevice/Android.bp
index 31e5ad0..6e2c782 100644
--- a/automotive/can/1.0/default/libnetdevice/Android.bp
+++ b/automotive/can/1.0/default/libnetdevice/Android.bp
@@ -25,6 +25,7 @@
         "can.cpp",
         "common.cpp",
         "libnetdevice.cpp",
+        "vlan.cpp",
     ],
     export_include_dirs: ["include"],
 }
diff --git a/automotive/can/1.0/default/libnetdevice/can.cpp b/automotive/can/1.0/default/libnetdevice/can.cpp
index a2a85dc..06fa8aa 100644
--- a/automotive/can/1.0/default/libnetdevice/can.cpp
+++ b/automotive/can/1.0/default/libnetdevice/can.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <libnetdevice/libnetdevice.h>
+#include <libnetdevice/can.h>
 
 #include "NetlinkRequest.h"
 #include "NetlinkSocket.h"
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 3818a31..54cbafc 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -22,6 +22,17 @@
 namespace android::netdevice {
 
 /**
+ * Configures libnetdevice to use PF_CAN sockets instead of AF_INET,
+ * what requires less permissive SEPolicy rules for a given process.
+ *
+ * In such case, the process would only be able to control CAN interfaces.
+ *
+ * TODO(b/158011272): consider less hacky solution
+ * \param yes true to use CAN sockets, false for general sockets
+ */
+void useCanSockets(bool yes);
+
+/**
  * Checks, if the network interface exists.
  *
  * \param ifname Interface to check
@@ -38,6 +49,16 @@
 std::optional<bool> isUp(std::string ifname);
 
 /**
+ * Checks, if the network interface exists and is up.
+ *
+ * This is a convenience function to call both exists() and isUp().
+ *
+ * \param ifname Interface to check
+ * \return true if the interface is up, false otherwise
+ */
+bool existsAndIsUp(const std::string& ifname);
+
+/**
  * Brings network interface up.
  *
  * \param ifname Interface to bring up
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/vlan.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/vlan.h
new file mode 100644
index 0000000..3e1b736
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/vlan.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android::netdevice::vlan {
+
+bool add(const std::string& eth, const std::string& vlan, uint16_t id);
+
+}  // namespace android::netdevice::vlan
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index b051442..827f8f3 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -27,14 +27,32 @@
 
 namespace android::netdevice {
 
+namespace socketparams {
+
+struct Params {
+    int domain;
+    int type;
+    int protocol;
+};
+
+static constexpr Params general = {AF_INET, SOCK_DGRAM, 0};
+static constexpr Params can = {PF_CAN, SOCK_RAW, CAN_RAW};
+
+static Params current = general;
+
+}  // namespace socketparams
+
+void useCanSockets(bool yes) {
+    socketparams::current = yes ? socketparams::can : socketparams::general;
+}
+
 bool exists(std::string ifname) {
     return nametoindex(ifname) != 0;
 }
 
 static bool sendIfreq(unsigned long request, struct ifreq& ifr) {
-    /* For general interfaces it would be socket(AF_INET, SOCK_DGRAM, 0),
-     * but SEPolicy forces us to limit our flexibility here. */
-    base::unique_fd sock(socket(PF_CAN, SOCK_RAW, CAN_RAW));
+    base::unique_fd sock(socket(socketparams::current.domain, socketparams::current.type,
+                                socketparams::current.protocol));
     if (!sock.ok()) {
         LOG(ERROR) << "Can't create socket";
         return false;
@@ -60,6 +78,10 @@
     return ifr.ifr_flags & IFF_UP;
 }
 
+bool existsAndIsUp(const std::string& ifname) {
+    return exists(ifname) && isUp(ifname).value_or(false);
+}
+
 bool up(std::string ifname) {
     struct ifreq ifr = ifreqFromName(ifname);
     if (!sendIfreq(SIOCGIFFLAGS, ifr)) return false;
diff --git a/automotive/can/1.0/default/libnetdevice/vlan.cpp b/automotive/can/1.0/default/libnetdevice/vlan.cpp
new file mode 100644
index 0000000..82ef0d9
--- /dev/null
+++ b/automotive/can/1.0/default/libnetdevice/vlan.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libnetdevice/vlan.h>
+
+#include "NetlinkRequest.h"
+#include "NetlinkSocket.h"
+#include "common.h"
+
+#include <android-base/logging.h>
+
+namespace android::netdevice::vlan {
+
+bool add(const std::string& eth, const std::string& vlan, uint16_t id) {
+    const auto ethidx = nametoindex(eth);
+    if (ethidx == 0) {
+        LOG(ERROR) << "Ethernet interface " << eth << " doesn't exist";
+        return false;
+    }
+
+    NetlinkRequest<struct ifinfomsg> req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL);
+    req.addattr(IFLA_IFNAME, vlan);
+    req.addattr<uint32_t>(IFLA_LINK, ethidx);
+
+    {
+        auto linkinfo = req.nest(IFLA_LINKINFO);
+        req.addattr(IFLA_INFO_KIND, "vlan");
+
+        {
+            auto linkinfo = req.nest(IFLA_INFO_DATA);
+            req.addattr(IFLA_VLAN_ID, id);
+        }
+    }
+
+    NetlinkSocket sock(NETLINK_ROUTE);
+    return sock.send(req) && sock.receiveAck();
+}
+
+}  // namespace android::netdevice::vlan
diff --git a/automotive/can/1.0/default/service.cpp b/automotive/can/1.0/default/service.cpp
index b52a54a..b5801c0 100644
--- a/automotive/can/1.0/default/service.cpp
+++ b/automotive/can/1.0/default/service.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/logging.h>
 #include <hidl/HidlTransportSupport.h>
+#include <libnetdevice/libnetdevice.h>
 
 namespace android::hardware::automotive::can::V1_0::implementation {
 
@@ -27,6 +28,8 @@
     configureRpcThreadpool(16, true);
     LOG(DEBUG) << "CAN controller service starting...";
 
+    netdevice::useCanSockets(true);
+
     sp<CanController> canController(new CanController);
     if (canController->registerAsService("socketcan") != OK) {
         LOG(FATAL) << "Failed to register CAN controller";