+"""
+This plugin sets up the NAT interfaces for PlanetStack. It processes
+each interface that has a 'nat' tag set.
+
+It communicates with OvS on the local node and Quantum to gather
+information about devices. It uses this information to:
+* add the Quantum-assigned IP address to the interface via dnsmasq
+* set up port forwarding rules through the NAT using iptables
+
+The iptables configuration uses a chain called 'planetstack-net' to
+hold the port forwarding rules. This is called from the PREROUTING
+chain of the nat table. The chain is flushed and rebuilt every time
+the plugin runs to avoid stale rules. This plugin also sets up the
+MASQ rule in the POSTROUTING chain.
+"""
+
# system provided modules
import os, string, time, socket
from socket import inet_aton
return '.'.join(start) + ',' + '.'.join(end)
+# Should possibly be using python-iptables for this stuff
+def run_iptables_cmd(args):
+ cmd = ['/sbin/iptables'] + args
+ logger.log('%s: %s' % (plugin, ' '.join(cmd)))
+ subprocess.check_call(cmd)
+
+def add_iptables_rule(table, chain, args):
+ args = ['-t', table, '-C', chain] + args
+ try:
+ run_iptables_cmd(args)
+ except:
+ args[2] = '-A'
+ try:
+ run_iptables_cmd(args)
+ except:
+ logger.log('%s: FAILED to add iptables rule' % plugin)
+
+def reset_iptables_chain():
+ try:
+ # Flush the planetstack-nat chain
+ run_iptables_cmd(['-t', 'nat', '-F', plugin])
+ except:
+ # Probably the chain doesn't exist, try creating it
+ run_iptables_cmd(['-t', 'nat', '-N', plugin])
+
+ add_iptables_rule('nat', 'PREROUTING', ['-j', plugin])
+
# Enable iptables MASQ for a device
def add_iptables_masq(dev, interface):
ipaddr = interface['ip']
% (plugin, ipaddr, netmask))
return
- cmd = ['/sbin/iptables', '-t', 'nat', '-C', 'POSTROUTING', '-s', cidr,
- '!', '-d', cidr, '-j', 'MASQUERADE']
- try:
- logger.log('%s: checking if NAT iptables rule present for device %s' % (plugin, dev))
- subprocess.check_call(cmd)
- except:
- logger.log('%s: adding NAT iptables NAT for device %s' % (plugin, dev))
- cmd[3] = '-A'
- try:
- subprocess.check_call(cmd)
- except:
- logger.log('%s: FAILED to add NAT iptables rule for device %s' % (plugin, dev))
+ args = ['-s', cidr, '!', '-d', cidr, '-j', 'MASQUERADE']
+ add_iptables_rule('nat', 'POSTROUTING', args)
def get_pidfile(dev):
return '/var/run/dnsmasq-%s.pid' % dev
return records
-# Generate a dhcp-hostsfile for dnsmasq for all the local interfaces
-# on the NAT network. The purpose is to make sure that the IP address
-# assigned by Quantum appears on NAT interfaces.
+# Do all processing associated with Quantum ports. It first gets a
+# list of local VM interfaces and then queries Quantum to get Port
+# records for these interfaces. Then for all interfaces on the NAT
+# network it does the following:
+#
+# 1) Generates a dhcp-hostsfile for dnsmasq. The purpose is to make
+# sure that the IP address assigned by Quantum appears on NAT
+# interface.
#
-# Workflow:
-# - Get list of local VM interfaces
-# - Query Quantum to get port information for these interfaces
-# - Throw away all those not belonging to nat-net network
-# - Write the MAC addr and IPs to the dhcp-hostsfile file (and log it)
+# 2) Sets up iptables rules in the 'planetstack-net' chain based on
+# the nat:forward_ports field in the Port record.
-def write_hostsfile(dev):
+def process_quantum_ports(dev):
# Get local information for VM interfaces from OvS
ovs_out = subprocess.check_output(['/usr/bin/ovs-vsctl', '-f', 'json', 'find',
'Interface', 'external_ids:iface-id!="absent"'])
tenant_name=quantum_tenant_name,
auth_url=quantum_auth_url)
ports = quantum.list_ports(id=port_ids)
+ # logger.log("%s: %s" % (plugin, ports))
# Write relevant entries to dnsmasq hostsfile
logger.log("%s: Writing hostsfile for %s" % (plugin, dev))
# Send SIGHUP to dnsmasq to make it re-read hostsfile
dnsmasq_sighup(dev)
+ # Set up iptables rules for port forwarding
+ for port in ports['ports']:
+ if port['network_id'] == nat_net_id:
+ for fw in port['nat:forward_ports']:
+ ipaddr = port['fixed_ips'][0]['ip_address']
+ protocol = fw['l4_protocol']
+ fwport = fw['l4_port']
+ # logger.log("%s: fwd port %s/%s to %s" % (plugin, protocol, fwport, ipaddr))
+ add_iptables_rule('nat', plugin, ['-i', 'br-eth0',
+ '-p', protocol, '--dport', fwport,
+ '-j', 'DNAT', '--to-destination', ipaddr])
+
def start():
global quantum_username
global quantum_password
logger.log("%s: %s id is %s..." % (plugin, nat_net_name, nat_net_id))
def GetSlivers(data, config=None, plc=None):
+ reset_iptables_chain()
for interface in data['interfaces']:
try:
settings = plc.GetInterfaceTags({'interface_tag_id': interface['interface_tag_ids']})
if 'NAT' in tags:
add_iptables_masq(dev, interface)
- write_hostsfile(dev)
+ process_quantum_ports(dev)
start_dnsmasq(dev, interface)