103
105
self.unwrapped_chains = set()
105
107
def add_chain(self, name, wrap=True):
106
"""Adds a named chain to the table
108
"""Adds a named chain to the table.
108
110
The chain name is wrapped to be unique for the component creating
109
111
it, so different components of Nova can safely create identically
110
112
named chains without interfering with one another.
112
114
At the moment, its wrapped name is <binary name>-<chain name>,
113
so if nova-compute creates a chain named "OUTPUT", it'll actually
114
end up named "nova-compute-OUTPUT".
115
so if nova-compute creates a chain named 'OUTPUT', it'll actually
116
end up named 'nova-compute-OUTPUT'.
117
120
self.chains.add(name)
147
151
self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules)
149
153
def add_rule(self, chain, rule, wrap=True, top=False):
150
"""Add a rule to the table
154
"""Add a rule to the table.
152
156
This is just like what you'd feed to iptables, just without
153
the "-A <chain name>" bit at the start.
157
the '-A <chain name>' bit at the start.
155
159
However, if you need to jump to one of your wrapped chains,
156
160
prepend its name with a '$' which will ensure the wrapping
157
161
is applied correctly.
159
164
if wrap and chain not in self.chains:
160
raise ValueError(_("Unknown chain: %r") % chain)
165
raise ValueError(_('Unknown chain: %r') % chain)
163
168
rule = ' '.join(map(self._wrap_target_chain, rule.split(' ')))
172
177
def remove_rule(self, chain, rule, wrap=True, top=False):
173
"""Remove a rule from a chain
178
"""Remove a rule from a chain.
175
180
Note: The rule must be exactly identical to the one that was added.
176
181
You cannot switch arguments around like you can with the iptables
180
186
self.rules.remove(IptablesRule(chain, rule, wrap, top))
181
187
except ValueError:
182
LOG.debug(_("Tried to remove rule that wasn't there:"
183
" %(chain)r %(rule)r %(wrap)r %(top)r"),
188
LOG.debug(_('Tried to remove rule that was not there:'
189
' %(chain)r %(rule)r %(wrap)r %(top)r'),
184
190
{'chain': chain, 'rule': rule,
185
191
'top': top, 'wrap': wrap})
188
194
class IptablesManager(object):
189
"""Wrapper for iptables
195
"""Wrapper for iptables.
191
197
See IptablesTable for some usage docs
350
359
def metadata_forward():
351
"""Create forwarding rule for metadata"""
352
iptables_manager.ipv4['nat'].add_rule("PREROUTING",
353
"-s 0.0.0.0/0 -d 169.254.169.254/32 "
354
"-p tcp -m tcp --dport 80 -j DNAT "
355
"--to-destination %s:%s" % \
360
"""Create forwarding rule for metadata."""
361
iptables_manager.ipv4['nat'].add_rule('PREROUTING',
362
'-s 0.0.0.0/0 -d 169.254.169.254/32 '
363
'-p tcp -m tcp --dport 80 -j DNAT '
364
'--to-destination %s:%s' % \
356
365
(FLAGS.ec2_dmz_host, FLAGS.ec2_port))
357
366
iptables_manager.apply()
361
"""Basic networking setup goes here"""
370
"""Basic networking setup goes here."""
362
371
# NOTE(devcamcar): Cloud public SNAT entries and the default
363
372
# SNAT rule for outbound traffic.
364
iptables_manager.ipv4['nat'].add_rule("snat",
365
"-s %s -j SNAT --to-source %s" % \
373
iptables_manager.ipv4['nat'].add_rule('snat',
374
'-s %s -j SNAT --to-source %s' % \
366
375
(FLAGS.fixed_range,
367
376
FLAGS.routing_source_ip))
369
iptables_manager.ipv4['nat'].add_rule("POSTROUTING",
370
"-s %s -d %s -j ACCEPT" % \
378
iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
379
'-s %s -d %s -j ACCEPT' % \
371
380
(FLAGS.fixed_range, FLAGS.dmz_cidr))
373
iptables_manager.ipv4['nat'].add_rule("POSTROUTING",
374
"-s %(range)s -d %(range)s "
382
iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
383
'-s %(range)s -d %(range)s '
376
385
{'range': FLAGS.fixed_range})
377
386
iptables_manager.apply()
380
389
def bind_floating_ip(floating_ip, check_exit_code=True):
381
"""Bind ip to public interface"""
390
"""Bind ip to public interface."""
382
391
_execute('sudo', 'ip', 'addr', 'add', floating_ip,
383
392
'dev', FLAGS.public_interface,
384
393
check_exit_code=check_exit_code)
387
396
def unbind_floating_ip(floating_ip):
388
"""Unbind a public ip from public interface"""
397
"""Unbind a public ip from public interface."""
389
398
_execute('sudo', 'ip', 'addr', 'del', floating_ip,
390
399
'dev', FLAGS.public_interface)
393
402
def ensure_metadata_ip():
394
"""Sets up local metadata ip"""
403
"""Sets up local metadata ip."""
395
404
_execute('sudo', 'ip', 'addr', 'add', '169.254.169.254/32',
396
405
'scope', 'link', 'dev', 'lo', check_exit_code=False)
399
408
def ensure_vlan_forward(public_ip, port, private_ip):
400
"""Sets up forwarding rules for vlan"""
401
iptables_manager.ipv4['filter'].add_rule("FORWARD",
404
"-j ACCEPT" % private_ip)
405
iptables_manager.ipv4['nat'].add_rule("PREROUTING",
407
"--dport %s -j DNAT --to %s:1194" %
409
"""Sets up forwarding rules for vlan."""
410
iptables_manager.ipv4['filter'].add_rule('FORWARD',
413
'-j ACCEPT' % private_ip)
414
iptables_manager.ipv4['nat'].add_rule('PREROUTING',
416
'--dport %s -j DNAT --to %s:1194' %
408
417
(public_ip, port, private_ip))
409
418
iptables_manager.ipv4['nat'].add_rule("OUTPUT",
416
425
def ensure_floating_forward(floating_ip, fixed_ip):
417
"""Ensure floating ip forwarding rule"""
426
"""Ensure floating ip forwarding rule."""
418
427
for chain, rule in floating_forward_rules(floating_ip, fixed_ip):
419
428
iptables_manager.ipv4['nat'].add_rule(chain, rule)
420
429
iptables_manager.apply()
423
432
def remove_floating_forward(floating_ip, fixed_ip):
424
"""Remove forwarding for floating ip"""
433
"""Remove forwarding for floating ip."""
425
434
for chain, rule in floating_forward_rules(floating_ip, fixed_ip):
426
435
iptables_manager.ipv4['nat'].remove_rule(chain, rule)
427
436
iptables_manager.apply()
430
439
def floating_forward_rules(floating_ip, fixed_ip):
431
return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)),
432
("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)),
434
"-s %s -j SNAT --to %s" % (fixed_ip, floating_ip))]
440
return [('PREROUTING', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)),
441
('OUTPUT', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)),
443
'-s %s -j SNAT --to %s' % (fixed_ip, floating_ip))]
437
446
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
438
"""Create a vlan and bridge unless they already exist"""
447
"""Create a vlan and bridge unless they already exist."""
439
448
interface = ensure_vlan(vlan_num)
440
449
ensure_bridge(bridge, interface, net_attrs)
443
452
def ensure_vlan(vlan_num):
444
"""Create a vlan unless it already exists"""
445
interface = "vlan%s" % vlan_num
453
"""Create a vlan unless it already exists."""
454
interface = 'vlan%s' % vlan_num
446
455
if not _device_exists(interface):
447
LOG.debug(_("Starting VLAN inteface %s"), interface)
456
LOG.debug(_('Starting VLAN inteface %s'), interface)
448
457
_execute('sudo', 'vconfig', 'set_name_type', 'VLAN_PLUS_VID_NO_PAD')
449
458
_execute('sudo', 'vconfig', 'add', FLAGS.vlan_interface, vlan_num)
450
459
_execute('sudo', 'ip', 'link', 'set', interface, 'up')
465
474
The code will attempt to move any ips that already exist on the interface
466
475
onto the bridge and reset the default gateway if necessary.
468
478
if not _device_exists(bridge):
469
LOG.debug(_("Starting Bridge interface for %s"), interface)
479
LOG.debug(_('Starting Bridge interface for %s'), interface)
470
480
_execute('sudo', 'brctl', 'addbr', bridge)
471
481
_execute('sudo', 'brctl', 'setfd', bridge, 0)
472
# _execute("sudo brctl setageing %s 10" % bridge)
482
# _execute('sudo brctl setageing %s 10' % bridge)
473
483
_execute('sudo', 'brctl', 'stp', bridge, 'off')
474
484
_execute('sudo', 'ip', 'link', 'set', bridge, 'up')
477
487
# bridge for it to respond to reqests properly
478
488
suffix = net_attrs['cidr'].rpartition('/')[2]
479
489
out, err = _execute('sudo', 'ip', 'addr', 'add',
481
491
(net_attrs['gateway'], suffix),
483
493
net_attrs['broadcast'],
486
496
check_exit_code=False)
487
if err and err != "RTNETLINK answers: File exists\n":
488
raise exception.Error("Failed to add ip: %s" % err)
497
if err and err != 'RTNETLINK answers: File exists\n':
498
raise exception.Error('Failed to add ip: %s' % err)
489
499
if(FLAGS.use_ipv6):
490
500
_execute('sudo', 'ip', '-f', 'inet6', 'addr',
491
501
'change', net_attrs['cidr_v6'],
501
511
# interface, so we move any ips to the bridge
503
513
out, err = _execute('sudo', 'route', '-n')
504
for line in out.split("\n"):
514
for line in out.split('\n'):
505
515
fields = line.split()
506
if fields and fields[0] == "0.0.0.0" and fields[-1] == interface:
516
if fields and fields[0] == '0.0.0.0' and fields[-1] == interface:
507
517
gateway = fields[1]
508
518
_execute('sudo', 'route', 'del', 'default', 'gw', gateway,
509
519
'dev', interface, check_exit_code=False)
510
520
out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface,
511
521
'scope', 'global')
512
for line in out.split("\n"):
522
for line in out.split('\n'):
513
523
fields = line.split()
514
if fields and fields[0] == "inet":
524
if fields and fields[0] == 'inet':
515
525
params = fields[1:-1]
516
526
_execute(*_ip_bridge_cmd('del', params, fields[-1]))
517
527
_execute(*_ip_bridge_cmd('add', params, bridge))
523
533
if (err and err != "device %s is already a member of a bridge; can't "
524
534
"enslave it to bridge %s.\n" % (interface, bridge)):
525
raise exception.Error("Failed to add interface: %s" % err)
535
raise exception.Error('Failed to add interface: %s' % err)
527
iptables_manager.ipv4['filter'].add_rule("FORWARD",
528
"--in-interface %s -j ACCEPT" % \
537
iptables_manager.ipv4['filter'].add_rule('FORWARD',
538
'--in-interface %s -j ACCEPT' % \
530
iptables_manager.ipv4['filter'].add_rule("FORWARD",
531
"--out-interface %s -j ACCEPT" % \
540
iptables_manager.ipv4['filter'].add_rule('FORWARD',
541
'--out-interface %s -j ACCEPT' % \
535
545
def get_dhcp_leases(context, network_id):
536
"""Return a network's hosts config in dnsmasq leasefile format"""
546
"""Return a network's hosts config in dnsmasq leasefile format."""
538
548
for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
574
585
# if dnsmasq is already running, then tell it to reload
576
out, _err = _execute('cat', "/proc/%d/cmdline" % pid,
587
out, _err = _execute('cat', '/proc/%d/cmdline' % pid,
577
588
check_exit_code=False)
578
589
if conffile in out:
580
591
_execute('sudo', 'kill', '-HUP', pid)
582
593
except Exception as exc: # pylint: disable=W0703
583
LOG.debug(_("Hupping dnsmasq threw %s"), exc)
594
LOG.debug(_('Hupping dnsmasq threw %s'), exc)
585
LOG.debug(_("Pid %d is stale, relaunching dnsmasq"), pid)
596
LOG.debug(_('Pid %d is stale, relaunching dnsmasq'), pid)
587
598
# FLAGFILE and DNSMASQ_INTERFACE in env
588
599
env = {'FLAGFILE': FLAGS.dhcpbridge_flagfile,
626
637
_execute('sudo', 'kill', pid)
627
638
except Exception as exc: # pylint: disable=W0703
628
LOG.debug(_("killing radvd threw %s"), exc)
639
LOG.debug(_('killing radvd threw %s'), exc)
630
LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
641
LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)
631
642
command = _ra_cmd(network_ref)
632
643
_execute(*command)
633
644
db.network_update(context, network_id,
635
646
utils.get_my_linklocal(network_ref['bridge'])})
638
649
def _host_lease(fixed_ip_ref):
639
"""Return a host string for an address in leasefile format"""
650
"""Return a host string for an address in leasefile format."""
640
651
instance_ref = fixed_ip_ref['instance']
641
652
if instance_ref['updated_at']:
642
653
timestamp = instance_ref['updated_at']
646
657
seconds_since_epoch = calendar.timegm(timestamp.utctimetuple())
648
return "%d %s %s %s *" % (seconds_since_epoch + FLAGS.dhcp_lease_time,
659
return '%d %s %s %s *' % (seconds_since_epoch + FLAGS.dhcp_lease_time,
649
660
instance_ref['mac_address'],
650
661
fixed_ip_ref['address'],
651
662
instance_ref['hostname'] or '*')
654
665
def _host_dhcp(fixed_ip_ref):
655
"""Return a host string for an address in dhcp-host format"""
666
"""Return a host string for an address in dhcp-host format."""
656
667
instance_ref = fixed_ip_ref['instance']
657
return "%s,%s.%s,%s" % (instance_ref['mac_address'],
668
return '%s,%s.%s,%s' % (instance_ref['mac_address'],
658
669
instance_ref['hostname'],
659
670
FLAGS.dhcp_domain,
660
671
fixed_ip_ref['address'])
663
674
def _execute(*cmd, **kwargs):
664
"""Wrapper around utils._execute for fake_network"""
675
"""Wrapper around utils._execute for fake_network."""
665
676
if FLAGS.fake_network:
666
LOG.debug("FAKE NET: %s", " ".join(map(str, cmd)))
677
LOG.debug('FAKE NET: %s', ' '.join(map(str, cmd)))
669
680
return utils.execute(*cmd, **kwargs)
672
683
def _device_exists(device):
673
"""Check if ethernet device exists"""
684
"""Check if ethernet device exists."""
674
685
(_out, err) = _execute('ip', 'link', 'show', 'dev', device,
675
686
check_exit_code=False)
679
690
def _dnsmasq_cmd(net):
680
"""Builds dnsmasq command"""
691
"""Builds dnsmasq command."""
681
692
cmd = ['sudo', '-E', 'dnsmasq',
682
693
'--strict-order',
683
694
'--bind-interfaces',
707
718
def _stop_dnsmasq(network):
708
"""Stops the dnsmasq instance for a given network"""
719
"""Stops the dnsmasq instance for a given network."""
709
720
pid = _dnsmasq_pid_for(network)
713
724
_execute('sudo', 'kill', '-TERM', pid)
714
725
except Exception as exc: # pylint: disable=W0703
715
LOG.debug(_("Killing dnsmasq threw %s"), exc)
726
LOG.debug(_('Killing dnsmasq threw %s'), exc)
718
729
def _dhcp_file(bridge, kind):
719
"""Return path to a pid, leases or conf file for a bridge"""
730
"""Return path to a pid, leases or conf file for a bridge."""
721
731
if not os.path.exists(FLAGS.networks_path):
722
732
os.makedirs(FLAGS.networks_path)
723
return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path,
733
return os.path.abspath('%s/nova-%s.%s' % (FLAGS.networks_path,
728
738
def _ra_file(bridge, kind):
729
"""Return path to a pid or conf file for a bridge"""
739
"""Return path to a pid or conf file for a bridge."""
731
741
if not os.path.exists(FLAGS.networks_path):
732
742
os.makedirs(FLAGS.networks_path)
733
return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
743
return os.path.abspath('%s/nova-ra-%s.%s' % (FLAGS.networks_path,
738
748
def _dnsmasq_pid_for(bridge):
739
"""Returns the pid for prior dnsmasq instance for a bridge
741
Returns None if no pid file exists
743
If machine has rebooted pid might be incorrect (caller should check)
749
"""Returns the pid for prior dnsmasq instance for a bridge.
751
Returns None if no pid file exists.
753
If machine has rebooted pid might be incorrect (caller should check).
746
756
pid_file = _dhcp_file(bridge, 'pid')
748
758
if os.path.exists(pid_file):