patchpanel: apply VPN routing tag
This patch adds to patchpanel datapath service the iptables rules for:
- Tagging with the VPN routing tag 0xfffe0000 the fwmark of all
packets that should be routed through a VPN.
- Saving in conntrack the VPN routing tag of all traffic routed through
a VPN.
- Restoring the routing tag (VPN or physical) of any established
connection for sockets in the host namespaces.
This patch together with the shill ip rule patch with Change-Id
I534337bc6cad8b16a723c38cf9bb5d71f89bd831 effectively migrates the VPN
routing implementation from being based fully routing rules and uid
matching to being based on the new fwmark routing tags managed by
patchpanel.
BUG=b:161507671
BUG=chromium:1022028
BUG=chromium:911954
BUG=chromium:973065
TEST=Flashed rammus with patchpanel routing changes, connected multiple
types of VPNs:
- l2tp full tunnel without Internet,
- OpenVPN split tunnel,
- Android VPNs full tunnel,
- Chrome AnyConnect split tunnel,
and conducted various testing:
- General connectivity using ping, traceroute, wget (or equivalent
actions) on Chrome, Linux VM terminal, ARC, crosh, shell.
- Checked DNS settings and DNS reachability.
- Checked Internet reachability for VPNs with Internet.
- Checked split routing for VPNs with split routing setup.
- Checked connection pinning for Chrome, Linux VMs, ARC, shell.
Cq-Depend: chromium:2532025
Disallow-Recycled-Builds: hatch-cq
Change-Id: I2f342faac4b662610be024d62e7c18d04cddfb09
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2499121
Tested-by: Hugo Benichi <hugobenichi@google.com>
Commit-Queue: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Garrick Evans <garrick@chromium.org>
diff --git a/patchpanel/datapath.cc b/patchpanel/datapath.cc
index 988f781..02a4145 100644
--- a/patchpanel/datapath.cc
+++ b/patchpanel/datapath.cc
@@ -15,7 +15,7 @@
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <vector>
+#include <algorithm>
#include <base/files/scoped_file.h>
#include <base/logging.h>
@@ -48,6 +48,11 @@
constexpr char kApplyLocalSourceMarkChain[] = "apply_local_source_mark";
constexpr char kApplyVpnMarkChain[] = "apply_vpn_mark";
+// Constant fwmark mask for matching local socket traffic that should be routed
+// through a VPN connection. The traffic must not be part of an existing
+// connection and must match exactly the VPN routing intent policy bit.
+const Fwmark kFwmarkVpnMatchingMask = kFwmarkRoutingMask | kFwmarkVpnMask;
+
std::string PrefixIfname(const std::string& prefix, const std::string& ifname) {
std::string n = prefix + ifname;
if (n.length() < IFNAMSIZ)
@@ -130,6 +135,11 @@
LOG(ERROR) << "Failed to set up NAT for TAP devices."
<< " Guest connectivity may be broken.";
+ // Applies the routing tag saved in conntrack for any established connection
+ // for sockets created in the host network namespace.
+ if (!ModifyConnmarkRestore(IpFamily::Dual, "OUTPUT", "-A", "" /*iif*/))
+ LOG(ERROR) << "Failed to add OUTPUT CONNMARK restore rule";
+
// Set up a mangle chain used in OUTPUT for applying the fwmark TrafficSource
// tag and tagging the local traffic that should be routed through a VPN.
if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kApplyLocalSourceMarkChain))
@@ -174,8 +184,6 @@
"0x0/" + kFwmarkRoutingMask.ToString(), "-j", "ACCEPT", "-w"}))
LOG(ERROR) << "Failed to add ACCEPT rule to VPN tagging chain for marked "
"connections";
- // TODO(b/161507671) Dynamically add fwmark routing tagging rules based on
- // the VPN tunnel interface.
}
void Datapath::Stop() {
@@ -213,6 +221,11 @@
LOG(ERROR) << "Failed to detach " << kApplyLocalSourceMarkChain
<< " from mangle OUTPUT";
+ // Stops applying routing tags saved in conntrack for sockets created in the
+ // host network namespace.
+ if (!ModifyConnmarkRestore(IpFamily::Dual, "OUTPUT", "-D", "" /*iif*/))
+ LOG(ERROR) << "Failed to remove OUTPUT CONNMARK restore rule";
+
// Delete the mangle chains
for (const auto* chain : {kApplyLocalSourceMarkChain, kApplyVpnMarkChain}) {
if (!ModifyChain(IpFamily::Dual, "mangle", "-F", chain))
@@ -615,10 +628,14 @@
LOG(ERROR) << "Failed to enable IP forwarding for " << ext_ifname << "<-"
<< int_ifname;
+ if (!ModifyFwmarkSourceTag("-A", int_ifname, source))
+ LOG(ERROR) << "Failed to add PREROUTING fwmark tagging rule for source "
+ << source << " for " << int_ifname;
+
if (!ext_ifname.empty()) {
// If |ext_ifname| is not null, mark egress traffic with the
// fwmark routing tag corresponding to |ext_ifname|.
- if (!ModifyFwmarkRoutingTag("-A", ext_ifname, int_ifname))
+ if (!ModifyFwmarkRoutingTag("PREROUTING", "-A", ext_ifname, int_ifname))
LOG(ERROR) << "Failed to add PREROUTING fwmark routing tag for "
<< ext_ifname << "<-" << int_ifname;
} else {
@@ -635,10 +652,6 @@
if (!ModifyFwmarkVpnJumpRule("PREROUTING", "-A", int_ifname, {}, {}))
LOG(ERROR) << "Failed to add jump rule to VPN chain for " << int_ifname;
}
-
- if (!ModifyFwmarkSourceTag("-A", int_ifname, source))
- LOG(ERROR) << "Failed to add PREROUTING fwmark tagging rule for source "
- << source << " for " << int_ifname;
}
void Datapath::StopRoutingDevice(const std::string& ext_ifname,
@@ -651,7 +664,7 @@
StopIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname);
ModifyFwmarkSourceTag("-D", int_ifname, source);
if (!ext_ifname.empty()) {
- ModifyFwmarkRoutingTag("-D", ext_ifname, int_ifname);
+ ModifyFwmarkRoutingTag("PREROUTING", "-D", ext_ifname, int_ifname);
} else {
ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-D", int_ifname);
ModifyFwmarkVpnJumpRule("PREROUTING", "-D", int_ifname, {}, {});
@@ -804,6 +817,18 @@
LOG(ERROR) << "Could not stop connection pinning on " << ext_ifname;
}
+void Datapath::StartVpnRouting(const std::string& vpn_ifname) {
+ StartConnectionPinning(vpn_ifname);
+ if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-A", vpn_ifname, ""))
+ LOG(ERROR) << "Failed to set up VPN set-mark rule for " << vpn_ifname;
+}
+
+void Datapath::StopVpnRouting(const std::string& vpn_ifname) {
+ if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-D", vpn_ifname, ""))
+ LOG(ERROR) << "Failed to remove VPN set-mark rule for " << vpn_ifname;
+ StopConnectionPinning(vpn_ifname);
+}
+
bool Datapath::ModifyConnmarkSetPostrouting(IpFamily family,
const std::string& op,
const std::string& oif) {
@@ -862,7 +887,8 @@
return ModifyIptables(family, "mangle", args);
}
-bool Datapath::ModifyFwmarkRoutingTag(const std::string& op,
+bool Datapath::ModifyFwmarkRoutingTag(const std::string& chain,
+ const std::string& op,
const std::string& ext_ifname,
const std::string& int_ifname) {
int ifindex = FindIfIndex(ext_ifname);
@@ -871,9 +897,8 @@
return false;
}
- return ModifyFwmark(IpFamily::Dual, "PREROUTING", op, int_ifname,
- "" /*uid_name*/, Fwmark::FromIfIndex(ifindex),
- kFwmarkRoutingMask);
+ return ModifyFwmark(IpFamily::Dual, chain, op, int_ifname, "" /*uid_name*/,
+ Fwmark::FromIfIndex(ifindex), kFwmarkRoutingMask);
}
bool Datapath::ModifyFwmarkSourceTag(const std::string& op,