def _handle_PacketIn(event): packet = event.parsed if packet.find("arp"): # Reply to ARP a = packet.find("arp") if a.opcode == a.REQUEST: r = pkt.arp() r.hwtype = a.hwtype r.prototype = a.prototype r.hwlen = a.hwlen r.protolen = a.protolen r.opcode = r.REPLY r.hwdst = a.hwsrc r.protodst = a.protosrc r.protosrc = a.protodst r.hwsrc = EthAddr("02:00:DE:AD:BE:EF") e = pkt.ethernet(type=packet.type, src=r.hwsrc, dst=a.hwsrc) e.payload = r msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port=of.OFPP_IN_PORT)) msg.in_port = event.port event.connection.send(msg) log.info("%s ARPed for %s", r.protodst, r.protosrc) elif packet.find("icmp"): # Reply to pings # Make the ping reply icmp = pkt.icmp() icmp.type = pkt.TYPE_ECHO_REPLY icmp.payload = packet.find("icmp").payload # Make the IP packet around it ipp = pkt.ipv4() ipp.protocol = ipp.ICMP_PROTOCOL ipp.srcip = packet.find("ipv4").dstip ipp.dstip = packet.find("ipv4").srcip # Ethernet around that... e = pkt.ethernet() e.src = packet.dst e.dst = packet.src e.type = e.IP_TYPE # Hook them up... ipp.payload = icmp e.payload = ipp # Send it back to the input port msg = of.ofp_packet_out() msg.actions.append(of.ofp_action_output(port=of.OFPP_IN_PORT)) msg.data = e.pack() msg.in_port = event.port event.connection.send(msg) log.debug("%s pinged %s", ipp.dstip, ipp.srcip)
def drop(): # Kill buffer on switch if event.ofp.buffer_id is not None: msg = of.ofp_packet_out() msg.buffer_id = event.ofp.buffer_id msg.in_port = event.port event.connection.send(msg)
def _handle_PacketIn(event): packet = event.parsed # Learn the source table[(event.connection, packet.src)] = event.port dst_port = table.get((event.connection, packet.dst)) if dst_port is None: # We don't know where the destination is yet. So, we'll just # send the packet out all ports (except the one it came in on!) # and hope the destination is out there somewhere. :) msg = of.ofp_packet_out(data=event.ofp) msg.actions.append(of.ofp_action_output(port=all_ports)) event.connection.send(msg) else: # Since we know the switch ports for both the source and dest # MACs, we can install rules for both directions. msg = of.ofp_flow_mod() msg.match.dl_dst = packet.src msg.match.dl_src = packet.dst msg.actions.append(of.ofp_action_output(port=event.port)) event.connection.send(msg) # This is the packet that just came in -- we want to # install the rule and also resend the packet. msg = of.ofp_flow_mod() msg.data = event.ofp # Forward the incoming packet msg.match.dl_src = packet.src msg.match.dl_dst = packet.dst msg.actions.append(of.ofp_action_output(port=dst_port)) event.connection.send(msg) log.debug("Installing %s <-> %s" % (packet.src, packet.dst))
def reply (self, event, msg): orig = event.parsed.find('dhcp') broadcast = (orig.flags & orig.BROADCAST_FLAG) != 0 msg.op = msg.BOOTREPLY msg.chaddr = event.parsed.src msg.htype = 1 msg.hlen = 6 msg.xid = orig.xid msg.add_option(pkt.DHCP.DHCPServerIdentifierOption(self.ip_addr)) ethp = pkt.ethernet(src=ip_for_event(event),dst=event.parsed.src) ethp.type = pkt.ethernet.IP_TYPE ipp = pkt.ipv4(srcip = self.ip_addr) ipp.dstip = event.parsed.find('ipv4').srcip if broadcast: ipp.dstip = IP_BROADCAST ethp.dst = pkt.ETHERNET.ETHER_BROADCAST ipp.protocol = ipp.UDP_PROTOCOL udpp = pkt.udp() udpp.srcport = pkt.dhcp.SERVER_PORT udpp.dstport = pkt.dhcp.CLIENT_PORT udpp.payload = msg ipp.payload = udpp ethp.payload = ipp po = of.ofp_packet_out(data=ethp.pack()) po.actions.append(of.ofp_action_output(port=event.port)) event.connection.send(po)
def _do_probe(self): """ Send an ARP to a server to see if it's still up """ self._do_expire() server = self.servers.pop(0) self.servers.append(server) r = arp() r.hwtype = r.HW_TYPE_ETHERNET r.prototype = r.PROTO_TYPE_IP r.opcode = r.REQUEST r.hwdst = ETHER_BROADCAST r.protodst = server r.hwsrc = self.mac r.protosrc = self.service_ip e = ethernet(type=ethernet.ARP_TYPE, src=self.mac, dst=ETHER_BROADCAST) e.set_payload(r) #self.log.debug("ARPing for %s", server) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) msg.in_port = of.OFPP_NONE self.con.send(msg) self.outstanding_probes[server] = time.time() + self.arp_timeout core.callDelayed(self._probe_wait_time, self._do_probe)
def send_arp_reply(reply_to, mac, src_mac=None, src_ip=None): """ Send an ARP reply. src_mac can be None to use the "DPID MAC" or True to use the port Mac. (or it can be an EthAddr) """ # reply_to should be a PacketIn event arpp = reply_to.parsed.find('arp') mac = EthAddr(mac) if src_mac is None: #src_mac = mac # Used to be this ??? src_mac = reply_to.connection.eth_addr elif src_mac is True: src_mac = reply_to.connection.ports[reply_to.port].hw_addr else: src_mac = EthAddr(src_mac) r = arp() r.opcode = r.REPLY r.hwdst = arpp.hwsrc r.protodst = arpp.protosrc r.hwsrc = EthAddr(src_mac) r.protosrc = IPAddr("0.0.0.0") if src_ip is None else IPAddr(src_ip) e = ethernet(type=ethernet.ARP_TYPE, src=src_mac, dst=r.hwdst) e.payload = r msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port=reply_to.port)) msg.in_port = of.OFPP_NONE reply_to.connection.send(msg)
def sendPing(self, macEntry, ipAddr): """ Builds an ETH/IP any-to-any ARP packet (an "ARP ping") """ r = arp() r.opcode = arp.REQUEST r.hwdst = macEntry.macaddr r.hwsrc = self.ping_src_mac r.protodst = ipAddr # src is IP_ANY e = ethernet(type=ethernet.ARP_TYPE, src=r.hwsrc, dst=r.hwdst) e.payload = r log.debug("%i %i sending ARP REQ to %s %s", macEntry.dpid, macEntry.port, str(r.hwdst), str(r.protodst)) msg = of.ofp_packet_out( data=e.pack(), action=of.ofp_action_output(port=macEntry.port)) if core.openflow.sendToDPID(macEntry.dpid, msg.pack()): ipEntry = macEntry.ipAddrs[ipAddr] ipEntry.pings.sent() else: # macEntry is stale, remove it. log.debug("%i %i ERROR sending ARP REQ to %s %s", macEntry.dpid, macEntry.port, str(r.hwdst), str(r.protodst)) del macEntry.ipAddrs[ipAddr] return
def create_discovery_packet (self, dpid, port_num, port_addr): """ Build discovery packet """ chassis_id = pkt.chassis_id(subtype=pkt.chassis_id.SUB_LOCAL) chassis_id.id = bytes('dpid:' + hex(long(dpid))[2:-1]) # Maybe this should be a MAC. But a MAC of what? Local port, maybe? port_id = pkt.port_id(subtype=pkt.port_id.SUB_PORT, id=str(port_num)) ttl = pkt.ttl(ttl = self._ttl) sysdesc = pkt.system_description() sysdesc.payload = bytes('dpid:' + hex(long(dpid))[2:-1]) discovery_packet = pkt.lldp() discovery_packet.tlvs.append(chassis_id) discovery_packet.tlvs.append(port_id) discovery_packet.tlvs.append(ttl) discovery_packet.tlvs.append(sysdesc) discovery_packet.tlvs.append(pkt.end_tlv()) eth = pkt.ethernet(type=pkt.ethernet.LLDP_TYPE) eth.src = port_addr eth.dst = pkt.ETHERNET.NDP_MULTICAST eth.payload = discovery_packet po = of.ofp_packet_out(action = of.ofp_action_output(port=port_num)) po.data = eth.pack() return po.pack()
def drop(): # Kill the buffer if event.ofp.buffer_id is not None: msg = of.ofp_packet_out() msg.buffer_id = event.ofp.buffer_id event.ofp.buffer_id = None # Mark is dead msg.in_port = event.port self.connection.send(msg)
def _handle_PacketIn(self, event): dpid = event.connection.dpid inport = event.port packet = event.parsed a = packet.find('arp') if not a: return if a.prototype != arp.PROTO_TYPE_IP: return if a.hwtype != arp.HW_TYPE_ETHERNET: return if a.opcode == arp.REQUEST: log.debug("%s ARP request %s => %s", dpid_to_str(dpid), a.protosrc, a.protodst) if self.use_port_mac: src_mac = event.connection.ports[inport].hw_addr else: src_mac = event.connection.eth_addr ev = ARPRequest(event.connection, a, src_mac, self.eat_packets, inport) self.raiseEvent(ev) if ev.reply is not None: r = arp() r.hwtype = a.hwtype r.prototype = a.prototype r.hwlen = a.hwlen r.protolen = a.protolen r.opcode = arp.REPLY r.hwdst = a.hwsrc r.protodst = a.protosrc r.protosrc = a.protodst r.hwsrc = EthAddr(ev.reply) e = ethernet(type=packet.type, src=ev.reply_from, dst=a.hwsrc) e.payload = r log.debug("%s answering ARP for %s" % (dpid_to_str(dpid), str(r.protosrc))) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port=of.OFPP_IN_PORT)) msg.in_port = inport event.connection.send(msg) return EventHalt if ev.eat_packet else None elif a.opcode == arp.REPLY: log.debug("%s ARP reply %s => %s", dpid_to_str(dpid), a.protosrc, a.hwsrc) ev = ARPReply(event.connection, a, self.eat_packets, inport) self.raiseEvent(ev) return EventHalt if ev.eat_packet else None return EventHalt if self.eat_packets else None
def flood(): """ Floods the packet """ if self.is_holding_down: log.warning("Not flooding -- holddown active") msg = of.ofp_packet_out() # OFPP_FLOOD is optional; some switches may need OFPP_ALL msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) msg.buffer_id = event.ofp.buffer_id msg.in_port = event.port self.connection.send(msg)
def install_path(self, dst_sw, last_port, match, event): """ Attempts to install a path between this switch and some destination """ p = _get_path(self, dst_sw, event.port, last_port) if p is None: log.warning("Can't get from %s to %s", match.dl_src, match.dl_dst) import pox.lib.packet as pkt if (match.dl_type == pkt.ethernet.IP_TYPE and event.parsed.find('ipv4')): # It's IP -- let's send a destination unreachable log.debug("Dest unreachable (%s -> %s)", match.dl_src, match.dl_dst) from pox.lib.addresses import EthAddr e = pkt.ethernet() e.src = EthAddr(dpid_to_str(self.dpid)) #FIXME: Hmm... e.dst = match.dl_src e.type = e.IP_TYPE ipp = pkt.ipv4() ipp.protocol = ipp.ICMP_PROTOCOL ipp.srcip = match.nw_dst #FIXME: Ridiculous ipp.dstip = match.nw_src icmp = pkt.icmp() icmp.type = pkt.ICMP.TYPE_DEST_UNREACH icmp.code = pkt.ICMP.CODE_UNREACH_HOST orig_ip = event.parsed.find('ipv4') d = orig_ip.pack() d = d[:orig_ip.hl * 4 + 8] import struct d = struct.pack("!HH", 0, 0) + d #FIXME: MTU icmp.payload = d ipp.payload = icmp e.payload = ipp msg = of.ofp_packet_out() msg.actions.append(of.ofp_action_output(port=event.port)) msg.data = e.pack() self.connection.send(msg) return log.debug("Installing path for %s -> %s %04x (%i hops)", match.dl_src, match.dl_dst, match.dl_type, len(p)) # We have a path -- install it self._install_path(p, match, event.ofp) # Now reverse it and install it backwards # (we'll just assume that will work) p = [(sw, out_port, in_port) for sw, in_port, out_port in p] self._install_path(p, match.flip())
def notify(self, event): """ Called when a barrier has been received """ self.xids.discard((event.dpid, event.xid)) if len(self.xids) == 0: # Done! if self.packet: log.debug("Sending delayed packet out %s" % (dpid_to_str(self.first_switch), )) msg = of.ofp_packet_out( data=self.packet, action=of.ofp_action_output(port=of.OFPP_TABLE)) core.openflow.sendToDPID(self.first_switch, msg) core.l2_multi.raiseEvent(PathInstalled(self.path))
def _send_lost_buffers (self, dpid, ipaddr, macaddr, port): """ We may have "lost" buffers -- packets we got but didn't know where to send at the time. We may know now. Try and see. """ if (dpid,ipaddr) in self.lost_buffers: # Yup! bucket = self.lost_buffers[(dpid,ipaddr)] del self.lost_buffers[(dpid,ipaddr)] log.debug("Sending %i buffered packets to %s from %s" % (len(bucket),ipaddr,dpid_to_str(dpid))) for _,buffer_id,in_port in bucket: po = of.ofp_packet_out(buffer_id=buffer_id,in_port=in_port) po.actions.append(of.ofp_action_dl_addr.set_dst(macaddr)) po.actions.append(of.ofp_action_output(port = port)) core.openflow.sendToDPID(dpid, po)
def _send_dhcp(self, msg): ethp = pkt.ethernet(src=self.port_eth, dst=pkt.ETHER_BROADCAST) ethp.type = pkt.ethernet.IP_TYPE ipp = pkt.ipv4() ipp.srcip = pkt.IP_ANY #NOTE: If rebinding, use existing local IP? ipp.dstip = pkt.IP_BROADCAST ipp.protocol = ipp.UDP_PROTOCOL udpp = pkt.udp() udpp.srcport = pkt.dhcp.CLIENT_PORT udpp.dstport = pkt.dhcp.SERVER_PORT udpp.payload = msg ipp.payload = udpp ethp.payload = ipp po = of.ofp_packet_out(data=ethp.pack()) po.actions.append(of.ofp_action_output(port=self.portno)) self._con.send(po)
def _handle_expiration (self): # Called by a timer so that we can remove old items. empty = [] for k,v in self.lost_buffers.iteritems(): dpid,ip = k for item in list(v): expires_at,buffer_id,in_port = item if expires_at < time.time(): # This packet is old. Tell this switch to drop it. v.remove(item) po = of.ofp_packet_out(buffer_id = buffer_id, in_port = in_port) core.openflow.sendToDPID(dpid, po) if len(v) == 0: empty.append(k) # Remove empty buffer bins for k in empty: del self.lost_buffers[k]
def send_arp_request(connection, ip, port=of.OFPP_FLOOD, src_mac=None, src_ip=None): """ Send an ARP request src_mac can be None to use the "DPID MAC" or True to use the port Mac. (or it can be an EthAddr) """ if src_mac is None: src_mac = connection.eth_addr elif src_mac is True: if port in (of.OFPP_FLOOD, of.OFPP_ALL): for p in connection.ports.values(): if p.config & OFPPC_NO_FLOOD: if port == of.ofPP_FLOOD: continue if p.port_no < 0: continue if p.port_no > of.OFPP_MAX: continue # Off by one? send_arp_request(connection, ip, p.port_no, src_mac=p.hw_addr, src_ip=src_ip) return src_mac = connection.ports[port].hw_addr else: src_mac = EthAddr(src_mac) r = arp() r.opcode = r.REQUEST r.hwdst = ETHER_BROADCAST r.protodst = IPAddr(ip) r.hwsrc = src_mac r.protosrc = IPAddr("0.0.0.0") if src_ip is None else IPAddr(src_ip) e = ethernet(type=ethernet.ARP_TYPE, src=src_mac, dst=r.hwdst) e.payload = r msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port=port)) msg.in_port = of.OFPP_NONE connection.send(msg)
def resend_packet(self, packet_in, out_port): """ Instructs the switch to resend a packet that it had sent to us. "packet_in" is the ofp_packet_in object the switch had sent to the controller due to a table-miss. """ msg = of.ofp_packet_out() msg.data = packet_in # Add an action to send to the specified port action = of.ofp_action_output(port=out_port) msg.actions.append(action) log.debug(" OF_TUTORIAL : Message being sent on the connection : %s " % msg) # Send message to switch self.connection.send(msg) log.debug(" OF_TUTORIAL : Message sent to the switch") log.debug(" OF_TUTORIAL : --------------------------")
def drop(duration=None): """ Drops this packet and optionally installs a flow to continue dropping similar ones for a while """ if duration is not None: if not isinstance(duration, tuple): duration = (duration, duration) msg = of.ofp_flow_mod() msg.match = of.ofp_match.from_packet(packet) msg.idle_timeout = duration[0] msg.hard_timeout = duration[1] msg.buffer_id = event.ofp.buffer_id self.connection.send(msg) elif event.ofp.buffer_id is not None: msg = of.ofp_packet_out() msg.buffer_id = event.ofp.buffer_id msg.in_port = event.port self.connection.send(msg)
def resend_packet_face(self, packet_in, face): """ Instructs the switch to resend a packet that it had sent to us. "packet_in" is the ofp_packet_in object the switch had sent to the controller due to a table-miss. """ print( "\n Sending PACKET_OUT message to switch to send the packet in a face\n" ) msg = of.ofp_packet_out() msg.data = packet_in # Add an action to send to the specified port action = of.ofp_action_outputface(face=face) msg.actions.append(action) log.debug(" OF_TUTORIAL : Message being sent on the connection : %s " % msg) # Send message to switch self.connection.send(msg) log.debug(" OF_TUTORIAL : Message sent to the switch") log.debug(" OF_TUTORIAL : --------------------------")
def dict_to_packet_out(d): """ Converts dict to packet_out Also, special key "output" is an output port. """ po = of.ofp_packet_out() po.buffer_id = d.get('buffer_id', -1) po.in_port = _fix_of_int(d.get('in_port', of.OFPP_NONE)) actions = d.get('actions', []) actions = [dict_to_action(a) for a in actions] po.actions = actions if 'output' in d: a = of.ofp_action_output(port=_fix_of_int(d['output'])) po.actions.append(a) if 'data' in d: data = dict_to_packet(d['data']) if hasattr(data, 'pack'): data = data.pack() po.data = data return po
def flood(message=None): """ Floods the packet """ msg = of.ofp_packet_out() if time.time() - self.connection.connect_time >= _flood_delay: # Only flood if we've been connected for a little while... if self.hold_down_expired is False: # Oh yes it is! self.hold_down_expired = True log.info("%s: Flood hold-down expired -- flooding", dpid_to_str(event.dpid)) if message is not None: log.debug(message) #log.debug("%i: flood %s -> %s", event.dpid,packet.src,packet.dst) # OFPP_FLOOD is optional; on some switches you may need to change # this to OFPP_ALL. msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) else: pass #log.info("Holding down flood for %s", dpid_to_str(event.dpid)) msg.data = event.ofp msg.in_port = event.port self.connection.send(msg)
def _handle_PacketIn(event): """ Handle messages the switch has sent us because it has no matching rule. """ def drop(): # Kill buffer on switch if event.ofp.buffer_id is not None: msg = of.ofp_packet_out() msg.buffer_id = event.ofp.buffer_id msg.in_port = event.port event.connection.send(msg) packet = event.parsed if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered(): return drop() # Learn the source table[(event.connection, packet.src)] = event.port if not packet.dst.is_multicast: dst_port = table.get((event.connection, packet.dst)) else: # Ideally, we'd install a flow entries that output multicasts # to all ports on the spanning tree. dst_port = None if dst_port is None: # We don't know where the destination is yet. So, we'll just # send the packet out all ports in the spanning tree # and hope the destination is out there somewhere. :) msg = of.ofp_packet_out(data=event.ofp) tree_ports = [p[1] for p in tree.get(event.dpid, [])] for p in event.connection.ports: if p >= of.OFPP_MAX: # Not a normal port continue if not core.openflow_discovery.is_edge_port(event.dpid, p): # If the port isn't a switch-to-switch port, it's fine to flood # through it. But if it IS a switch-to-switch port, we only # want to use it if it's on the spanning tree. if p not in tree_ports: continue msg.actions.append(of.ofp_action_output(port=p)) event.connection.send(msg) else: # Since we know the switch ports for both the source and dest # MACs, we can install rules for both directions. msg = of.ofp_flow_mod() msg.match.dl_dst = packet.src msg.match.dl_src = packet.dst msg.actions.append(of.ofp_action_output(port=event.port)) event.connection.send(msg) # This is the packet that just came in -- we want to # install the rule and also resend the packet. msg = of.ofp_flow_mod() msg.data = event.ofp # Forward the incoming packet msg.match.dl_src = packet.src msg.match.dl_dst = packet.dst msg.actions.append(of.ofp_action_output(port=dst_port)) event.connection.send(msg) log.debug("Installing %s <-> %s" % (packet.src, packet.dst))
def drop(): if event.ofp.buffer_id is not None: # Kill the buffer msg = of.ofp_packet_out(data=event.ofp) self.con.send(msg) return None
def _handle_openflow_PacketIn (self, event): """ Receive and process LLDP packets """ packet = event.parsed if (packet.effective_ethertype != pkt.ethernet.LLDP_TYPE or packet.dst != pkt.ETHERNET.NDP_MULTICAST): if not self._eat_early_packets: return if not event.connection.connect_time: return enable_time = time.time() - self.send_cycle_time - 1 if event.connection.connect_time > enable_time: return EventHalt return if self._explicit_drop: if event.ofp.buffer_id is not None: log.debug("Dropping LLDP packet %i", event.ofp.buffer_id) msg = of.ofp_packet_out() msg.buffer_id = event.ofp.buffer_id msg.in_port = event.port event.connection.send(msg) lldph = packet.find(pkt.lldp) if lldph is None or not lldph.parsed: log.error("LLDP packet could not be parsed") return EventHalt if len(lldph.tlvs) < 3: log.error("LLDP packet without required three TLVs") return EventHalt if lldph.tlvs[0].tlv_type != pkt.lldp.CHASSIS_ID_TLV: log.error("LLDP packet TLV 1 not CHASSIS_ID") return EventHalt if lldph.tlvs[1].tlv_type != pkt.lldp.PORT_ID_TLV: log.error("LLDP packet TLV 2 not PORT_ID") return EventHalt if lldph.tlvs[2].tlv_type != pkt.lldp.TTL_TLV: log.error("LLDP packet TLV 3 not TTL") return EventHalt def lookInSysDesc (): r = None for t in lldph.tlvs[3:]: if t.tlv_type == pkt.lldp.SYSTEM_DESC_TLV: # This is our favored way... for line in t.payload.split('\n'): if line.startswith('dpid:'): try: return int(line[5:], 16) except: pass if len(t.payload) == 8: # Maybe it's a FlowVisor LLDP... # Do these still exist? try: return struct.unpack("!Q", t.payload)[0] except: pass return None originatorDPID = lookInSysDesc() if originatorDPID == None: # We'll look in the CHASSIS ID if lldph.tlvs[0].subtype == pkt.chassis_id.SUB_LOCAL: if lldph.tlvs[0].id.startswith('dpid:'): # This is how NOX does it at the time of writing try: originatorDPID = int(lldph.tlvs[0].id[5:], 16) except: pass if originatorDPID == None: if lldph.tlvs[0].subtype == pkt.chassis_id.SUB_MAC: # Last ditch effort -- we'll hope the DPID was small enough # to fit into an ethernet address if len(lldph.tlvs[0].id) == 6: try: s = lldph.tlvs[0].id originatorDPID = struct.unpack("!Q",'\x00\x00' + s)[0] except: pass if originatorDPID == None: log.warning("Couldn't find a DPID in the LLDP packet") return EventHalt if originatorDPID not in core.openflow.connections: log.info('Received LLDP packet from unknown switch') return EventHalt # Get port number from port TLV if lldph.tlvs[1].subtype != pkt.port_id.SUB_PORT: log.warning("Thought we found a DPID, but packet didn't have a port") return EventHalt originatorPort = None if lldph.tlvs[1].id.isdigit(): # We expect it to be a decimal value originatorPort = int(lldph.tlvs[1].id) elif len(lldph.tlvs[1].id) == 2: # Maybe it's a 16 bit port number... try: originatorPort = struct.unpack("!H", lldph.tlvs[1].id)[0] except: pass if originatorPort is None: log.warning("Thought we found a DPID, but port number didn't " + "make sense") return EventHalt if (event.dpid, event.port) == (originatorDPID, originatorPort): log.warning("Port received its own LLDP packet; ignoring") return EventHalt link = Discovery.Link(originatorDPID, originatorPort, event.dpid, event.port) if link not in self.adjacency: self.adjacency[link] = time.time() log.info('link detected: %s', link) self.raiseEventNoErrors(LinkEvent, True, link) else: # Just update timestamp self.adjacency[link] = time.time() return EventHalt # Probably nobody else needs this event
def _handle_PacketIn (self, event): # Note: arp.hwsrc is not necessarily equal to ethernet.src # (one such example are arp replies generated by this module itself # as ethernet mac is set to switch dpid) so we should be careful # to use only arp addresses in the learning code! squelch = False dpid = event.connection.dpid inport = event.port packet = event.parsed if not packet.parsed: log.warning("%s: ignoring unparsed packet", dpid_to_str(dpid)) return a = packet.find('arp') if not a: return log.debug("%s ARP %s %s => %s", dpid_to_str(dpid), {arp.REQUEST:"request",arp.REPLY:"reply"}.get(a.opcode, 'op:%i' % (a.opcode,)), str(a.protosrc), str(a.protodst)) if a.prototype == arp.PROTO_TYPE_IP: if a.hwtype == arp.HW_TYPE_ETHERNET: if a.protosrc != 0: if _learn: # Learn or update port/MAC info if a.protosrc in _arp_table: if _arp_table[a.protosrc] != a.hwsrc: log.warn("%s RE-learned %s: %s->%s", dpid_to_str(dpid), a.protosrc, _arp_table[a.protosrc].mac, a.hwsrc) else: log.info("%s learned %s", dpid_to_str(dpid), a.protosrc) _arp_table[a.protosrc] = Entry(a.hwsrc) if a.opcode == arp.REQUEST: # Maybe we can answer if a.protodst in _arp_table: # We have an answer... r = arp() r.hwtype = a.hwtype r.prototype = a.prototype r.hwlen = a.hwlen r.protolen = a.protolen r.opcode = arp.REPLY r.hwdst = a.hwsrc r.protodst = a.protosrc r.protosrc = a.protodst mac = _arp_table[a.protodst].mac if mac is True: # Special case -- use ourself mac = _dpid_to_mac(dpid) r.hwsrc = mac e = ethernet(type=packet.type, src=_dpid_to_mac(dpid), dst=a.hwsrc) e.payload = r if packet.type == ethernet.VLAN_TYPE: v_rcv = packet.find('vlan') e.payload = vlan(eth_type = e.type, payload = e.payload, id = v_rcv.id, pcp = v_rcv.pcp) e.type = ethernet.VLAN_TYPE log.info("%s answering ARP for %s" % (dpid_to_str(dpid), str(r.protosrc))) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port = of.OFPP_IN_PORT)) msg.in_port = inport event.connection.send(msg) return EventHalt if _eat_packets else None else: # Keep track of failed queries squelch = a.protodst in _failed_queries _failed_queries[a.protodst] = time.time() if self._check_for_flood(dpid, a): # Didn't know how to handle this ARP, so just flood it msg = "%s flooding ARP %s %s => %s" % (dpid_to_str(dpid), {arp.REQUEST:"request",arp.REPLY:"reply"}.get(a.opcode, 'op:%i' % (a.opcode,)), a.protosrc, a.protodst) if squelch: log.debug(msg) else: log.info(msg) msg = of.ofp_packet_out() msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) msg.data = event.ofp event.connection.send(msg.pack()) return EventHalt if _eat_packets else None
def _handle_PacketIn (self, event): dpid = event.connection.dpid inport = event.port packet = event.parsed if not packet.parsed: log.warning("%i %i ignoring unparsed packet", dpid, inport) return if dpid not in self.arpTable: # New switch -- create an empty table self.arpTable[dpid] = {} for fake in self.fakeways: self.arpTable[dpid][IPAddr(fake)] = Entry(of.OFPP_NONE, dpid_to_mac(dpid)) if packet.type == ethernet.LLDP_TYPE: # Ignore LLDP packets return if isinstance(packet.next, ipv4): log.debug("%i %i IP %s => %s", dpid,inport, packet.next.srcip,packet.next.dstip) # Send any waiting packets... self._send_lost_buffers(dpid, packet.next.srcip, packet.src, inport) # Learn or update port/MAC info if packet.next.srcip in self.arpTable[dpid]: if self.arpTable[dpid][packet.next.srcip] != (inport, packet.src): log.info("%i %i RE-learned %s", dpid,inport,packet.next.srcip) else: log.debug("%i %i learned %s", dpid,inport,str(packet.next.srcip)) self.arpTable[dpid][packet.next.srcip] = Entry(inport, packet.src) # Try to forward dstaddr = packet.next.dstip if dstaddr in self.arpTable[dpid]: # We have info about what port to send it out on... prt = self.arpTable[dpid][dstaddr].port mac = self.arpTable[dpid][dstaddr].mac if prt == inport: log.warning("%i %i not sending packet for %s back out of the " + "input port" % (dpid, inport, str(dstaddr))) else: log.debug("%i %i installing flow for %s => %s out port %i" % (dpid, inport, packet.next.srcip, dstaddr, prt)) actions = [] actions.append(of.ofp_action_dl_addr.set_dst(mac)) actions.append(of.ofp_action_output(port = prt)) match = of.ofp_match.from_packet(packet, inport) match.dl_src = None # Wildcard source MAC msg = of.ofp_flow_mod(command=of.OFPFC_ADD, idle_timeout=FLOW_IDLE_TIMEOUT, hard_timeout=of.OFP_FLOW_PERMANENT, buffer_id=event.ofp.buffer_id, actions=actions, match=of.ofp_match.from_packet(packet, inport)) event.connection.send(msg.pack()) elif self.arp_for_unknowns: # We don't know this destination. # First, we track this buffer so that we can try to resend it later # if we learn the destination, second we ARP for the destination, # which should ultimately result in it responding and us learning # where it is # Add to tracked buffers if (dpid,dstaddr) not in self.lost_buffers: self.lost_buffers[(dpid,dstaddr)] = [] bucket = self.lost_buffers[(dpid,dstaddr)] entry = (time.time() + MAX_BUFFER_TIME,event.ofp.buffer_id,inport) bucket.append(entry) while len(bucket) > MAX_BUFFERED_PER_IP: del bucket[0] # Expire things from our outstanding ARP list... self.outstanding_arps = {k:v for k,v in self.outstanding_arps.iteritems() if v > time.time()} # Check if we've already ARPed recently if (dpid,dstaddr) in self.outstanding_arps: # Oop, we've already done this one recently. return # And ARP... self.outstanding_arps[(dpid,dstaddr)] = time.time() + 4 r = arp() r.hwtype = r.HW_TYPE_ETHERNET r.prototype = r.PROTO_TYPE_IP r.hwlen = 6 r.protolen = r.protolen r.opcode = r.REQUEST r.hwdst = ETHER_BROADCAST r.protodst = dstaddr r.hwsrc = packet.src r.protosrc = packet.next.srcip e = ethernet(type=ethernet.ARP_TYPE, src=packet.src, dst=ETHER_BROADCAST) e.set_payload(r) log.debug("%i %i ARPing for %s on behalf of %s" % (dpid, inport, str(r.protodst), str(r.protosrc))) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) msg.in_port = inport event.connection.send(msg) elif isinstance(packet.next, arp): a = packet.next log.debug("%i %i ARP %s %s => %s", dpid, inport, {arp.REQUEST:"request",arp.REPLY:"reply"}.get(a.opcode, 'op:%i' % (a.opcode,)), str(a.protosrc), str(a.protodst)) if a.prototype == arp.PROTO_TYPE_IP: if a.hwtype == arp.HW_TYPE_ETHERNET: if a.protosrc != 0: # Learn or update port/MAC info if a.protosrc in self.arpTable[dpid]: if self.arpTable[dpid][a.protosrc] != (inport, packet.src): log.info("%i %i RE-learned %s", dpid,inport,str(a.protosrc)) else: log.debug("%i %i learned %s", dpid,inport,str(a.protosrc)) self.arpTable[dpid][a.protosrc] = Entry(inport, packet.src) # Send any waiting packets... self._send_lost_buffers(dpid, a.protosrc, packet.src, inport) if a.opcode == arp.REQUEST: # Maybe we can answer if a.protodst in self.arpTable[dpid]: # We have an answer... if not self.arpTable[dpid][a.protodst].isExpired(): # .. and it's relatively current, so we'll reply ourselves r = arp() r.hwtype = a.hwtype r.prototype = a.prototype r.hwlen = a.hwlen r.protolen = a.protolen r.opcode = arp.REPLY r.hwdst = a.hwsrc r.protodst = a.protosrc r.protosrc = a.protodst r.hwsrc = self.arpTable[dpid][a.protodst].mac e = ethernet(type=packet.type, src=dpid_to_mac(dpid), dst=a.hwsrc) e.set_payload(r) log.debug("%i %i answering ARP for %s" % (dpid, inport, str(r.protosrc))) msg = of.ofp_packet_out() msg.data = e.pack() msg.actions.append(of.ofp_action_output(port = of.OFPP_IN_PORT)) msg.in_port = inport event.connection.send(msg) return # Didn't know how to answer or otherwise handle this ARP, so just flood it log.debug("%i %i flooding ARP %s %s => %s" % (dpid, inport, {arp.REQUEST:"request",arp.REPLY:"reply"}.get(a.opcode, 'op:%i' % (a.opcode,)), str(a.protosrc), str(a.protodst))) msg = of.ofp_packet_out(in_port = inport, data = event.ofp, action = of.ofp_action_output(port = of.OFPP_FLOOD)) event.connection.send(msg)