def packet_in(self, dpid, port, payload): p_eth = NetUtils.packet(payload, 'ethernet') if p_eth.ethertype != 0x0806: return # Handle ARP requests. p_arp = NetUtils.packet(payload, 'arp') src_ip = p_arp.src_ip dst_ip = p_arp.dst_ip switch = self.nib.dpid_to_switch(dpid) if p_arp.opcode == arp.ARP_REQUEST: preferred_path = self.nib.get_preferred_path() # If the request is for a host in the net we're already in, just broadcast it. The host # itself will answer. if NetUtils.ip_in_network(src_ip, self.nib.actual_net_for(switch)) and \ NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)): real_dest_ip = None else: # It's an imaginary host on one of the alternate paths real_dest_ip = self.nib.translate_alternate_net(dst_ip) if real_dest_ip == None: logging.info("Flooding ARP Request") self.main_app.flood(switch, port, payload) elif self.nib.learned_ip(real_dest_ip): real_dest_mac = self.nib.mac_for_ip(real_dest_ip) self.arp_reply(switch, port, p_eth.src, src_ip, real_dest_mac, dst_ip) else: # Send an ARP request to all ports, then just stay out of the way. If the host is up # on an unlearned port, it'll send a response, and that'll trigger learning. Then # when the NEXT ARP request for this address is received (it'll get retried a bunch of # times in practice), the reply can be generated from the ARP cache. # It doesn't matter so much where the ARP reply goes, because this switch will pick it up. switch_net = self.nib.actual_net_for(switch) # TODO: 250 will work as a host on subnets with a /24, but not any higher. src_ip = NetUtils.ip_for_network(switch_net, 250) self.main_app.send_arp_request(switch, src_ip, real_dest_ip) # We don't do anything special to ARP replies, just forward them onto their destination # unidirectionally # TODO: Can't this be handled by L2Switch, since it's bound for a real Mac? elif p_arp.opcode == arp.ARP_REPLY: # We ignore the text of ARP replies bound for us. We just used them for learning the port. if p_eth.dst == self.main_app.BOGUS_MAC: pass # The destination port was definitely learned because that's where the request originated elif not self.nib.seen_mac(p_eth.dst): logging.error("Ooops! ARP reply bound for a destination we don't know") return elif self.nib.switch_for_mac(p_eth.dst) != switch: logging.error("Ooops! ARP reply is destined for a different network. Can't happen.") return else: direct_net_port = self.nib.port_for_mac(p_eth.dst) output_actions = [Output(Physical(direct_net_port))] self.main_app.pkt_out(dpid, payload, output_actions)
def packet_in_router_learning(self, dpid, port, payload): p = NetUtils.packet(payload, 'arp') p_eth = NetUtils.packet(payload, 'ethernet') switch = self.nib.dpid_to_switch(dpid) logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+p.src_ip+" -> ("+switch+", "+str(port)+") -> "+p.dst_ip) # Supposedly, the src mac in the ARP reply and the ethernet frame itself should be the # the same, but that's not always the case. The Ethernet frame is definitive. self.nib.learn(switch, self.nib.ROUTER_PORT, port, p_eth.src, p.src_ip) self.waiting_for_router_arps -= 1 if self.waiting_for_router_arps == 0: self.normal_mode()
def packet_in(self, dpid, port, payload): p_eth = NetUtils.packet(payload, "ethernet") if p_eth.ethertype != 0x0800: return p_ip = NetUtils.packet(payload, "ipv4") src_ip = p_ip.src dst_ip = p_ip.dst switch = self.nib.dpid_to_switch(dpid) # If we haven't seen this source, dest pair yet, add it, and the rule with it. # Which list we put it in depends on whether we're at the ingress or egress switch if self.nib.at_ingress_switch(switch, port): # TODO: If this packet is bound for hosts outside the CoSciN network, in production just forward them, # For now, just drop them. if not self.nib.ip_in_coscin_network(dst_ip): logging.info("Internet-bound packet dropped in this test network") return # It's possible that this is an intra-network packet even though the rule should 've been installed # to handle such packets directly. send_along_direct_path will handle it below. elif NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)): pass elif not self.nib.seen_src_dest_pair_at_ingress(src_ip, dst_ip): self.nib.add_ingress_src_dest_pair(src_ip, dst_ip) self.nib.set_dirty() elif self.nib.at_egress_switch(switch, port): if not self.nib.seen_src_dest_pair_at_egress(src_ip, dst_ip): self.nib.add_egress_src_dest_pair(src_ip, dst_ip) self.nib.set_dirty() # If we have seen it, the rule should've taken care of the next packets, but it might # not be in effect yet so we handle it manually opposite_switch = self.nib.opposite_switch(switch) if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(opposite_switch)): self.send_along_preferred_path(switch, src_ip, dst_ip, payload) elif self.nib.at_ingress_switch(switch, port): if NetUtils.ip_in_network(dst_ip, self.nib.actual_net_for(switch)): self.send_to_host_without_rewrite(switch, src_ip, dst_ip, payload) else: self.send_along_direct_path(switch, src_ip, dst_ip, payload) elif self.nib.at_egress_switch(switch, port): self.send_to_host(switch, src_ip, dst_ip, payload)
def packet_in_normal_operation(self, dpid, port, payload): switch = self.nib.dpid_to_switch(dpid) # TODO: deal with non-IP packets, although that's fairly unlikely p_eth = NetUtils.packet(payload, 'ethernet') if p_eth.ethertype == 0x0806: p_arp = NetUtils.packet(payload, 'arp') src_ip = p_arp.src_ip dst_ip = p_arp.dst_ip elif p_eth.ethertype == 0x0800: p_ip = NetUtils.packet(payload, 'ipv4') src_ip = p_ip.src dst_ip = p_ip.dst else: src_ip = '0.0.0.0' dst_ip = '0.0.0.0' logging.info("Received packet of type "+str(p_eth.ethertype)) # TODO: Handle DHCP requests someday, ... maybe if src_ip == '0.0.0.0': return if self.nib.learn(switch, self.nib.ENDHOST_PORT, port, p_eth.src, src_ip): self.nib.set_dirty() logging.info("Received ("+str(p_eth.ethertype)+"): "+p_eth.src+"/"+src_ip+" -> ("+switch+", "+str(port)+") -> "+dst_ip) self.arp_handler.packet_in(dpid, port, payload) #self.broadcast_handler.packet_in(dpid, port, payload) self.intranet_handler.packet_in(dpid, port, payload) self.cross_campus_handler.packet_in(dpid, port, payload) if self.nib.is_dirty(): logging.info("Installing new policy") # This doesn't actually wait two seconds, but it seems to serialize the updates so they occur in the right # order, as opposed to just calling update_and_clear_dirty on its own. IOLoop.instance().add_timeout(datetime.timedelta(seconds=2), self.update_and_clear_dirty)
def packet_in(self, dpid, port, payload): p_eth = NetUtils.packet(payload, 'ethernet') if p_eth.dst == 0xffffffffffff: self.main_app.flood( self.nib.dpid_to_switch(dpid), port, payload )