patchpanel: route arcbr0 through non-ARC VPNs

This patch sets ARC's arcbr0 routing tags so that traffic originated
through arcbr0 by ARC is routed through any non-ARC VPN connection.

Inside ARC, arcbr0 is used to expose a fake "facade" VPN network
corresponding to the non-ARC VPN established on the host environment.

BUG=b:161507671
BUG=b:161508179
BUG=chromium:1157515
TEST=unit tests. Checked that ARC traffic is correctly routed on the
host when a full tunnel VPN connection is set up on the host.

Change-Id: I7375af1a604bfc6ecb8a729decb55b13a8717af0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2586457
Tested-by: Hugo Benichi <hugobenichi@google.com>
Commit-Queue: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Taoyu Li <taoyl@chromium.org>
diff --git a/patchpanel/datapath.cc b/patchpanel/datapath.cc
index fc84da5..b9f0fef 100644
--- a/patchpanel/datapath.cc
+++ b/patchpanel/datapath.cc
@@ -26,6 +26,7 @@
 #include <brillo/userdb_utils.h>
 
 #include "patchpanel/adb_proxy.h"
+#include "patchpanel/arc_service.h"
 #include "patchpanel/net_util.h"
 #include "patchpanel/scoped_ns.h"
 
@@ -619,6 +620,7 @@
                                   TrafficSource source,
                                   bool route_on_vpn) {
   if (source == TrafficSource::ARC && !ext_ifname.empty() &&
+      int_ipv4_addr != 0 &&
       !AddInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr)))
     LOG(ERROR) << "Failed to configure ingress traffic rules for " << ext_ifname
                << "->" << int_ifname;
@@ -665,7 +667,7 @@
                                  uint32_t int_ipv4_addr,
                                  TrafficSource source,
                                  bool route_on_vpn) {
-  if (source == TrafficSource::ARC && !ext_ifname.empty())
+  if (source == TrafficSource::ARC && !ext_ifname.empty() && int_ipv4_addr != 0)
     RemoveInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr));
   StopIpForwarding(IpFamily::IPv4, ext_ifname, int_ifname);
   StopIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname);
@@ -854,9 +856,15 @@
   StartConnectionPinning(vpn_ifname);
   if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-A", vpn_ifname, ""))
     LOG(ERROR) << "Failed to set up VPN set-mark rule for " << vpn_ifname;
+  if (vpn_ifname != kArcBridge)
+    StartRoutingDevice(vpn_ifname, kArcBridge, 0 /*no inbound DNAT */,
+                       TrafficSource::ARC, true /* route_on_vpn */);
 }
 
 void Datapath::StopVpnRouting(const std::string& vpn_ifname) {
+  if (vpn_ifname != kArcBridge)
+    StopRoutingDevice(vpn_ifname, kArcBridge, 0 /* no inbound DNAT */,
+                      TrafficSource::ARC, false /* route_on_vpn */);
   if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-D", vpn_ifname, ""))
     LOG(ERROR) << "Failed to remove VPN set-mark rule for " << vpn_ifname;
   StopConnectionPinning(vpn_ifname);