def format_network_cidr(cidr, strict=False): ip, f, prefix = cidr.partition('/') try: ip = check_ip_address(ip) except Exception: raise ValueError("Invalid CIDR " + cidr) if f and prefix: try: prefix = int(prefix) assert 0 <= prefix <= 32 except Exception: raise ValueError("Invalid CIDR " + cidr) else: netmask = get_netmask(prefix) ip = ip & netmask return ip4_addr.formatter(ip) + "/" + str(prefix) else: if strict: raise ValueError("Invalid CIDR " + cidr) return ip4_addr.formatter(ip) + "/32"
async def ipam_releaseaddress(self, env, params): poolid = params['PoolID'] address = params['Address'] address = ip4_addr.formatter(ip4_addr(address)) def _updater(keys, values, timestamp): pool = values[0] if pool is None: return ((), ()) self._remove_staled_ips(pool, timestamp) if address in pool.reserved_ips: del pool.reserved_ips[address] return ((keys[0], ), (pool, )) await call_api( self, 'objectdb', 'transact', { 'keys': (IPAMReserve.default_key(poolid), ), 'updater': _updater, 'withtime': True }) env.outputjson({})
def _updater(keys, values, timestamp): reservepool = values[0] reserve_new_pool = set_new(values[1], new_pool) remove_keys = self._remove_staled_pools( reservepool, timestamp) used_cidrs = set( cidr for _, (cidr, _) in reservepool.reserved_pools.items()) if not reserve_new_pool.pool: # pool is not specified for _ in range(0, self.cidrrange_end): reservepool.nextalloc += 1 if reservepool.nextalloc >= self.cidrrange_end: reservepool.nextalloc = 0 new_subnet = self.cidrrange_subnet | ( reservepool.nextalloc << 8) new_cidr = ip4_addr.formatter(new_subnet) + '/24' if new_cidr not in used_cidrs: break reserve_new_pool.pool = new_cidr reserve_new_pool.subpool = '' rets[:] = [reserve_new_pool.pool] if reserve_new_pool.pool in used_cidrs: # We must wait until this CIDR is released raise IPAMUsingException reservepool.reserved_pools[reserve_new_pool.id] = \ [reserve_new_pool.pool, timestamp + self.pooltimeout * 1000000] marker = IPAMReserveMarker.create_instance( reserve_new_pool.pool) if marker.getkey() in remove_keys: remove_keys.remove(marker.getkey()) return (tuple(keys[0:2]) + tuple(remove_keys), (reservepool, reserve_new_pool) + (None, ) * len(remove_keys)) else: return (tuple(keys[0:2]) + (marker.getkey(), ) + tuple(remove_keys), (reservepool, reserve_new_pool, marker) + (None, ) * len(remove_keys))
def updateflow(self, connection, addvalues, removevalues, updatedvalues): try: allobjs = set(o for o in self._savedresult if o is not None and not o.isdeleted()) # Create DHCP entries lastlognetinfo = self._lastlognetinfo lastlogportinfo = self._lastlogportinfo lastserveraddresses = self._lastserveraddresses currentlognetinfo = dict( (n, id) for n, id in self._lastlognets if n in allobjs) currentlogportinfo = dict( (p, (id, currentlognetinfo[p.network], p.ip_address, p.mac_address, getattr(p.subnet, 'dhcp_server', self._parent.serveraddress)) ) for p, id in self._lastlogports if p in allobjs and p.network in currentlognetinfo and hasattr(p, 'subnet')) currentserveraddresses = set( (v[4], self._parent.servermac, p.network.id, True) for p, v in currentlogportinfo.items()) self._lastlognetinfo = currentlognetinfo self._lastlogportinfo = currentlogportinfo self._lastserveraddresses = currentserveraddresses dhcp_entries = {} t1 = self._parent.t1 t2 = self._parent.t2 for p in self._savedresult: if p is not None and not p.isdeleted() and p.isinstance(LogicalPort) and hasattr(p, 'subnet') \ and hasattr(p, 'ip_address') and hasattr(p, 'mac_address'): if p.network in currentlognetinfo and p in currentlogportinfo: try: pid, _, ip_address, mac_address, server_address = currentlogportinfo[ p] if getattr(p.subnet, 'dhcp_enabled', False): continue cidr = p.subnet.cidr network, mask = parse_ip4_network(cidr) # options from default settings entries = { d.OPTION_LEASE_TIME: (lambda x: d.DHCPTIME_INFINITE if x is None else x)(self._parent.leasetime) } # options from network if hasattr(p.network, 'dns_nameservers'): entries[ d. OPTION_DNSSERVER] = p.network.dns_nameservers if hasattr(p.network, 'domain_name'): entries[ d. OPTION_DOMAINNAME] = p.network.domain_name if hasattr(p.network, 'mtu'): entries[d.OPTION_MTU] = p.network.mtu if hasattr(p.network, 'ntp_servers'): entries[ d.OPTION_NTPSERVER] = p.network.ntp_servers if hasattr(p.network, 'lease_time'): entries[ d.OPTION_LEASE_TIME] = p.network.lease_time if hasattr(p.network, 'extra_dhcp_options'): entries.update(p.network.extra_dhcp_options) options = d.create_dhcp_options( entries, True, True) # options from subnet entries = { d.OPTION_NETMASK: ip4_addr.formatter(get_netmask(mask)), d.OPTION_BROADCAST: ip4_addr.formatter(get_broadcast( network, mask)) } if hasattr(p.subnet, 'gateway'): entries[d.OPTION_ROUTER] = p.subnet.gateway if hasattr(p.subnet, 'dns_nameservers'): entries[ d. OPTION_DNSSERVER] = p.subnet.dns_nameservers if hasattr(p.subnet, 'domain_name'): entries[ d.OPTION_DOMAINNAME] = p.subnet.domain_name if hasattr(p.subnet, 'mtu'): entries[d.OPTION_MTU] = p.subnet.mtu if hasattr(p.subnet, 'ntp_servers'): entries[ d.OPTION_NTPSERVER] = p.subnet.ntp_servers if hasattr(p.subnet, 'lease_time'): entries[ d.OPTION_LEASE_TIME] = p.subnet.lease_time # Routes is special if hasattr(p.subnet, 'host_routes'): routes = list(p.subnet.host_routes) if routes and not any( parse_ip4_network(r[0]) == (0, 0) for r in routes): routes.append( ['0.0.0.0/0', p.subnet.gateway]) entries[d.OPTION_CLASSLESSROUTE] = routes # TODO: add extra routes from routers if hasattr(p.subnet, 'extra_dhcp_options'): entries.update(p.subnet.extra_dhcp_options) options.update( d.create_dhcp_options(entries, True, True)) entries = {} if hasattr(p, 'hostname'): entries[d.OPTION_HOSTNAME] = p.hostname if hasattr(p, 'extra_dhcp_options'): entries.update(p.extra_dhcp_options) options.update( d.create_dhcp_options(entries, True, True)) entries = { d.OPTION_SERVER_IDENTIFIER: server_address } options.update( d.create_dhcp_options(entries, True, True)) options = dict((k, v) for k, v in options.items() if v is not None) if d.OPTION_LEASE_TIME not in options: options[ d. OPTION_LEASE_TIME] = d.create_option_from_value( d.OPTION_LEASE_TIME, d.DHCPTIME_INFINITE) if options[ d. OPTION_LEASE_TIME].value != d.DHCPTIME_INFINITE: leasetime = options[d.OPTION_LEASE_TIME].value if d.OPTION_T1 not in options and t1 is not None and t1 < leasetime: options[ d. OPTION_T1] = d.create_option_from_value( d.OPTION_T1, t1) if d.OPTION_T2 not in options and t2 is not None and t2 < leasetime: options[ d. OPTION_T1] = d.create_option_from_value( d.OPTION_T1, t2) dhcp_entries[pid] = (mac_addr(mac_address), ip4_addr(ip_address), ip4_addr(server_address), options) except Exception: self._logger.warning( "Failed to create DHCP options for port id=%r. Will disable DHCP on this port", p.id, exc_info=True) self._dhcpentries = dhcp_entries # Create flows # For every logical port, create two flows ofdef = connection.openflowdef vhost = connection.protocol.vhost l3 = self._parent._gettableindex('l3input', vhost) cmds = [] if connection.protocol.disablenxext: def match_network(nid): return [ ofdef.create_oxm(ofdef.OXM_OF_METADATA_W, ((nid & 0xffff) << 32) | (0xffff << 16), b'\x00\x00\xff\xff\x40\x00\x00\x00') ] else: def match_network(nid): return [ ofdef.create_oxm(ofdef.NXM_NX_REG5, nid), ofdef.create_oxm(ofdef.NXM_NX_REG7_W, 0x4000, 0x4000) ] def _delete_flows(nid): return ofdef.ofp_flow_mod( cookie=0x1, cookie_mask=0xffffffffffffffff, table_id=l3, command=ofdef.OFPFC_DELETE, priority=ofdef.OFP_DEFAULT_PRIORITY, buffer_id=ofdef.OFP_NO_BUFFER, out_port=ofdef.OFPP_ANY, out_group=ofdef.OFPG_ANY, match=ofdef.ofp_match_oxm(oxm_fields=match_network(nid) + [ ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ])) def _delete_flows2(nid, serveraddr): return ofdef.ofp_flow_mod( cookie=0x1, cookie_mask=0xffffffffffffffff, table_id=l3, command=ofdef.OFPFC_DELETE, priority=ofdef.OFP_DEFAULT_PRIORITY, buffer_id=ofdef.OFP_NO_BUFFER, out_port=ofdef.OFPP_ANY, out_group=ofdef.OFPG_ANY, match=ofdef.ofp_match_oxm(oxm_fields=match_network(nid) + [ ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_IPV4_DST, ip4_addr_bytes(serveraddr)), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ])) def _create_flows(nid): return ofdef.ofp_flow_mod( cookie=0x1, cookie_mask=0xffffffffffffffff, table_id=l3, command=ofdef.OFPFC_ADD, priority=ofdef.OFP_DEFAULT_PRIORITY, buffer_id=ofdef.OFP_NO_BUFFER, out_port=ofdef.OFPP_ANY, out_group=ofdef.OFPG_ANY, match=ofdef.ofp_match_oxm(oxm_fields=match_network(nid) + [ ofdef.create_oxm(ofdef.OXM_OF_ETH_DST_W, b'\x01\x00\x00\x00\x00\x00', b'\x01\x00\x00\x00\x00\x00'), ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ]), instructions=[ ofdef.ofp_instruction_actions(actions=[ ofdef.ofp_action_output( port=ofdef.OFPP_CONTROLLER, max_len=ofdef.OFPCML_NO_BUFFER) ]) ]) def _create_flows2(nid, serveraddr): return ofdef.ofp_flow_mod( cookie=0x1, cookie_mask=0xffffffffffffffff, table_id=l3, command=ofdef.OFPFC_ADD, priority=ofdef.OFP_DEFAULT_PRIORITY, buffer_id=ofdef.OFP_NO_BUFFER, out_port=ofdef.OFPP_ANY, out_group=ofdef.OFPG_ANY, match=ofdef.ofp_match_oxm(oxm_fields=match_network(nid) + [ ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_IPV4_DST, ip4_addr_bytes(serveraddr)), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ]), instructions=[ ofdef.ofp_instruction_actions(actions=[ ofdef.ofp_action_output( port=ofdef.OFPP_CONTROLLER, max_len=ofdef.OFPCML_NO_BUFFER) ]) ]) for n in removevalues: if n in lastlognetinfo: nid = lastlognetinfo[n] cmds.append(_delete_flows(nid)) lastnetdict = {n.id: n for n in lastlognetinfo} for serveraddr in lastserveraddresses: if serveraddr not in currentserveraddresses: addr, _, networkid, _ = serveraddr if networkid in lastnetdict: n = lastnetdict[networkid] nid = lastlognetinfo[n] cmds.append(_delete_flows2(nid, addr)) for m in self.execute_commands(connection, cmds): yield m # Remove old ARP entries; Add new ARP entries remove_arps = lastserveraddresses.difference( currentserveraddresses) if remove_arps: for m in callAPI(self, 'arpresponder', 'removeproxyarp', { 'connection': connection, 'arpentries': remove_arps }): yield m add_arps = currentserveraddresses.difference(lastserveraddresses) if add_arps: for m in callAPI(self, 'arpresponder', 'createproxyarp', { 'connection': connection, 'arpentries': add_arps }): yield m del cmds[:] for n in addvalues: if n in currentlognetinfo: nid = currentlognetinfo[n] cmds.append(_create_flows(nid)) currnetdict = {n.id: n for n in currentlognetinfo} for serveraddr in currentserveraddresses: if serveraddr not in lastserveraddresses: addr, _, networkid, _ = serveraddr if networkid in currnetdict: n = currnetdict[networkid] nid = currentlognetinfo[n] cmds.append(_create_flows2(nid, addr)) for m in self.execute_commands(connection, cmds): yield m except Exception: self._logger.warning( "Unexpected exception in DHCPUpdater. Will ignore and continue.", exc_info=True)
def _updater(keys, values, timestamp): pool = values[0] if pool is None: raise ValueError('PoolID %r does not exist' % (poolid, )) self._remove_staled_ips(pool, timestamp) if len(values) > 1: subnetmap = values[1] if not hasattr( pool, 'subnetmap') or pool.subnetmap.getkey() != keys[1]: raise RetryUpdateException subnet = values[2] else: subnetmap = None if hasattr(pool, 'subnetmap'): raise RetryUpdateException if address: # check ip_address in cidr if address in pool.reserved_ips: raise ValueError("IP address " + address + " has been reserved") if pool.subpool: cidr = pool.subpool else: cidr = pool.pool network, mask = parse_ip4_network(cidr) addr_num = ip4_addr(address) if not ip_in_network(addr_num, network, mask): raise ValueError('IP address ' + address + " is not in the network CIDR") if subnetmap is not None: start = ip4_addr(subnet.allocated_start) end = ip4_addr(subnet.allocated_end) try: assert start <= addr_num <= end if hasattr(subnet, 'gateway'): assert addr_num != ip4_addr(subnet.gateway) except Exception: raise ValueError("specified IP address " + address + " is not valid") if str(addr_num) in subnetmap.allocated_ips: raise ValueError("IP address " + address + " has been used") new_address = address else: # allocated ip_address from cidr gateway = None cidr = pool.pool if pool.subpool: cidr = pool.subpool network, prefix = parse_ip4_network(cidr) start = network_first(network, prefix) end = network_last(network, prefix) if subnetmap is not None: start = max(start, ip4_addr(subnet.allocated_start)) end = min(end, ip4_addr(subnet.allocated_end)) if hasattr(subnet, "gateway"): gateway = ip4_addr(subnet.gateway) for ip_address in range(start, end): new_address = ip4_addr.formatter(ip_address) if ip_address != gateway and \ (subnetmap is None or str(ip_address) not in subnetmap.allocated_ips) and \ new_address not in pool.reserved_ips: break else: raise ValueError("No available IP address can be used") pool.reserved_ips[ new_address] = timestamp + self.iptimeout * 1000000 _, mask = parse_ip4_network(pool.pool) rets[:] = [new_address + '/' + str(mask)] return ((keys[0], ), (pool, ))
def ipam_requestaddress(self, env, params): poolid = params['PoolID'] address = params['Address'] if address: address = ip4_addr.formatter(ip4_addr(address)) reserve_key = IPAMReserve.default_key(poolid) rets = [] def _updater(keys, values, timestamp): pool = values[0] if pool is None: raise ValueError('PoolID %r does not exist' % (poolid, )) self._remove_staled_ips(pool, timestamp) if len(values) > 1: subnetmap = values[1] if not hasattr( pool, 'subnetmap') or pool.subnetmap.getkey() != keys[1]: raise RetryUpdateException subnet = values[2] else: subnetmap = None if hasattr(pool, 'subnetmap'): raise RetryUpdateException if address: # check ip_address in cidr if address in pool.reserved_ips: raise ValueError("IP address " + address + " has been reserved") if pool.subpool: cidr = pool.subpool else: cidr = pool.pool network, mask = parse_ip4_network(cidr) addr_num = ip4_addr(address) if not ip_in_network(addr_num, network, mask): raise ValueError('IP address ' + address + " is not in the network CIDR") if subnetmap is not None: start = ip4_addr(subnet.allocated_start) end = ip4_addr(subnet.allocated_end) try: assert start <= addr_num <= end if hasattr(subnet, 'gateway'): assert addr_num != ip4_addr(subnet.gateway) except Exception: raise ValueError("specified IP address " + address + " is not valid") if str(addr_num) in subnetmap.allocated_ips: raise ValueError("IP address " + address + " has been used") new_address = address else: # allocated ip_address from cidr gateway = None cidr = pool.pool if pool.subpool: cidr = pool.subpool network, prefix = parse_ip4_network(cidr) start = network_first(network, prefix) end = network_last(network, prefix) if subnetmap is not None: start = max(start, ip4_addr(subnet.allocated_start)) end = min(end, ip4_addr(subnet.allocated_end)) if hasattr(subnet, "gateway"): gateway = ip4_addr(subnet.gateway) for ip_address in range(start, end): new_address = ip4_addr.formatter(ip_address) if ip_address != gateway and \ (subnetmap is None or str(ip_address) not in subnetmap.allocated_ips) and \ new_address not in pool.reserved_ips: break else: raise ValueError("No available IP address can be used") pool.reserved_ips[ new_address] = timestamp + self.iptimeout * 1000000 _, mask = parse_ip4_network(pool.pool) rets[:] = [new_address + '/' + str(mask)] return ((keys[0], ), (pool, )) while True: # First get the current data, to determine that whether the pool has already # been connected to a network self._reqid += 1 reqid = ('dockerplugin_ipam', self._reqid) for m in callAPI(self, 'objectdb', 'get', { 'key': reserve_key, 'requestid': reqid, 'nostale': True }): yield m reserve_pool = self.retvalue with watch_context([reserve_key], [reserve_pool], reqid, self): if reserve_pool is None or reserve_pool.isdeleted(): raise ValueError('PoolID %r does not exist' % (poolid, )) if hasattr(reserve_pool, 'subnetmap'): # Retrieve both the reserve pool and the subnet map keys = (reserve_key, reserve_pool.subnetmap.getkey(), SubNet.default_key(reserve_pool.subnetmap.id)) else: keys = (reserve_key, ) try: for m in callAPI(self, 'objectdb', 'transact', { 'keys': keys, 'updater': _updater, 'withtime': True }): yield m except RetryUpdateException: continue else: env.outputjson({'Address': rets[0], 'Data': {}}) break
def ipam_requestpool(self, env, params): if params['AddressSpace'] != _GLOBAL_SPACE: raise ValueError( 'Unsupported address space: must use this IPAM driver together with network driver' ) if params['V6']: raise ValueError('IPv6 is not supported') new_pool = IPAMReserve.create_instance(uuid1().hex) new_pool.pool = params['Pool'] if new_pool.pool: subnet, mask = parse_ip4_network(new_pool.pool) new_pool.pool = ip4_addr.formatter(subnet) + '/' + str(mask) new_pool.subpool = params['SubPool'] if new_pool.subpool: subnet, mask = parse_ip4_network(new_pool.subpool) new_pool.subpool = ip4_addr.formatter(subnet) + '/' + str(mask) new_pool.options = params['Options'] if new_pool.pool: l = Lock(('dockerplugin_ipam_request_pool', new_pool.pool), self.scheduler) for m in l.lock(self): yield m else: l = None try: while True: fail = 0 rets = [] def _updater(keys, values, timestamp): reservepool = values[0] reserve_new_pool = set_new(values[1], new_pool) remove_keys = self._remove_staled_pools( reservepool, timestamp) used_cidrs = set( cidr for _, (cidr, _) in reservepool.reserved_pools.items()) if not reserve_new_pool.pool: # pool is not specified for _ in range(0, self.cidrrange_end): reservepool.nextalloc += 1 if reservepool.nextalloc >= self.cidrrange_end: reservepool.nextalloc = 0 new_subnet = self.cidrrange_subnet | ( reservepool.nextalloc << 8) new_cidr = ip4_addr.formatter(new_subnet) + '/24' if new_cidr not in used_cidrs: break reserve_new_pool.pool = new_cidr reserve_new_pool.subpool = '' rets[:] = [reserve_new_pool.pool] if reserve_new_pool.pool in used_cidrs: # We must wait until this CIDR is released raise IPAMUsingException reservepool.reserved_pools[reserve_new_pool.id] = \ [reserve_new_pool.pool, timestamp + self.pooltimeout * 1000000] marker = IPAMReserveMarker.create_instance( reserve_new_pool.pool) if marker.getkey() in remove_keys: remove_keys.remove(marker.getkey()) return (tuple(keys[0:2]) + tuple(remove_keys), (reservepool, reserve_new_pool) + (None, ) * len(remove_keys)) else: return (tuple(keys[0:2]) + (marker.getkey(), ) + tuple(remove_keys), (reservepool, reserve_new_pool, marker) + (None, ) * len(remove_keys)) try: for m in callAPI( self, 'objectdb', 'transact', { 'keys': (IPAMPoolReserve.default_key(), new_pool.getkey()), 'updater': _updater, 'withtime': True }): yield m except IPAMUsingException: # Wait for the CIDR to be released self._reqid += 1 fail += 1 reqid = ('dockerplugin_ipam', self._reqid) marker_key = IPAMReserveMarker.default_key(rets[0]) for m in callAPI(self, 'objectdb', 'get', { 'key': marker_key, 'requestid': reqid, 'nostale': True }): yield m retvalue = self.retvalue with watch_context([marker_key], [retvalue], reqid, self): if retvalue is not None and not retvalue.isdeleted(): for m in self.executeWithTimeout( self.pooltimeout, retvalue.waitif(self, lambda x: x.isdeleted())): yield m else: env.outputjson({ 'PoolID': new_pool.id, 'Pool': rets[0], 'Data': {} }) break finally: if l is not None: l.unlock()
def format_ip_address(ipaddress): return ip4_addr.formatter(check_ip_address(ipaddress))
def walker(walk, write, timestamp): with suppress(WalkKeyNotRetrieved): pool = walk(reserve_key) if pool is None: raise ValueError('PoolID %r does not exist' % (poolid, )) self._remove_staled_ips(pool, timestamp) if hasattr(pool, 'subnetmap'): subnetmap_key = pool.subnetmap.getkey() subnet_key = SubNetMap._subnet.rightkey(subnetmap_key) ensure_keys(walk, subnet_key, subnetmap_key) subnetmap = walk(subnetmap_key) subnet = walk(subnet_key) else: subnetmap = None subnet = None if address: # check ip_address in cidr if address in pool.reserved_ips: raise ValueError("IP address " + address + " has been reserved") if pool.subpool: cidr = pool.subpool else: cidr = pool.pool network, mask = parse_ip4_network(cidr) addr_num = ip4_addr(address) if not ip_in_network(addr_num, network, mask): raise ValueError('IP address ' + address + " is not in the network CIDR") if subnetmap is not None: start = ip4_addr(subnet.allocated_start) end = ip4_addr(subnet.allocated_end) try: assert start <= addr_num <= end if hasattr(subnet, 'gateway'): assert addr_num != ip4_addr(subnet.gateway) except Exception: raise ValueError("specified IP address " + address + " is not valid") if str(addr_num) in subnetmap.allocated_ips: raise ValueError("IP address " + address + " has been used") new_address = address else: # allocated ip_address from cidr gateway = None cidr = pool.pool if pool.subpool: cidr = pool.subpool network, prefix = parse_ip4_network(cidr) start = network_first(network, prefix) end = network_last(network, prefix) if subnetmap is not None: start = max(start, ip4_addr(subnet.allocated_start)) end = min(end, ip4_addr(subnet.allocated_end)) if hasattr(subnet, "gateway"): gateway = ip4_addr(subnet.gateway) for ip_address in range(start, end): new_address = ip4_addr.formatter(ip_address) if ip_address != gateway and \ (subnetmap is None or str(ip_address) not in subnetmap.allocated_ips) and \ new_address not in pool.reserved_ips: break else: raise ValueError("No available IP address can be used") pool.reserved_ips[ new_address] = timestamp + self.iptimeout * 1000000 _, mask = parse_ip4_network(pool.pool) rets[:] = [new_address + '/' + str(mask)] write(pool.getkey(), pool)
def updateflow(self, connection, addvalues, removevalues, updatedvalues): try: allobjs = set(o for o in self._savedresult if o is not None and not o.isdeleted()) # Create DHCP entries lastlognetinfo = self._lastlognetinfo lastlogportinfo = self._lastlogportinfo lastserveraddresses = self._lastserveraddresses currentlognetinfo = dict((n, id) for n,id in self._lastlognets if n in allobjs) currentlogportinfo = dict((p, (id, currentlognetinfo[p.network], p.ip_address, p.mac_address, getattr(p.subnet, 'dhcp_server', self._parent.serveraddress))) for p,id in self._lastlogports if p in allobjs and p.network in currentlognetinfo and hasattr(p,'subnet')) currentserveraddresses = set((v[4], self._parent.servermac, p.network.id, True) for p,v in currentlogportinfo.items()) self._lastlognetinfo = currentlognetinfo self._lastlogportinfo = currentlogportinfo self._lastserveraddresses = currentserveraddresses dhcp_entries = {} t1 = self._parent.t1 t2 = self._parent.t2 for p in self._savedresult: if p is not None and not p.isdeleted() and p.isinstance(LogicalPort) and hasattr(p, 'subnet') \ and hasattr(p, 'ip_address') and hasattr(p, 'mac_address'): if p.network in currentlognetinfo and p in currentlogportinfo: try: pid, _, ip_address, mac_address, server_address = currentlogportinfo[p] if getattr(p.subnet, 'dhcp_enabled', False): continue cidr = p.subnet.cidr network, mask = parse_ip4_network(cidr) # options from default settings entries = {d.OPTION_LEASE_TIME: (lambda x: d.DHCPTIME_INFINITE if x is None else x)(self._parent.leasetime) } # options from network if hasattr(p.network, 'dns_nameservers'): entries[d.OPTION_DNSSERVER] = p.network.dns_nameservers if hasattr(p.network, 'domain_name'): entries[d.OPTION_DOMAINNAME] = p.network.domain_name if hasattr(p.network, 'mtu'): entries[d.OPTION_MTU] = p.network.mtu if hasattr(p.network, 'ntp_servers'): entries[d.OPTION_NTPSERVER] = p.network.ntp_servers if hasattr(p.network, 'lease_time'): entries[d.OPTION_LEASE_TIME] = p.network.lease_time if hasattr(p.network, 'extra_dhcp_options'): entries.update(p.network.extra_dhcp_options) options = d.create_dhcp_options(entries, True, True) # options from subnet entries = {d.OPTION_NETMASK : ip4_addr.formatter(get_netmask(mask)), d.OPTION_BROADCAST : ip4_addr.formatter(get_broadcast(network, mask))} if hasattr(p.subnet, 'gateway'): entries[d.OPTION_ROUTER] = p.subnet.gateway if hasattr(p.subnet, 'dns_nameservers'): entries[d.OPTION_DNSSERVER] = p.subnet.dns_nameservers if hasattr(p.subnet, 'domain_name'): entries[d.OPTION_DOMAINNAME] = p.subnet.domain_name if hasattr(p.subnet, 'mtu'): entries[d.OPTION_MTU] = p.subnet.mtu if hasattr(p.subnet, 'ntp_servers'): entries[d.OPTION_NTPSERVER] = p.subnet.ntp_servers if hasattr(p.subnet, 'lease_time'): entries[d.OPTION_LEASE_TIME] = p.subnet.lease_time # Routes is special if hasattr(p.subnet, 'host_routes'): routes = list(p.subnet.host_routes) if routes and not any(parse_ip4_network(r[0]) == (0,0) for r in routes): routes.append(['0.0.0.0/0', p.subnet.gateway]) entries[d.OPTION_CLASSLESSROUTE] = routes # TODO: add extra routes from routers if hasattr(p.subnet, 'extra_dhcp_options'): entries.update(p.subnet.extra_dhcp_options) options.update(d.create_dhcp_options(entries, True, True)) entries = {} if hasattr(p, 'hostname'): entries[d.OPTION_HOSTNAME] = p.hostname if hasattr(p, 'extra_dhcp_options'): entries.update(p.extra_dhcp_options) options.update(d.create_dhcp_options(entries, True, True)) entries = {d.OPTION_SERVER_IDENTIFIER: server_address} options.update(d.create_dhcp_options(entries, True, True)) options = dict((k,v) for k,v in options.items() if v is not None) if d.OPTION_LEASE_TIME not in options: options[d.OPTION_LEASE_TIME] = d.create_option_from_value(d.OPTION_LEASE_TIME, d.DHCPTIME_INFINITE) if options[d.OPTION_LEASE_TIME].value != d.DHCPTIME_INFINITE: leasetime = options[d.OPTION_LEASE_TIME].value if d.OPTION_T1 not in options and t1 is not None and t1 < leasetime: options[d.OPTION_T1] = d.create_option_from_value(d.OPTION_T1, t1) if d.OPTION_T2 not in options and t2 is not None and t2 < leasetime: options[d.OPTION_T1] = d.create_option_from_value(d.OPTION_T1, t2) dhcp_entries[pid] = (mac_addr(mac_address), ip4_addr(ip_address), ip4_addr(server_address), options) except Exception: self._logger.warning("Failed to create DHCP options for port id=%r. Will disable DHCP on this port", p.id, exc_info = True) self._dhcpentries = dhcp_entries # Create flows # For every logical port, create two flows ofdef = connection.openflowdef vhost = connection.protocol.vhost l3 = self._parent._gettableindex('l3input', vhost) cmds = [] if connection.protocol.disablenxext: def match_network(nid): return [ofdef.create_oxm(ofdef.OXM_OF_METADATA_W, ((nid & 0xffff) << 32) | (0xffff << 16), b'\x00\x00\xff\xff\x40\x00\x00\x00')] else: def match_network(nid): return [ofdef.create_oxm(ofdef.NXM_NX_REG5, nid), ofdef.create_oxm(ofdef.NXM_NX_REG7_W, 0x4000, 0x4000)] def _delete_flows(nid): return ofdef.ofp_flow_mod(cookie = 0x1, cookie_mask = 0xffffffffffffffff, table_id = l3, command = ofdef.OFPFC_DELETE, priority = ofdef.OFP_DEFAULT_PRIORITY, buffer_id = ofdef.OFP_NO_BUFFER, out_port = ofdef.OFPP_ANY, out_group = ofdef.OFPG_ANY, match = ofdef.ofp_match_oxm( oxm_fields = match_network(nid) + [ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ] ) ) def _delete_flows2(nid, serveraddr): return ofdef.ofp_flow_mod(cookie = 0x1, cookie_mask = 0xffffffffffffffff, table_id = l3, command = ofdef.OFPFC_DELETE, priority = ofdef.OFP_DEFAULT_PRIORITY, buffer_id = ofdef.OFP_NO_BUFFER, out_port = ofdef.OFPP_ANY, out_group = ofdef.OFPG_ANY, match = ofdef.ofp_match_oxm( oxm_fields = match_network(nid) + [ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_IPV4_DST, ip4_addr_bytes(serveraddr)), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ] ) ) def _create_flows(nid): return ofdef.ofp_flow_mod(cookie = 0x1, cookie_mask = 0xffffffffffffffff, table_id = l3, command = ofdef.OFPFC_ADD, priority = ofdef.OFP_DEFAULT_PRIORITY, buffer_id = ofdef.OFP_NO_BUFFER, out_port = ofdef.OFPP_ANY, out_group = ofdef.OFPG_ANY, match = ofdef.ofp_match_oxm( oxm_fields = match_network(nid) + [ofdef.create_oxm(ofdef.OXM_OF_ETH_DST_W, b'\x01\x00\x00\x00\x00\x00', b'\x01\x00\x00\x00\x00\x00'), ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ] ), instructions = [ ofdef.ofp_instruction_actions( actions = [ ofdef.ofp_action_output(port = ofdef.OFPP_CONTROLLER, max_len = ofdef.OFPCML_NO_BUFFER ) ] ) ] ) def _create_flows2(nid, serveraddr): return ofdef.ofp_flow_mod(cookie = 0x1, cookie_mask = 0xffffffffffffffff, table_id = l3, command = ofdef.OFPFC_ADD, priority = ofdef.OFP_DEFAULT_PRIORITY, buffer_id = ofdef.OFP_NO_BUFFER, out_port = ofdef.OFPP_ANY, out_group = ofdef.OFPG_ANY, match = ofdef.ofp_match_oxm( oxm_fields = match_network(nid) + [ofdef.create_oxm(ofdef.OXM_OF_ETH_TYPE, ofdef.ETHERTYPE_IP), ofdef.create_oxm(ofdef.OXM_OF_IP_PROTO, ofdef.IPPROTO_UDP), ofdef.create_oxm(ofdef.OXM_OF_IPV4_DST, ip4_addr_bytes(serveraddr)), ofdef.create_oxm(ofdef.OXM_OF_UDP_DST, 67) ] ), instructions = [ ofdef.ofp_instruction_actions( actions = [ ofdef.ofp_action_output(port = ofdef.OFPP_CONTROLLER, max_len = ofdef.OFPCML_NO_BUFFER ) ] ) ] ) for n in removevalues: if n in lastlognetinfo: nid = lastlognetinfo[n] cmds.append(_delete_flows(nid)) lastnetdict = dict((n.id,n) for n in lastlognetinfo) for serveraddr in lastserveraddresses: if serveraddr not in currentserveraddresses: addr, _, networkid, _ = serveraddr if networkid in lastnetdict: n = lastnetdict[networkid] nid = lastlognetinfo[n] cmds.append(_delete_flows2(nid, addr)) for m in self.execute_commands(connection, cmds): yield m # Remove old ARP entries; Add new ARP entries remove_arps = lastserveraddresses.difference(currentserveraddresses) if remove_arps: for m in callAPI(self, 'arpresponder', 'removeproxyarp', {'connection': connection, 'arpentries': remove_arps}): yield m add_arps = currentserveraddresses.difference(lastserveraddresses) if add_arps: for m in callAPI(self, 'arpresponder', 'createproxyarp', {'connection': connection, 'arpentries': add_arps}): yield m del cmds[:] for n in addvalues: if n in currentlognetinfo: nid = currentlognetinfo[n] cmds.append(_create_flows(nid)) currnetdict = dict((n.id,n) for n in currentlognetinfo) for serveraddr in currentserveraddresses: if serveraddr not in lastserveraddresses: addr, _, networkid, _ = serveraddr if networkid in currnetdict: n = currnetdict[networkid] nid = currentlognetinfo[n] cmds.append(_create_flows2(nid, addr)) for m in self.execute_commands(connection, cmds): yield m except Exception: self._logger.warning("Unexpected exception in DHCPUpdater. Will ignore and continue.", exc_info = True)