1 # Copyright 2012 OpenStack Foundation
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
19 from oslo_config import cfg
20 from oslo_log import log as logging
23 from neutron._i18n import _, _LE, _LI, _LW
24 from neutron.agent.common import ovs_lib
25 from neutron.agent.linux import ip_lib
26 from neutron.agent.linux import utils
27 from neutron.common import constants as n_const
28 from neutron.common import exceptions
29 from neutron.common import ipv6_utils
32 LOG = logging.getLogger(__name__)
35 cfg.StrOpt('ovs_integration_bridge',
37 help=_('Name of Open vSwitch bridge to use')),
38 cfg.BoolOpt('ovs_use_veth',
40 help=_('Uses veth for an OVS interface or not. '
41 'Support kernels with limited namespace support '
42 '(e.g. RHEL 6.5) so long as ovs_use_veth is set to '
44 cfg.IntOpt('network_device_mtu',
45 deprecated_for_removal=True,
46 help=_('MTU setting for device. This option will be removed in '
47 'Newton. Please use the system-wide segment_mtu setting '
48 'which the agents will take into account when wiring '
53 @six.add_metaclass(abc.ABCMeta)
54 class LinuxInterfaceDriver(object):
56 # from linux IF_NAMESIZE
58 DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
60 def __init__(self, conf):
62 if self.conf.network_device_mtu:
63 self._validate_network_device_mtu()
65 def _validate_network_device_mtu(self):
66 if (ipv6_utils.is_enabled() and
67 self.conf.network_device_mtu < n_const.IPV6_MIN_MTU):
68 LOG.error(_LE("IPv6 protocol requires a minimum MTU of "
69 "%(min_mtu)s, while the configured value is "
70 "%(current_mtu)s"), {'min_mtu': n_const.IPV6_MIN_MTU,
71 'current_mtu': self.conf.network_device_mtu})
75 def use_gateway_ips(self):
76 """Whether to use gateway IPs instead of unique IP allocations.
78 In each place where the DHCP agent runs, and for each subnet for
79 which DHCP is handling out IP addresses, the DHCP port needs -
80 at the Linux level - to have an IP address within that subnet.
81 Generally this needs to be a unique Neutron-allocated IP
82 address, because the subnet's underlying L2 domain is bridged
83 across multiple compute hosts and network nodes, and for HA
84 there may be multiple DHCP agents running on that same bridged
87 However, if the DHCP ports - on multiple compute/network nodes
88 but for the same network - are _not_ bridged to each other,
89 they do not need each to have a unique IP address. Instead
90 they can all share the same address from the relevant subnet.
91 This works, without creating any ambiguity, because those
92 ports are not all present on the same L2 domain, and because
93 no data within the network is ever sent to that address.
94 (DHCP requests are broadcast, and it is the network's job to
95 ensure that such a broadcast will reach at least one of the
96 available DHCP servers. DHCP responses will be sent _from_
97 the DHCP port address.)
99 Specifically, for networking backends where it makes sense,
100 the DHCP agent allows all DHCP ports to use the subnet's
101 gateway IP address, and thereby to completely avoid any unique
102 IP address allocation. This behaviour is selected by running
103 the DHCP agent with a configured interface driver whose
104 'use_gateway_ips' property is True.
106 When an operator deploys Neutron with an interface driver that
107 makes use_gateway_ips True, they should also ensure that a
108 gateway IP address is defined for each DHCP-enabled subnet,
109 and that the gateway IP address doesn't change during the
114 def init_l3(self, device_name, ip_cidrs, namespace=None,
115 preserve_ips=None, clean_connections=False):
116 """Set the L3 settings for the interface using data from the port.
118 ip_cidrs: list of 'X.X.X.X/YY' strings
119 preserve_ips: list of ip cidrs that should not be removed from device
120 clean_connections: Boolean to indicate if we should cleanup connections
121 associated to removed ips
123 preserve_ips = preserve_ips or []
124 device = ip_lib.IPDevice(device_name, namespace=namespace)
126 # The LLA generated by the operating system is not known to
127 # Neutron, so it would be deleted if we added it to the 'previous'
129 default_ipv6_lla = ip_lib.get_ipv6_lladdr(device.link.address)
130 previous = {addr['cidr'] for addr in device.addr.list(
131 filters=['permanent'])} - {default_ipv6_lla}
134 for ip_cidr in ip_cidrs:
136 net = netaddr.IPNetwork(ip_cidr)
137 # Convert to compact IPv6 address because the return values of
138 # "ip addr list" are compact.
141 if ip_cidr in previous:
142 previous.remove(ip_cidr)
145 device.addr.add(ip_cidr)
147 # clean up any old addresses
148 for ip_cidr in previous:
149 if ip_cidr not in preserve_ips:
150 if clean_connections:
151 device.delete_addr_and_conntrack_state(ip_cidr)
153 device.addr.delete(ip_cidr)
155 def init_router_port(self,
161 clean_connections=False):
162 """Set the L3 settings for a router interface using data from the port.
164 ip_cidrs: list of 'X.X.X.X/YY' strings
165 preserve_ips: list of ip cidrs that should not be removed from device
166 clean_connections: Boolean to indicate if we should cleanup connections
167 associated to removed ips
168 extra_subnets: An iterable of cidrs to add as routes without address
170 LOG.debug("init_router_port: device_name(%s), namespace(%s)",
171 device_name, namespace)
172 self.init_l3(device_name=device_name,
175 preserve_ips=preserve_ips or [],
176 clean_connections=clean_connections)
178 device = ip_lib.IPDevice(device_name, namespace=namespace)
180 # Manage on-link routes (routes without an associated address)
181 new_onlink_cidrs = set(s['cidr'] for s in extra_subnets or [])
183 v4_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_4)
184 v6_onlink = device.route.list_onlink_routes(n_const.IP_VERSION_6)
185 existing_onlink_cidrs = set(r['cidr'] for r in v4_onlink + v6_onlink)
187 for route in new_onlink_cidrs - existing_onlink_cidrs:
188 LOG.debug("adding onlink route(%s)", route)
189 device.route.add_onlink_route(route)
190 for route in (existing_onlink_cidrs - new_onlink_cidrs -
191 set(preserve_ips or [])):
192 LOG.debug("deleting onlink route(%s)", route)
193 device.route.delete_onlink_route(route)
195 def add_ipv6_addr(self, device_name, v6addr, namespace, scope='global'):
196 device = ip_lib.IPDevice(device_name,
198 net = netaddr.IPNetwork(v6addr)
199 device.addr.add(str(net), scope)
201 def delete_ipv6_addr(self, device_name, v6addr, namespace):
202 device = ip_lib.IPDevice(device_name,
204 device.delete_addr_and_conntrack_state(v6addr)
206 def delete_ipv6_addr_with_prefix(self, device_name, prefix, namespace):
207 """Delete the first listed IPv6 address that falls within a given
210 device = ip_lib.IPDevice(device_name, namespace=namespace)
211 net = netaddr.IPNetwork(prefix)
212 for address in device.addr.list(scope='global', filters=['permanent']):
213 ip_address = netaddr.IPNetwork(address['cidr'])
214 if ip_address in net:
215 device.delete_addr_and_conntrack_state(address['cidr'])
218 def get_ipv6_llas(self, device_name, namespace):
219 device = ip_lib.IPDevice(device_name,
222 return device.addr.list(scope='link', ip_version=6)
224 def check_bridge_exists(self, bridge):
225 if not ip_lib.device_exists(bridge):
226 raise exceptions.BridgeDoesNotExist(bridge=bridge)
228 def get_device_name(self, port):
229 return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
232 def configure_ipv6_ra(namespace, dev_name):
233 """Configure acceptance of IPv6 route advertisements on an intf."""
234 # Learn the default router's IP address via RAs
235 ip_lib.IPWrapper(namespace=namespace).netns.execute(
236 ['sysctl', '-w', 'net.ipv6.conf.%s.accept_ra=2' % dev_name])
239 def plug_new(self, network_id, port_id, device_name, mac_address,
240 bridge=None, namespace=None, prefix=None, mtu=None):
241 """Plug in the interface only for new devices that don't exist yet."""
243 def plug(self, network_id, port_id, device_name, mac_address,
244 bridge=None, namespace=None, prefix=None, mtu=None):
245 if not ip_lib.device_exists(device_name,
246 namespace=namespace):
248 self.plug_new(network_id, port_id, device_name, mac_address,
249 bridge, namespace, prefix, mtu)
251 self.plug_new(network_id, port_id, device_name, mac_address,
252 bridge, namespace, prefix)
254 LOG.info(_LI("Device %s already exists"), device_name)
257 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
258 """Unplug the interface."""
262 """Whether the DHCP port is bridged to the VM TAP interfaces.
264 When the DHCP port is bridged to the TAP interfaces for the
265 VMs for which it is providing DHCP service - as is the case
266 for most Neutron network implementations - the DHCP server
267 only needs to listen on the DHCP port, and will still receive
268 DHCP requests from all the relevant VMs.
270 If the DHCP port is not bridged to the relevant VM TAP
271 interfaces, the DHCP server needs to listen explicitly on
272 those TAP interfaces, and to treat those as aliases of the
273 DHCP port where the IP subnet is defined.
278 class NullDriver(LinuxInterfaceDriver):
279 def plug_new(self, network_id, port_id, device_name, mac_address,
280 bridge=None, namespace=None, prefix=None, mtu=None):
283 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
286 class NSDriver(LinuxInterfaceDriver):
287 """Device independent driver enabling creation of a non device specific
288 interface in network spaces. Attachment to the device is not performed.
290 MAX_TIME_FOR_DEVICE_EXISTENCE = 30
293 def _device_is_created_in_time(cls, device_name):
294 """See if device is created, within time limit."""
296 while attempt < NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE:
297 if ip_lib.device_exists(device_name):
301 LOG.error(_LE("Device %(dev)s was not created in %(time)d seconds"),
303 'time': NSDriver.MAX_TIME_FOR_DEVICE_EXISTENCE})
306 def _configure_mtu(self, ns_dev, mtu=None):
307 # Need to set MTU, after added to namespace. See review
308 # https://review.openstack.org/327651
310 # Note: network_device_mtu will be deprecated in future
311 mtu_override = self.conf.network_device_mtu
312 except cfg.NoSuchOptError:
313 LOG.warning(_LW("Config setting for MTU deprecated - any "
314 "override will be ignored."))
318 LOG.debug("Overriding MTU to %d", mtu)
320 ns_dev.link.set_mtu(mtu)
322 LOG.debug("No MTU provided - skipping setting value")
324 def plug(self, network_id, port_id, device_name, mac_address,
325 bridge=None, namespace=None, prefix=None, mtu=None):
327 # Overriding this, we still want to add an existing device into the
329 self.plug_new(network_id, port_id, device_name, mac_address,
330 bridge, namespace, prefix, mtu)
332 def plug_new(self, network_id, port_id, device_name, mac_address,
333 bridge=None, namespace=None, prefix=None, mtu=None):
335 ip = ip_lib.IPWrapper()
336 ns_dev = ip.device(device_name)
338 LOG.debug("Plugging dev: '%s' into namespace: '%s' ",
339 device_name, namespace)
341 # Wait for device creation
342 if not self._device_is_created_in_time(device_name):
345 ns_dev.link.set_address(mac_address)
348 namespace_obj = ip.ensure_namespace(namespace)
349 namespace_obj.add_device_to_namespace(ns_dev)
351 self._configure_mtu(ns_dev, mtu)
355 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
356 # Device removal is done externally. Just remove the namespace
357 LOG.debug("Removing namespace: '%s'", namespace)
358 ip_lib.IPWrapper(namespace).garbage_collect_namespace()
361 class OVSInterfaceDriver(LinuxInterfaceDriver):
362 """Driver for creating an internal interface on an OVS bridge."""
364 DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
366 def __init__(self, conf):
367 super(OVSInterfaceDriver, self).__init__(conf)
368 if self.conf.ovs_use_veth:
369 self.DEV_NAME_PREFIX = 'ns-'
371 def _get_tap_name(self, dev_name, prefix=None):
372 if self.conf.ovs_use_veth:
373 dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
374 n_const.TAP_DEVICE_PREFIX)
377 def _ovs_add_port(self, bridge, device_name, port_id, mac_address,
379 attrs = [('external_ids', {'iface-id': port_id,
380 'iface-status': 'active',
381 'attached-mac': mac_address})]
383 attrs.insert(0, ('type', 'internal'))
385 ovs = ovs_lib.OVSBridge(bridge)
386 ovs.replace_port(device_name, *attrs)
388 def plug_new(self, network_id, port_id, device_name, mac_address,
389 bridge=None, namespace=None, prefix=None, mtu=None):
390 """Plug in the interface."""
392 bridge = self.conf.ovs_integration_bridge
394 self.check_bridge_exists(bridge)
396 ip = ip_lib.IPWrapper()
397 tap_name = self._get_tap_name(device_name, prefix)
399 if self.conf.ovs_use_veth:
400 # Create ns_dev in a namespace if one is configured.
401 root_dev, ns_dev = ip.add_veth(tap_name,
403 namespace2=namespace)
404 root_dev.disable_ipv6()
406 ns_dev = ip.device(device_name)
408 internal = not self.conf.ovs_use_veth
409 self._ovs_add_port(bridge, tap_name, port_id, mac_address,
412 ns_dev.link.set_address(mac_address)
414 # Add an interface created by ovs to the namespace.
415 if not self.conf.ovs_use_veth and namespace:
416 namespace_obj = ip.ensure_namespace(namespace)
417 namespace_obj.add_device_to_namespace(ns_dev)
419 # NOTE(ihrachys): the order here is significant: we must set MTU after
420 # the device is moved into a namespace, otherwise OVS bridge does not
421 # allow to set MTU that is higher than the least of all device MTUs on
423 mtu = self.conf.network_device_mtu or mtu
425 ns_dev.link.set_mtu(mtu)
426 if self.conf.ovs_use_veth:
427 root_dev.link.set_mtu(mtu)
429 LOG.warning(_LW("No MTU configured for port %s"), port_id)
432 if self.conf.ovs_use_veth:
433 root_dev.link.set_up()
435 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
436 """Unplug the interface."""
438 bridge = self.conf.ovs_integration_bridge
440 tap_name = self._get_tap_name(device_name, prefix)
441 self.check_bridge_exists(bridge)
442 ovs = ovs_lib.OVSBridge(bridge)
445 ovs.delete_port(tap_name)
446 if self.conf.ovs_use_veth:
447 device = ip_lib.IPDevice(device_name, namespace=namespace)
449 LOG.debug("Unplugged interface '%s'", device_name)
451 LOG.error(_LE("Failed unplugging interface '%s'"),
455 class IVSInterfaceDriver(LinuxInterfaceDriver):
456 """Driver for creating an internal interface on an IVS bridge."""
458 DEV_NAME_PREFIX = n_const.TAP_DEVICE_PREFIX
460 def __init__(self, conf):
461 super(IVSInterfaceDriver, self).__init__(conf)
462 self.DEV_NAME_PREFIX = 'ns-'
464 def _get_tap_name(self, dev_name, prefix=None):
465 dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX,
466 n_const.TAP_DEVICE_PREFIX)
469 def _ivs_add_port(self, device_name, port_id, mac_address):
470 cmd = ['ivs-ctl', 'add-port', device_name]
471 utils.execute(cmd, run_as_root=True)
473 def plug_new(self, network_id, port_id, device_name, mac_address,
474 bridge=None, namespace=None, prefix=None, mtu=None):
475 """Plug in the interface."""
476 ip = ip_lib.IPWrapper()
477 tap_name = self._get_tap_name(device_name, prefix)
479 root_dev, ns_dev = ip.add_veth(tap_name, device_name)
480 root_dev.disable_ipv6()
482 self._ivs_add_port(tap_name, port_id, mac_address)
484 ns_dev = ip.device(device_name)
485 ns_dev.link.set_address(mac_address)
487 mtu = self.conf.network_device_mtu or mtu
489 ns_dev.link.set_mtu(mtu)
490 root_dev.link.set_mtu(mtu)
492 LOG.warning(_LW("No MTU configured for port %s"), port_id)
495 namespace_obj = ip.ensure_namespace(namespace)
496 namespace_obj.add_device_to_namespace(ns_dev)
499 root_dev.link.set_up()
501 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
502 """Unplug the interface."""
503 tap_name = self._get_tap_name(device_name, prefix)
505 cmd = ['ivs-ctl', 'del-port', tap_name]
506 utils.execute(cmd, run_as_root=True)
507 device = ip_lib.IPDevice(device_name, namespace=namespace)
509 LOG.debug("Unplugged interface '%s'", device_name)
511 LOG.error(_LE("Failed unplugging interface '%s'"),
515 class BridgeInterfaceDriver(LinuxInterfaceDriver):
516 """Driver for creating bridge interfaces."""
518 DEV_NAME_PREFIX = 'ns-'
520 def plug_new(self, network_id, port_id, device_name, mac_address,
521 bridge=None, namespace=None, prefix=None, mtu=None):
522 """Plugin the interface."""
523 ip = ip_lib.IPWrapper()
525 # Enable agent to define the prefix
526 tap_name = device_name.replace(prefix or self.DEV_NAME_PREFIX,
527 n_const.TAP_DEVICE_PREFIX)
528 # Create ns_veth in a namespace if one is configured.
529 root_veth, ns_veth = ip.add_veth(tap_name, device_name,
530 namespace2=namespace)
531 root_veth.disable_ipv6()
532 ns_veth.link.set_address(mac_address)
534 mtu = self.conf.network_device_mtu or mtu
536 root_veth.link.set_mtu(mtu)
537 ns_veth.link.set_mtu(mtu)
539 LOG.warning(_LW("No MTU configured for port %s"), port_id)
541 root_veth.link.set_up()
542 ns_veth.link.set_up()
544 def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
545 """Unplug the interface."""
546 device = ip_lib.IPDevice(device_name, namespace=namespace)
549 LOG.debug("Unplugged interface '%s'", device_name)
551 LOG.error(_LE("Failed unplugging interface '%s'"),