def _control_plane_icmpv6_handler(self, now, pkt_meta, ipv6_pkt): ofmsgs = [] if not pkt_meta.packet_complete(): return ofmsgs # Must be ICMPv6 and have no extended headers. if ipv6_pkt.nxt != valve_of.inet.IPPROTO_ICMPV6: return ofmsgs if ipv6_pkt.ext_hdrs: return ofmsgs dst_ip = ipaddress.IPv6Address(btos(ipv6_pkt.dst)) # Explicitly ignore messages to all notes. if dst_ip == valve_packet.IPV6_ALL_NODES: return ofmsgs src_ip = ipaddress.IPv6Address(btos(ipv6_pkt.src)) vlan = pkt_meta.vlan if not vlan.ip_in_vip_subnet(src_ip): return ofmsgs pkt_meta.reparse_ip(payload=32) icmpv6_pkt = pkt_meta.pkt.get_protocol(icmpv6.icmpv6) if icmpv6_pkt is None: return ofmsgs icmpv6_type = icmpv6_pkt.type_ if (ipv6_pkt.hop_limit != valve_packet.IPV6_MAX_HOP_LIM and icmpv6_type != icmpv6.ICMPV6_ECHO_REQUEST): return ofmsgs if icmpv6_type in self._icmpv6_handlers: ofmsgs = self._icmpv6_handlers[icmpv6_type](self, now, pkt_meta, ipv6_pkt, icmpv6_pkt, src_ip, dst_ip) return ofmsgs
def _control_plane_arp_handler(self, pkt_meta, arp_pkt): src_ip = ipaddress.IPv4Address(btos(arp_pkt.src_ip)) dst_ip = ipaddress.IPv4Address(btos(arp_pkt.dst_ip)) vlan = pkt_meta.vlan opcode = arp_pkt.opcode ofmsgs = [] if vlan.from_connected_to_vip(src_ip, dst_ip): in_port = pkt_meta.port.number eth_src = pkt_meta.eth_src if opcode == arp.ARP_REQUEST: ofmsgs.extend( self._add_host_fib_route(vlan, src_ip)) vid = self._vlan_vid(vlan, in_port) arp_reply = valve_packet.arp_reply( self.faucet_mac, eth_src, vid, dst_ip, src_ip) ofmsgs.append( valve_of.packetout(in_port, arp_reply.data)) self.logger.info( 'Responded to ARP request for %s from %s (%s)', dst_ip, src_ip, eth_src) elif (opcode == arp.ARP_REPLY and pkt_meta.eth_dst == self.faucet_mac): ofmsgs.extend( self._update_nexthop(vlan, in_port, eth_src, src_ip)) self.logger.info( 'ARP response %s (%s)', src_ip, eth_src) return ofmsgs
def _bgp_route_handler(self, path_change, vlan): """Handle a BGP change event. Args: path_change (ryu.services.protocols.bgp.bgpspeaker.EventPrefix): path change vlan (vlan): Valve VLAN this path change was received for. """ prefix = ipaddress.ip_network(btos(path_change.prefix)) nexthop = ipaddress.ip_address(btos(path_change.nexthop)) withdraw = path_change.is_withdraw flowmods = [] if not self._valves or vlan.dp_id not in self._valves: return valve = self._valves[vlan.dp_id] if vlan.is_faucet_vip(nexthop): self.logger.error('BGP nexthop %s for prefix %s cannot be us', nexthop, prefix) return if vlan.ip_in_vip_subnet(nexthop) is None: self.logger.error( 'BGP nexthop %s for prefix %s is not a connected network', nexthop, prefix) return if withdraw: self.logger.info('BGP withdraw %s nexthop %s', prefix, nexthop) flowmods = valve.del_route(vlan, prefix) else: self.logger.info('BGP add %s nexthop %s', prefix, nexthop) flowmods = valve.add_route(vlan, nexthop, prefix) if flowmods: self._send_flow_msgs(vlan.dp_id, flowmods)
def __init__(self, _id, dp_id, conf=None): super(VLAN, self).__init__(_id, conf) self.dp_id = dp_id self.tagged = [] self.untagged = [] self.dyn_host_cache = {} self.dyn_faucet_vips_by_ipv = collections.defaultdict(list) self.dyn_routes_by_ipv = collections.defaultdict(dict) self.dyn_neigh_cache_by_ipv = collections.defaultdict(dict) self.dyn_ipvs = [] if self.faucet_vips: self.faucet_vips = [ ipaddress.ip_interface(btos(ip)) for ip in self.faucet_vips ] for faucet_vip in self.faucet_vips: self.dyn_faucet_vips_by_ipv[faucet_vip.version].append( faucet_vip) self.dyn_ipvs = list(self.dyn_faucet_vips_by_ipv.keys()) if self.bgp_as: assert self.bgp_port assert ipaddress.IPv4Address(btos(self.bgp_routerid)) for neighbor_ip in self.bgp_neighbor_addresses: assert ipaddress.ip_address(btos(neighbor_ip)) assert self.bgp_neighbor_as if self.routes: self.routes = [route['route'] for route in self.routes] for route in self.routes: ip_gw = ipaddress.ip_address(btos(route['ip_gw'])) ip_dst = ipaddress.ip_network(btos(route['ip_dst'])) assert ip_gw.version == ip_dst.version self.dyn_routes_by_ipv[ip_gw.version][ip_dst] = ip_gw
def _control_plane_icmp_handler(self, pkt_meta, ipv4_pkt): ofmsgs = [] if not pkt_meta.packet_complete(): return ofmsgs src_ip = ipaddress.IPv4Address(btos(ipv4_pkt.src)) dst_ip = ipaddress.IPv4Address(btos(ipv4_pkt.dst)) vlan = pkt_meta.vlan if vlan.from_connected_to_vip(src_ip, dst_ip): if pkt_meta.eth_dst != vlan.faucet_mac: return ofmsgs if ipv4_pkt.proto != valve_of.inet.IPPROTO_ICMP: return ofmsgs pkt_meta.reparse_all() icmp_pkt = pkt_meta.pkt.get_protocol(icmp.icmp) if icmp_pkt is None: return ofmsgs if icmp_pkt.type == icmp.ICMP_ECHO_REQUEST: port = pkt_meta.port vid = self._vlan_vid(vlan, port) echo_reply = valve_packet.echo_reply( vid, vlan.faucet_mac, pkt_meta.eth_src, dst_ip, src_ip, icmp_pkt.data) ofmsgs.append( valve_of.packetout(port.number, echo_reply.data)) return ofmsgs
def _control_plane_arp_handler(self, pkt_meta): ofmsgs = [] if not pkt_meta.packet_complete(): return ofmsgs pkt_meta.reparse_ip() arp_pkt = pkt_meta.pkt.get_protocol(arp.arp) if arp_pkt is None: return ofmsgs src_ip = ipaddress.IPv4Address(btos(arp_pkt.src_ip)) dst_ip = ipaddress.IPv4Address(btos(arp_pkt.dst_ip)) vlan = pkt_meta.vlan if vlan.from_connected_to_vip(src_ip, dst_ip): opcode = arp_pkt.opcode port = pkt_meta.port eth_src = pkt_meta.eth_src if opcode == arp.ARP_REQUEST: ofmsgs.extend( self._add_host_fib_route(vlan, src_ip, blackhole=False)) ofmsgs.extend(self._update_nexthop(vlan, port, eth_src, src_ip)) ofmsgs.append( vlan.pkt_out_port(valve_packet.arp_reply, port, vlan.faucet_mac, eth_src, dst_ip, src_ip)) self.logger.info( 'Responded to ARP request for %s from %s (%s) on VLAN %u' % (dst_ip, src_ip, eth_src, vlan.vid)) elif (opcode == arp.ARP_REPLY and pkt_meta.eth_dst == vlan.faucet_mac): ofmsgs.extend(self._update_nexthop(vlan, port, eth_src, src_ip)) self.logger.info('ARP response %s (%s) on VLAN %u' % (src_ip, eth_src, vlan.vid)) return ofmsgs
def _control_plane_arp_handler(self, pkt_meta): ofmsgs = [] pkt_meta.reparse_ip(ether.ETH_TYPE_ARP) arp_pkt = pkt_meta.pkt.get_protocol(arp.arp) if arp_pkt is None: return ofmsgs src_ip = ipaddress.IPv4Address(btos(arp_pkt.src_ip)) dst_ip = ipaddress.IPv4Address(btos(arp_pkt.dst_ip)) vlan = pkt_meta.vlan if vlan.from_connected_to_vip(src_ip, dst_ip): opcode = arp_pkt.opcode port = pkt_meta.port eth_src = pkt_meta.eth_src vid = self._vlan_vid(vlan, port) if opcode == arp.ARP_REQUEST: ofmsgs.extend( self._add_host_fib_route(vlan, src_ip)) ofmsgs.extend(self._update_nexthop( vlan, port, eth_src, src_ip)) arp_reply = valve_packet.arp_reply( vid, vlan.faucet_mac, eth_src, dst_ip, src_ip) ofmsgs.append( valve_of.packetout(port.number, arp_reply.data)) self.logger.info( 'Responded to ARP request for %s from %s (%s) on VLAN %u' % ( dst_ip, src_ip, eth_src, vlan.vid)) elif (opcode == arp.ARP_REPLY and pkt_meta.eth_dst == vlan.faucet_mac): ofmsgs.extend( self._update_nexthop(vlan, port, eth_src, src_ip)) self.logger.info( 'ARP response %s (%s) on VLAN %u' % ( src_ip, eth_src, vlan.vid)) return ofmsgs
def reparse_ip(self, payload=0): """Reparse packet with specified IP header type and optionally payload.""" if self.eth_type in self.ETH_TYPES_PARSERS: header_size = self.MIN_ETH_TYPE_PKT_SIZE[self.eth_type] ip_ver, ip_parseable, pkt_parser = self.ETH_TYPES_PARSERS[ self.eth_type] if ip_ver is not None: if ip_ver != self.ip_ver(): return if self.vlan is not None and self.vlan.minimum_ip_size_check: if len(self.data) < header_size: return ip_header_data = self.data[ETH_VLAN_HEADER_SIZE:] if ip_parseable is not None and not ip_parseable( ip_header_data): return parse_limit = header_size + payload self.reparse(parse_limit) self.l3_pkt = self.pkt.get_protocol(pkt_parser) if self.l3_pkt: if hasattr(self.l3_pkt, 'src'): self.l3_src = self.l3_pkt.src self.l3_dst = self.l3_pkt.dst elif hasattr(self.l3_pkt, 'src_ip'): self.l3_src = self.l3_pkt.src_ip self.l3_dst = self.l3_pkt.dst_ip self.l3_src = ipaddress.ip_address(valve_util.btos( self.l3_src)) self.l3_dst = ipaddress.ip_address(valve_util.btos( self.l3_dst))
def check_config(self): super(VLAN, self).check_config() assert self.vid_valid(self.vid), 'invalid VID %s' % self.vid assert netaddr.valid_mac( self.faucet_mac), 'invalid MAC address %s' % self.faucet_mac if self.faucet_vips: try: self.faucet_vips = [ ipaddress.ip_interface(btos(ip)) for ip in self.faucet_vips ] except (ValueError, AttributeError, TypeError) as err: assert False, 'Invalid IP address in faucet_vips: %s' % err for faucet_vip in self.faucet_vips: self.dyn_faucet_vips_by_ipv[faucet_vip.version].append( faucet_vip) self.dyn_ipvs = list(self.dyn_faucet_vips_by_ipv.keys()) if self.bgp_as: assert self.bgp_port assert ipaddress.IPv4Address(btos(self.bgp_routerid)) for neighbor_ip in self.bgp_neighbor_addresses: assert ipaddress.ip_address(btos(neighbor_ip)) assert self.bgp_neighbor_as if self.routes: try: self.routes = [route['route'] for route in self.routes] for route in self.routes: try: ip_gw = ipaddress.ip_address(btos(route['ip_gw'])) ip_dst = ipaddress.ip_network(btos(route['ip_dst'])) except (ValueError, AttributeError, TypeError) as err: assert False, 'Invalid IP address in route: %s' % err assert ip_gw.version == ip_dst.version self.dyn_routes_by_ipv[ip_gw.version][ip_dst] = ip_gw except KeyError: assert False, 'missing route config' except TypeError: assert False, '%s is not a valid routes value' % self.routes if self.acl_in and self.acls_in: assert False, 'found both acl_in and acls_in, use only acls_in' if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [ self.acl_in, ] self.acl_in = None if self.acls_in: for acl in self.acls_in: assert isinstance(acl, (int, str)), 'acl names must be int or str'
def _control_plane_icmp_handler(self, pkt_meta, ipv4_pkt, icmp_pkt): src_ip = ipaddress.IPv4Address(btos(ipv4_pkt.src)) dst_ip = ipaddress.IPv4Address(btos(ipv4_pkt.dst)) vlan = pkt_meta.vlan icmpv4_type = icmp_pkt.type ofmsgs = [] if vlan.from_connected_to_vip(src_ip, dst_ip): if (icmpv4_type == icmp.ICMP_ECHO_REQUEST and pkt_meta.eth_dst == self.faucet_mac): in_port = pkt_meta.port.number vid = self._vlan_vid(vlan, in_port) echo_reply = valve_packet.echo_reply( self.faucet_mac, pkt_meta.eth_src, vid, dst_ip, src_ip, icmp_pkt.data) ofmsgs.append( valve_of.packetout(in_port, echo_reply.data)) return ofmsgs
def _nd_advert_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt, _src_ip, _dst_ip): ofmsgs = [] target_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) vlan = pkt_meta.vlan if vlan.ip_in_vip_subnet(target_ip): ofmsgs.extend(self._update_nexthop( now, vlan, pkt_meta.port, pkt_meta.eth_src, target_ip)) self.logger.info( 'ND advert %s (%s) on VLAN %u' % ( target_ip, pkt_meta.eth_src, vlan.vid)) return ofmsgs
def control_plane_handler(self, now, pkt_meta): ipv4_pkt = pkt_meta.pkt.get_protocol(ipv4.ipv4) if ipv4_pkt is None: return self._control_plane_arp_handler(now, pkt_meta) icmp_replies = self._control_plane_icmp_handler(pkt_meta, ipv4_pkt) if icmp_replies: return icmp_replies dst_ip = ipaddress.IPv4Address(btos(ipv4_pkt.dst)) vlan = pkt_meta.vlan return self._proactive_resolve_neighbor(now, self._routed_vlans(vlan), dst_ip)
def control_plane_handler(self, pkt_meta): pkt = pkt_meta.pkt ipv6_pkt = pkt.get_protocol(ipv6.ipv6) if ipv6_pkt is not None: icmp_replies = self._control_plane_icmpv6_handler( pkt_meta, ipv6_pkt) if icmp_replies: return icmp_replies dst_ip = ipaddress.IPv6Address(btos(ipv6_pkt.dst)) return self._proactive_resolve_neighbor( self._routed_vlans(pkt_meta.vlan), dst_ip) return []
def _control_plane_icmpv6_handler(self, pkt_meta, ipv6_pkt, icmpv6_pkt): vlan = pkt_meta.vlan src_ip = ipaddress.IPv6Address(btos(ipv6_pkt.src)) dst_ip = ipaddress.IPv6Address(btos(ipv6_pkt.dst)) icmpv6_type = icmpv6_pkt.type_ ofmsgs = [] if vlan.ip_in_vip_subnet(src_ip): in_port = pkt_meta.port.number vid = self._vlan_vid(vlan, in_port) eth_src = pkt_meta.eth_src if icmpv6_type == icmpv6.ND_NEIGHBOR_SOLICIT: solicited_ip = btos(icmpv6_pkt.data.dst) if vlan.is_faucet_vip(ipaddress.ip_address(solicited_ip)): ofmsgs.extend( self._add_host_fib_route(vlan, src_ip)) nd_reply = valve_packet.nd_reply( self.faucet_mac, eth_src, vid, solicited_ip, src_ip, ipv6_pkt.hop_limit) ofmsgs.append( valve_of.packetout(in_port, nd_reply.data)) self.logger.info( 'Responded to ND solicit for %s to %s (%s)', solicited_ip, src_ip, eth_src) elif icmpv6_type == icmpv6.ND_NEIGHBOR_ADVERT: ofmsgs.extend(self._update_nexthop( vlan, in_port, eth_src, src_ip)) self.logger.info( 'ND advert %s (%s)', src_ip, eth_src) elif vlan.from_connected_to_vip(src_ip, dst_ip): if (icmpv6_type == icmpv6.ICMPV6_ECHO_REQUEST and pkt_meta.eth_dst == self.faucet_mac): icmpv6_echo_reply = valve_packet.icmpv6_echo_reply( self.faucet_mac, eth_src, vid, dst_ip, src_ip, ipv6_pkt.hop_limit, icmpv6_pkt.data.id, icmpv6_pkt.data.seq, icmpv6_pkt.data.data) ofmsgs.append( valve_of.packetout(in_port, icmpv6_echo_reply.data)) return ofmsgs
def _nd_advert_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt, _src_ip, _dst_ip): ofmsgs = [] if isinstance(icmpv6_pkt.data, icmpv6.nd_neighbor): target_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) vlan = pkt_meta.vlan if vlan.ip_in_vip_subnet(target_ip): if self._stateful_gw(vlan, target_ip): ofmsgs.extend(self._update_nexthop( now, vlan, pkt_meta.port, pkt_meta.eth_src, target_ip)) self.logger.info( 'Received ND advert for %s from %s' % ( target_ip, pkt_meta.log())) return ofmsgs
def __init__(self, _id, dp_id, conf=None): if conf is None: conf = {} self._id = _id self.dp_id = dp_id self.update(conf) self.set_defaults() self._id = _id self.tagged = [] self.untagged = [] self.dyn_ipv4_routes = {} self.dyn_ipv6_routes = {} self.dyn_arp_cache = {} self.dyn_nd_cache = {} self.dyn_host_cache = {} if self.faucet_vips: self.faucet_vips = [ ipaddress.ip_interface(btos(ip)) for ip in self.faucet_vips ] if self.bgp_as: assert self.bgp_port assert ipaddress.IPv4Address(btos(self.bgp_routerid)) for neighbor_ip in self.bgp_neighbor_addresses: assert ipaddress.ip_address(btos(neighbor_ip)) assert self.bgp_neighbor_as if self.routes: self.routes = [route['route'] for route in self.routes] for route in self.routes: ip_gw = ipaddress.ip_address(btos(route['ip_gw'])) ip_dst = ipaddress.ip_network(btos(route['ip_dst'])) assert ip_gw.version == ip_dst.version if ip_gw.version == 4: self.ipv4_routes[ip_dst] = ip_gw else: self.ipv6_routes[ip_dst] = ip_gw
def ipv6_solicited_node_from_ucast(ucast): """Return IPv6 solicited node multicast address from IPv6 unicast address. See RFC 3513 section 2.7.1. Args: ucast (ipaddress.IPv6Address): IPv6 unicast address. Returns: ipaddress.IPv6Address: IPv6 solicited node multicast address. """ link_mcast_prefix = ipaddress.ip_interface(btos('ff02::1:ff00:0/104')) mcast_bytes = link_mcast_prefix.packed[:13] + ucast.packed[-3:] link_mcast = ipaddress.IPv6Address(mcast_bytes) return link_mcast
def check_config(self): super(VLAN, self).check_config() assert self.vid_valid(self.vid), 'invalid VID %s' % self.vid assert netaddr.valid_mac(self.faucet_mac), 'invalid MAC address %s' % self.faucet_mac if self.faucet_vips: try: self.faucet_vips = [ ipaddress.ip_interface(btos(ip)) for ip in self.faucet_vips] except (ValueError, AttributeError, TypeError) as err: assert False, 'Invalid IP address in faucet_vips: %s' % err for faucet_vip in self.faucet_vips: self.dyn_faucet_vips_by_ipv[faucet_vip.version].append( faucet_vip) self.dyn_ipvs = list(self.dyn_faucet_vips_by_ipv.keys()) if self.bgp_as: assert self.bgp_port assert ipaddress.IPv4Address(btos(self.bgp_routerid)) for neighbor_ip in self.bgp_neighbor_addresses: assert ipaddress.ip_address(btos(neighbor_ip)) assert self.bgp_neighbor_as if self.routes: try: self.routes = [route['route'] for route in self.routes] for route in self.routes: try: ip_gw = ipaddress.ip_address(btos(route['ip_gw'])) ip_dst = ipaddress.ip_network(btos(route['ip_dst'])) except (ValueError, AttributeError, TypeError) as err: assert False, 'Invalid IP address in route: %s' % err assert ip_gw.version == ip_dst.version self.dyn_routes_by_ipv[ip_gw.version][ip_dst] = ip_gw except KeyError: assert False, 'missing route config'
def _nd_solicit_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt, src_ip, _dst_ip): ofmsgs = [] solicited_ip = btos(icmpv6_pkt.data.dst) vlan = pkt_meta.vlan if vlan.is_faucet_vip(ipaddress.ip_address(solicited_ip)): ofmsgs.extend( self._add_host_fib_route(vlan, src_ip, blackhole=False)) ofmsgs.extend(self._update_nexthop( now, vlan, pkt_meta.port, pkt_meta.eth_src, src_ip)) ofmsgs.append( vlan.pkt_out_port( valve_packet.nd_advert, pkt_meta.port, vlan.faucet_mac, pkt_meta.eth_src, solicited_ip, src_ip)) self.logger.info( 'Responded to ND solicit for %s to %s (%s) on VLAN %u' % ( solicited_ip, src_ip, pkt_meta.eth_src, vlan.vid)) return ofmsgs
def add_host_fib_route_from_pkt(self, pkt_meta): """Add a host FIB route given packet from host. Args: pkt_meta (PacketMeta): received packet. Returns: list: OpenFlow messages. """ ip_pkt = self._ip_pkt(pkt_meta.pkt) ofmsgs = [] if ip_pkt: src_ip = ipaddress.ip_address(btos(ip_pkt.src)) if src_ip and pkt_meta.vlan.ip_in_vip_subnet(src_ip): ofmsgs.extend( self._add_host_fib_route(pkt_meta.vlan, src_ip)) ofmsgs.extend(self._update_nexthop( pkt_meta.vlan, pkt_meta.port, pkt_meta.eth_src, src_ip)) return ofmsgs
def _nd_solicit_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt, src_ip, _dst_ip): ofmsgs = [] if isinstance(icmpv6_pkt.data, icmpv6.nd_neighbor): solicited_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) vlan = pkt_meta.vlan if vlan.is_faucet_vip(solicited_ip): if self._stateful_gw(vlan, src_ip): ofmsgs.extend( self._add_host_fib_route(vlan, src_ip, blackhole=False)) ofmsgs.extend(self._update_nexthop( now, vlan, pkt_meta.port, pkt_meta.eth_src, src_ip)) ofmsgs.append( vlan.pkt_out_port( valve_packet.nd_advert, pkt_meta.port, vlan.faucet_mac, pkt_meta.eth_src, solicited_ip, src_ip)) self.logger.info( 'Responded to ND solicit for %s from %s' % ( solicited_ip, pkt_meta.log())) return ofmsgs
def _control_plane_icmpv6_handler(self, pkt_meta, ipv6_pkt): ofmsgs = [] if not pkt_meta.packet_complete(): return ofmsgs src_ip = ipaddress.IPv6Address(btos(ipv6_pkt.src)) dst_ip = ipaddress.IPv6Address(btos(ipv6_pkt.dst)) vlan = pkt_meta.vlan if vlan.ip_in_vip_subnet(src_ip): # Must be ICMPv6 and have no extended headers. if ipv6_pkt.nxt != valve_of.inet.IPPROTO_ICMPV6: return ofmsgs if ipv6_pkt.ext_hdrs: return ofmsgs # Explicitly ignore messages to all notes. if dst_ip == valve_packet.IPV6_ALL_NODES: return ofmsgs pkt_meta.reparse_ip(payload=32) icmpv6_pkt = pkt_meta.pkt.get_protocol(icmpv6.icmpv6) if icmpv6_pkt is None: return ofmsgs icmpv6_type = icmpv6_pkt.type_ if (ipv6_pkt.hop_limit != valve_packet.IPV6_MAX_HOP_LIM and icmpv6_type != icmpv6.ICMPV6_ECHO_REQUEST): return ofmsgs port = pkt_meta.port eth_src = pkt_meta.eth_src if icmpv6_type == icmpv6.ND_NEIGHBOR_SOLICIT: solicited_ip = btos(icmpv6_pkt.data.dst) if vlan.is_faucet_vip(ipaddress.ip_address(solicited_ip)): ofmsgs.extend( self._add_host_fib_route(vlan, src_ip, blackhole=False)) ofmsgs.extend( self._update_nexthop(vlan, port, eth_src, src_ip)) ofmsgs.append( vlan.pkt_out_port(valve_packet.nd_advert, port, vlan.faucet_mac, eth_src, solicited_ip, src_ip)) self.logger.info( 'Responded to ND solicit for %s to %s (%s) on VLAN %u' % (solicited_ip, src_ip, eth_src, vlan.vid)) elif icmpv6_type == icmpv6.ND_NEIGHBOR_ADVERT: target_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) if vlan.ip_in_vip_subnet(target_ip): ofmsgs.extend( self._update_nexthop(vlan, port, eth_src, target_ip)) self.logger.info('ND advert %s (%s) on VLAN %u' % (target_ip, eth_src, vlan.vid)) elif icmpv6_type == icmpv6.ND_ROUTER_SOLICIT: link_local_vips, other_vips = self._link_and_other_vips(vlan) for vip in link_local_vips: if src_ip in vip.network: ofmsgs.extend( self._add_host_fib_route(vlan, src_ip, blackhole=False)) ofmsgs.extend( self._update_nexthop(vlan, port, eth_src, src_ip)) ofmsgs.append( vlan.pkt_out_port(valve_packet.router_advert, port, vlan.faucet_mac, eth_src, vip.ip, src_ip, other_vips)) self.logger.info( 'Responded to RS solicit from %s (%s) to VIP %s on VLAN %u' % (src_ip, eth_src, vip, vlan.vid)) break elif icmpv6_type == icmpv6.ICMPV6_ECHO_REQUEST: if (vlan.from_connected_to_vip(src_ip, dst_ip) and pkt_meta.eth_dst == vlan.faucet_mac): ofmsgs.append( vlan.pkt_out_port(valve_packet.icmpv6_echo_reply, port, vlan.faucet_mac, eth_src, dst_ip, src_ip, ipv6_pkt.hop_limit, icmpv6_pkt.data.id, icmpv6_pkt.data.seq, icmpv6_pkt.data.data)) return ofmsgs
def _nd_solicit_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt): ofmsgs = [] solicited_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) ofmsgs.extend(self._resolve_vip_response(pkt_meta, solicited_ip, now)) return ofmsgs
def _nd_advert_handler(self, now, pkt_meta, _ipv6_pkt, icmpv6_pkt): ofmsgs = [] target_ip = ipaddress.ip_address(btos(icmpv6_pkt.data.dst)) ofmsgs.extend(self._gw_advert(pkt_meta, target_ip, now)) return ofmsgs
FAUCET_MAC = '0e:00:00:00:00:01' # Default FAUCET MAC address ETH_HEADER_SIZE = 14 ETH_VLAN_HEADER_SIZE = ETH_HEADER_SIZE + 4 # https://en.wikipedia.org/wiki/IEEE_802.1Q#Frame_format IPV4_HEADER_SIZE = 20 # https://en.wikipedia.org/wiki/IPv4#Header IPV6_HEADER_SIZE = 40 # https://en.wikipedia.org/wiki/IPv6_packet#Fixed_header ARP_PKT_SIZE = 28 # https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure SLOW_PROTOCOL_MULTICAST = slow.SLOW_PROTOCOL_MULTICAST BRIDGE_GROUP_ADDRESS = bpdu.BRIDGE_GROUP_ADDRESS BRIDGE_GROUP_MASK = 'ff:ff:ff:ff:ff:f0' LLDP_MAC_NEAREST_BRIDGE = lldp.LLDP_MAC_NEAREST_BRIDGE CISCO_SPANNING_GROUP_ADDRESS = '01:00:0c:cc:cc:cd' IPV6_ALL_NODES_MCAST = '33:33:00:00:00:01' IPV6_ALL_ROUTERS_MCAST = '33:33:00:00:00:02' IPV6_LINK_LOCAL = ipaddress.IPv6Network(btos('fe80::/10')) IPV6_ALL_NODES = ipaddress.IPv6Address(btos('ff02::1')) IPV6_MAX_HOP_LIM = 255 LLDP_FAUCET_DP_ID = 1 LLDP_FAUCET_STACK_STATE = 2 def ipv4_parseable(ip_header_data): """Return True if an IPv4 packet we could parse.""" # TODO: python library parsers are fragile # Perform sanity checking on the header to limit exposure of the parser ipv4_header = struct.unpack('!BBHHHBBH4s4s', ip_header_data[:IPV4_HEADER_SIZE]) header_size = (ipv4_header[0] & 0xf) * 32 / 8 if header_size < IPV4_HEADER_SIZE:
def _check_ip_str(ip_str, ip_method=ipaddress.ip_address): try: return ip_method(btos(ip_str)) except (ValueError, AttributeError, TypeError) as err: raise InvalidConfigError('Invalid IP address %s: %s' % (ip_str, err))
def check_config(self): super(VLAN, self).check_config() test_config_condition(not self.vid_valid(self.vid), 'invalid VID %s' % self.vid) test_config_condition(not netaddr.valid_mac(self.faucet_mac), ('invalid MAC address %s' % self.faucet_mac)) if self.max_hosts: if not self.proactive_arp_limit: self.proactive_arp_limit = 2 * self.max_hosts if not self.proactive_nd_limit: self.proactive_nd_limit = 2 * self.max_hosts if self.faucet_vips: try: self.faucet_vips = [ ipaddress.ip_interface(btos(ip)) for ip in self.faucet_vips ] except (ValueError, AttributeError, TypeError) as err: raise InvalidConfigError( 'Invalid IP address in faucet_vips: %s' % err) for faucet_vip in self.faucet_vips: self.dyn_faucet_vips_by_ipv[faucet_vip.version].append( faucet_vip) self.dyn_ipvs = list(self.dyn_faucet_vips_by_ipv.keys()) if self.bgp_neighbor_addresses or self.bgp_neighbour_addresses: neigh_addresses = set(self.bgp_neighbor_addresses + self.bgp_neighbour_addresses) try: self.bgp_neighbor_addresses = [ ipaddress.ip_address(btos(ip)) for ip in neigh_addresses ] except (ValueError, AttributeError, TypeError) as err: raise InvalidConfigError( 'Invalid IP address in bgp_neighbor_addresses: %s' % err) for bgp_neighbor_address in self.bgp_neighbor_addresses: self.dyn_bgp_neighbor_addresses_by_ipv[ bgp_neighbor_address.version].append(bgp_neighbor_address) if self.bgp_server_addresses: try: self.bgp_server_addresses = [ ipaddress.ip_address(btos(ip)) for ip in self.bgp_server_addresses ] except (ValueError, AttributeError, TypeError) as err: raise InvalidConfigError( 'Invalid IP address in bgp_server_addresses: %s' % err) for bgp_server_address in self.bgp_server_addresses: self.dyn_bgp_server_addresses_by_ipv[ bgp_server_address.version].append(bgp_server_address) self.dyn_bgp_ipvs = list( self.dyn_bgp_server_addresses_by_ipv.keys()) if self.bgp_as: test_config_condition(not isinstance(self.bgp_port, int), ('BGP port must be %s not %s' % (int, type(self.bgp_port)))) test_config_condition( self.bgp_connect_mode not in ('active', 'passive', 'both'), ('%s must be active, passive or both' % self.bgp_connect_mode)) test_config_condition( not ipaddress.IPv4Address(btos(self.bgp_routerid)), ('%s is not a valid IPv4 address' % (self.bgp_routerid))) test_config_condition(not self.bgp_neighbor_as, 'No BGP neighbor AS') test_config_condition(not self.bgp_neighbor_addresses, 'No BGP neighbor addresses') neighbor_ips = self.bgp_neighbor_addresses test_config_condition( len(neighbor_ips) != len(self.bgp_neighbor_addresses), ('Neighbor IPs is not the same length as BGP neighbor addresses' )) peer_versions = [ip.version for ip in neighbor_ips] test_config_condition( len(peer_versions) != 1 and self.bgp_connect_mode != 'active', ('if using multiple address families bgp_connect_mode must be active' )) if self.routes: try: self.routes = [route['route'] for route in self.routes] for route in self.routes: try: ip_gw = ipaddress.ip_address(btos(route['ip_gw'])) ip_dst = ipaddress.ip_network(btos(route['ip_dst'])) except (ValueError, AttributeError, TypeError) as err: raise InvalidConfigError( 'Invalid IP address in route: %s' % err) test_config_condition( ip_gw.version != ip_dst.version, 'ip_gw version does not match the ip_dst version') self.add_route(ip_dst, ip_gw) except KeyError as err: raise InvalidConfigError('missing route config %s' % err) except TypeError: raise InvalidConfigError('%s is not a valid routes value' % self.routes) test_config_condition( self.acl_in and self.acls_in, 'found both acl_in and acls_in, use only acls_in') if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [ self.acl_in, ] self.acl_in = None if self.acls_in: for acl in self.acls_in: test_config_condition(not isinstance(acl, (int, str)), 'acl names must be int or str')
def _host_from_faucet_vip(self, faucet_vip): max_prefixlen = faucet_vip.ip.max_prefixlen return ipaddress.ip_interface( btos('/'.join((faucet_vip.ip.exploded, str(max_prefixlen)))))
FAUCET_MAC = '0e:00:00:00:00:01' # Default FAUCET MAC address ETH_HEADER_SIZE = 14 ETH_VLAN_HEADER_SIZE = ETH_HEADER_SIZE + 4 # https://en.wikipedia.org/wiki/IEEE_802.1Q#Frame_format IPV4_HEADER_SIZE = 20 # https://en.wikipedia.org/wiki/IPv4#Header IPV6_HEADER_SIZE = 40 # https://en.wikipedia.org/wiki/IPv6_packet#Fixed_header ARP_PKT_SIZE = 28 # https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Packet_structure SLOW_PROTOCOL_MULTICAST = slow.SLOW_PROTOCOL_MULTICAST BRIDGE_GROUP_ADDRESS = bpdu.BRIDGE_GROUP_ADDRESS BRIDGE_GROUP_MASK = 'ff:ff:ff:ff:ff:f0' LLDP_MAC_NEAREST_BRIDGE = lldp.LLDP_MAC_NEAREST_BRIDGE CISCO_SPANNING_GROUP_ADDRESS = '01:00:0c:cc:cc:cd' IPV6_ALL_NODES_MCAST = '33:33:00:00:00:01' IPV6_ALL_ROUTERS_MCAST = '33:33:00:00:00:02' IPV6_ALL_NODES = ipaddress.IPv6Address(valve_util.btos('ff02::1')) IPV6_MAX_HOP_LIM = 255 LLDP_FAUCET_DP_ID = 1 LLDP_FAUCET_STACK_STATE = 2 def int_from_mac(mac): int_hi, int_lo = [int(i, 16) for i in mac.split(':')[-2:]] return (int_hi << 8) + int_lo def int_in_mac(mac, to_int): int_mac = mac.split(':')[:4] + [ '%x' % (to_int >> 8), '%x' % (to_int & 0xff) ]
def check_config(self): super(VLAN, self).check_config() test_config_condition(not self.vid_valid(self.vid), 'invalid VID %s' % self.vid) test_config_condition(not netaddr.valid_mac(self.faucet_mac), ('invalid MAC address %s' % self.faucet_mac)) test_config_condition( self.acl_in and self.acls_in, 'found both acl_in and acls_in, use only acls_in') if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [ self.acl_in, ] self.acl_in = None if self.acls_in: for acl in self.acls_in: test_config_condition(not isinstance(acl, (int, str)), 'acl names must be int or str') if self.max_hosts: if not self.proactive_arp_limit: self.proactive_arp_limit = 2 * self.max_hosts if not self.proactive_nd_limit: self.proactive_nd_limit = 2 * self.max_hosts if self.faucet_vips: self.faucet_vips = frozenset([ self._check_ip_str(ip_str, ip_method=ipaddress.ip_interface) for ip_str in self.faucet_vips ]) if self.bgp_neighbor_addresses or self.bgp_neighbour_addresses: neigh_addresses = frozenset(self.bgp_neighbor_addresses + self.bgp_neighbour_addresses) self.bgp_neighbor_addresses = frozenset( [self._check_ip_str(ip_str) for ip_str in neigh_addresses]) if self.bgp_server_addresses: self.bgp_server_addresses = frozenset([ self._check_ip_str(ip_str) for ip_str in self.bgp_server_addresses ]) for ipv in self.bgp_ipvs(): test_config_condition( len(self.bgp_server_addresses_by_ipv(ipv)) != 1, 'Only one BGP server address per IP version supported') if self.bgp_as: test_config_condition(not isinstance(self.bgp_port, int), ('BGP port must be %s not %s' % (int, type(self.bgp_port)))) test_config_condition(self.bgp_connect_mode not in ('passive'), ('BGP connect mode %s must be passive' % self.bgp_connect_mode)) test_config_condition( not ipaddress.IPv4Address(btos(self.bgp_routerid)), ('%s is not a valid IPv4 address' % (self.bgp_routerid))) test_config_condition(not self.bgp_neighbor_as, 'No BGP neighbor AS') test_config_condition(not self.bgp_neighbor_addresses, 'No BGP neighbor addresses') test_config_condition( len(self.bgp_neighbor_addresses) != len( self.bgp_neighbor_addresses), ('Must be as many BGP neighbor addresses as BGP server addresses' )) if self.routes: test_config_condition(not isinstance(self.routes, list), 'invalid VLAN routes format') try: self.routes = [route['route'] for route in self.routes] except TypeError: raise InvalidConfigError('%s is not a valid routes value' % self.routes) except KeyError: pass for route in self.routes: test_config_condition(not isinstance(route, dict), 'invalid VLAN route format') test_config_condition('ip_gw' not in route, 'missing ip_gw in VLAN route') test_config_condition('ip_dst' not in route, 'missing ip_dst in VLAN route') ip_gw = self._check_ip_str(route['ip_gw']) ip_dst = self._check_ip_str(route['ip_dst'], ip_method=ipaddress.ip_network) test_config_condition( ip_gw.version != ip_dst.version, 'ip_gw version does not match the ip_dst version') self.add_route(ip_dst, ip_gw)