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
This commit is contained in:
Ian Southam 2015-03-05 17:12:21 +01:00 committed by wilderrodrigues
parent 6fc680be71
commit 7bfccd699b
6 changed files with 111 additions and 36 deletions

View File

@ -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"])

View File

@ -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"])

View File

@ -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']

View File

@ -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)

View File

@ -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):

View File

@ -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