def _send_router_advertisement(self, ipv6_src: str, tun_id: int, tun_ipv4_dst: str, output_port): """ Generates the Router Advertisement response packet """ ofproto, parser = self._datapath.ofproto, self._datapath.ofproto_parser if not tun_id or not tun_ipv4_dst: self.logger.error("Packet missing tunnel information, can't reply") return prefix = self.get_custom_prefix(ipv6_src) if not prefix: self.logger.debug("Can't reply to RS for UE ip %s", ipv6_src) return pkt = packet.Packet() pkt.add_protocol( ethernet.ethernet( dst=self.MAC_MULTICAST, src=self.config.ll_addr, ethertype=ether_types.ETH_TYPE_IPV6, )) pkt.add_protocol( ipv6.ipv6( dst=self.DEVICE_MULTICAST, src=self.config.ipv6_src, nxt=in_proto.IPPROTO_ICMPV6, )) pkt.add_protocol( icmpv6.icmpv6( type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert(options=[ icmpv6.nd_option_sla(hw_src=self.config.ll_addr, ), icmpv6.nd_option_pi( pl=self.config.prefix_len, prefix=prefix, ) ]), )) pkt.serialize() actions_out = [ parser.NXActionSetTunnel(tun_id=tun_id), parser.NXActionRegLoad2(dst='tun_ipv4_dst', value=tun_ipv4_dst), parser.OFPActionOutput(port=output_port) ] out = parser.OFPPacketOut(datapath=self._datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=ofproto.OFPP_CONTROLLER, actions=actions_out, data=pkt.data) ret = self._datapath.send_msg(out) if not ret: self.logger.error("Datapath disconnected, couldn't send RA")
def generate_ra_pkt(self, datapath, in_port): nd_option_sla = icmpv6.nd_option_sla(hw_src=self.port_mac_dic[datapath.id][in_port]) nd_option_pi = icmpv6.nd_option_pi( pl=64, res1=6, val_l=2592000, pre_l=604800, prefix=setting.Prefix_dic[datapath.id][in_port]) ra_data = icmpv6.nd_router_advert(ch_l=64, res=0, rou_l=1800, rea_t=0, ret_t=0, options=[nd_option_sla, nd_option_pi]) ra_pkt = packet.Packet() ra_pkt.add_protocol(ethernet.ethernet(ethertype=0x86DD, dst='33:33:00:00:00:01', src=self.port_mac_dic[datapath.id][in_port])) ra_pkt.add_protocol(ipv6.ipv6(dst='ff02::1', src=setting.Port_link_dic[datapath.id][in_port], nxt=58)) ra_pkt.add_protocol(icmpv6.icmpv6(type_=134, code=0, data=ra_data)) return ra_pkt
def router_advert(vid, eth_src, eth_dst, src_ip, dst_ip, vips, hop_limit=255, pi_flags=0x6): """Return IPv6 ICMP echo reply packet. Args: vid (int or None): VLAN VID to use (or None). eth_src (str): source Ethernet MAC address. eth_dst (str): dest Ethernet MAC address. src_ip (ipaddress.IPv6Address): source IPv6 address. vips (list): prefixes (ipaddress.IPv6Address) to advertise. hop_limit (int): IPv6 hop limit. pi_flags (int): flags to set in prefix information field (default set A and L) Returns: ryu.lib.packet.ethernet: Serialized IPv6 ICMP RA packet. """ pkt = build_pkt_header(vid, eth_src, eth_dst, ether.ETH_TYPE_IPV6) ipv6_pkt = ipv6.ipv6(src=src_ip, dst=dst_ip, nxt=inet.IPPROTO_ICMPV6, hop_limit=hop_limit) pkt.add_protocol(ipv6_pkt) options = [] for vip in vips: options.append( icmpv6.nd_option_pi( prefix=vip.network.network_address, pl=vip.network.prefixlen, res1=pi_flags, val_l=86400, pre_l=14400, )) options.append(icmpv6.nd_option_sla(hw_src=eth_src)) # https://tools.ietf.org/html/rfc4861#section-4.6.2 icmpv6_ra_pkt = icmpv6.icmpv6(type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert( rou_l=1800, ch_l=hop_limit, options=options)) pkt.add_protocol(icmpv6_ra_pkt) pkt.serialize() return pkt
def test_default_args(self): pi = icmpv6.nd_option_pi() buf = pi.serialize() res = struct.unpack(icmpv6.nd_option_pi._PACK_STR, str(buf)) eq_(res[0], icmpv6.ND_OPTION_PI) eq_(res[1], len(icmpv6.nd_option_pi()) / 8) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) eq_(res[5], 0) eq_(res[6], 0) eq_(res[7], addrconv.ipv6.text_to_bin('::')) # with nd_router_advert prev = ipv6(nxt=inet.IPPROTO_ICMPV6) ic = icmpv6.icmpv6( type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert( options=[icmpv6.nd_option_pi()])) prev.serialize(ic, None) buf = ic.serialize(bytearray(), prev) res = struct.unpack(icmpv6.icmpv6._PACK_STR, str(buf[:4])) eq_(res[0], icmpv6.ND_ROUTER_ADVERT) eq_(res[1], 0) eq_(res[2], icmpv6_csum(prev, buf)) res = struct.unpack(icmpv6.nd_router_advert._PACK_STR, str(buf[4:16])) eq_(res[0], 0) eq_(res[1], 0) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) res = struct.unpack(icmpv6.nd_option_pi._PACK_STR, str(buf[16:])) eq_(res[0], icmpv6.ND_OPTION_PI) eq_(res[1], 4) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) eq_(res[5], 0) eq_(res[6], 0) eq_(res[7], addrconv.ipv6.text_to_bin('::'))
def test_default_args(self): pi = icmpv6.nd_option_pi() buf = pi.serialize() res = struct.unpack(icmpv6.nd_option_pi._PACK_STR, str(buf)) eq_(res[0], icmpv6.ND_OPTION_PI) eq_(res[1], len(icmpv6.nd_option_pi()) / 8) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) eq_(res[5], 0) eq_(res[6], 0) eq_(res[7], addrconv.ipv6.text_to_bin('::')) # with nd_router_advert prev = ipv6(nxt=inet.IPPROTO_ICMPV6) ic = icmpv6.icmpv6( type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert(options=[icmpv6.nd_option_pi()])) prev.serialize(ic, None) buf = ic.serialize(bytearray(), prev) res = struct.unpack(icmpv6.icmpv6._PACK_STR, str(buf[:4])) eq_(res[0], icmpv6.ND_ROUTER_ADVERT) eq_(res[1], 0) eq_(res[2], icmpv6_csum(prev, buf)) res = struct.unpack(icmpv6.nd_router_advert._PACK_STR, str(buf[4:16])) eq_(res[0], 0) eq_(res[1], 0) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) res = struct.unpack(icmpv6.nd_option_pi._PACK_STR, str(buf[16:])) eq_(res[0], icmpv6.ND_OPTION_PI) eq_(res[1], 4) eq_(res[2], 0) eq_(res[3], 0) eq_(res[4], 0) eq_(res[5], 0) eq_(res[6], 0) eq_(res[7], addrconv.ipv6.text_to_bin('::'))
def router_advert(vid, eth_src, eth_dst, src_ip, dst_ip, vips, pi_flags=0x6): """Return IPv6 ICMP Router Advert. Args: vid (int or None): VLAN VID to use (or None). eth_src (str): source Ethernet MAC address. eth_dst (str): dest Ethernet MAC address. src_ip (ipaddress.IPv6Address): source IPv6 address. vips (list): prefixes (ipaddress.IPv6Address) to advertise. pi_flags (int): flags to set in prefix information field (default set A and L) Returns: ryu.lib.packet.ethernet: Serialized IPv6 ICMP RA packet. """ pkt = build_pkt_header( vid, eth_src, eth_dst, valve_of.ether.ETH_TYPE_IPV6) ipv6_pkt = ipv6.ipv6( src=src_ip, dst=dst_ip, nxt=valve_of.inet.IPPROTO_ICMPV6, hop_limit=IPV6_MAX_HOP_LIM) pkt.add_protocol(ipv6_pkt) options = [] for vip in vips: options.append( icmpv6.nd_option_pi( prefix=vip.network.network_address, pl=vip.network.prefixlen, res1=pi_flags, val_l=86400, pre_l=14400, )) options.append(icmpv6.nd_option_sla(hw_src=eth_src)) # https://tools.ietf.org/html/rfc4861#section-4.6.2 icmpv6_ra_pkt = icmpv6.icmpv6( type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert( rou_l=1800, ch_l=IPV6_RA_HOP_LIM, options=options)) pkt.add_protocol(icmpv6_ra_pkt) pkt.serialize() return pkt
def __init__(self, mac_src, mac_dst, ipv6_src, ipv6_dst, rou_l, ipv6_options): """ ================ ========================================================= Input Parameter Description ================ ========================================================= mac_src String instance mac_dst String instance ipv6_src String instance ipv6_dst String instance rou_l Int instance ipv6_options Tuple instance: (prefix, prefix_length, valid_lft, preferred_lft) ================ ========================================================= ================ ========================================================= Attribute Description ================ ========================================================= pkt The Neighbor Solicitation generated packet ================ ========================================================= """ self.pkt = packet.Packet() e = ethernet.ethernet(mac_dst, mac_src, ether.ETH_TYPE_IPV6) i6 = ipv6.ipv6(src=ipv6_src, dst=ipv6_dst, nxt=inet.IPPROTO_ICMPV6) # ipv6_options element structure: (prefix, prefix_length, valid_lft, preferred_lft) options = [] for ipv6_opt in ipv6_options: options.append( icmpv6.nd_option_pi(prefix=ipv6_opt[0], pl=ipv6_opt[1], val_l=ipv6_opt[2], pre_l=ipv6_opt[3], res1=2) ) options.append(icmpv6.nd_option_sla(hw_src=mac_src)) ic = icmpv6.icmpv6(type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert(rou_l=rou_l, options=options)) self.pkt.add_protocol(e) self.pkt.add_protocol(i6) self.pkt.add_protocol(ic)
def __init__(self, mac_src, mac_dst, ipv6_src, ipv6_dst, rou_l, ipv6_options): """ ================ ========================================================= Input Parameter Description ================ ========================================================= mac_src String instance mac_dst String instance ipv6_src String instance ipv6_dst String instance rou_l Int instance ipv6_options Tuple instance: (prefix, prefix_length, valid_lft, preferred_lft) ================ ========================================================= ================ ========================================================= Attribute Description ================ ========================================================= pkt The Neighbor Solicitation generated packet ================ ========================================================= """ self.pkt = packet.Packet() e = ethernet.ethernet(mac_dst, mac_src, ether.ETH_TYPE_IPV6) i6 = ipv6.ipv6(src = ipv6_src, dst = ipv6_dst, nxt = inet.IPPROTO_ICMPV6) # ipv6_options element structure: (prefix, prefix_length, valid_lft, preferred_lft) options = [] for ipv6_opt in ipv6_options: options.append(icmpv6.nd_option_pi(prefix = ipv6_opt[0], pl = ipv6_opt[1], val_l = ipv6_opt[2], pre_l = ipv6_opt[3], res1 = 2)) options.append(icmpv6.nd_option_sla(hw_src = mac_src)) ic = icmpv6.icmpv6(type_ = icmpv6.ND_ROUTER_ADVERT, data = icmpv6.nd_router_advert(rou_l = rou_l, options = options)) self.pkt.add_protocol(e) self.pkt.add_protocol(i6) self.pkt.add_protocol(ic)
def _packet_in_handler(self, ev): #print("===============NEW PACKET===============") # If you hit this you might want to increase # the "miss_send_length" of your switch if ev.msg.msg_len < ev.msg.total_len: self.logger.debug("packet truncated: only %s of %s bytes",ev.msg.msg_len, ev.msg.total_len) #Extracting Message informations #Topology stuff msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] #Protocol stuff pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src i = pkt.get_protocol(ipv6) #if it's not related to IPV6, not considered if i is None: #Here you can get lldp packets return 0 print("------------IPV6 PACKET------------") if(self.RoutingDone==False): #If it's the first ipv6 packet received routing must be done before self.collectRoutingInfo() pkt_type =0 dpid = datapath.id print 'DPID::{}'.format(dpid) #Examining protocols in the IP packet for p in pkt.protocols: #Handling icmpv6 packets if p.protocol_name == 'icmpv6': pkt_type=1 if pkt_type == 1: print("-----------------ICMPv6-----------------") icmp = pkt.get_protocols(icmpv6.icmpv6)[0] itype = 0 found = 0 prefix ='' if icmp.type_== 133: print 'Type : Router Solicitation' itype = 1 elif icmp.type_== 134: print 'Type : Router Advertisement' itype = 2 elif icmp.type_== 128: print 'Type : Echo Request' itype = 4 elif icmp.type_== 129: print 'Type : Echo Reply' itype = 5 elif icmp.type_ ==136: print 'Type : Neighbour Advertisement' itype=3 elif icmp.type_ ==135: print'Type : Neighbour Solicitation' itype=6 else: print 'Type : other ICMPV6 message' print (icmp.type_) #mac_to_port is not used self.mac_to_port.setdefault(dpid, {}) self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) print("Details : packet in ", dpid, src, dst, in_port) #once protocols are known, it's time to prepare answers #temporary solution : here no authentication protocol #every user are granted found=1 #In case of Router Sollicitation if((itype == 1)&(found == 1)): #Mobility Management Procedure is fired #Asking for the list of the prior network #And updating it with the current one #host ID based on MAC address #the currrent datapath is also provided priorNetworks = self.mobTracker.getTraceAndUpdate(src,datapath); print('~~~~~~~~~~NODE HAS REGISTERED~~~~~~~~~~') print('previous networks : ') print (priorNetworks) print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') #if the list is empty there is nothing more to do #if not tunnels must be set up: if priorNetworks is not None: #creating tunnels with all the previous network and the current one for priorDp in priorNetworks[:-1]: #Getting new tunnel identifier tunID = self.tunnelID self.tunnelID += 1 #set up tunnel, host MAC @ is considered as identifier self.setUpTunnel(src,priorDp,datapath,tunID) #once flows are set up, router advertisement has to be sent #create RA including the allocated prefix (should consider multiple prefixes later) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() e= ethernet.ethernet(dst=str(eth.src),src=self.generateMAC(dpid,in_port), ethertype=ether.ETH_TYPE_IPV6) #AS IT IS A REPLY TO ROUTER SOLLICITATION : SOURCE @ MUST BE LOCAL SCOPE!! srcIP = self.generateLL(dpid,in_port) ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #setting up prefix : the dependant Local Network prefix is returned prefix = '200'+str(dpid)+'::1' icmp_v6 = icmpv6.icmpv6(type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert(ch_l=64, rou_l=4, options=[icmpv6.nd_option_pi(length=4, pl=64, res1=7, val_l=86400, pre_l=14400, prefix=prefix)])) pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(icmp_v6) pkt_generated.serialize() actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('>>>>>>>>>> ROUTER ADVERTISEMENT SENT <<<<<<<<<<') return #handling neighbour solicitation elif itype==6: neighSol = pkt.get_protocols(icmpv6.icmpv6)[0] print (neighSol) opt = neighSol.data.option trg = neighSol.data.dst if opt is not None : #if opt is not None, the NS is for getting the MAC @ of a given IP @ if isinstance(opt,ryu.lib.packet.icmpv6.nd_option_sla): #link layer address request #check if the solicited @ is the one of the router trgPort = None for localPort in range(1,len(self.switchList)+1): if str(trg)==(self.bindingList[dpid,localPort]): trgPort = localPort break; #if the request concerns the router, prepare answer: if localPort is not None : #get hw@ hw_addr = opt.hw_src #reply with a neighbor adv neigh_adv = icmpv6.icmpv6(type_=icmpv6.ND_NEIGHBOR_ADVERT, data=icmpv6.nd_neighbor(res=7, dst=str(trg), option=icmpv6.nd_option_tla(hw_src=self.generateMAC(dpid,localPort)))) e= ethernet.ethernet(dst=str(hw_addr),src=self.generateMAC(dpid,in_port), ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ srcIP = self.bindingList[dpid,in_port] ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(neigh_adv) pkt_generated.serialize() #TODO : think about the flow to set up # MATCH : router sollicitation for one of the local @: #creat tuple with all the local @ : # listTemp=[] # for i in range(1,len(self.switchList)): # listTemp.append.(self.bindingList[dpid,i]) # tupleLocalAdd = tuple(listTemp) # matchs = [parser.OFPMatch(icmpv6_type=135,ipv6_nd_target=tupleLocalAdd)] #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('..........neighbor advertisement sent..........') else: print('conflict resolution') #if option is None, the NS is sent for conflict resolution : #we store the annouced MAC the global dict. if they are new if trg[0:4] != 'fe80' : #registering globlal @ lst = [eth.src,trg] if lst not in self.coveredHosts[dpid]: #the couple is added only if it's a new one self.coveredHosts[dpid].append(lst) print ('registered hosts', self.coveredHosts) #otherwise nothing to do #handling ping requests and reply elif itype == 4 or itype == 5: #looking at destination address, finding out which is the next hope, changing MAC @ ping_src = i.src ping_dst = i.dst echo = pkt.get_protocols(icmpv6.icmpv6)[0]; print(echo) #when ip dst @ is known : 3 cases: #destination is behind another router #destination is behind the current router #destination is the current router #fetching all the local addresses of the current switch localAddressesList = [] for localPort in range(1,len(self.switchList)+1): localAddressesList.append(self.bindingList[dpid,localPort]) if ping_dst in localAddressesList : print('ping addressed to the router') #the ping is addressed to the switch: #!!!! not really working only with local network interfaces #!!!!not working with backbones interfaces!!! #if it's a request : reply if itype == 4: #copy request data into the reply reqData = echo.data pingReply = icmpv6.icmpv6(type_=icmpv6.ICMPV6_ECHO_REPLY, data=reqData) #direct reply on the incomming switch port out_port = in_port e= ethernet.ethernet(dst=src,src=dst, ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=str(ping_dst), dst=str(ping_src)) pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(pingReply) print('.........................') print(pkt_generated) pkt_generated.serialize() #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) #TODO Flow to set up... print('..........Ping Reply sent..........') else: print('ping another host or switch received by ', dpid, 'going to', ping_dst) #TODO : handle ping to backbone interface of a distant router #pinging switches interfaces # for localPort in range(2,len(self.switchList)): # match = parser.OFPMatch(ipv6_dst=(self.bindingList[dpid,localPort])) # action = [parser.OFPActionOutput(localPort)] # self.add_flow(datapath, 1, match, action) #pinging hosts for dp_ID in range(1,len(self.switchList)+1): if ping_dst[0:4]==self.bindingList[dp_ID,1][0:4]: print ('ping going to ', ping_dst , ' must be routed to ', str(dp_ID) ,' as localNW domain is ', self.bindingList[dp_ID,1]) break #handle the case where no sub domain is found else: print ('no subdomain found deleting packet') #throw exception return 0 print(self.bindingList[dp_ID,1][0:4]) if dp_ID == dpid:#PING GOING TO LOCAL NETWORK outputIntf = 1 print('ping toward local network') #setting new addresses MAC: new_mac_src = self.generateMAC(dpid,1) for idx,host in enumerate(self.coveredHosts[dpid]): if host[1] == ping_dst: new_mac_dst = self.coveredHosts[dpid][idx][0] break else: print ('host unknown is the subdomain deleting packet') #throw exception else:#PING GOING OUTSIDE LOCAL NETWORK outputIntf = self.routing(dpid,dp_ID) new_mac_src = self.generateMAC(dpid,outputIntf) new_mac_dst = self.generateMAC(dp_ID,self.routing(dp_ID,dpid)) print ('ping toward neighbor ', outputIntf) action = [parser.OFPActionDecNwTtl(), parser.OFPActionSetField(eth_src=new_mac_src), parser.OFPActionSetField(eth_dst=new_mac_dst),parser.OFPActionOutput(outputIntf) ] match = parser.OFPMatch( eth_type=0x86dd, ip_proto=58, ipv6_dst=(ping_dst,'ffff:ffff:ffff:ffff::')) print('ready to push flow to ',datapath) self.add_flow(datapath, 1, match, action) print('flow pushed') else: print ('') print("========================================")
def _packet_in_handler(self, ev): #print("===============NEW PACKET===============") # If you hit this you might want to increase # the "miss_send_length" of your switch if ev.msg.msg_len < ev.msg.total_len: self.logger.debug("packet truncated: only %s of %s bytes", ev.msg.msg_len, ev.msg.total_len) #Extracting Message informations #Topology stuff msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] #Protocol stuff pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src i = pkt.get_protocol(ipv6) #if it's not related to IPV6, not considered if i is None: #Here you can get lldp packets return 0 print("------------IPV6 PACKET------------") if (self.RoutingDone == False): #If it's the first ipv6 packet received routing must be done before self.collectRoutingInfo() pkt_type = 0 dpid = datapath.id print 'DPID::{}'.format(dpid) #Examining protocols in the IP packet for p in pkt.protocols: #Handling icmpv6 packets if p.protocol_name == 'icmpv6': pkt_type = 1 if pkt_type == 1: print("-----------------ICMPv6-----------------") icmp = pkt.get_protocols(icmpv6.icmpv6)[0] itype = 0 found = 0 prefix = '' if icmp.type_ == 133: print 'Type : Router Solicitation' itype = 1 elif icmp.type_ == 134: print 'Type : Router Advertisement' itype = 2 elif icmp.type_ == 128: print 'Type : Echo Request' itype = 4 elif icmp.type_ == 129: print 'Type : Echo Reply' itype = 5 elif icmp.type_ == 136: print 'Type : Neighbour Advertisement' itype = 3 elif icmp.type_ == 135: print 'Type : Neighbour Solicitation' itype = 6 else: print 'Type : other ICMPV6 message' print(icmp.type_) #mac_to_port is not used self.mac_to_port.setdefault(dpid, {}) self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) print("Details : packet in ", dpid, src, dst, in_port) #once protocols are known, it's time to prepare answers #temporary solution : here no authentication protocol #every user are granted found = 1 #In case of Router Sollicitation if ((itype == 1) & (found == 1)): #Mobility Management Procedure is fired #Asking for the list of the prior network #And updating it with the current one #host ID based on MAC address #the currrent datapath is also provided priorNetworks = self.mobTracker.getTraceAndUpdate(src, datapath) print('~~~~~~~~~~NODE HAS REGISTERED~~~~~~~~~~') print('previous networks : ') print(priorNetworks) print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') #if the list is empty there is nothing more to do #if not tunnels must be set up: if priorNetworks is not None: #maintaining a list in order to know when to stop in the tunnel creation/updating #and in order not to create a tunnel that will be updated in the same procedure updatedTunnels = [] #creating tunnels with all the previous network and the current one for priorDp in priorNetworks[:-1]: #Getting new tunnel identifier, a tunnelID is build with #the concatenation of the old router ID and the new router ID tunID = int(str(priorDp.id) + str(datapath.id)) #when a already updated tunnel is met, it is skipped if tunID in updatedTunnels: continue #else it's registered to the list and the procedure is launched updatedTunnels.append(tunID) if priorDp.id != datapath.id: #set up tunnel, host MAC @ is considered as identifier self.setUpTunnel(src, priorDp, datapath, tunID) else: #if the network is back in a previously visited network #redirect the tunneled flow on the local interface priorPrefix = str('200') + str(priorDp.id) priorAddress = self.forgeHostGlobalIP(src, priorPrefix) matchBack = datapath.ofproto_parser.OFPMatch( eth_type=0x86dd, ip_proto=58, ipv6_dst=( priorAddress, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')) new_mac_src = self.generateMAC(priorDp.id, 1) new_mac_dst = src actionsBack = [ datapath.ofproto_parser.OFPActionDecNwTtl(), datapath.ofproto_parser.OFPActionSetField( eth_src=new_mac_src), datapath.ofproto_parser.OFPActionSetField( eth_dst=new_mac_dst), datapath.ofproto_parser.OFPActionOutput(1) ] self.add_flow(datapath, 65535, matchBack, actionsBack) #once flows are set up, router advertisement has to be sent #create RA including the allocated prefix (should consider multiple prefixes later) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() e = ethernet.ethernet(dst=str(eth.src), src=self.generateMAC(dpid, in_port), ethertype=ether.ETH_TYPE_IPV6) #AS IT IS A REPLY TO ROUTER SOLLICITATION : SOURCE @ MUST BE LOCAL SCOPE!! srcIP = self.generateLL(dpid, in_port) ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #setting up prefix : the dependant Local Network prefix is returned prefix = '200' + str(dpid) + '::1' icmp_v6 = icmpv6.icmpv6(type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert( ch_l=64, rou_l=4, options=[ icmpv6.nd_option_pi(length=4, pl=64, res1=7, val_l=86400, pre_l=14400, prefix=prefix) ])) pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(icmp_v6) pkt_generated.serialize() actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('>>>>>>>>>> ROUTER ADVERTISEMENT SENT <<<<<<<<<<') return #handling neighbour solicitation elif itype == 6: neighSol = pkt.get_protocols(icmpv6.icmpv6)[0] print(neighSol) opt = neighSol.data.option trg = neighSol.data.dst if opt is not None: #if opt is not None, the NS is for getting the MAC @ of a given IP @ if isinstance(opt, ryu.lib.packet.icmpv6.nd_option_sla): #link layer address request #check if the solicited @ is the one of the router trgPort = None for localPort in range(1, len(self.switchList) + 1): if str(trg) == (self.bindingList[dpid, localPort]): trgPort = localPort break #if the request concerns the router, prepare answer: if localPort is not None: #get hw@ hw_addr = opt.hw_src #reply with a neighbor adv neigh_adv = icmpv6.icmpv6( type_=icmpv6.ND_NEIGHBOR_ADVERT, data=icmpv6.nd_neighbor( res=7, dst=str(trg), option=icmpv6.nd_option_tla( hw_src=self.generateMAC(dpid, localPort)))) e = ethernet.ethernet(dst=str(hw_addr), src=self.generateMAC( dpid, in_port), ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ srcIP = self.bindingList[dpid, in_port] ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(neigh_adv) pkt_generated.serialize() #TODO : think about the flow to set up # MATCH : router sollicitation for one of the local @: #creat tuple with all the local @ : # listTemp=[] # for i in range(1,len(self.switchList)): # listTemp.append.(self.bindingList[dpid,i]) # tupleLocalAdd = tuple(listTemp) # matchs = [parser.OFPMatch(icmpv6_type=135,ipv6_nd_target=tupleLocalAdd)] #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut( datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print( '..........neighbor advertisement sent..........') else: print('conflict resolution') #if option is None, the NS is sent for conflict resolution : #we store the annouced MAC the global dict. if they are new if trg[0:4] != 'fe80': #registering globlal @ lst = [eth.src, trg] if lst not in self.coveredHosts[dpid]: #the couple is added only if it's a new one self.coveredHosts[dpid].append(lst) print('registered hosts', self.coveredHosts) #otherwise nothing to do #handling ping requests and reply elif itype == 4 or itype == 5: #looking at destination address, finding out which is the next hope, changing MAC @ ping_src = i.src ping_dst = i.dst echo = pkt.get_protocols(icmpv6.icmpv6)[0] print(echo) #when ip dst @ is known : 3 cases: #destination is behind another router #destination is behind the current router #destination is the current router #fetching all the local addresses of the current switch localAddressesList = [] for localPort in range(1, len(self.switchList) + 1): localAddressesList.append(self.bindingList[dpid, localPort]) if ping_dst in localAddressesList: print('ping addressed to the router') #the ping is addressed to the switch: #!!!! not really working only with local network interfaces #!!!!not working with backbones interfaces!!! #if it's a request : reply if itype == 4: #copy request data into the reply reqData = echo.data pingReply = icmpv6.icmpv6(type_=icmpv6.ICMPV6_ECHO_REPLY, data=reqData) #direct reply on the incomming switch port out_port = in_port e = ethernet.ethernet(dst=src, src=dst, ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=str(ping_dst), dst=str(ping_src)) pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(pingReply) print('.........................') print(pkt_generated) pkt_generated.serialize() #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut( datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) #TODO Flow to set up... print('..........Ping Reply sent..........') else: print('ping another host or switch received by ', dpid, 'going to', ping_dst) #TODO : handle ping to backbone interface of a distant router #pinging switches interfaces # for localPort in range(2,len(self.switchList)): # match = parser.OFPMatch(ipv6_dst=(self.bindingList[dpid,localPort])) # action = [parser.OFPActionOutput(localPort)] # self.add_flow(datapath, 1, match, action) #pinging hosts for dp_ID in range(1, len(self.switchList) + 1): if ping_dst[0:4] == self.bindingList[dp_ID, 1][0:4]: print('ping going to ', ping_dst, ' must be routed to ', str(dp_ID), ' as localNW domain is ', self.bindingList[dp_ID, 1]) break #handle the case where no sub domain is found else: print('no subdomain found deleting packet') #throw exception return 0 print(self.bindingList[dp_ID, 1][0:4]) if dp_ID == dpid: #PING GOING TO LOCAL NETWORK outputIntf = 1 print('ping toward local network') #setting new addresses MAC: new_mac_src = self.generateMAC(dpid, 1) for idx, host in enumerate(self.coveredHosts[dpid]): if host[1] == ping_dst: new_mac_dst = self.coveredHosts[dpid][idx][0] break else: print('host unknown is the subdomain deleting packet') #throw exception else: #PING GOING OUTSIDE LOCAL NETWORK outputIntf = self.routing(dpid, dp_ID) new_mac_src = self.generateMAC(dpid, outputIntf) new_mac_dst = self.generateMAC(dp_ID, self.routing(dp_ID, dpid)) print('ping toward neighbor ', outputIntf) action = [ parser.OFPActionDecNwTtl(), parser.OFPActionSetField(eth_src=new_mac_src), parser.OFPActionSetField(eth_dst=new_mac_dst), parser.OFPActionOutput(outputIntf) ] match = parser.OFPMatch(eth_type=0x86dd, ip_proto=58, ipv6_dst=(ping_dst, 'ffff:ffff:ffff:ffff::')) print('ready to push flow to ', datapath) #routing related flow then pushed to table 1 self.add_flow(datapath, 1, match, action, tblId=1) print('flow pushed') else: print('') print("========================================")
def _packet_in_handler(self, ev): # If you hit this you might want to increase # the "miss_send_length" of your switch if ev.msg.msg_len < ev.msg.total_len: self.logger.debug("packet truncated: only %s of %s bytes",ev.msg.msg_len, ev.msg.total_len) #Extracting Message informations #Topology stuff msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] #Protocol stuff pkt = packet.Packet(msg.data) eth = pkt.get_protocols(ethernet.ethernet)[0] dst = eth.dst src = eth.src i = pkt.get_protocol(ipv6) #if it's not related to IPV6, not considered if i is None: #Here you can get lldp packets return 0 print("------------IPV6 PACKET------------") if(self.RoutingDone==False): #If it's the first ipv6 packet received routing must be done before self.collectRoutingInfo() pkt_type =0 dpid = datapath.id print 'DPID::{}'.format(dpid) #Examining protocols in the IP packet for p in pkt.protocols: #Handling icmpv6 packets if p.protocol_name == 'icmpv6': pkt_type=1 if pkt_type == 1: print("-----------------ICMPv6-----------------") icmp = pkt.get_protocols(icmpv6.icmpv6)[0] itype = 0 found = 0 prefix ='' if icmp.type_== 133: print 'Type : Router Solicitation' itype = 1 elif icmp.type_== 134: print 'Type : Router Advertisement' itype = 2 elif icmp.type_== 128: print 'Type : Echo Request' itype = 4 elif icmp.type_== 129: print 'Type : Echo Reply' itype = 5 elif icmp.type_ ==136: print 'Type : Neighbour Advertisement' itype=3 elif icmp.type_ ==135: print'Type : Neighbour Solicitation' itype=6 else: print 'Type : other ICMPV6 message' print (icmp.type_) #mac_to_port is not used self.mac_to_port.setdefault(dpid, {}) self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) print("Details : packet in ", dpid, src, dst, in_port) #once protocols are known, it's time to prepare answers #temporary solution : here no authentication protocol #every user are granted found=1 #In case of Router Solicitation if((itype == 1)&(found == 1)): #checking if the incomming port is not a backbone port: validIntf=True; for link in self.linkList: if (link.src.dpid,link.src.port_no) == (dpid,in_port) or (link.dst.dpid,link.dst.port_no) == (dpid,in_port): validIntf = False; print('local host registration : non valid input interface (belongs to backbone)') break if not validIntf : return 0; #computing the global ipv6 address the host will forge nbrZeros=3-len(str(dpid)) newPrefix='2'+'0'*nbrZeros+str(dpid) newAddress = self.forgeHostGlobalIP(src,newPrefix) #registering or updating host in the current router hostDetails = (eth.src,in_port) self.coveredHosts[dpid][newAddress]=hostDetails print ('coveredHost updated : switch ', dpid) print self.coveredHosts #registering the interface in the bindingList #done if the interface is not discovered already (dynamic linking), if (dpid,in_port) not in self.bindingList.keys(): self.bindingList[dpid,in_port]='2'+'0'*nbrZeros+str(dpid)+'::'+str(in_port) print ('bindingList updated : switch ', dpid) print self.bindingList print ('local host registration : host : ', eth.src , ' registered under ',dpid,' coverage at interface number ',in_port, 'with ip@ :', newAddress ) #Mobility Management Procedure is fired #Asking for the list of the prior network #And updating it with the current one #host ID based on MAC address #the currrent datapath is also provided priorNetworks = self.mobTracker.getTraceAndUpdate(src,datapath); print('~~~~~~~~~~NODE HAS REGISTERED~~~~~~~~~~') print('previous networks : ') print (priorNetworks) print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') #if the list is empty there is nothing more to do #if not tunnels must be set up: if priorNetworks is not None: #maintaining a list in order to know when to stop in the tunnel creation/updating #and in order not to create a tunnel that will be updated in the same procedure updatedTunnels=[] #creating tunnels with all the previous network and the current one for priorDp in priorNetworks[:-1]: print('###########SETTING UP TUNNEL############') #Getting new tunnel identifier, a tunnelID is build with #the concatenation of the old router ID and the new router ID tunID = int(str(priorDp.id)+str(datapath.id)) #when a already updated tunnel is met, it is skipped if tunID in updatedTunnels: continue; #else it's registered to the list and the procedure is launched updatedTunnels.append(tunID) #fetching IP addr the host forged in the previous network #in asking coveredHost dict with the provided MAC @ priorAddress = None for ipAddress in self.coveredHosts[priorDp.id].keys(): #comparing MAC addresses if self.coveredHosts[priorDp.id][ipAddress][0]==src: priorAddress=ipAddress break else: #if the mac address is not associated to any ip address, exit print('the host with mac @ : ',src,' has never visited ' , priorDp.id , ' here is its covered Hosts : ') print( self.coveredHosts[priorDp.id]) return 0; # nbrZeros=3-len(str(priorDp.id)) # priorPrefix='2'+'0'*nbrZeros+str(priorDp.id) # #priorPrefix = str('200')+str(priorDp.id) # priorAddress = self.forgeHostGlobalIP(src,priorPrefix) if priorDp.id != datapath.id: #set up tunnel, host MAC @ is considered as identifier self.setUpTunnel(priorAddress,priorDp,datapath,tunID) print('tunnel set up from ', priorDp.id, ' to ', datapath.id) #Each previously built address is included in a proactively pushed flow for #forwarding on the input interface which is registered in table 3 of the new #covering router. so that the NewInput flow forwards packets to this #table to resolve the local output interface # #in_port is the interface to which packets comming from the tunnel have to be forwarded to #if the node change the interface it is linked to #the switch, a new router solicitation is sent, #all the tunnel are then updated and so is table 2 output_intf = in_port match2 = parser.OFPMatch( eth_type=0x86dd, ip_proto=58, ipv6_dst=(priorAddress,'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')) #set up mac addresses new_mac_src = self.generateMAC(dpid,output_intf) new_mac_dst = eth.src action2 = [parser.OFPActionDecNwTtl(), parser.OFPActionSetField(eth_src=new_mac_src), parser.OFPActionSetField(eth_dst=new_mac_dst),parser.OFPActionOutput(output_intf) ] #remote address routing related flow then pushed to table 2 self.add_flow(datapath, 1, match2, action2,tblId=2) print('Remote Routing Flow : prior address ', priorAddress, ' -> interface : ',output_intf,' written in switch ', datapath.id) else: #if the network is back in a previously visited network #redirect the tunneled flow on the local interface print('Mobile node ',src,' is back to network ', dpid) matchBack = datapath.ofproto_parser.OFPMatch( eth_type=0x86dd, ip_proto=58,vlan_vid=0x000, ipv6_dst=(priorAddress,'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')) output_port = in_port new_mac_src = self.generateMAC(priorDp.id,output_port) new_mac_dst = src actionsBack = [datapath.ofproto_parser.OFPActionDecNwTtl(), datapath.ofproto_parser.OFPActionSetField(eth_src=new_mac_src), datapath.ofproto_parser.OFPActionSetField(eth_dst=new_mac_dst)] instsBack = [datapath.ofproto_parser.OFPInstructionActions(datapath.ofproto.OFPIT_APPLY_ACTIONS,actionsBack)] #Forward to normal routing table (table 1) instsBack.append(datapath.ofproto_parser.OFPInstructionGotoTable(1)) modBack = datapath.ofproto_parser.OFPFlowMod(datapath=datapath, priority=65535, match=matchBack,instructions=instsBack) datapath.send_msg(modBack) print('Tunnel flow pushed to switch ' ,datapath.id ,' to make packets going to ', priorAddress, ' going to local network again') #once flows are set up, router advertisement has to be sent #create RA including the allocated prefix (should consider multiple prefixes later) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() e= ethernet.ethernet(dst=str(eth.src),src=self.generateMAC(dpid,in_port), ethertype=ether.ETH_TYPE_IPV6) #AS IT IS A REPLY TO ROUTER SOLLICITATION : SOURCE @ MUST BE LOCAL SCOPE!! srcIP = self.generateLL(dpid,in_port) ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #setting up prefix : the dependant Local Network prefix is returned #nbrZeros=3-len(str(dpid)) #prefix='2'+'0'*nbrZeros+str(dpid)+'::1' prefix=self.bindingList[dpid,in_port] icmp_v6 = icmpv6.icmpv6(type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert(ch_l=64, rou_l=4, options=[icmpv6.nd_option_pi(length=4, pl=64, res1=7, val_l=86400, pre_l=14400, prefix=prefix)])) pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(icmp_v6) pkt_generated.serialize() actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('>>>>>>>>>> ROUTER ADVERTISEMENT SENT <<<<<<<<<<') return #handling neighbour solicitation elif itype==6: neighSol = pkt.get_protocols(icmpv6.icmpv6)[0] print (neighSol) opt = neighSol.data.option trg = neighSol.data.dst if opt is not None : #if opt is not None, the NS is for getting the MAC @ of a given IP @ if isinstance(opt,ryu.lib.packet.icmpv6.nd_option_sla): #link layer address request #check if the solicited @ is the one of the router #fetching all the local addresses and the associated port of the current switch trgPort = None localAddressesIntfList = [ [self.bindingList[localPort],localPort[1]] for localPort in self.bindingList.keys() if localPort[0]==dpid ] addrList = [addr[0] for addr in localAddressesIntfList ] if str(trg) in addrList: print('resolving switch mac address') index = addrList.index(str(trg)) trgPort = localAddressesIntfList[index][1] #if no address has been found, switch checks if the asking address #is one of another local host since there are now several local #interfaces under which hosts have the same prefix if trgPort is None: #checking if mac @ of another local host is asked if str(trg) in self.coveredHosts[dpid].keys(): print('resolving other local host address') #returning incomming port, so that the asking node #will use the mac @ of the switch's interface it's linked to trgPort = in_port #if the request concerns the router, prepare answer: if trgPort is not None : #get hw@ hw_addr = opt.hw_src #reply with a neighbor adv neigh_adv = icmpv6.icmpv6(type_=icmpv6.ND_NEIGHBOR_ADVERT, data=icmpv6.nd_neighbor(res=7, dst=str(trg), option=icmpv6.nd_option_tla(hw_src=self.generateMAC(dpid,trgPort)))) e= ethernet.ethernet(dst=str(hw_addr),src=self.generateMAC(dpid,in_port), ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ srcIP = self.bindingList[dpid,in_port] ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=srcIP, dst=str(i.src)) #direct reply on the incomming switch port out_port = in_port pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(neigh_adv) pkt_generated.serialize() #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('..........neighbor advertisement sent..........') else: print(trg, ' address neither belongs to switch or local host') else: print('neighbor solicitation conflict resolution') #nothing is done here all the registration porcess is now done a the reception #or the router solicitation #handling ping requests and reply elif itype == 4 or itype == 5: #looking at destination address, finding out which is the next hope, changing MAC @ ping_src = i.src ping_dst = i.dst echo = pkt.get_protocols(icmpv6.icmpv6)[0]; print(echo) #when ip dst @ is known : 3 cases: #destination is behind another router #destination is behind the current router #destination is the current router #fetching all the local addresses of the current switch localAddressesList = [ self.bindingList[localPort] for localPort in self.bindingList.keys() if localPort[0]==dpid ] if ping_dst in localAddressesList : print('ping addressed to the router') #the ping is addressed to the switch: #if it's a request : reply if itype == 4: #copy request data into the reply reqData = echo.data pingReply = icmpv6.icmpv6(type_=icmpv6.ICMPV6_ECHO_REPLY, data=reqData) #direct reply on the incomming switch port out_port = in_port e= ethernet.ethernet(dst=src,src=dst, ethertype=ether.ETH_TYPE_IPV6) #here reply with global scope @ ip = ipv6(nxt=inet.IPPROTO_ICMPV6, src=str(ping_dst), dst=str(ping_src)) pkt_generated = packet.Packet() pkt_generated.add_protocol(e) pkt_generated.add_protocol(ip) pkt_generated.add_protocol(pingReply) print('.........................') print(pkt_generated) pkt_generated.serialize() #ACTION : the NA must be forwarded on the incomming switch port actions = [parser.OFPActionOutput(out_port)] out_ra = parser.OFPPacketOut(datapath=datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=0, actions=actions, data=pkt_generated.data) datapath.send_msg(out_ra) print('..........Ping Reply sent..........') else: print('ping another host or switch received by ', dpid, 'going to', ping_dst) #first step : finding out the destination switch #now only host pinging is considered (no ping to backbone intf of remote switch) extractedDomain = re.match(r"20*(?P<trgDpid>[1-9]{1,3})",ping_dst[0:4]) #getting the dpid covering the destination host extractedDpid = int(extractedDomain.group('trgDpid')) if extractedDpid is None : #if extractedDpid is None it mean that dest address begins with 2000: #this is a backbone interface ip address # #extracting the switch to which this interface belongs extractedDest = re.match(r".+:{1,2}(?P<trgDpid>[0-9]{1,4})$",ping_dst) extractedDpid = int(extractedDest.group('trgDpid')) #checking validity of the obtained dpid if extractedDpid in [s.dp.id for s in self.switchList]: print ('ping going to ', ping_dst , ' must be routed to router ', str(extractedDpid) ,' as destination domain is ', ping_dst[0:4]) #handle the case where no sub domain is found else: print ('no subdomain found deleting packet') #throw exception return 0 destDpid = extractedDpid #1st case destination covering switch is the current one if destDpid == dpid: #checking if the ping destination is linked to one of the local interfaces print('ping toward local network, resolving local interface') if ping_dst not in self.coveredHosts[destDpid].keys(): print('destination: ', ping_dst ,' host is not linked to the domain switch, deleting packet') return 0 #if destination host is linked to the switch, resolving the interface outputIntf = self.coveredHosts[destDpid][ping_dst][1] #setting new addresses MAC: new_mac_src = self.generateMAC(destDpid,outputIntf) new_mac_dst = self.coveredHosts[destDpid][ping_dst][0] print('ping local host ', ping_dst , ' through interface ', outputIntf) #2nd case destination covering switch is not the current one else:#PING GOING OUTSIDE LOCAL NETWORK #finding nexthop nextHopID = self.next_hop(dpid,destDpid) #finding output interface outputIntf = self.routing(dpid,nextHopID) new_mac_src = self.generateMAC(dpid,outputIntf) new_mac_dst = self.generateMAC(nextHopID,self.routing(nextHopID,dpid)) print ('ping toward neighbor ', outputIntf) action = [parser.OFPActionDecNwTtl(), parser.OFPActionSetField(eth_src=new_mac_src), parser.OFPActionSetField(eth_dst=new_mac_dst),parser.OFPActionOutput(outputIntf) ] match = parser.OFPMatch( eth_type=0x86dd, ip_proto=58, ipv6_dst=(ping_dst,'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')) print('ready to push flow to ',datapath) #routing related flow then pushed to table 1 self.add_flow(datapath, 1, match, action,tblId=1) print('flow pushed') else: print ('') print("========================================")
def _send_router_advertisement( self, ipv6_src: str, tun_id: int, output_port, ): """ Generates the Router Advertisement response packet """ ofproto, parser = self._datapath.ofproto, self._datapath.ofproto_parser if not tun_id: self.logger.debug( "Packet missing tunnel-id information, can't reply") return prefix = self.get_custom_prefix(ipv6_src) if not prefix: self.logger.debug("Can't reply to RS for UE ip %s", ipv6_src) return pkt = packet.Packet() pkt.add_protocol( ethernet.ethernet( dst=self.MAC_MULTICAST, src=self.config.ll_addr, ethertype=ether_types.ETH_TYPE_IPV6, ), ) pkt.add_protocol( ipv6.ipv6( dst=ipv6_src, src=self.config.ipv6_src, nxt=in_proto.IPPROTO_ICMPV6, ), ) pkt.add_protocol( icmpv6.icmpv6( type_=icmpv6.ND_ROUTER_ADVERT, data=icmpv6.nd_router_advert( ch_l=MAX_HOP_LIMIT, rou_l=MAX_ROUTE_ADVT_LIFE_TIME, options=[ icmpv6.nd_option_sla(hw_src=self.config.ll_addr, ), icmpv6.nd_option_pi( pl=self.config.prefix_len, prefix=prefix, val_l=MAX_ROUTE_LIFE_TIME, pre_l=MAX_ROUTE_LIFE_TIME, res1=int( hex(ROUTE_PREFIX_HDR_FLAG_L | ROUTE_PREFIX_HDR_FLAG_A), 16), ), ], ), ), ) self.logger.debug("RA pkt response ->") for p in pkt.protocols: self.logger.debug(p) pkt.serialize() actions_out = [ parser.NXActionSetTunnel(tun_id=tun_id), parser.OFPActionOutput(port=output_port), ] out = parser.OFPPacketOut( datapath=self._datapath, buffer_id=ofproto.OFP_NO_BUFFER, in_port=ofproto.OFPP_CONTROLLER, actions=actions_out, data=pkt.data, ) ret = self._datapath.send_msg(out) if not ret: self.logger.error("Datapath disconnected, couldn't send RA")