diff options
Diffstat (limited to 'emulators/py-nova/files/06-freebsd-net.patch')
-rw-r--r-- | emulators/py-nova/files/06-freebsd-net.patch | 1245 |
1 files changed, 0 insertions, 1245 deletions
diff --git a/emulators/py-nova/files/06-freebsd-net.patch b/emulators/py-nova/files/06-freebsd-net.patch deleted file mode 100644 index 365347e7e25c..000000000000 --- a/emulators/py-nova/files/06-freebsd-net.patch +++ /dev/null @@ -1,1245 +0,0 @@ -From 2dd71331d4d204466e7b066f62952990e55c2e24 Mon Sep 17 00:00:00 2001 -From: Alexander Nusov <alexander.nusov@nfvexpress.com> -Date: Tue, 29 Nov 2016 14:21:41 +0300 -Subject: [PATCH] add freebsd_net driver - ---- - nova/network/freebsd_net.py | 1226 +++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 1226 insertions(+) - create mode 100644 nova/network/freebsd_net.py - -diff --git a/nova/network/freebsd_net.py b/nova/network/freebsd_net.py -new file mode 100644 -index 0000000..b71fcf6 ---- /dev/null -+++ b/nova/network/freebsd_net.py -@@ -0,0 +1,1226 @@ -+# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -+# Copyright 2010 United States Government as represented by the -+# Administrator of the National Aeronautics and Space Administration. -+# All Rights Reserved. -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); you may -+# not use this file except in compliance with the License. You may obtain -+# a copy of the License at -+# -+# http://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -+# License for the specific language governing permissions and limitations -+# under the License. -+ -+"""Implements vlans, bridges, and iptables rules using linux utilities.""" -+ -+import calendar -+import inspect -+import os -+import re -+import time -+import json -+ -+import netaddr -+import netifaces -+import socket -+import struct -+ -+from oslo_concurrency import processutils -+from oslo_log import log as logging -+from oslo_serialization import jsonutils -+from oslo_utils import excutils -+from oslo_utils import fileutils -+from oslo_utils import importutils -+from oslo_utils import timeutils -+import six -+ -+import nova.conf -+from nova import exception -+from nova.i18n import _, _LE, _LW -+from nova.network import model as network_model -+from nova import objects -+from nova.pci import utils as pci_utils -+from nova import utils -+ -+LOG = logging.getLogger(__name__) -+ -+ -+CONF = nova.conf.CONF -+ -+ -+# NOTE(vish): Iptables supports chain names of up to 28 characters, and we -+# add up to 12 characters to binary_name which is used as a prefix, -+# so we limit it to 16 characters. -+# (max_chain_name_length - len('-POSTROUTING') == 16) -+def get_binary_name(): -+ """Grab the name of the binary we're running in.""" -+ return os.path.basename(inspect.stack()[-1][1])[:16] -+ -+binary_name = get_binary_name() -+ -+ -+# NOTE(jkoelker) This is just a nice little stub point since mocking -+# builtins with mox is a nightmare -+def write_to_file(file, data, mode='w'): -+ with open(file, mode) as f: -+ f.write(data) -+ -+ -+def is_pid_cmdline_correct(pid, match): -+ """Ensure that the cmdline for a pid seems sane -+ -+ Because pids are recycled, blindly killing by pid is something to -+ avoid. This provides the ability to include a substring that is -+ expected in the cmdline as a safety check. -+ """ -+ try: -+ with open('/proc/%d/cmdline' % pid) as f: -+ cmdline = f.read() -+ return match in cmdline -+ except EnvironmentError: -+ return False -+ -+ -+def metadata_forward(): -+ """Create forwarding rule for metadata.""" -+ firewall_manager.add_rule("rdr proto tcp from any to 169.254.169.254 " -+ "port 80 -> %s port %s" % -+ (CONF.metadata_host, CONF.metadata_port)) -+ firewall_manager.add_rule("pass out route-to (lo0 127.0.0.1) proto tcp " -+ "from any to 169.254.169.254 port 80") -+ firewall_manager.apply() -+ -+ -+def metadata_accept(): -+ """Create the filter accept rule for metadata.""" -+ firewall_manager.add_rule("pass in inet proto tcp from any to " -+ "169.254.169.254 port = http " -+ "flags S/SA keep state") -+ firewall_manager.apply() -+ -+ -+def init_host(ip_range, is_external=False): -+ """Basic networking setup goes here.""" -+ # NOTE(devcamcar): Cloud public SNAT entries and the default -+ # SNAT rule for outbound traffic. -+ -+ firewall_manager.add_snat_rule(ip_range, is_external) -+ if is_external: -+ for snat_range in CONF.force_snat_range: -+ firewall_manager.add_rule("pass quick inet from %s to %s" % -+ (ip_range, snat_range)) -+ firewall_manager.add_rule("pass quick inet from %s to %s/32" % -+ (ip_range, CONF.metadata_host)) -+ for dmz in CONF.dmz_cidr: -+ firewall_manager.add_rule("pass quick inet from %s to %s" % -+ (ip_range, dmz)) -+ -+ """ -+ iptables_manager.ipv4['nat'].add_rule('POSTROUTING', -+ '-s %(range)s -d %(range)s ' -+ '-m conntrack ! --ctstate DNAT ' -+ '-j ACCEPT' % -+ {'range': ip_range}) -+ """ -+ firewall_manager.apply() -+ -+ -+def send_arp_for_ip(ip, device, count): -+ out, err = _execute('arping', '-U', '-i', device, '-c', str(count), ip, -+ run_as_root=True, check_exit_code=False) -+ -+ if err: -+ LOG.debug('arping error for IP %s', ip) -+ -+ -+def bind_floating_ip(floating_ip, device): -+ """Bind IP to public interface.""" -+ _execute('ifconfig', device, str(floating_ip) + '/32', 'add', -+ run_as_root=True, check_exit_code=0) -+ -+ if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0: -+ send_arp_for_ip(floating_ip, device, CONF.send_arp_for_ha_count) -+ -+ -+def unbind_floating_ip(floating_ip, device): -+ """Unbind a public IP from public interface.""" -+ _execute('ifconfig', device, str(floating_ip) + '/32', 'delete', -+ run_as_root=True, check_exit_code=0) -+ -+ -+def ensure_metadata_ip(): -+ """Sets up local metadata IP.""" -+ _execute('ifconfig', 'lo0', 'alias', '169.254.169.254/32', -+ run_as_root=True, check_exit_code=0) -+ -+ -+def ensure_vpn_forward(public_ip, port, private_ip): -+ """Sets up forwarding rules for vlan.""" -+ firewall_manager.add_rule("pass in proto udp " -+ "from any to %s port 1194 " % -+ (private_ip)) -+ firewall_manager.add_rule("rdr proto udp from any to %s port %s -> " -+ "%s port 1194" % -+ (public_ip, port, private_ip)) -+ firewall_manager.apply() -+ -+ -+def ensure_floating_forward(floating_ip, fixed_ip, device, network): -+ """Ensure floating IP forwarding rule.""" -+ firewall_manager.ensure_floating_rules(floating_ip, fixed_ip, device) -+ if device != network['bridge']: -+ firewall_manager.ensure_in_network_traffic_rules(fixed_ip, network) -+ firewall_manager.apply() -+ -+ -+def remove_floating_forward(floating_ip, fixed_ip, device, network): -+ """Remove forwarding for floating IP.""" -+ firewall_manager.remove_floating_rules(floating_ip, fixed_ip, device) -+ if device != network['bridge']: -+ firewall_manager.remove_in_network_traffic_rules(fixed_ip, network) -+ firewall_manager.apply() -+ -+ -+def clean_conntrack(fixed_ip): -+ pass -+ -+ -+def _enable_ipv4_forwarding(): -+ sysctl_key = 'net.inet.ip.forwarding' -+ stdout, stderr = _execute('sysctl', '-n', sysctl_key) -+ if stdout.strip() is not '1': -+ _execute('sysctl', '%s=1' % sysctl_key, run_as_root=True) -+ -+ -+@utils.synchronized('lock_gateway', external=True) -+def initialize_gateway_device(dev, network_ref): -+ if not network_ref: -+ return -+ -+ _enable_ipv4_forwarding() -+ -+ # NOTE(vish): The ip for dnsmasq has to be the first address on the -+ # bridge for it to respond to requests properly -+ try: -+ prefix = network_ref.cidr.prefixlen -+ except AttributeError: -+ prefix = network_ref['cidr'].rpartition('/')[2] -+ -+ full_ip = '%s/%s' % (network_ref['dhcp_server'], prefix) -+ new_ip_params = [['inet', full_ip, 'broadcast', network_ref['broadcast']]] -+ old_ip_params = [] -+ out, err = _execute('ifconfig', dev) -+ for line in out.split('\n'): -+ fields = line.split() -+ if fields and fields[0] == 'inet': -+ old_ip_params.append(fields) -+ if _address_to_cidr(fields[1], fields[3]) != full_ip: -+ new_ip_params.append(fields) -+ if not old_ip_params or _address_to_cidr(old_ip_params[0][1], old_ip_params[0][3]) != full_ip: -+ old_routes = [] -+ result = _execute('netstat', '-nrW', '-f', 'inet') -+ if result: -+ out, err = result -+ for line in out.split('\n'): -+ fields = line.split() -+ if len(fields) > 6 and (fields[6] == dev) and ('G' in fields[2]): -+ old_routes.append(fields) -+ _execute('route', '-q', 'delete', fields[0], fields[1], -+ run_as_root=True) -+ for ip_params in old_ip_params: -+ _execute(*_ifconfig_tail_cmd(dev, ip_params, 'delete'), -+ run_as_root=True) -+ for ip_params in new_ip_params: -+ _execute(*_ifconfig_tail_cmd(dev, ip_params, 'add'), -+ run_as_root=True) -+ -+ for fields in old_routes: -+ _execute('route', '-q', 'add', fields[0], fields[1], -+ run_as_root=True) -+ if CONF.send_arp_for_ha and CONF.send_arp_for_ha_count > 0: -+ send_arp_for_ip(network_ref['dhcp_server'], dev, -+ CONF.send_arp_for_ha_count) -+ if CONF.use_ipv6: -+ _execute('ifconfig', dev, 'inet6', network_ref['cidr_v6'], -+ run_as_root=True) -+ -+ -+def get_dhcp_leases(context, network_ref): -+ """Return a network's hosts config in dnsmasq leasefile format.""" -+ hosts = [] -+ host = None -+ if network_ref['multi_host']: -+ host = CONF.host -+ for fixedip in objects.FixedIPList.get_by_network(context, -+ network_ref, -+ host=host): -+ # NOTE(cfb): Don't return a lease entry if the IP isn't -+ # already leased -+ if fixedip.leased: -+ hosts.append(_host_lease(fixedip)) -+ -+ return '\n'.join(hosts) -+ -+ -+def get_dhcp_hosts(context, network_ref, fixedips): -+ """Get network's hosts config in dhcp-host format.""" -+ hosts = [] -+ macs = set() -+ for fixedip in fixedips: -+ if fixedip.allocated: -+ if fixedip.virtual_interface.address not in macs: -+ hosts.append(_host_dhcp(fixedip)) -+ macs.add(fixedip.virtual_interface.address) -+ return '\n'.join(hosts) -+ -+ -+def get_dns_hosts(context, network_ref): -+ """Get network's DNS hosts in hosts format.""" -+ hosts = [] -+ for fixedip in objects.FixedIPList.get_by_network(context, network_ref): -+ if fixedip.allocated: -+ hosts.append(_host_dns(fixedip)) -+ return '\n'.join(hosts) -+ -+ -+def _add_dnsmasq_accept_rules(dev): -+ """Allow DHCP and DNS traffic through to dnsmasq.""" -+ for port in [67, 53]: -+ for proto in ['udp', 'tcp']: -+ firewall_manager.add_rule("pass in on %s inet proto %s " -+ "from any to any port %s" % -+ (dev, proto, port)) -+ firewall_manager.apply() -+ -+ -+def _remove_dnsmasq_accept_rules(dev): -+ """Remove DHCP and DNS traffic allowed through to dnsmasq.""" -+ for port in [67, 53]: -+ for proto in ['udp', 'tcp']: -+ firewall_manager.remove_rule("pass in on %s inet proto %s " -+ "from any to any port %s" % -+ (dev, proto, port)) -+ firewall_manager.apply() -+ -+ -+def get_dhcp_opts(context, network_ref, fixedips): -+ """Get network's hosts config in dhcp-opts format.""" -+ gateway = network_ref['gateway'] -+ # NOTE(vish): if we are in multi-host mode and we are not sharing -+ # addresses, then we actually need to hand out the -+ # dhcp server address as the gateway. -+ if network_ref['multi_host'] and not (network_ref['share_address'] or -+ CONF.share_dhcp_address): -+ gateway = network_ref['dhcp_server'] -+ hosts = [] -+ if CONF.use_single_default_gateway: -+ for fixedip in fixedips: -+ if fixedip.allocated: -+ vif_id = fixedip.virtual_interface_id -+ if fixedip.default_route: -+ hosts.append(_host_dhcp_opts(vif_id, gateway)) -+ else: -+ hosts.append(_host_dhcp_opts(vif_id)) -+ else: -+ hosts.append(_host_dhcp_opts(None, gateway)) -+ return '\n'.join(hosts) -+ -+ -+def release_dhcp(dev, address, mac_address): -+ if device_exists(dev): -+ try: -+ utils.execute('dhcp_release', dev, address, mac_address, -+ run_as_root=True) -+ except processutils.ProcessExecutionError: -+ raise exception.NetworkDhcpReleaseFailed(address=address, -+ mac_address=mac_address) -+ -+ -+def update_dhcp(context, dev, network_ref): -+ conffile = _dhcp_file(dev, 'conf') -+ host = None -+ if network_ref['multi_host']: -+ host = CONF.host -+ fixedips = objects.FixedIPList.get_by_network(context, -+ network_ref, -+ host=host) -+ write_to_file(conffile, get_dhcp_hosts(context, network_ref, fixedips)) -+ restart_dhcp(context, dev, network_ref, fixedips) -+ -+ -+def update_dns(context, dev, network_ref): -+ hostsfile = _dhcp_file(dev, 'hosts') -+ host = None -+ if network_ref['multi_host']: -+ host = CONF.host -+ fixedips = objects.FixedIPList.get_by_network(context, -+ network_ref, -+ host=host) -+ write_to_file(hostsfile, get_dns_hosts(context, network_ref)) -+ restart_dhcp(context, dev, network_ref, fixedips) -+ -+ -+def kill_dhcp(dev): -+ pid = _dnsmasq_pid_for(dev) -+ if pid: -+ # Check that the process exists and looks like a dnsmasq process -+ conffile = _dhcp_file(dev, 'conf') -+ if is_pid_cmdline_correct(pid, conffile.split('/')[-1]): -+ _execute('kill', '-9', pid, run_as_root=True) -+ else: -+ LOG.debug('Pid %d is stale, skip killing dnsmasq', pid) -+ _remove_dnsmasq_accept_rules(dev) -+ -+ -+# NOTE(ja): Sending a HUP only reloads the hostfile, so any -+# configuration options (like dchp-range, vlan, ...) -+# aren't reloaded. -+@utils.synchronized('dnsmasq_start') -+def restart_dhcp(context, dev, network_ref, fixedips): -+ """(Re)starts a dnsmasq server for a given network. -+ -+ If a dnsmasq instance is already running then send a HUP -+ signal causing it to reload, otherwise spawn a new instance. -+ -+ """ -+ conffile = _dhcp_file(dev, 'conf') -+ -+ optsfile = _dhcp_file(dev, 'opts') -+ write_to_file(optsfile, get_dhcp_opts(context, network_ref, fixedips)) -+ os.chmod(optsfile, 0o644) -+ -+ # Make sure dnsmasq can actually read it (it setuid()s to "nobody") -+ os.chmod(conffile, 0o644) -+ -+ pid = _dnsmasq_pid_for(dev) -+ -+ # if dnsmasq is already running, then tell it to reload -+ if pid: -+ if is_pid_cmdline_correct(pid, conffile.split('/')[-1]): -+ try: -+ _execute('kill', '-HUP', pid, run_as_root=True) -+ _add_dnsmasq_accept_rules(dev) -+ return -+ except Exception as exc: -+ LOG.error(_LE('kill -HUP dnsmasq threw %s'), exc) -+ else: -+ LOG.debug('Pid %d is stale, relaunching dnsmasq', pid) -+ -+ cmd = ['env', -+ 'CONFIG_FILE=%s' % jsonutils.dumps(CONF.dhcpbridge_flagfile), -+ 'NETWORK_ID=%s' % str(network_ref['id']), -+ 'dnsmasq', -+ '--strict-order', -+ '--bind-interfaces', -+ '--conf-file=%s' % CONF.dnsmasq_config_file, -+ '--pid-file=%s' % _dhcp_file(dev, 'pid'), -+ '--dhcp-optsfile=%s' % _dhcp_file(dev, 'opts'), -+ '--listen-address=%s' % network_ref['dhcp_server'], -+ '--except-interface=lo', -+ '--dhcp-range=set:%s,%s,static,%s,%ss' % -+ (network_ref['label'], -+ network_ref['dhcp_start'], -+ network_ref['netmask'], -+ CONF.dhcp_lease_time), -+ '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(network_ref['cidr'])), -+ '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), -+ '--dhcp-script=%s' % CONF.dhcpbridge, -+ '--no-hosts', -+ '--leasefile-ro'] -+ -+ # dnsmasq currently gives an error for an empty domain, -+ # rather than ignoring. So only specify it if defined. -+ if CONF.dhcp_domain: -+ cmd.append('--domain=%s' % CONF.dhcp_domain) -+ -+ dns_servers = CONF.dns_server -+ if CONF.use_network_dns_servers: -+ if network_ref.get('dns1'): -+ dns_servers.append(network_ref.get('dns1')) -+ if network_ref.get('dns2'): -+ dns_servers.append(network_ref.get('dns2')) -+ if network_ref['multi_host']: -+ cmd.append('--addn-hosts=%s' % _dhcp_file(dev, 'hosts')) -+ if dns_servers: -+ cmd.append('--no-resolv') -+ for dns_server in dns_servers: -+ cmd.append('--server=%s' % dns_server) -+ -+ _execute(*cmd, run_as_root=True) -+ -+ _add_dnsmasq_accept_rules(dev) -+ -+ -+@utils.synchronized('radvd_start') -+def update_ra(context, dev, network_ref): -+ conffile = _ra_file(dev, 'conf') -+ conf_str = """ -+interface %s -+{ -+ AdvSendAdvert on; -+ MinRtrAdvInterval 3; -+ MaxRtrAdvInterval 10; -+ prefix %s -+ { -+ AdvOnLink on; -+ AdvAutonomous on; -+ }; -+}; -+""" % (dev, network_ref['cidr_v6']) -+ write_to_file(conffile, conf_str) -+ -+ # Make sure radvd can actually read it (it setuid()s to "nobody") -+ os.chmod(conffile, 0o644) -+ -+ pid = _ra_pid_for(dev) -+ -+ # if radvd is already running, then tell it to reload -+ if pid: -+ if is_pid_cmdline_correct(pid, conffile): -+ try: -+ _execute('kill', pid, run_as_root=True) -+ except Exception as exc: -+ LOG.error(_LE('killing radvd threw %s'), exc) -+ else: -+ LOG.debug('Pid %d is stale, relaunching radvd', pid) -+ -+ cmd = ['radvd', -+ '-C', '%s' % _ra_file(dev, 'conf'), -+ '-p', '%s' % _ra_file(dev, 'pid')] -+ -+ _execute(*cmd, run_as_root=True) -+ -+ -+def _host_lease(fixedip): -+ """Return a host string for an address in leasefile format.""" -+ timestamp = timeutils.utcnow() -+ seconds_since_epoch = calendar.timegm(timestamp.utctimetuple()) -+ return '%d %s %s %s *' % (seconds_since_epoch + CONF.dhcp_lease_time, -+ fixedip.virtual_interface.address, -+ fixedip.address, -+ fixedip.instance.hostname or '*') -+ -+ -+def _host_dhcp_network(vif_id): -+ return 'NW-%s' % vif_id -+ -+ -+def _host_dhcp(fixedip): -+ """Return a host string for an address in dhcp-host format.""" -+ # NOTE(cfb): dnsmasq on linux only supports 64 characters in the hostname -+ # field (LP #1238910). Since the . counts as a character we need -+ # to truncate the hostname to only 63 characters. -+ hostname = fixedip.instance.hostname -+ if len(hostname) > 63: -+ LOG.warning(_LW('hostname %s too long, truncating.'), hostname) -+ hostname = fixedip.instance.hostname[:2] + '-' +\ -+ fixedip.instance.hostname[-60:] -+ if CONF.use_single_default_gateway: -+ net = _host_dhcp_network(fixedip.virtual_interface_id) -+ return '%s,%s.%s,%s,net:%s' % (fixedip.virtual_interface.address, -+ hostname, -+ CONF.dhcp_domain, -+ fixedip.address, -+ net) -+ else: -+ return '%s,%s.%s,%s' % (fixedip.virtual_interface.address, -+ hostname, -+ CONF.dhcp_domain, -+ fixedip.address) -+ -+ -+def _host_dns(fixedip): -+ return '%s\t%s.%s' % (fixedip.address, -+ fixedip.instance.hostname, -+ CONF.dhcp_domain) -+ -+ -+def _host_dhcp_opts(vif_id=None, gateway=None): -+ """Return an empty gateway option.""" -+ values = [] -+ if vif_id is not None: -+ values.append(_host_dhcp_network(vif_id)) -+ # NOTE(vish): 3 is the dhcp option for gateway. -+ values.append('3') -+ if gateway: -+ values.append('%s' % gateway) -+ return ','.join(values) -+ -+ -+def _execute(*cmd, **kwargs): -+ """Wrapper around utils._execute for fake_network.""" -+ if CONF.fake_network: -+ LOG.debug('FAKE NET: %s', ' '.join(map(str, cmd))) -+ return 'fake', 0 -+ else: -+ return utils.execute(*cmd, **kwargs) -+ -+ -+def device_exists(device): -+ """Check if ethernet device exists.""" -+ try: -+ _execute('ifconfig', device, run_as_root=True, check_exit_code=[0]) -+ except processutils.ProcessExecutionError: -+ return False -+ else: -+ return True -+ -+ -+def _dhcp_file(dev, kind): -+ """Return path to a pid, leases, hosts or conf file for a bridge/device.""" -+ fileutils.ensure_tree(CONF.networks_path) -+ return os.path.abspath('%s/nova-%s.%s' % (CONF.networks_path, -+ dev, -+ kind)) -+ -+ -+def _ra_file(dev, kind): -+ """Return path to a pid or conf file for a bridge/device.""" -+ fileutils.ensure_tree(CONF.networks_path) -+ return os.path.abspath('%s/nova-ra-%s.%s' % (CONF.networks_path, -+ dev, -+ kind)) -+ -+ -+def _dnsmasq_pid_for(dev): -+ """Returns the pid for prior dnsmasq instance for a bridge/device. -+ -+ Returns None if no pid file exists. -+ -+ If machine has rebooted pid might be incorrect (caller should check). -+ -+ """ -+ pid_file = _dhcp_file(dev, 'pid') -+ -+ if os.path.exists(pid_file): -+ try: -+ with open(pid_file, 'r') as f: -+ return int(f.read()) -+ except (ValueError, IOError): -+ return None -+ -+ -+def _ra_pid_for(dev): -+ """Returns the pid for prior radvd instance for a bridge/device. -+ -+ Returns None if no pid file exists. -+ -+ If machine has rebooted pid might be incorrect (caller should check). -+ -+ """ -+ pid_file = _ra_file(dev, 'pid') -+ -+ if os.path.exists(pid_file): -+ with open(pid_file, 'r') as f: -+ return int(f.read()) -+ -+ -+def _address_to_cidr(address, hexmask): -+ """Produce a CIDR format address/netmask.""" -+ netmask = socket.inet_ntoa(struct.pack(">I", int(hexmask, 16))) -+ ip_cidr = netaddr.IPNetwork("%s/%s" % (address, netmask)) -+ return str(ip_cidr) -+ -+ -+def _ifconfig_tail_cmd(netif, params, action): -+ """Construct ifconfig command""" -+ cmd = ['ifconfig', netif] -+ cmd.extend(params) -+ cmd.extend([action]) -+ return cmd -+ -+ -+def _set_device_mtu(dev, mtu=None): -+ """Set the device MTU.""" -+ if mtu: -+ utils.execute('ifconfig', dev, 'mtu', mtu, -+ run_as_root=True, check_exit_code=0) -+ -+ -+def _ovs_vsctl(args): -+ full_args = ['ovs-vsctl', '--timeout=%s' % CONF.ovs_vsctl_timeout] + args -+ try: -+ return utils.execute(*full_args, run_as_root=True) -+ except Exception as e: -+ LOG.error(_LE("Unable to execute %(cmd)s. Exception: %(exception)s"), -+ {'cmd': full_args, 'exception': e}) -+ raise exception.OvsConfigurationFailure(inner_exception=e) -+ -+ -+def _create_ovs_vif_cmd(bridge, dev, iface_id, mac, -+ instance_id, interface_type=None): -+ cmd = ['--', '--if-exists', 'del-port', dev, '--', -+ 'add-port', bridge, dev, -+ '--', 'set', 'Interface', dev, -+ 'external-ids:iface-id=%s' % iface_id, -+ 'external-ids:iface-status=active', -+ 'external-ids:attached-mac=%s' % mac, -+ 'external-ids:vm-uuid=%s' % instance_id] -+ if interface_type: -+ cmd += ['type=%s' % interface_type] -+ return cmd -+ -+ -+def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id, -+ mtu=None, interface_type=None): -+ _ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id, -+ mac, instance_id, -+ interface_type)) -+ # Note at present there is no support for setting the -+ # mtu for vhost-user type ports. -+ if interface_type != network_model.OVS_VHOSTUSER_INTERFACE_TYPE: -+ _set_device_mtu(dev, mtu) -+ else: -+ LOG.debug("MTU not set on %(interface_name)s interface " -+ "of type %(interface_type)s.", -+ {'interface_name': dev, -+ 'interface_type': interface_type}) -+ -+ -+def delete_ovs_vif_port(bridge, dev, delete_dev=True): -+ _ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev]) -+ if delete_dev: -+ delete_net_dev(dev) -+ -+ -+def create_tap_dev(dev, mac_address=None): -+ if not device_exists(dev): -+ utils.execute('ifconfig', 'tap', 'create', 'name', dev, -+ run_as_root=True, check_exit_code=[0]) -+ if mac_address: -+ utils.execute('ifconfig', dev, 'ether', mac_address, -+ run_as_root=True, check_exit_code=[0]) -+ utils.execute('ifconfig', dev, 'up', -+ run_as_root=True, check_exit_code=[0]) -+ -+ -+def delete_net_dev(dev): -+ """Delete a network device only if it exists.""" -+ if device_exists(dev): -+ try: -+ utils.execute('ifconfig', dev, 'destroy', -+ run_as_root=True, check_exit_code=0) -+ LOG.debug("Net device removed: '%s'", dev) -+ except processutils.ProcessExecutionError: -+ with excutils.save_and_reraise_exception(): -+ LOG.error(_LE("Failed removing net device: '%s'"), dev) -+ -+ -+def delete_bridge_dev(dev): -+ """Delete a network bridge.""" -+ if device_exists(dev): -+ try: -+ utils.execute('ifconfig', dev, 'down', run_as_root=True) -+ utils.execute('ifconfig', dev, 'destroy', run_as_root=True) -+ except processutils.ProcessExecutionError: -+ with excutils.save_and_reraise_exception(): -+ LOG.error(_LE("Failed removing bridge device: '%s'"), dev) -+ -+ -+# Similar to compute virt layers, the FreeBSD network node -+# code uses a flexible driver model to support different ways -+# of creating ethernet interfaces and attaching them to the network. -+# In the case of a network host, these interfaces -+# act as gateway/dhcp/vpn/etc. endpoints not VM interfaces. -+interface_driver = None -+ -+ -+def _get_interface_driver(): -+ global interface_driver -+ if not interface_driver: -+ interface_driver = importutils.import_object( -+ CONF.freebsdnet_interface_driver) -+ return interface_driver -+ -+ -+def plug(network, mac_address, gateway=True): -+ return _get_interface_driver().plug(network, mac_address, gateway) -+ -+ -+def unplug(network): -+ return _get_interface_driver().unplug(network) -+ -+ -+def get_dev(network): -+ return _get_interface_driver().get_dev(network) -+ -+ -+class FreeBSDNetInterfaceDriver(object): -+ """Abstract class that defines generic network host API -+ for all FreeBSD interface drivers. -+ """ -+ -+ def plug(self, network, mac_address): -+ """Create FreeBSD device, return device name.""" -+ raise NotImplementedError() -+ -+ def unplug(self, network): -+ """Destroy FreeBSD device, return device name.""" -+ raise NotImplementedError() -+ -+ def get_dev(self, network): -+ """Get device name.""" -+ raise NotImplementedError() -+ -+ -+# plugs interfaces using FreeBSD Bridge -+class FreeBSDBridgeInterfaceDriver(FreeBSDNetInterfaceDriver): -+ -+ def plug(self, network, mac_address, gateway=True): -+ vlan = network.get('vlan') -+ if vlan is not None: -+ iface = CONF.vlan_interface or network['bridge_interface'] -+ FreeBSDBridgeInterfaceDriver.ensure_vlan_bridge( -+ vlan, -+ network['bridge'], -+ iface, -+ network, -+ mac_address, -+ network.get('mtu')) -+ iface = 'vlan%s' % vlan -+ else: -+ iface = CONF.flat_interface or network['bridge_interface'] -+ FreeBSDBridgeInterfaceDriver.ensure_bridge( -+ network['bridge'], -+ iface, -+ network, gateway) -+ -+ if network['share_address'] or CONF.share_dhcp_address: -+ isolate_dhcp_address(iface, network['dhcp_server']) -+ # NOTE(vish): applying here so we don't get a lock conflict -+ firewall_manager.apply() -+ return network['bridge'] -+ -+ def unplug(self, network, gateway=True): -+ vlan = network.get('vlan') -+ if vlan is not None: -+ iface = 'vlan%s' % vlan -+ FreeBSDBridgeInterfaceDriver.remove_vlan_bridge(vlan, -+ network['bridge']) -+ else: -+ iface = CONF.flat_interface or network['bridge_interface'] -+ FreeBSDBridgeInterfaceDriver.remove_bridge(network['bridge'], -+ gateway) -+ -+ if network['share_address'] or CONF.share_dhcp_address: -+ remove_isolate_dhcp_address(iface, network['dhcp_server']) -+ -+ firewall_manager.apply() -+ return self.get_dev(network) -+ -+ def get_dev(self, network): -+ return network['bridge'] -+ -+ @staticmethod -+ def ensure_vlan_bridge(vlan_num, bridge, bridge_interface, -+ net_attrs=None, mac_address=None, -+ mtu=None): -+ """Create a vlan and bridge unless they already exist.""" -+ interface = FreeBSDBridgeInterfaceDriver.ensure_vlan(vlan_num, -+ bridge_interface, mac_address, -+ mtu) -+ FreeBSDBridgeInterfaceDriver.ensure_bridge(bridge, interface, net_attrs) -+ return interface -+ -+ @staticmethod -+ def remove_vlan_bridge(vlan_num, bridge): -+ """Delete a bridge and vlan.""" -+ FreeBSDBridgeInterfaceDriver.remove_bridge(bridge) -+ FreeBSDBridgeInterfaceDriver.remove_vlan(vlan_num) -+ -+ @staticmethod -+ @utils.synchronized('lock_vlan', external=True) -+ def ensure_vlan(vlan_num, bridge_interface, mac_address=None, mtu=None, -+ interface=None): -+ """Create a vlan unless it already exists.""" -+ if interface is None: -+ interface = 'vlan%s' % vlan_num -+ if not device_exists(interface): -+ LOG.debug('Starting VLAN interface %s', interface) -+ out, err = _execute('ifconfig', 'vlan', 'create', -+ 'vlan', vlan_num, -+ 'vlandev', bridge_interface, -+ 'name', interface, -+ run_as_root=True) -+ if err and 'File exists' not in err: -+ msg = _('Failed to add vlan: %s') % err -+ raise exception.NovaException(msg) -+ # (danwent) the bridge will inherit this address, so we want to -+ # make sure it is the value set from the NetworkManager -+ if mac_address: -+ _execute('ifconfig', interface, 'ether', mac_address, -+ run_as_root=True) -+ _execute('ifconfig',interface, 'up', -+ run_as_root=True) -+ # NOTE(vish): set mtu every time to ensure that changes to mtu get -+ # propagated -+ _set_device_mtu(interface, mtu) -+ return interface -+ -+ @staticmethod -+ @utils.synchronized('lock_vlan', external=True) -+ def remove_vlan(vlan_num): -+ """Delete a vlan.""" -+ vlan_interface = 'vlan%s' % vlan_num -+ delete_net_dev(vlan_interface) -+ -+ @staticmethod -+ @utils.synchronized('lock_bridge', external=True) -+ def ensure_bridge(bridge, interface, net_attrs=None, gateway=True, -+ filtering=True): -+ """Create a bridge unless it already exists. -+ -+ :param interface: the interface to create the bridge on. -+ :param net_attrs: dictionary with attributes used to create bridge. -+ :param gateway: whether or not the bridge is a gateway. -+ :param filtering: whether or not to create filters on the bridge. -+ -+ If net_attrs is set, it will add the net_attrs['gateway'] to the bridge -+ using net_attrs['broadcast'] and net_attrs['cidr']. It will also add -+ the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set. -+ -+ The code will attempt to move any IPs that already exist on the -+ interface onto the bridge and reset the default gateway if necessary. -+ -+ """ -+ if not device_exists(bridge): -+ LOG.debug('Starting Bridge %s', bridge) -+ out, err = _execute('ifconfig', 'bridge', 'create', 'name', bridge, -+ check_exit_code=False, run_as_root=True) -+ if err and 'File exists' not in err: -+ msg = _('Failed to add bridge: %s') % err -+ raise exception.NovaException(msg) -+ -+ _execute('ifconfig', bridge, 'up', run_as_root=True) -+ -+ if interface: -+ LOG.debug('Adding interface %(interface)s to bridge %(bridge)s', -+ {'interface': interface, 'bridge': bridge}) -+ out, err = _execute('ifconfig', bridge, 'addm', interface, -+ check_exit_code=False, run_as_root=True) -+ if err and 'File exists' not in err: -+ msg = _('Failed to add interface: %s') % err -+ raise exception.NovaException(msg) -+ -+ # NOTE(apmelton): Linux bridge's default behavior is to use the -+ # lowest mac of all plugged interfaces. This isn't a problem when -+ # it is first created and the only interface is the bridged -+ # interface. But, as instance interfaces are plugged, there is a -+ # chance for the mac to change. So, set it here so that it won't -+ # change in the future. -+ if not CONF.fake_network: -+ interface_addrs = netifaces.ifaddresses(interface) -+ interface_mac = interface_addrs[netifaces.AF_LINK][0]['addr'] -+ _execute('ifconfig', bridge, 'ether', interface_mac, -+ run_as_root=True) -+ -+ out, err = _execute('ifconfig', interface, 'up', -+ check_exit_code=False, run_as_root=True) -+ -+ # NOTE(vish): This will break if there is already an ip on the -+ # interface, so we move any ips to the bridge -+ # NOTE(danms): We also need to copy routes to the bridge so as -+ # not to break existing connectivity on the interface -+ old_routes = [] -+ out, err = _execute('netstat', '-nrW', '-f', 'inet') -+ for line in out.split('\n'): -+ fields = line.split() -+ if len(fields) > 6 and (fields[6] == interface) and ('G' in fields[2]): -+ old_routes.append(fields) -+ _execute('route', '-q', 'delete', fields[0], fields[1], -+ run_as_root=True) -+ out, err = _execute('ifconfig', interface) -+ for line in out.split('\n'): -+ fields = line.split() -+ if fields and fields[0] == 'inet': -+ _execute(*_ifconfig_tail_cmd(interface, fields, 'delete'), -+ run_as_root=True) -+ _execute(*_ifconfig_tail_cmd(bridge, fields, 'add'), -+ run_as_root=True) -+ for fields in old_routes: -+ _execute('route', '-q', 'add', fields[0], fields[1], -+ run_as_root=True) -+ -+ if filtering: -+ # Don't forward traffic unless we were told to be a gateway -+ if gateway: -+ firewall_manager.ensure_gateway_rules(bridge) -+ else: -+ firewall_manager.ensure_bridge_rules(bridge) -+ -+ @staticmethod -+ @utils.synchronized('lock_bridge', external=True) -+ def remove_bridge(bridge, gateway=True, filtering=True): -+ """Delete a bridge.""" -+ if not device_exists(bridge): -+ return -+ else: -+ if filtering: -+ if gateway: -+ firewall_manager.remove_gateway_rules(bridge) -+ else: -+ firewall_manager.remove_bridge_rules(bridge) -+ delete_bridge_dev(bridge) -+ -+ -+def isolate_dhcp_address(interface, address): -+ # block arp traffic to address across the interface -+ firewall_manager.ensure_dhcp_isolation(interface, address) -+ -+ -+def remove_isolate_dhcp_address(interface, address): -+ # block arp traffic to address across the interface -+ firewall_manager.remove_dhcp_isolation(interface, address) -+ -+ -+# plugs interfaces using Open vSwitch -+class FreeBSDOVSInterfaceDriver(FreeBSDNetInterfaceDriver): -+ -+ def plug(self, network, mac_address, gateway=True): -+ dev = self.get_dev(network) -+ if not device_exists(dev): -+ bridge = CONF.freebsdnet_ovs_integration_bridge -+ _ovs_vsctl(['--', '--may-exist', 'add-port', bridge, dev, -+ '--', 'set', 'Interface', dev, 'type=internal', -+ '--', 'set', 'Interface', dev, -+ 'external-ids:iface-id=%s' % dev, -+ '--', 'set', 'Interface', dev, -+ 'external-ids:iface-status=active', -+ '--', 'set', 'Interface', dev, -+ 'external-ids:attached-mac=%s' % mac_address]) -+ _execute('ifconfig', dev, 'ether', mac_address, run_as_root=True) -+ _set_device_mtu(dev, network.get('mtu')) -+ _execute('ifconfig', dev, 'up', run_as_root=True) -+ if not gateway: -+ # If we weren't instructed to act as a gateway then add the -+ # appropriate flows to block all non-dhcp traffic. -+ _execute('ovs-ofctl', -+ 'add-flow', bridge, 'priority=1,actions=drop', -+ run_as_root=True) -+ _execute('ovs-ofctl', 'add-flow', bridge, -+ 'udp,tp_dst=67,dl_dst=%s,priority=2,actions=normal' % -+ mac_address, run_as_root=True) -+ # .. and make sure iptbles won't forward it as well. -+ firewall_manager.ensure_bridge_rules(bridge) -+ else: -+ firewall_manager.ensure_gateway_rules(bridge) -+ -+ return dev -+ -+ def unplug(self, network): -+ dev = self.get_dev(network) -+ bridge = CONF.freebsdnet_ovs_integration_bridge -+ _ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev]) -+ return dev -+ -+ def get_dev(self, network): -+ dev = 'gw-' + str(network['uuid'][0:11]) -+ return dev -+ -+ -+# plugs interfaces using FreeBSD Bridge when using NeutronManager -+class NeutronFreeBSDBridgeInterfaceDriver(FreeBSDNetInterfaceDriver): -+ -+ BRIDGE_NAME_PREFIX = 'brq' -+ GATEWAY_INTERFACE_PREFIX = 'gw-' -+ -+ def plug(self, network, mac_address, gateway=True): -+ dev = self.get_dev(network) -+ bridge = self.get_bridge(network) -+ if not gateway: -+ # If we weren't instructed to act as a gateway then add the -+ # appropriate flows to block all non-dhcp traffic. -+ # .. and make sure iptbles won't forward it as well. -+ firewall_manager.ensure_bridge_rules(bridge) -+ return bridge -+ else: -+ firewall_manager.ensure_gateway_rules(bridge) -+ -+ create_tap_dev(dev, mac_address) -+ -+ if not device_exists(bridge): -+ LOG.debug("Starting bridge %s ", bridge) -+ utils.execute('ifconfig', 'bridge', 'create', 'name', bridge, run_as_root=True) -+ utils.execute('ifconfig', bridge, 'ether', mac_address, run_as_root=True) -+ utils.execute('ifconfig', bridge, 'up', run_as_root=True) -+ LOG.debug("Done starting bridge %s", bridge) -+ -+ full_ip = '%s/%s' % (network['dhcp_server'], -+ network['cidr'].rpartition('/')[2]) -+ utils.execute('ifconfig', bridge, full_ip, 'add', run_as_root=True) -+ -+ return dev -+ -+ def unplug(self, network): -+ dev = self.get_dev(network) -+ if not device_exists(dev): -+ return None -+ else: -+ delete_net_dev(dev) -+ return dev -+ -+ def get_dev(self, network): -+ dev = self.GATEWAY_INTERFACE_PREFIX + str(network['uuid'][0:11]) -+ return dev -+ -+ def get_bridge(self, network): -+ bridge = self.BRIDGE_NAME_PREFIX + str(network['uuid'][0:11]) -+ return bridge -+ -+ -+class FirewallManager(object): -+ def __init__(self, execute=_execute): -+ self.execute = execute -+ self.apply_deferred = False -+ self.anchor = 'org.openstack/%s' % get_binary_name() -+ self.rules = { -+ "translation": [], -+ "filtering": [] -+ } -+ self.is_dirty = False -+ -+ def _get_rule_section(self, rule): -+ LOG.warning("processing rule: %s" % rule) -+ head, tail = rule.split(' ', 1) -+ if head in ('nat', 'rdr'): -+ return 'translation' -+ elif head in ('pass', 'block'): -+ return 'filtering' -+ else: -+ return None -+ -+ def add_rule(self, rule): -+ cleaned_rule = rule.strip() -+ section = self._get_rule_section(cleaned_rule) -+ if section: -+ if cleaned_rule not in self.rules[section]: -+ self.rules[section].append(cleaned_rule) -+ self.is_dirty = True -+ LOG.warning("Added rule to %s: %s", section, cleaned_rule) -+ -+ def remove_rule(self, rule): -+ cleaned_rule = rule.strip() -+ section = self._get_rule_section(cleaned_rule) -+ LOG.warning("Removing rule from %s: %s", section, cleaned_rule) -+ if section: -+ try: -+ self.rules[section].remove(cleaned_rule) -+ self.is_dirty = True -+ except: -+ pass -+ -+ def defer_apply_on(self): -+ self.apply_deferred = True -+ -+ def defer_apply_off(self): -+ self.apply_deferred = False -+ self.apply() -+ -+ def dirty(self): -+ return self.is_dirty -+ -+ def apply(self): -+ if self.apply_deferred: -+ return -+ if self.dirty(): -+ self._apply() -+ else: -+ LOG.debug("Skipping apply due to lack of new rules") -+ -+ @utils.synchronized('pfctl', external=True) -+ def _apply(self): -+ all_lines = [] -+ all_lines.extend(self.rules['translation']) -+ all_lines.extend(self.rules['filtering']) -+ all_lines.extend(["\n"]) -+ -+ self.is_dirty = False -+ self.execute("pfctl", "-a", self.anchor, "-f", "-", -+ process_input="\n".join(all_lines), -+ run_as_root=True) -+ LOG.warning("FirewallManager.apply completed with success") -+ -+ def get_gateway_rules(self, bridge): -+ LOG.warning("FirewallManager.get_gateway_rules: " -+ "Please configure rules in pf.conf") -+ return [] -+ -+ def ensure_gateway_rules(self, bridge): -+ for rule in self.get_gateway_rules(bridge): -+ self.add_rule(rule) -+ -+ def remove_gateway_rules(self, bridge): -+ for rule in self.get_gateway_rules(bridge): -+ self.remove_rule(rule) -+ -+ def ensure_bridge_rules(self, bridge): -+ LOG.warning("FirewallManager.ensure_bridge_rules: " -+ "Please configure rules in pf.conf") -+ -+ def remove_bridge_rules(self, bridge): -+ LOG.warning("FirewallManager.remove_bridge_rules: " -+ "Please configure rules in pf.conf") -+ -+ def ensure_dhcp_isolation(self, interface, address): -+ LOG.warning("FirewallManager.ensure_dhcp_isolation: " -+ "DHCP isolation is not yet implemented") -+ -+ def remove_dhcp_isolation(self, interface, address): -+ LOG.warning("FirewallManager.remove_dhcp_isolation: " -+ "DHCP isolation is not yet implemented") -+ -+ def ensure_in_network_traffic_rules(self, fixed_ip, network): -+ LOG.warning("FirewallManager.ensure_in_network_traffic_rules: " -+ "Please configure rules in pf.conf") -+ -+ def remove_in_network_traffic_rules(self, fixed_ip, network): -+ LOG.warning("FirewallManager.remove_in_network_traffic_rules: " -+ "Please configure rules in pf.conf") -+ -+ def floating_forward_rules(self, floating_ip, fixed_ip, device): -+ rules = [] -+ rules.append("rdr inet from any to %s -> %s" % (floating_ip, fixed_ip)) -+ -+ return rules -+ -+ def ensure_floating_rules(self, floating_ip, fixed_ip, device): -+ for rule in self.floating_forward_rules(floating_ip, fixed_ip, device): -+ self.add_rule(rule) -+ -+ def remove_floating_rules(self, floating_ip, fixed_ip, device): -+ for rule in self.floating_forward_rules(floating_ip, fixed_ip, device): -+ self.remove_rule(rule) -+ -+ def add_snat_rule(self, ip_range, is_external=False): -+ if CONF.routing_source_ip: -+ if is_external: -+ if CONF.force_snat_range: -+ snat_range = CONF.force_snat_range -+ else: -+ snat_range = [] -+ else: -+ snat_range = ['0.0.0.0/0'] -+ for dest_range in snat_range: -+ if not is_external and CONF.public_interface: -+ firewall_manager.add_rule("nat on %s inet from %s to %s -> %s" % -+ (CONF.public_interface, -+ ip_range, -+ dest_range, -+ CONF.routing_source_ip)) -+ else: -+ firewall_manager.add_rule("nat inet from %s to %s -> %s" % -+ (ip_range, -+ dest_range, -+ CONF.routing_source_ip)) -+ firewall_manager.apply() -+ -+ -+firewall_manager = FirewallManager() -+ -+ -+def get_firewall_manager(): -+ return firewall_manager --- -2.8.1 - |