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 _handle_ConnectionUp (self, event): if _install_flow: fm = of.ofp_flow_mod() fm.priority = 0x7000 # Pretty high fm.match.dl_type = ethernet.ARP_TYPE fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) event.connection.send(fm)
def clear_table(self, xid=None): fm = of.ofp_flow_mod() fm.xid = xid fm.command = of.OFPFC_DELETE self._con.send(fm) bar = of.ofp_barrier_request() bar.xid = xid self._con.send(bar)
def _install(self, switch, in_port, out_port, match, buf=None): msg = of.ofp_flow_mod() msg.match = match msg.match.in_port = in_port msg.idle_timeout = FLOW_IDLE_TIMEOUT msg.hard_timeout = FLOW_HARD_TIMEOUT msg.actions.append(of.ofp_action_output(port=out_port)) msg.buffer_id = buf switch.connection.send(msg)
def _handle_ConnectionUp (self, event): if self._install_flow: msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_proto = pkt.ipv4.UDP_PROTOCOL msg.match.tp_src = 53 msg.actions.append(of.ofp_action_output(port = of.OFPP_CONTROLLER)) event.connection.send(msg)
def _handle_LinkEvent(self, event): def flip(link): return Discovery.Link(link[2], link[3], link[0], link[1]) l = event.link sw1 = switches[l.dpid1] sw2 = switches[l.dpid2] # Invalidate all flows and path info. # For link adds, this makes sure that if a new link leads to an # improved path, we use it. # For link removals, this makes sure that we don't use a # path that may have been broken. #NOTE: This could be radically improved! (e.g., not *ALL* paths break) clear = of.ofp_flow_mod(command=of.OFPFC_DELETE) for sw in switches.itervalues(): if sw.connection is None: continue sw.connection.send(clear) path_map.clear() if event.removed: # This link no longer okay if sw2 in adjacency[sw1]: del adjacency[sw1][sw2] if sw1 in adjacency[sw2]: del adjacency[sw2][sw1] # But maybe there's another way to connect these... for ll in core.openflow_discovery.adjacency: if ll.dpid1 == l.dpid1 and ll.dpid2 == l.dpid2: if flip(ll) in core.openflow_discovery.adjacency: # Yup, link goes both ways adjacency[sw1][sw2] = ll.port1 adjacency[sw2][sw1] = ll.port2 # Fixed -- new link chosen to connect these break else: # If we already consider these nodes connected, we can # ignore this link up. # Otherwise, we might be interested... if adjacency[sw1][sw2] is None: # These previously weren't connected. If the link # exists in both directions, we consider them connected now. if flip(l) in core.openflow_discovery.adjacency: # Yup, link goes both ways -- connected! adjacency[sw1][sw2] = l.port1 adjacency[sw2][sw1] = l.port2 # If we have learned a MAC on this port which we now know to # be connected to a switch, unlearn it. bad_macs = set() for mac, (sw, port) in mac_map.iteritems(): if sw is sw1 and port == l.port1: bad_macs.add(mac) if sw is sw2 and port == l.port2: bad_macs.add(mac) for mac in bad_macs: log.debug("Unlearned %s", mac) del mac_map[mac]
def _send_rewrite_rule(self, ip, mac): p = ipinfo(ip)[1] msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_dst = ip msg.actions.append(of.ofp_action_dl_addr.set_src(self.mac)) msg.actions.append(of.ofp_action_dl_addr.set_dst(mac)) msg.actions.append(of.ofp_action_output(port=p)) self.connection.send(msg)
def _handle_ConnectionUp (self, event): if self._install_flow: msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_proto = pkt.ipv4.UDP_PROTOCOL #msg.match.nw_dst = IP_BROADCAST msg.match.tp_src = pkt.dhcp.CLIENT_PORT msg.match.tp_dst = pkt.dhcp.SERVER_PORT msg.actions.append(of.ofp_action_output(port = of.OFPP_CONTROLLER)) #msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) event.connection.send(msg)
def _handle_openflow_ConnectionUp(self, event): if not self.install_flow: return log.debug("Installing flow for ARP ping responses") m = of.ofp_flow_mod() m.priority += 1 # Higher than normal m.match.dl_type = ethernet.ARP_TYPE m.match.dl_dst = self.ping_src_mac m.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) event.connection.send(m)
def _start(self, connection): self._connection = connection self._outside_portno = connection.ports[self.outside_port].port_no fm = of.ofp_flow_mod() fm.match.in_port = self._outside_portno fm.priority = 1 connection.send(fm) fm = of.ofp_flow_mod() fm.match.in_port = self._outside_portno fm.match.dl_type = 0x800 # IP fm.match.nw_dst = self.outside_ip fm.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) fm.priority = 2 connection.send(fm) connection.addListeners(self) # Need to find gateway MAC -- send an ARP self._arp_for_gateway()
def get_flow(broadcast=False): fm = of.ofp_flow_mod() if broadcast: fm.match.dl_dst = pkt.ETHER_BROADCAST else: fm.match.dl_dst = self.port_eth fm.match.in_port = self.portno fm.match.dl_type = pkt.ethernet.IP_TYPE fm.match.nw_proto = pkt.ipv4.UDP_PROTOCOL fm.match.tp_src = pkt.dhcp.SERVER_PORT fm.match.tp_dst = pkt.dhcp.CLIENT_PORT fm.priority += 1 return fm
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 install_flow (self, con_or_dpid, priority = None): if priority is None: priority = self._flow_priority if isinstance(con_or_dpid, (int,long)): con = core.openflow.connections.get(con_or_dpid) if con is None: log.warn("Can't install flow for %s", dpid_to_str(con_or_dpid)) return False else: con = con_or_dpid match = of.ofp_match(dl_type = pkt.ethernet.LLDP_TYPE, dl_dst = pkt.ETHERNET.NDP_MULTICAST) msg = of.ofp_flow_mod() msg.priority = priority msg.match = match msg.actions.append(of.ofp_action_output(port = of.OFPP_CONTROLLER)) con.send(msg) return True
def dict_to_flow_mod(flow): match = flow.get('match') if match is None: match = of.ofp_match() else: match = dict_to_match(match) actions = flow.get('actions', []) if not isinstance(actions, list): actions = [actions] actions = [dict_to_action(a) for a in actions] if 'output' in flow: a = of.ofp_action_output(port=_fix_of_int(flow['output'])) po.actions.append(a) fm = of.ofp_flow_mod(match=match) fm.actions = actions for k in ['cookie', 'idle_timeout', 'hard_timeout', 'priority']: if k in flow: setattr(fm, k, flow[k]) return fm
def _exec_cmd_set_table(self, event): try: msg = event.msg dpid = strToDPID(msg['dpid']) con = core.openflow.getConnection(dpid) if con is None: raise RuntimeError("No such switch") xid = of.generate_xid() fm = of.ofp_flow_mod() fm.xid = xid fm.command = of.OFPFC_DELETE con.send(fm) bar = of.ofp_barrier_request() bar.xid = xid con.send(bar) for flow in msg.get('flows', []): fm = dict_to_flow_mod(flow) fm.xid = xid con.send(fm) #con.send(of.ofp_barrier_request(xid=xid)) con.send(of.ofp_barrier_request(xid=xid)) self.reply(event, **{'type': 'set_table', 'xid': xid}) except: #log.exception("Exception in set_table") log.debug("Exception in set_table - %s:%s", sys.exc_info()[0].__name__, sys.exc_info()[1]) self.reply(event, exception="%s: %s" % (sys.exc_info()[0], sys.exc_info()[1]), traceback=traceback.format_exc())
def _handle_ConnectionUp(event): msg = of.ofp_flow_mod() msg.actions.append(of.ofp_action_output(port=of.OFPP_FLOOD)) event.connection.send(msg) log.info("Hubifying %s", dpidToStr(event.dpid))
def handle_FEATURES_REPLY(con, msg): connecting = con.connect_time == None con.features = msg con.original_ports._ports = set(msg.ports) con.ports._reset() con.dpid = msg.datapath_id if not connecting: con.ofnexus._connect(con) e = con.ofnexus.raiseEventNoErrors(FeaturesReceived, con, msg) if e is None or e.halt != True: con.raiseEventNoErrors(FeaturesReceived, con, msg) return nexus = core.OpenFlowConnectionArbiter.getNexus(con) if nexus is None: # Cancel connection con.info("No OpenFlow nexus for " + pox.lib.util.dpidToStr(msg.datapath_id)) con.disconnect() return con.ofnexus = nexus con.ofnexus._connect(con) #connections[con.dpid] = con barrier = of.ofp_barrier_request() listeners = [] def finish_connecting(event): if event.xid != barrier.xid: con.dpid = None con.err("failed connect") con.disconnect() else: con.info("connected : TEST TEST TEST") con.connect_time = time.time() e = con.ofnexus.raiseEventNoErrors(ConnectionUp, con, msg) if e is None or e.halt != True: con.raiseEventNoErrors(ConnectionUp, con, msg) e = con.ofnexus.raiseEventNoErrors(FeaturesReceived, con, msg) if e is None or e.halt != True: con.raiseEventNoErrors(FeaturesReceived, con, msg) con.removeListeners(listeners) listeners.append(con.addListener(BarrierIn, finish_connecting)) def also_finish_connecting(event): if event.xid != barrier.xid: return if event.ofp.type != of.OFPET_BAD_REQUEST: return if event.ofp.code != of.OFPBRC_BAD_TYPE: return # Okay, so this is probably an HP switch that doesn't support barriers # (ugh). We'll just assume that things are okay. finish_connecting(event) listeners.append(con.addListener(ErrorIn, also_finish_connecting)) #TODO: Add a timeout for finish_connecting if con.ofnexus.miss_send_len is not None: con.send(of.ofp_set_config(miss_send_len=con.ofnexus.miss_send_len)) if con.ofnexus.clear_flows_on_connect: con.send(of.ofp_flow_mod(match=of.ofp_match(), command=of.OFPFC_DELETE)) con.send(barrier) """
def _handle_PacketIn(self, event): msg = of.ofp_flow_mod() self.connection.send(msg)
def _handle_PacketIn(self, event): inport = event.port packet = event.parsed 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 tcpp = packet.find('tcp') if not tcpp: arpp = packet.find('arp') if arpp: # Handle replies to our server-liveness probes if arpp.opcode == arpp.REPLY: if arpp.protosrc in self.outstanding_probes: # A server is (still?) up; cool. del self.outstanding_probes[arpp.protosrc] if (self.live_servers.get(arpp.protosrc, (None, None)) == (arpp.hwsrc, inport)): # Ah, nothing new here. pass else: # Ooh, new server. self.live_servers[ arpp.protosrc] = arpp.hwsrc, inport self.log.info("Server %s up", arpp.protosrc) return # Not TCP and not ARP. Don't know what to do with this. Drop it. return drop() # It's TCP. ipp = packet.find('ipv4') if ipp.srcip in self.servers: # It's FROM one of our balanced servers. # Rewrite it BACK to the client key = ipp.srcip, ipp.dstip, tcpp.srcport, tcpp.dstport entry = self.memory.get(key) if entry is None: # We either didn't install it, or we forgot about it. self.log.debug("No client for %s", key) return drop() # Refresh time timeout and reinstall. entry.refresh() #self.log.debug("Install reverse flow for %s", key) # Install reverse table entry mac, port = self.live_servers[entry.server] actions = [] actions.append(of.ofp_action_dl_addr.set_src(self.mac)) actions.append(of.ofp_action_nw_addr.set_src(self.service_ip)) actions.append(of.ofp_action_output(port=entry.client_port)) match = of.ofp_match.from_packet(packet, inport) msg = of.ofp_flow_mod(command=of.OFPFC_ADD, idle_timeout=FLOW_IDLE_TIMEOUT, hard_timeout=of.OFP_FLOW_PERMANENT, data=event.ofp, actions=actions, match=match) self.con.send(msg) elif ipp.dstip == self.service_ip: # Ah, it's for our service IP and needs to be load balanced # Do we already know this flow? key = ipp.srcip, ipp.dstip, tcpp.srcport, tcpp.dstport entry = self.memory.get(key) if entry is None or entry.server not in self.live_servers: # Don't know it (hopefully it's new!) if len(self.live_servers) == 0: self.log.warn("No servers!") return drop() # Pick a server for this flow server = self._pick_server(key, inport) self.log.debug("Directing traffic to %s", server) entry = MemoryEntry(server, packet, inport) self.memory[entry.key1] = entry self.memory[entry.key2] = entry # Update timestamp entry.refresh() # Set up table entry towards selected server mac, port = self.live_servers[entry.server] actions = [] actions.append(of.ofp_action_dl_addr.set_dst(mac)) actions.append(of.ofp_action_nw_addr.set_dst(entry.server)) actions.append(of.ofp_action_output(port=port)) match = of.ofp_match.from_packet(packet, inport) msg = of.ofp_flow_mod(command=of.OFPFC_ADD, idle_timeout=FLOW_IDLE_TIMEOUT, hard_timeout=of.OFP_FLOW_PERMANENT, data=event.ofp, actions=actions, match=match) self.con.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 send_table(self): if self.connection is None: self.log.debug("Can't send table: disconnected") return clear = of.ofp_flow_mod(command=of.OFPFC_DELETE) self.connection.send(clear) self.connection.send(of.ofp_barrier_request()) # From DHCPD msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_proto = pkt.ipv4.UDP_PROTOCOL #msg.match.nw_dst = IP_BROADCAST msg.match.tp_src = pkt.dhcp.CLIENT_PORT msg.match.tp_dst = pkt.dhcp.SERVER_PORT msg.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) #msg.actions.append(of.ofp_action_output(port = of.OFPP_FLOOD)) self.connection.send(msg) core.openflow_discovery.install_flow(self.connection) src = self for dst in switches_by_dpid.itervalues(): if dst is src: continue p = _get_path(src, dst) if p is None: continue msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE #msg.match.nw_dst = "%s/%s" % (dst.network, dst.subnet) msg.match.nw_dst = "%s/%s" % (dst.network, "255.255.0.0") msg.actions.append(of.ofp_action_output(port=p[0][1])) self.connection.send(msg) """ # Can just do this instead of MAC learning if you run arp_responder... for port in self.ports: p = port.port_no if p < 0 or p >= of.OFPP_MAX: continue msg = of.ofp_flow_mod() msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_dst = "10.%s.%s.0/255.255.255.0" % (self._id,p) msg.actions.append(of.ofp_action_output(port=p)) self.connection.send(msg) """ for ip, mac in self.ip_to_mac.iteritems(): self._send_rewrite_rule(ip, mac) flood_ports = [] for port in self.ports: p = port.port_no if p < 0 or p >= of.OFPP_MAX: continue if core.openflow_discovery.is_edge_port(self.dpid, p): flood_ports.append(p) msg = of.ofp_flow_mod() msg.priority -= 1 msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_dst = "10.%s.%s.0/255.255.255.0" % (self._id, p) msg.actions.append(of.ofp_action_output(port=of.OFPP_CONTROLLER)) self.connection.send(msg) msg = of.ofp_flow_mod() msg.priority -= 1 msg.match = of.ofp_match() msg.match.dl_type = pkt.ethernet.IP_TYPE msg.match.nw_dst = "255.255.255.255" for p in flood_ports: msg.actions.append(of.ofp_action_output(port=p)) self.connection.send(msg)
def _handle_PacketIn(self, event): if self._outside_eth is None: return #print #print "PACKET",event.connection.ports[event.port].name,event.port, #print self.outside_port, self.make_match(event.ofp) incoming = event.port == self._outside_portno if self._gateway_eth is None: # Need to find gateway MAC -- send an ARP self._arp_for_gateway() return packet = event.parsed dns_hack = False # We only handle TCP and UDP tcpp = packet.find('tcp') if not tcpp: tcpp = packet.find('udp') if not tcpp: return if tcpp.dstport == 53 and tcpp.prev.dstip == self.inside_ip: if self.dns_ip and not incoming: # Special hack for DNS since we've lied and claimed to be the server dns_hack = True ipp = tcpp.prev if not incoming: # Assume we only NAT public addresses if self._is_local(ipp.dstip) and not dns_hack: return else: # Assume we only care about ourselves if ipp.dstip != self.outside_ip: return match = self.make_match(event.ofp) if incoming: match2 = match.clone() match2.dl_dst = None # See note below record = self._record_by_incoming.get(match2) if record is None: # Ignore for a while fm = of.ofp_flow_mod() fm.idle_timeout = 1 fm.hard_timeout = 10 fm.match = of.ofp_match.from_packet(event.ofp) event.connection.send(fm) return log.debug("%s reinstalled", record) record.incoming_fm.data = event.ofp # Hacky! else: record = self._record_by_outgoing.get(match) if record is None: record = Record() record.real_srcport = tcpp.srcport record.fake_srcport = self._pick_port(match) # Outside heading in fm = of.ofp_flow_mod() fm.flags |= of.OFPFF_SEND_FLOW_REM fm.hard_timeout = FLOW_TIMEOUT fm.match = match.flip() fm.match.in_port = self._outside_portno fm.match.nw_dst = self.outside_ip fm.match.tp_dst = record.fake_srcport fm.match.dl_src = self._gateway_eth # We should set dl_dst, but it can get in the way. Why? Because # in some situations, the ARP may ARP for and get the local host's # MAC, but in others it may not. #fm.match.dl_dst = self._outside_eth fm.match.dl_dst = None fm.actions.append(of.ofp_action_dl_addr.set_src(packet.dst)) fm.actions.append(of.ofp_action_dl_addr.set_dst(packet.src)) fm.actions.append(of.ofp_action_nw_addr.set_dst(ipp.srcip)) if dns_hack: fm.match.nw_src = self.dns_ip fm.actions.append( of.ofp_action_nw_addr.set_src(self.inside_ip)) if record.fake_srcport != record.real_srcport: fm.actions.append( of.ofp_action_tp_port.set_dst(record.real_srcport)) fm.actions.append(of.ofp_action_output(port=event.port)) record.incoming_match = self.strip_match(fm.match) record.incoming_fm = fm # Inside heading out fm = of.ofp_flow_mod() fm.data = event.ofp fm.flags |= of.OFPFF_SEND_FLOW_REM fm.hard_timeout = FLOW_TIMEOUT fm.match = match.clone() fm.match.in_port = event.port fm.actions.append( of.ofp_action_dl_addr.set_src(self._outside_eth)) fm.actions.append( of.ofp_action_nw_addr.set_src(self.outside_ip)) if dns_hack: fm.actions.append( of.ofp_action_nw_addr.set_dst(self.dns_ip)) if record.fake_srcport != record.real_srcport: fm.actions.append( of.ofp_action_tp_port.set_src(record.fake_srcport)) fm.actions.append( of.ofp_action_dl_addr.set_dst(self._gateway_eth)) fm.actions.append( of.ofp_action_output(port=self._outside_portno)) record.outgoing_match = self.strip_match(fm.match) record.outgoing_fm = fm self._record_by_incoming[record.incoming_match] = record self._record_by_outgoing[record.outgoing_match] = record log.debug("%s installed", record) else: log.debug("%s reinstalled", record) record.outgoing_fm.data = event.ofp # Hacky! record.touch() # Send/resend the flow mods if incoming: data = record.outgoing_fm.pack() + record.incoming_fm.pack() else: data = record.incoming_fm.pack() + record.outgoing_fm.pack() self._connection.send(data) # We may have set one of the data fields, but they should be reset since # they won't be valid in the future. Kind of hacky. record.outgoing_fm.data = None record.incoming_fm.data = None
def clear_flows(): """ Clear flows on all switches """ for c in core.openflow.connections: d = of.ofp_flow_mod(command=of.OFPFC_DELETE) c.send(d)
def _handle_PacketIn(self, event): """ Handle packet in messages from the switch to implement above algorithm. """ packet = event.parsed 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 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) self.macToPort[packet.src] = event.port # 1 if not self.transparent: # 2 if packet.type == packet.LLDP_TYPE or packet.dst.isBridgeFiltered( ): drop() # 2a return if packet.dst.is_multicast: flood() # 3a else: if packet.dst not in self.macToPort: # 4 flood("Port for %s unknown -- flooding" % (packet.dst, )) # 4a else: port = self.macToPort[packet.dst] if port == event.port: # 5 # 5a log.warning( "Same port for packet from %s -> %s on %s.%s. Drop." % (packet.src, packet.dst, dpid_to_str( event.dpid), port)) drop(10) return # 6 log.debug("installing flow for %s.%i -> %s.%i" % (packet.src, event.port, packet.dst, port)) msg = of.ofp_flow_mod() msg.match = of.ofp_match.from_packet(packet, event.port) msg.idle_timeout = 10 msg.hard_timeout = 30 msg.actions.append(of.ofp_action_output(port=port)) msg.data = event.ofp # 6a self.connection.send(msg)
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)