From 7bfccd699b1d17eb2b8c681b11d1f10c21bb2516 Mon Sep 17 00:00:00 2001 From: Ian Southam Date: Thu, 5 Mar 2015 17:12:21 +0100 Subject: [PATCH] Fixed Port forwarding (DNAT/SNAT) for isolated networks Fixed failure on using eth10 (which CS now supports) Refactored CSForward to us CsInterface object instead of teh way it was kludged together Fixed hex conversion for device numbers and iptables marks --- .../debian/config/opt/cloud/bin/configure.py | 90 ++++++++++++++++--- .../config/opt/cloud/bin/cs/CsAddress.py | 26 ++++-- .../config/opt/cloud/bin/cs/CsDatabag.py | 23 ++--- .../debian/config/opt/cloud/bin/cs/CsDhcp.py | 2 +- .../debian/config/opt/cloud/bin/cs/CsRoute.py | 2 +- .../debian/config/opt/cloud/bin/cs/CsRule.py | 4 +- 6 files changed, 111 insertions(+), 36 deletions(-) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index 399c9c420cc..246c9959bb7 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -496,15 +496,22 @@ class CsForwardingRules(CsDataBag): elif rule["type"] == "staticnat": self.processStaticNatRule(rule) - def getDeviceByIp(self, ip): - ips = CsDataBag("ips") - dbag = ips.get_bag() - for device in dbag: - if device == "id": - continue - for addy in dbag[device]: - if addy["public_ip"] == ip: - return device + def getDeviceByIp(self, ipa): + for ip in self.config.address().get_ips(): + if ip.ip_in_subnet(ipa): + return ip.get_device() + return None + + def getNetworkByIp(self, ipa): + for ip in self.config.address().get_ips(): + if ip.ip_in_subnet(ipa): + return ip.get_network() + return None + + def getGatewayByIp(self, ipa): + for ip in self.config.address().get_ips(): + if ip.ip_in_subnet(ipa): + return ip.get_gateway() return None def portsToString(self, ports, delimiter): @@ -515,7 +522,70 @@ class CsForwardingRules(CsDataBag): return "%s%s%s" % (ports_parts[0], delimiter, ports_parts[1]) def processForwardRule(self, rule): - # FIXME this seems to be different for regular VRs? + if self.config.is_vpc(): + self.forward_vpc(rule) + else: + self.forward_vr(rule) + + def forward_vr(self, rule): + fw1 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ + ( rule['public_ip'], + self.getDeviceByIp(rule['public_ip']), + rule['protocol'], + rule['protocol'], + self.portsToString(rule['public_ports'], ':'), + rule['internal_ip'], + self.portsToString(rule['internal_ports'], '-') + ) + fw2 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ + ( rule['public_ip'], + self.getDeviceByIp(rule['internal_ip']), + rule['protocol'], + rule['protocol'], + self.portsToString(rule['public_ports'], ':'), + rule['internal_ip'], + self.portsToString(rule['internal_ports'], '-') + ) + fw3 = "-A OUTPUT -d %s/32 -p %s -m %s --dport %s -j DNAT --to-destination %s:%s" % \ + ( rule['public_ip'], + rule['protocol'], + rule['protocol'], + self.portsToString(rule['public_ports'], ':'), + rule['internal_ip'], + self.portsToString(rule['internal_ports'], '-') + ) + fw4 = "-j SNAT --to-source %s -A POSTROUTING -s %s -d %s/32 -o %s -p %s -m %s --dport %s" % \ + ( self.getGatewayByIp(rule['internal_ip']), + self.getNetworkByIp(rule['internal_ip']), + rule['internal_ip'], + self.getDeviceByIp(rule['internal_ip']), + rule['protocol'], + rule['protocol'], + self.portsToString(rule['internal_ports'], ':') + ) + fw5 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -j MARK --set-xmark %s/0xffffffff" % \ + ( rule['public_ip'], + self.getDeviceByIp(rule['public_ip']), + rule['protocol'], + rule['protocol'], + self.portsToString(rule['public_ports'], ':'), + hex(int(self.getDeviceByIp(rule['public_ip'])[3:])) + ) + fw6 = "-A PREROUTING -d %s/32 -i %s -p %s -m %s --dport %s -m state --state NEW -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff" % \ + ( rule['public_ip'], + self.getDeviceByIp(rule['public_ip']), + rule['protocol'], + rule['protocol'], + self.portsToString(rule['public_ports'], ':'), + ) + self.fw.append(["nat", "", fw1]) + self.fw.append(["nat", "", fw2]) + self.fw.append(["nat", "", fw3]) + self.fw.append(["nat", "", fw4]) + self.fw.append(["nat", "", fw5]) + self.fw.append(["nat", "", fw6]) + + def forward_vpc(self, rule): fwrule = "-A PREROUTING -d %s/32" % rule["public_ip"] if not rule["protocol"] == "any": fwrule += " -m %s -p %s" % (rule["protocol"], rule["protocol"]) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py index f675a6554cd..7dc357bc1e8 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsAddress.py @@ -19,6 +19,7 @@ from CsDatabag import CsDataBag, CsCmdLine from CsApp import CsApache, CsDnsmasq, CsPasswdSvc import CsHelper import logging +from netaddr import IPAddress, IPNetwork import CsHelper import subprocess @@ -118,6 +119,9 @@ class CsInterface: def get_ip(self): return self.get_attr("public_ip") + def get_network(self): + return self.get_attr("network") + def get_netmask(self): return self.get_attr("netmask") @@ -125,7 +129,15 @@ class CsInterface: if self.config.is_vpc(): return self.get_attr("gateway") else: - return self.config.cmdline().get_guest_gw() + if self.config.cmdline().is_redundant(): + return self.config.cmdline().get_guest_gw() + else: + return self.get_ip() + + def ip_in_subnet(self, ip): + ipo = IPAddress(ip) + net = IPNetwork("%s/%s" % (self.get_ip(), self.get_size())) + return ipo in list(net) def get_gateway_cidr(self): return "%s/%s" % (self.get_gateway(), self.get_size()) @@ -185,7 +197,7 @@ class CsDevice: self.table = '' self.tableNo = '' if dev != '': - self.tableNo = dev[3] + self.tableNo = dev[3:] self.table = "Table_%s" % dev self.fw = config.get_fw() self.cl = config.cmdline() @@ -228,7 +240,7 @@ class CsIP: def __init__(self, dev, config): self.dev = dev - self.dnum = dev[3] + self.dnum = hex(int(dev[3:])) self.iplist = {} self.address = {} self.list() @@ -275,8 +287,8 @@ class CsIP: CsHelper.execute(cmd2) def set_mark(self): - cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark 0x%s/0xffffffff" % \ - (self.getDevice(), self.getDevice()[3]) + cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark %s/0xffffffff" % \ + (self.getDevice(), self.dnum) self.fw.append(["mangle", "", cmd]) def get_type(self): @@ -327,7 +339,7 @@ class CsIP: "-A POSTROUTING -o eth2 -j SNAT --to-source %s" % self.address['public_ip']]) self.fw.append(["mangle", "", "-A PREROUTING -i %s -m state --state NEW " % self.dev + - "-j CONNMARK --set-xmark 0x%s/0xffffffff" % self.dnum]) + "-j CONNMARK --set-xmark %s/0xffffffff" % self.dnum]) self.fw.append(["mangle", "", "-A FIREWALL_%s -j DROP" % self.address['public_ip']]) self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"]) @@ -350,7 +362,7 @@ class CsIP: self.fw.append(["filter", "", "-A FORWARD -i eth0 -o eth2 -j FW_OUTBOUND"]) self.fw.append(["mangle", "", "-A PREROUTING -i %s -m state --state NEW " % self.dev + - "-j CONNMARK --set-xmark 0x%s/0xffffffff" % self.dnum]) + "-j CONNMARK --set-xmark %s/0xffffffff" % self.dnum]) if self.get_type() in ["control"]: self.fw.append(["filter", "", "-A FW_OUTBOUND -m state --state RELATED,ESTABLISHED -j ACCEPT"]) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py index 035e864bc34..e8f15fed9ba 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDatabag.py @@ -62,6 +62,14 @@ class CsCmdLine(CsDataBag): return self.idata()['router_pr'] return 99 + def set_guest_gw(self, val): + self.idata()['guestgw'] = val + + def get_guest_gw(self): + if "guestgw" in self.idata(): + return self.idata()['guestgw'] + return False + def set_priority(self, val): self.idata()['router_pr'] = val @@ -73,21 +81,6 @@ class CsCmdLine(CsDataBag): def set_redundant(self, val="true"): self.idata()['redundant_router'] = val - def set_guest_gw(self, val): - self.idata()['guestgw'] = val - - def get_guest_gw(self): - if "guestgw" in self.idata(): - return self.idata()['guestgw'] - else: - return "1.2.3.4" - - def get_guest_gw_cidr(self): - if "guestgw" in self.idata(): - return "%s/%s" % (self.idata()['guestgw'], self.idata()['guestcidrsize']) - else: - return "1.2.3.4/8" - def get_name(self): if "name" in self.idata(): return self.idata()['name'] diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py index 821444f7459..234ed4cb1dd 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsDhcp.py @@ -98,7 +98,7 @@ class CsDhcp(CsDataBag): to = {"device": bits[0], "mac": bits[1], "ip": bits[2], - "host": bits[3], + "host": bits[3:], "del": False } changed.append(to) diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRoute.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRoute.py index f44ce5897e9..6fb6e1c6749 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRoute.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRoute.py @@ -24,7 +24,7 @@ class CsRoute: def __init__(self, dev): self.dev = dev - self.tableNo = dev[3] + self.tableNo = dev[3:] self.table = "Table_%s" % (dev) def routeTable(self): diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRule.py b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRule.py index e2ca806f029..ed164b3bdc6 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRule.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs/CsRule.py @@ -27,7 +27,7 @@ class CsRule: def __init__(self, dev): self.dev = dev - self.tableNo = dev[3] + self.tableNo = int(dev[3:]) self.table = "Table_%s" % (dev) def addMark(self): @@ -37,7 +37,7 @@ class CsRule: logging.info("Added fwmark rule for %s" % (self.table)) def findMark(self): - srch = "from all fwmark 0x%s lookup %s" % (self.tableNo, self.table) + srch = "from all fwmark %s lookup %s" % (hex(self.tableNo), self.table) for i in CsHelper.execute("ip rule show"): if srch in i.strip(): return True