Skip to content

Commit

Permalink
add routes
Browse files Browse the repository at this point in the history
  • Loading branch information
MelvinTo committed Jan 19, 2024
1 parent 55f13f2 commit 08810a7
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
16 changes: 13 additions & 3 deletions extension/vpnclient/VPNClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,16 @@ class VPNClient {
return;
}
const remoteIP = await this._getRemoteIP();
const remoteIP6 = await this._getRemoteIP6();
const intf = this.getInterfaceName();
const snatNeeded = await this.isSNATNeeded();
if (snatNeeded)
if (snatNeeded) {
await exec(iptables.wrapIptables(`sudo iptables -w -t nat -A FW_POSTROUTING -o ${intf} -j MASQUERADE`)).catch((err) => {});
log.info(`Refresh VPN client routes for ${this.profileId}, remote: ${remoteIP}, intf: ${intf}`);
if(remoteIP6) {
await exec(iptables.wrapIptables(`sudo ip6tables -w -t nat -A FW_POSTROUTING -o ${intf} -j MASQUERADE`)).catch((err) => {});
}
}
log.info(`Refresh VPN client routes for ${this.profileId}, remote: ${remoteIP}, remote6: ${remoteIP6} intf: ${intf}`);
// remove routes from main table which is inserted by VPN client automatically,
// otherwise tunnel will be enabled globally
await routing.removeRouteFromTable("0.0.0.0/1", remoteIP, intf, "main").catch((err) => { log.info("No need to remove 0.0.0.0/1 for " + this.profileId) });
Expand All @@ -314,7 +319,7 @@ class VPNClient {

log.info(`Adding routes for vpn ${this.profileId}`, routedSubnets);
// always add default route into VPN client's routing table, the switch is implemented in ipset, so no need to implement it in routing tables
await vpnClientEnforcer.enforceVPNClientRoutes(remoteIP, intf, routedSubnets, dnsServers, true);
await vpnClientEnforcer.enforceVPNClientRoutes(remoteIP, remoteIP6, intf, routedSubnets, dnsServers, true);
// loosen reverse path filter
await exec(`sudo sysctl -w net.ipv4.conf.${intf}.rp_filter=2`).catch((err) => { });
const rtId = await vpnClientEnforcer.getRtId(this.getInterfaceName());
Expand Down Expand Up @@ -650,6 +655,10 @@ class VPNClient {
return null;
}

async _getRemoteIP6() {
return null;
}

async _getLocalIP() {
const intf = this.getInterfaceName();
return exec(`ip addr show dev ${intf} | awk '/inet /' | awk '{print $2}' | head -n 1`).then(result => result.stdout.trim().split('/')[0]).catch((err) => null);
Expand Down Expand Up @@ -896,6 +905,7 @@ class VPNClient {
await vpnClientEnforcer.flushVPNClientRoutes(intf);
await vpnClientEnforcer.removeVPNClientIPRules(intf);
await exec(iptables.wrapIptables(`sudo iptables -w -t nat -D FW_POSTROUTING -o ${intf} -j MASQUERADE`)).catch((err) => {});
await exec(iptables.wrapIptables(`sudo ip6tables -w -t nat -D FW_POSTROUTING -o ${intf} -j MASQUERADE`)).catch((err) => {});
await this.loadSettings();
const dnsServers = await this._getDNSServers() || [];
if (dnsServers.length > 0) {
Expand Down
9 changes: 7 additions & 2 deletions extension/vpnclient/VPNClientEnforcer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const platform = platformLoader.getPlatform();
const Mode = require('../../net2/Mode.js');
const {Address4, Address6} = require('ip-address');
const FireRouter = require('../../net2/FireRouter.js');
const _ = require('lodash');

const execAsync = util.promisify(cp.exec);

Expand Down Expand Up @@ -93,7 +94,7 @@ class VPNClientEnforcer {
});
}

async enforceVPNClientRoutes(remoteIP, vpnIntf, routedSubnets = [], dnsServers = [], overrideDefaultRoute = true) {
async enforceVPNClientRoutes(remoteIP, remoteIP6, vpnIntf, routedSubnets = [], dnsServers = [], overrideDefaultRoute = true) {
if (!vpnIntf)
throw "Interface is not specified";
const tableName = this._getRoutingTableName(vpnIntf);
Expand Down Expand Up @@ -167,6 +168,10 @@ class VPNClientEnforcer {
if (overrideDefaultRoute) {
// then add remote IP as gateway of default route to vpn client table
await routing.addRouteToTable("default", remoteIP, vpnIntf, tableName).catch((err) => {}); // this usually happens when multiple function calls are executed simultaneously. It should have no side effect and will be consistent eventually
// FIXME: need to handle server subnets also in the future
if(remoteIP6) {
await routing.addRouteToTable("default", remoteIP6, vpnIntf, tableName, null, 6).catch((err) => {}); // this usually happens when multiple function calls are executed simultaneously. It should have no side effect and will be consistent eventually
}
}
// add inbound connmark rule for vpn client interface
await execAsync(wrapIptables(`sudo iptables -w -t nat -A FW_PREROUTING_VC_INBOUND -i ${vpnIntf} -j CONNMARK --set-xmark ${rtId}/${routing.MASK_ALL}`)).catch((err) => {
Expand Down Expand Up @@ -224,4 +229,4 @@ class VPNClientEnforcer {
}

const instance = new VPNClientEnforcer();
module.exports = instance;
module.exports = instance;
12 changes: 12 additions & 0 deletions extension/vpnclient/docker/DockerBaseVPNClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,22 @@ class DockerBaseVPNClient extends VPNClient {
return null;
}

async _getRemoteIP6() {
const subnet = await this._getSubnet6();
if (subnet) {
return Address6.fromBigInteger(new Address6(subnet).bigInteger().add(new BigInteger("2"))).correctForm(); // IPv6 address of gateway in container always uses second address in subnet
}
return null;
}

async _getSubnet() {
return await fs.readFileAsync(this._getSubnetFilePath(), {encoding: "utf8"}).then(content => content.trim()).catch((err) => null);
}

async _getSubnet6() {
return await fs.readFileAsync(this._getV6SubnetFilePath(), {encoding: "utf8"}).then(content => content.trim()).catch((err) => null);
}

async _getOrGenerateSubnet() {
let subnet = await this._getSubnet();
if (!subnet) {
Expand Down

0 comments on commit 08810a7

Please sign in to comment.