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 __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 _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 = [] 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 _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): 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)) ofmsgs.extend(self._update_nexthop(vlan, port, eth_src, src_ip)) vid = self._vlan_vid(vlan, port) arp_reply = valve_packet.arp_reply(vid, self.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)', 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, port, eth_src, src_ip)) self.logger.info('ARP response %s (%s)', src_ip, eth_src) return ofmsgs
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): port = pkt_meta.port vid = self._vlan_vid(vlan, port) echo_reply = valve_packet.echo_reply(vid, self.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 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): now = time.time() nexthop_fresh = self._nexthop_fresh(pkt_meta.vlan, src_ip, now) self._update_nexthop_cache(pkt_meta.vlan, pkt_meta.eth_src, src_ip) if not nexthop_fresh: if self.use_group_table: ofmsgs.extend( self._update_nexthop_group(False, src_ip, pkt_meta.vlan, pkt_meta.port, pkt_meta.eth_src)) ofmsgs.extend( self._add_host_fib_route(pkt_meta.vlan, src_ip)) return ofmsgs
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([pkt_meta.vlan], dst_ip) return []
def control_plane_handler(self, pkt_meta): ipv4_pkt = pkt_meta.pkt.get_protocol(ipv4.ipv4) if ipv4_pkt is None: return self._control_plane_arp_handler(pkt_meta) else: 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([vlan], dst_ip) return []
def _control_plane_icmp_handler(self, pkt_meta, ipv4_pkt): 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 != 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 __init__(self, _id, dp_id, conf=None): if conf is None: conf = {} self._id = _id self.dp_id = dp_id valve_util.check_unknown_conf(conf, self.defaults) 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 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 _control_plane_icmpv6_handler(self, pkt_meta, ipv6_pkt): 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 != 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: ofmsgs.extend(self._update_nexthop( vlan, port, eth_src, src_ip)) self.logger.info( 'ND advert %s (%s) on VLAN %u' % ( src_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
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import ipaddress from ryu.lib import mac from ryu.lib.packet import arp, ethernet, icmp, icmpv6, ipv4, ipv6, stream_parser, packet, vlan from ryu.ofproto import ether from ryu.ofproto import inet from valve_util import btos 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')) def mac_addr_is_unicast(mac_addr): """Returns True if mac_addr is a unicast Ethernet address. Args: mac_addr (str): MAC address. Returns: bool: True if a unicast Ethernet address. """ msb = mac_addr.split(':')[0] return msb[-1] in '02468aAcCeE'