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('::'))
Exemple #2
0
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
Exemple #3
0
    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 [email protected]
                        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):
        # 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 [email protected] :', 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 [email protected]
                        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("========================================")