def _flood_stack_links(self, pkt_builder, vlan, multi_out=True, *args): """Return flood packet-out actions to stack ports for gw resolving""" ofmsgs = [] if self.stack_manager: ports = [] if self.stack_manager.stack.is_root(): ports = list(self.stack_manager.away_ports - self.stack_manager.inactive_away_ports - self.stack_manager.pruned_away_ports) else: if self.stack_manager.chosen_towards_port is not None: ports = [self.stack_manager.chosen_towards_port] if ports: running_port_nos = [ port.number for port in ports if port.running() ] pkt = pkt_builder(vlan.vid, *args) if running_port_nos: random.shuffle(running_port_nos) if multi_out: ofmsgs.append( valve_of.packetouts(running_port_nos, bytes(pkt.data))) else: ofmsgs.extend([ valve_of.packetout(port_no, bytes(pkt.data)) for port_no in running_port_nos ]) 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 pkt_out_port(self, packet_builder, port, *args): """Return packet-out actions with VLAN tag if port is tagged""" vid = None if self.port_is_tagged(port): vid = self.vid pkt = packet_builder(vid, *args) return valve_of.packetout(port.number, pkt.data)
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, 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 flood_pkt(self, packet_builder, *args): ofmsgs = [] for vid, ports in ( (self.vid, self.tagged_flood_ports(False)), (None, self.untagged_flood_ports(False))): if ports: pkt = packet_builder(vid, *args) flood_ofmsgs = [valve_of.packetout(port.number, pkt.data) for port in ports if port.running()] ofmsgs.extend(flood_ofmsgs) return ofmsgs
def _neighbor_resolver(self, ip_gw, faucet_vip, vlan, ports): ofmsgs = [] if ports: port_num = ports[0].number vid = self._vlan_vid(vlan, port_num) resolver_pkt = self._neighbor_resolver_pkt( vid, faucet_vip, ip_gw) for port in ports: ofmsgs.append(valve_of.packetout( port.number, resolver_pkt.data)) return ofmsgs
def lacp_req_reply(self, lacp_pkt, port): """ Constructs a LACP req-reply packet. Args: lacp_pkt (PacketMeta): LACP packet received port: LACP port other_valves (list): List of other valves Returns: list packetout OpenFlow msgs. """ if port.lacp_passthrough: for peer_num in port.lacp_passthrough: lacp_peer = self.ports.get(peer_num, None) if not lacp_peer.dyn_lacp_up: self.logger.warning( 'Suppressing LACP LAG %s on %s, peer %s link is down' % (port.lacp, port, lacp_peer)) return [] actor_state_activity = 0 if port.lacp_active: actor_state_activity = 1 actor_state_sync, actor_state_col, actor_state_dist = port.get_lacp_flags( ) if lacp_pkt: pkt = valve_packet.lacp_reqreply( self.faucet_dp_mac, self.faucet_dp_mac, port.lacp, port.lacp_port_id, port.lacp_port_priority, actor_state_sync, actor_state_activity, actor_state_col, actor_state_dist, lacp_pkt.actor_system, lacp_pkt.actor_key, lacp_pkt.actor_port, lacp_pkt.actor_system_priority, lacp_pkt.actor_port_priority, lacp_pkt.actor_state_defaulted, lacp_pkt.actor_state_expired, lacp_pkt.actor_state_timeout, lacp_pkt.actor_state_collecting, lacp_pkt.actor_state_distributing, lacp_pkt.actor_state_aggregation, lacp_pkt.actor_state_synchronization, lacp_pkt.actor_state_activity) else: pkt = valve_packet.lacp_reqreply( self.faucet_dp_mac, self.faucet_dp_mac, port.lacp, port.lacp_port_id, port.lacp_port_priority, actor_state_synchronization=actor_state_sync, actor_state_activity=actor_state_activity, actor_state_collecting=actor_state_col, actor_state_distributing=actor_state_dist) self.logger.debug('Sending LACP %s on %s activity %s' % (pkt, port, actor_state_activity)) return [valve_of.packetout(port.number, pkt.data)]
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 _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 lacp_handler(self, pkt_meta): """Handle a LACP packet. We are a currently a passive, non-aggregateable LACP partner. Args: pkt_meta (PacketMeta): packet for control plane. Returns: list: OpenFlow messages, if any. """ # TODO: ensure config consistent between LAG ports. ofmsgs = [] if (pkt_meta.eth_dst == valve_packet.SLOW_PROTOCOL_MULTICAST and pkt_meta.eth_type == valve_of.ether.ETH_TYPE_SLOW and pkt_meta.port.lacp): pkt_meta.reparse_all() lacp_pkt = valve_packet.parse_lacp_pkt(pkt_meta.pkt) if lacp_pkt: last_lacp_up = pkt_meta.port.dyn_lacp_up pkt_meta.port.dyn_last_lacp_pkt = lacp_pkt pkt_meta.port.dyn_lacp_up = lacp_pkt.actor_state_synchronization pkt_meta.port.dyn_lacp_updated_time = time.time() if last_lacp_up != pkt_meta.port.dyn_lacp_up: self.logger.info( 'LACP state change from %s to %s on %s to %s LAG %u' % (last_lacp_up, pkt_meta.port.dyn_lacp_up, pkt_meta.port, lacp_pkt.actor_system, pkt_meta.port.lacp)) if pkt_meta.port.dyn_lacp_up: ofmsgs.extend(self.lacp_up(pkt_meta.port)) pkt = valve_packet.lacp_reqreply( pkt_meta.vlan.faucet_mac, pkt_meta.vlan.faucet_mac, pkt_meta.port.lacp, pkt_meta.port.number, lacp_pkt.actor_system, lacp_pkt.actor_key, lacp_pkt.actor_port, lacp_pkt.actor_system_priority, lacp_pkt.actor_port_priority, lacp_pkt.actor_state_defaulted, lacp_pkt.actor_state_expired, lacp_pkt.actor_state_timeout, lacp_pkt.actor_state_collecting, lacp_pkt.actor_state_distributing, lacp_pkt.actor_state_aggregation, lacp_pkt.actor_state_synchronization, lacp_pkt.actor_state_activity) ofmsgs.append( valve_of.packetout(pkt_meta.port.number, pkt.data)) return ofmsgs
def flood_pkt(self, packet_builder, multi_out=True, *args): ofmsgs = [] for vid, ports in ( (self.vid, self.tagged_flood_ports(False)), (None, self.untagged_flood_ports(False))): if ports: pkt = packet_builder(vid, *args) exclude_ports = self.exclude_same_lag_member_ports() running_port_nos = [ port.number for port in ports if port.running() and port not in exclude_ports] if running_port_nos: random.shuffle(running_port_nos) if multi_out: ofmsgs.append(valve_of.packetouts(running_port_nos, pkt.data)) else: ofmsgs.extend( [valve_of.packetout(port_no, pkt.data) for port_no in running_port_nos]) return ofmsgs
def send_lldp_beacons(self): """Called periodically to send LLDP beacon packets.""" # TODO: the beacon service should be able to send configurable TLVs. # TODO: the beacon service is specifically NOT to discover topology. # It is intended to facilitate physical troubleshooting (e.g. # a standard cable tester can display OF port information) # A seperate system will be used to probe link/neighbor activity, # addressing issues such as authenticity of the probes. ofmsgs = [] if self.dp.lldp_beacon_ports: now = time.time() beacons_sent = 0 cutoff_beacon_time = now - self.dp.lldp_beacon['send_interval'] ttl = self.dp.lldp_beacon['send_interval'] * 3 for port in self.dp.lldp_beacon_ports: if (port.dyn_last_lldp_beacon_time is None or port.dyn_last_lldp_beacon_time < cutoff_beacon_time): lldp_beacon = port.lldp_beacon chassis_id = str(port.native_vlan.faucet_mac) org_tlvs = [] for org_tlv in lldp_beacon['org_tlvs']: org_tlvs.append((org_tlv['oui'], org_tlv['subtype'], org_tlv['info'])) # if the port doesn't have a system name set, default to # using the system name from the dp if lldp_beacon['system_name'] is None: lldp_beacon['system_name'] = self.dp.lldp_beacon[ 'system_name'] lldp_beacon_pkt = valve_packet.lldp_beacon( port.native_vlan.faucet_mac, chassis_id, port.number, ttl, org_tlvs=org_tlvs, system_name=lldp_beacon['system_name'], port_descr=lldp_beacon['port_descr']) ofmsgs.append( valve_of.packetout(port.number, lldp_beacon_pkt.data)) port.dyn_last_lldp_beacon_time = now beacons_sent += 1 if beacons_sent == self.dp.lldp_beacon['max_per_interval']: break return ofmsgs
def flood_pkt(self, packet_builder, multi_out=True, *args): ofmsgs = [] for vid, ports in ( (self.vid, self.tagged_flood_ports(False)), (None, self.untagged_flood_ports(False))): if ports: pkt = packet_builder(vid, *args) exclude_ports = self.exclude_same_lag_member_ports() running_ports = [ port for port in ports if port.running() and port not in exclude_ports] random.shuffle(running_ports) if multi_out: ofmsgs.append(valve_of.packetouts( [port.number for port in running_ports], pkt.data)) else: ofmsgs.extend( [valve_of.packetout(port.number, pkt.data) for port in running_ports]) return ofmsgs
def flood_pkt(self, packet_builder, multi_out=True, *args): """Return Packet-out actions via flooding""" ofmsgs = [] for vid, ports in ( (self.vid, self.tagged_flood_ports(False)), (None, self.untagged_flood_ports(False))): if ports: pkt = packet_builder(vid, *args) exclude_ports = self.excluded_lag_ports() running_port_nos = [ port.number for port in ports if port.running() and port not in exclude_ports] if running_port_nos: random.shuffle(running_port_nos) if multi_out: ofmsgs.append(valve_of.packetouts(running_port_nos, pkt.data)) else: ofmsgs.extend( [valve_of.packetout(port_no, pkt.data) for port_no in running_port_nos]) return ofmsgs
def _flood_stack_links(self, pkt_builder, vlan, multi_out=True, *args): """Return flood packet-out actions to stack ports for gw resolving""" ofmsgs = [] if isinstance(self.switch_manager, ValveSwitchStackManagerBase): ports = self.switch_manager._stack_flood_ports() if ports: running_port_nos = [ port.number for port in ports if port.running() ] pkt = pkt_builder(vlan.vid, *args) if running_port_nos: random.shuffle(running_port_nos) if multi_out: ofmsgs.append( valve_of.packetouts(running_port_nos, pkt.data)) else: ofmsgs.extend([ valve_of.packetout(port_no, pkt.data) for port_no in running_port_nos ]) return ofmsgs
def pkt_out_port(self, packet_builder, port, *args): vid = None if self.port_is_tagged(port): vid = self.vid pkt = packet_builder(vid, *args) return valve_of.packetout(port.number, pkt.data)
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(self.ETH_TYPE, 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 vid = self._vlan_vid(vlan, 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)) ofmsgs.extend(self._update_nexthop( vlan, port, eth_src, src_ip)) nd_reply = valve_packet.nd_advert( vid, vlan.faucet_mac, eth_src, solicited_ip, src_ip) ofmsgs.append( valve_of.packetout(port.number, nd_reply.data)) 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 = btos(icmpv6_pkt.data.dst) if vlan.ip_in_vip_subnet(ipaddress.ip_address(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)) ofmsgs.extend(self._update_nexthop( vlan, port, eth_src, src_ip)) ra_advert = valve_packet.router_advert( vlan, vid, vlan.faucet_mac, eth_src, vip.ip, src_ip, other_vips) ofmsgs.append( valve_of.packetout(port.number, ra_advert.data)) 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): icmpv6_echo_reply = valve_packet.icmpv6_echo_reply( vid, 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) ofmsgs.append( valve_of.packetout(port.number, icmpv6_echo_reply.data)) return ofmsgs