def _two_way_or_exstart(self):
        #whether become adjacent in broadcast, rfc 10.4
        if self.state == NSM_STATE['NSM_Init'] or self.state == NSM_STATE['NSM_TwoWay']:
            #need to make adjacency
            if self.ism.bdrip == self.src or self.ism.drip == self.src or self.ism.link_type == 'Point-to-Point':
                self.change_nsm_state('NSM_ExStart')

                #Generate dd_sum from each lsa list, and exception for virtual link and stub area.
                #But as a probe, we need not to send dd_sum.

                #start dd sync procedure
                self.dd_seqnum = int(time.time())
                self.dd_flags = 7    # set init bit, more bit, master bit
                self._send_dd()
                if self._dd_exstart_timer is None:
                    pass
                elif self._dd_exstart_timer.is_stop():
                    del self._dd_exstart_timer
                else:
                    self._dd_exstart_timer.stop()
                    del self._dd_exstart_timer
                self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
                self._dd_exstart_timer.start()

            #do not need to make adjacency
            else:
                if self.state == NSM_STATE['NSM_Init']:
                    #change to 2-way state
                    self.change_nsm_state('NSM_TwoWay')
                else:
                    #stay in 2-way state
                    pass
Esempio n. 2
0
 def _waiting(self):
     self.change_ism_state('ISM_Waiting')
     self._begin_hello()
     self._hello_timer = Timer(self.hello_interval, self._begin_hello)
     self._hello_timer.start()
     self._elect_timer = Timer(self.dead_interval, self._elect_dr, once=True)
     self._elect_timer.start()
Esempio n. 3
0
    def send_dd(self, pkt):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        self._sock.conn(self.dst)

        LOG.info('[Exchange] Send DD to %s.' % self.dst)
        self._sock.sendp(pkt)

        self.nsm.ism.ai.oi.stat.send_dd_count += 1
        self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

        if self.nsm.state == NSM_STATE[
                'NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:
            self.nsm.last_send = pkt  # save the last sent packet

            #start a timer to wait dead interval to release the last recv packet
            if self._rls_last_dd_timer is None or self._rls_last_dd_timer.is_stop(
            ):
                self._rls_last_dd_timer = Timer(self.nsm.ism.dead_interval,
                                                self._rls_last_dd,
                                                once=True)
                self._rls_last_dd_timer.start()
            else:
                self._rls_last_dd_timer.reset()
Esempio n. 4
0
 def _waiting(self):
     self.change_ism_state('ISM_Waiting')
     self._begin_hello()
     self._hello_timer = Timer(self.hello_interval, self._begin_hello)
     self._hello_timer.start()
     self._elect_timer = Timer(self.dead_interval,
                               self._elect_dr,
                               once=True)
     self._elect_timer.start()
 def _loading(self):
     self.change_nsm_state('NSM_Loading')
     #start to send first lsr
     if len(self.ls_req) != 0:
         self._send_lsr(self.ls_req)
         self.lsr_resend_timer = Timer(self.ism.rxmt_interval, self._send_lsr, self.ls_req)
         self.lsr_resend_timer.start()
     else:
         self._full()
 def _hello_received(self):
     if self.state == NSM_STATE['NSM_Down']:
         self.change_nsm_state('NSM_Init')
         #start inactive timer
         if self._inactive_timer is None or self._inactive_timer.is_stop():
             self._inactive_timer = Timer(self.inactive_timer_interval, self.dead)
             self._inactive_timer.start()
             LOG.debug('[NSM] %s starts inactive timer.' % util.int2ip(self.rtid))
         else:
             self._inactive_timer.reset()
     else:
         self._inactive_timer.reset()
Esempio n. 7
0
 def _dr_other(self):
     """
     send hello in broadcast or nbma, not be dr
     """
     self.change_ism_state('ISM_DROther')
     if not self._elect_timer.is_stop():
         self._elect_timer.stop()
     #start a timer to check all lsa age per lsaAgeStep second
     #TODO: this implementation should be checked.
     if self._lsa_age_timer is None:
         self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
         self._lsa_age_timer.start()
         LOG.debug('[ISM] Start LSA age timer.')
Esempio n. 8
0
 def _point_to_point(self):
     """
     send hello in p2p or virtual link
     """
     self.change_ism_state('ISM_PointToPoint')
     self._begin_hello()
     self._hello_timer = Timer(self.hello_interval, self._begin_hello)
     self._hello_timer.start()
     #start a timer to check all lsa age per lsaAgeStep second
     #TODO: this implementation should be checked.
     if self._lsa_age_timer is None:
         self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
         self._lsa_age_timer.start()
         LOG.debug('[ISM] Start LSA age timer.')
Esempio n. 9
0
 def _point_to_point(self):
     """
     send hello in p2p or virtual link
     """
     self.change_ism_state('ISM_PointToPoint')
     self._begin_hello()
     self._hello_timer = Timer(self.hello_interval, self._begin_hello)
     self._hello_timer.start()
     #start a timer to check all lsa age per lsaAgeStep second
     #TODO: this implementation should be checked.
     if self._lsa_age_timer is None:
         self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
         self._lsa_age_timer.start()
         LOG.debug('[ISM] Start LSA age timer.')
Esempio n. 10
0
    def _two_way_or_exstart(self):
        # whether become adjacent in broadcast, rfc 10.4
        if self.state == NSM_STATE["NSM_Init"] or self.state == NSM_STATE["NSM_TwoWay"]:
            # need to make adjacency
            if self.ism.bdrip == self.src or self.ism.drip == self.src or self.ism.link_type == "Point-to-Point":
                self.change_nsm_state("NSM_ExStart")

                # Generate dd_sum from each lsa list, and exception for virtual link and stub area.
                # But as a probe, we need not to send dd_sum.

                # start dd sync procedure
                self.dd_seqnum = int(time.time())
                self.dd_flags = 7  # set init bit, more bit, master bit
                self._send_dd()
                if self._dd_exstart_timer is None:
                    pass
                elif self._dd_exstart_timer.is_stop():
                    del self._dd_exstart_timer
                else:
                    self._dd_exstart_timer.stop()
                    del self._dd_exstart_timer
                self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
                self._dd_exstart_timer.start()

            # do not need to make adjacency
            else:
                if self.state == NSM_STATE["NSM_Init"]:
                    # change to 2-way state
                    self.change_nsm_state("NSM_TwoWay")
                else:
                    # stay in 2-way state
                    pass
Esempio n. 11
0
 def _loading(self):
     self.change_nsm_state("NSM_Loading")
     # start to send first lsr
     if len(self.ls_req) != 0:
         self._send_lsr(self.ls_req)
         self.lsr_resend_timer = Timer(self.ism.rxmt_interval, self._send_lsr, self.ls_req)
         self.lsr_resend_timer.start()
     else:
         self._full()
Esempio n. 12
0
 def _hello_received(self):
     if self.state == NSM_STATE["NSM_Down"]:
         self.change_nsm_state("NSM_Init")
         # start inactive timer
         if self._inactive_timer is None or self._inactive_timer.is_stop():
             self._inactive_timer = Timer(self.inactive_timer_interval, self.dead)
             self._inactive_timer.start()
             LOG.debug("[NSM] %s starts inactive timer." % util.int2ip(self.rtid))
         else:
             self._inactive_timer.reset()
     else:
         self._inactive_timer.reset()
Esempio n. 13
0
 def _dr_other(self):
     """
     send hello in broadcast or nbma, not be dr
     """
     self.change_ism_state('ISM_DROther')
     if not self._elect_timer.is_stop():
         self._elect_timer.stop()
     #start a timer to check all lsa age per lsaAgeStep second
     #TODO: this implementation should be checked.
     if self._lsa_age_timer is None:
         self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
         self._lsa_age_timer.start()
         LOG.debug('[ISM] Start LSA age timer.')
    def _seq_mismatch_or_bad_lsr(self):
        LOG.warn('[NSM] %s sequence mismatch or bad LSR.' % util.int2ip(self.rtid))
        #make sure that all these are clear
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        self.change_nsm_state('NSM_ExStart')
        self.dd_flags = 7    # set init bit, more bit, master bit
        self.dd_seqnum = int(time.time())
        self._send_dd()
        if self._dd_exstart_timer is None:
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        elif self._dd_exstart_timer.is_stop():
            del self._dd_exstart_timer
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        else:
            self._dd_exstart_timer.reset()
Esempio n. 15
0
    def _seq_mismatch_or_bad_lsr(self):
        LOG.warn("[NSM] %s sequence mismatch or bad LSR." % util.int2ip(self.rtid))
        # make sure that all these are clear
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        self.change_nsm_state("NSM_ExStart")
        self.dd_flags = 7  # set init bit, more bit, master bit
        self.dd_seqnum = int(time.time())
        self._send_dd()
        if self._dd_exstart_timer is None:
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        elif self._dd_exstart_timer.is_stop():
            del self._dd_exstart_timer
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        else:
            self._dd_exstart_timer.reset()
Esempio n. 16
0
    def send_dd(self, pkt):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        self._sock.conn(self.dst)

        LOG.info('[Exchange] Send DD to %s.' % self.dst)
        self._sock.sendp(pkt)

        self.nsm.ism.ai.oi.stat.send_dd_count += 1
        self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

        if self.nsm.state == NSM_STATE['NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:
            self.nsm.last_send = pkt    # save the last sent packet

            #start a timer to wait dead interval to release the last recv packet
            if self._rls_last_dd_timer is None or self._rls_last_dd_timer.is_stop():
                self._rls_last_dd_timer = Timer(self.nsm.ism.dead_interval, self._rls_last_dd, once=True)
                self._rls_last_dd_timer.start()
            else:
                self._rls_last_dd_timer.reset()
Esempio n. 17
0
class ExchangeProtocol(OspfProtocol):

    def __init__(self, nsm):

        OspfProtocol.__init__(self)

        self.init = 0
        self.more = 0
        self.ms = 0
        self._rls_last_dd_timer = None

        self.nsm = nsm
        self.dst = ALL_SPF_ROUTER   # by default

        if self.nsm.ism.link_type == 'Broadcast':
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        elif self.nsm.ism.link_type == 'Point-to-Point':
            self.dst = util.int2ip(self.nsm.src)
        else:
        #TODO: if link type is virtual or others, set dst specific.
            pass

        #ospf socket
        self._sock = OspfSock()
        self._sock.bind(self.nsm.ism.ip_intf_addr)

    def __del__(self):
        self._sock.close()

    def set_dd_options(self):
        dolist = list(util.int2bin(self.nsm.dd_flags))
        self.init, self.more, self.ms = dolist[-3], dolist[-2], dolist[-1]

    def gen_dd(self, lsa=None):
        #In dd packet, the mtu is 1500 by default, but in virtual link, mtu is must set to 0
        #TODO: set virtual link mtu
        dd = DBDesc(
            mtu=self.nsm.ism.mtu,
            ddoptions=self.nsm.dd_flags,
            options=self.options,
            ddseq=self.nsm.dd_seqnum,
        )

        #TODO: unimplemented to send probe's dd
        if not lsa is None:
            pass

        ospf_packet = OSPF(
            v=self.version,
            type=2,             # 2 for dd
            area=self.area,
            len=len(dd)+len(OSPF()),
            router=self.rid,
            data=dd
        )

        return str(ospf_packet)

    def check_dd(self, pkt):
        """
        check received dd packet,
        return True means the packet is handled correctly,
        return False means the packet is not in right procedure
        """

        seq = pkt['V']['V']['DDSEQ']
        mss, i, m = pkt['V']['V']['MS'], pkt['V']['V']['INIT'], pkt['V']['V']['MORE']
        r, mtu = pkt['V']['RID'], pkt['V']['V']['MTU']
        opt = pkt['V']['V']['OPTS']

        last_ospf_opt = self.nsm.options  # save current ospf options
        self.nsm.options = opt   # update nsm ospf options

        tmp_last_recv = None      # save last recv packet
        if self.nsm.last_recv != (seq, i, m, mss):
            tmp_last_recv = self.nsm.last_recv
            self.nsm.last_recv = (seq, i, m, mss)

        #check mtu, if neighbor's mtu greater than our interface, drop it
        if self.nsm.ism.mtu < mtu:
            LOG.warn('[Exchange] Deny for bigger MTU.')
            return False

        if self.nsm.state == NSM_STATE['NSM_Down'] or self.nsm.state == NSM_STATE['NSM_Attempt']:
            LOG.warn('[Exchange] Deny for inappropriate state.')
            return False
        elif self.nsm.state == NSM_STATE['NSM_Init']:
            self.nsm.fire('NSM_TwoWayReceived')
            return False
        elif self.nsm.state == NSM_STATE['NSM_TwoWay']:
            LOG.warn('[Exchange] Ignore for inappropriate state.')
            return False

        elif self.nsm.state == NSM_STATE['NSM_ExStart']:
            #Negotiate DD exchange
            if mss == 1 and m == 1 and i == 1 and r > self.rid:
                self.init = 0
                self.more = 1
                self.ms = 0     # set myself slave, use master dd seq number

                self.nsm.dd_seqnum = pkt['V']['V']['DDSEQ']
                self.nsm.dd_flags = 4 * self.init + 2 * self.more + self.ms
                LOG.info('[NSM] Event: NSM_NegotiationDone')
                LOG.info('[NSM] We are slave.')
                self.nsm.fire('NSM_NegotiationDone')
                return True
            elif mss == 0 and i == 0 and r < self.rid:
                self.init = 0
                self.more = 1
                self.ms = 1     # set myself master, use my dd seq number
                self.nsm.dd_seqnum += 1

                self.nsm.dd_flags = 4 * self.init + 2 * self.more + self.ms
                LOG.info('[NSM] Event: NSM_NegotiationDone')
                LOG.info('[NSM] We are master.')
                self.nsm.fire('NSM_NegotiationDone')
                if m == 0:
                    self.nsm.fire('NSM_ExchangeDone')
                return True
            else:
                LOG.warn('[Exchange] Ignore for inappropriate dd options.')
                return False

        if self.nsm.state == NSM_STATE['NSM_Exchange']:

            if (seq, i, m, mss) == tmp_last_recv:
                if self.ms == 1:
                    LOG.warn('[Exchange] Duplicate DD packet, drop as master.')
                    return False
                else:
                    #retransmit the last send packet
                    LOG.warn('[Exchange] Duplicate DD packet, retransmit as slave.')
                    self.send_dd(self.nsm.last_send)
                    return False
            #check whether master/slave bit match or init bit is set unexpected
            if mss == self.ms or i == 1:
                LOG.warn('[Exchange] DD packet wrong options.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            #check whether ospf option is as same as the last received ospf packet
            if not last_ospf_opt is None:
                if last_ospf_opt != pkt['V']['V']['OPTS']:
                    LOG.warn('[Exchange] DD packet OSPF options are not same as the last received packet.')
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False

            #new recv dd packet is not last recv dd packet, get its lsa header
            if not self._get_lsa(pkt):
                #if there's some thing wrong in lsa header, fire SeqNumMismatch
                LOG.error('[Exchange] DD packet has wrong LSA header.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            #when more bit is 0, exchange stop, goto loading state
            if m == 0:
                if self.ms == 0:    # If we are slave, send dd as reply.
                    self.nsm.dd_seqnum = seq
                    #all pass checked, send my dd to neighbor
                    self.exchange()
                self.nsm.fire('NSM_ExchangeDone')
            else:
                #if probe is master, ddseq + 1; if slave, set ddseq to master ddseq
                if self.ms == 1:
                    self.nsm.dd_seqnum += 1
                else:
                    self.nsm.dd_seqnum = seq
                #all pass checked, send my dd to neighbor
                self.exchange()

            return True

        elif self.nsm.state == NSM_STATE['NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:

            #check whether ospf option is as same as the last received ospf packet
            if not last_ospf_opt is None:
                if last_ospf_opt != pkt['V']['V']['OPTS']:
                    LOG.warn('[Exchange] DD packet OSPF options are not same as the last received packet.')
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False
            #Unexpected init bit
            if i == 1:
                LOG.error('[Exchange] Unexpected init bit in DD packet.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            if self.ms == 1:
                LOG.error('[Exchange] Duplicate DD packet, drop as master.')
                return False
            else:
                #retransmit the last send packet
                if self.nsm.last_send is None:
                    LOG.error('[Exchange] Cannot retransmit last DD packet.')
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False
                LOG.warn('[Exchange] Duplicate DD packet, retransmit as slave.')
                self.send_dd(self.nsm.last_send)
                return True
        else:
            pass

    def send_dd(self, pkt):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        self._sock.conn(self.dst)

        LOG.info('[Exchange] Send DD to %s.' % self.dst)
        self._sock.sendp(pkt)

        self.nsm.ism.ai.oi.stat.send_dd_count += 1
        self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

        if self.nsm.state == NSM_STATE['NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:
            self.nsm.last_send = pkt    # save the last sent packet

            #start a timer to wait dead interval to release the last recv packet
            if self._rls_last_dd_timer is None or self._rls_last_dd_timer.is_stop():
                self._rls_last_dd_timer = Timer(self.nsm.ism.dead_interval, self._rls_last_dd, once=True)
                self._rls_last_dd_timer.start()
            else:
                self._rls_last_dd_timer.reset()

    def _rls_last_dd(self):
        LOG.debug('[Exchange] Release last send DD packet.')
        self.nsm.last_send = None

    def _get_lsa(self, pkt):
        aid = pkt['V']['AID']
        lsa_hdr_list = pkt['V']['V']['LSAS']
        for lsah in lsa_hdr_list.keys():
            tp, lsid, adv, seq = lsa_hdr_list[lsah]['T'],\
                                 lsa_hdr_list[lsah]['LSID'],\
                                 lsa_hdr_list[lsah]['ADVRTR'],\
                                 lsa_hdr_list[lsah]['LSSEQNO'],\

            #generate lsa key according to lsa type.
            if tp == 5:
                #check if a type-5 lsa into a stub area, return false
                if self.nsm.ism.options['E'] == 0:
                    return False
                lsakey = (tp, lsid, adv)
            else:
                lsakey = (tp, aid, lsid, adv)

            lsalist = self.nsm.ism.ai.oi.lsdb.lookup_lsa_list(tp)
            if not lsalist is None:
                lsa = self.lookup_lsa(lsakey, lsalist)
                if lsa is None:
                    self.nsm.ls_req.append(lsakey)
                elif lsa['H']['LSSEQNO'] < seq:
                    #the lsa in dd is newer than lsa in the database
                    self.nsm.ls_req.append(lsakey)
                else:
                    continue
            #if did not find the lsa list
            else:
                LOG.error('[Exchange] Wrong LSA type in DD.')
                return False
        return True

    def exchange(self):
        #TODO: The probe always set more bit to 0 when exchange. Need to modify if we want to send probe's DD summary.
        lsa = self.nsm.db_sum
        tosend = lsa
        self.more = 0   # set more bit 0
        self.nsm.dd_flags = 4 * int(self.init) + 2 * int(self.more) + int(self.ms)
        LOG.debug('[Exchange] DD flag is %s.' % self.nsm.dd_flags)

        self.send_dd(self.gen_dd(tosend))

    def gen_lsr(self, rq):
        pkts = []
        maxlsa = 100
        more = True

        #In each LSR packet, it contains 100 lsa headers at max
        while more:
            if len(rq) - maxlsa > 0:
                lsas, rq = rq[:maxlsa], rq[maxlsa:]
            else:
                lsas = rq
                more = False

            lsrdata = []
            lsrlen = len(lsas) * len(LSR())

            for r in lsas:
                if r[0] == 5:
                    lsrdata.append(str(LSR(
                        lstype=r[0],
                        lsid=r[1],
                        adv=r[2]
                    )))
                else:
                    lsrdata.append(str(LSR(
                        lstype=r[0],
                        lsid=r[2],
                        adv=r[3]
                    )))

            ospf_packet = OSPF(
                v=self.version,
                type=3,             # 3 for lsr
                area=self.area,
                len=lsrlen + len(OSPF()),
                router=self.rid,
                data=''.join(lsrdata)
            )

            pkts.append(str(ospf_packet))
        return pkts

    def send_lsr(self, pkts):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        LOG.debug('[Exchange] Send LSR to %s.' % self.dst)
        self._sock.conn(self.dst)
        for p in pkts:
            self._sock.sendp(p)

            self.nsm.ism.ai.oi.stat.send_lsr_count += 1
            self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

    def check_lsr(self, pkt):
        """
        The probe doesn't need to receive LSR
        """
        pass
class NSM(object):
    """
    Neighbor State Machine
    :param ism: ISM objects.
    :param rtid: RouterID.
    :param pkt: coming hello packet.
    """

    def __init__(self, ism, rtid, pkt):
        #OSPF neighbor information
        self.state = NSM_STATE['NSM_Down']           # NSM status.
        self._inactive_timer = None
        self._dd_exstart_timer = None
        self.lsr_resend_timer = None

        self.dd_flags = 0        # DD bit flags. Slave or master.
        self.dd_seqnum = 0       # DD Sequence Number.

        #Last sent Database Description packet.
        self.last_send = None   # just the packet, not tuple
        #Timestemp when last Database Description packet was sent
        self.last_send_ts = 0   # not used
        #Last received Database Description packet.
        self.last_recv = tuple()     # pattern: (ddseq, init, more, ms)

        #LSA data.
        self.ls_rxmt = list()       # Link state retransmission list
        self.db_sum = list()        # Database summary list
        self.ls_req = list()        # Link state request list

        self.ism = ism
        self.rtid = rtid        # neighbor router id

        self.nsm = dict()

        #Neighbor Information from Hello.
        self.src = pkt['H']['SRC']
        self.options = pkt['V']['V']['OPTS']
        self.priority = pkt['V']['V']['PRIO']
        self.d_router = pkt['V']['V']['DESIG']
        self.bd_router = pkt['V']['V']['BDESIG']

        #inactive timer is equal to dead interval
        self.inactive_timer_interval = self.ism.dead_interval

        self.ep = ExchangeProtocol(self)
        self.fp = FloodProtocol(self)

        #register all nsm events
        for nsmEvent in NSM_EVENT.keys():
            if nsmEvent == 'NSM_PacketReceived':
                self.nsm[nsmEvent] = self._hello_received
            elif nsmEvent == 'NSM_TwoWayReceived':
                self.nsm[nsmEvent] = self._two_way_or_exstart
            elif nsmEvent == 'NSM_OneWayReceived':
                self.nsm[nsmEvent] = self._init
            elif nsmEvent == 'NSM_NegotiationDone':
                self.nsm[nsmEvent] = self._exchange
            elif nsmEvent == 'NSM_SeqNumberMismatch':
                self.nsm[nsmEvent] = self._seq_mismatch_or_bad_lsr
            elif nsmEvent == 'NSM_ExchangeDone':
                self.nsm[nsmEvent] = self._loading
            elif nsmEvent == 'NSM_BadLSReq':
                self.nsm[nsmEvent] = self._seq_mismatch_or_bad_lsr
            elif nsmEvent == 'NSM_LoadingDone':
                self.nsm[nsmEvent] = self._full
            else:
                continue

    def fire(self, event):
        self.nsm[event]()

    def reset(self):
        self.change_nsm_state('NSM_Down')
        if self._dd_exstart_timer is not None:
            self._dd_exstart_timer.stop()
            del self._dd_exstart_timer
        if self.lsr_resend_timer is not None:
            self.lsr_resend_timer.stop()
            del self.lsr_resend_timer
        self._dd_exstart_timer = None
        self.lsr_resend_timer = None

        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        #Delete all LSA in LSDB
        self.ism.ai.oi.lsdb.empty_lsdb()

    def dead(self):
        neighborLock.acquire()
        self.reset()
        self.rtid = 0
        self.src = 0
        self.options = {'E': 0, 'MC': 0, 'L': 0, 'NP': 0, 'DC': 0, 'O': 0, 'DN': 0, 'Q': 0}
        self.priority = 0
        self.d_router = 0
        self.bd_router = 0
        if self._inactive_timer is not None:
            self._inactive_timer.stop()
            del self._inactive_timer
            self._inactive_timer = None
        LOG.info('[ISM] Event: ISM_NeighborChange')
        self.ism.fire('ISM_NeighborChange')
        neighborLock.release()

    def _hello_received(self):
        if self.state == NSM_STATE['NSM_Down']:
            self.change_nsm_state('NSM_Init')
            #start inactive timer
            if self._inactive_timer is None or self._inactive_timer.is_stop():
                self._inactive_timer = Timer(self.inactive_timer_interval, self.dead)
                self._inactive_timer.start()
                LOG.debug('[NSM] %s starts inactive timer.' % util.int2ip(self.rtid))
            else:
                self._inactive_timer.reset()
        else:
            self._inactive_timer.reset()

    def _attempt(self):
        """
        Only for nbma network
        """
        self.change_nsm_state('NSM_Attempt')

    def _init(self):
        if self.state == NSM_STATE['NSM_Init']:
            return
        self.change_nsm_state('NSM_Init')
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        if not self.lsr_resend_timer is None:
            self.lsr_resend_timer.stop()

    def _two_way_or_exstart(self):
        #whether become adjacent in broadcast, rfc 10.4
        if self.state == NSM_STATE['NSM_Init'] or self.state == NSM_STATE['NSM_TwoWay']:
            #need to make adjacency
            if self.ism.bdrip == self.src or self.ism.drip == self.src or self.ism.link_type == 'Point-to-Point':
                self.change_nsm_state('NSM_ExStart')

                #Generate dd_sum from each lsa list, and exception for virtual link and stub area.
                #But as a probe, we need not to send dd_sum.

                #start dd sync procedure
                self.dd_seqnum = int(time.time())
                self.dd_flags = 7    # set init bit, more bit, master bit
                self._send_dd()
                if self._dd_exstart_timer is None:
                    pass
                elif self._dd_exstart_timer.is_stop():
                    del self._dd_exstart_timer
                else:
                    self._dd_exstart_timer.stop()
                    del self._dd_exstart_timer
                self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
                self._dd_exstart_timer.start()

            #do not need to make adjacency
            else:
                if self.state == NSM_STATE['NSM_Init']:
                    #change to 2-way state
                    self.change_nsm_state('NSM_TwoWay')
                else:
                    #stay in 2-way state
                    pass

    def _exchange(self):
        self.change_nsm_state('NSM_Exchange')
        self._dd_exstart_timer.stop()
        self.fp.set_ospf_header(
            self.ism.version,
            self.ism.area_id,
            self.ism.rid,
            self.ism.options,
        )
        self.ep.exchange()

    def _loading(self):
        self.change_nsm_state('NSM_Loading')
        #start to send first lsr
        if len(self.ls_req) != 0:
            self._send_lsr(self.ls_req)
            self.lsr_resend_timer = Timer(self.ism.rxmt_interval, self._send_lsr, self.ls_req)
            self.lsr_resend_timer.start()
        else:
            self._full()

    def _full(self):
        self.change_nsm_state('NSM_Full')

    def _seq_mismatch_or_bad_lsr(self):
        LOG.warn('[NSM] %s sequence mismatch or bad LSR.' % util.int2ip(self.rtid))
        #make sure that all these are clear
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        self.change_nsm_state('NSM_ExStart')
        self.dd_flags = 7    # set init bit, more bit, master bit
        self.dd_seqnum = int(time.time())
        self._send_dd()
        if self._dd_exstart_timer is None:
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        elif self._dd_exstart_timer.is_stop():
            del self._dd_exstart_timer
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        else:
            self._dd_exstart_timer.reset()

    def _send_dd(self, lsa=None):

        self.ep.set_ospf_header(
            self.ism.version,
            self.ism.area_id,
            self.ism.rid,
            self.ism.options,
        )
        self.ep.set_dd_options()
        self.ep.send_dd(self.ep.gen_dd(lsa))

    def _send_lsr(self, lsr):
        self.ep.send_lsr(self.ep.gen_lsr(lsr))

    def change_nsm_state(self, newstate):
        LOG.info('[NSM] %s change state to %s.' % (util.int2ip(self.rtid), newstate))
        self.state = NSM_STATE[newstate]
Esempio n. 19
0
class NSM(object):
    """
    Neighbor State Machine
    :param ism: ISM objects.
    :param rtid: RouterID.
    :param pkt: coming hello packet.
    """

    def __init__(self, ism, rtid, pkt):
        # OSPF neighbor information
        self.state = NSM_STATE["NSM_Down"]  # NSM status.
        self._inactive_timer = None
        self._dd_exstart_timer = None
        self.lsr_resend_timer = None

        self.dd_flags = 0  # DD bit flags. Slave or master.
        self.dd_seqnum = 0  # DD Sequence Number.

        # Last sent Database Description packet.
        self.last_send = None  # just the packet, not tuple
        # Timestemp when last Database Description packet was sent
        self.last_send_ts = 0  # not used
        # Last received Database Description packet.
        self.last_recv = tuple()  # pattern: (ddseq, init, more, ms)

        # LSA data.
        self.ls_rxmt = list()  # Link state retransmission list
        self.db_sum = list()  # Database summary list
        self.ls_req = list()  # Link state request list

        self.ism = ism
        self.rtid = rtid  # neighbor router id

        self.nsm = dict()

        # Neighbor Information from Hello.
        self.src = pkt["H"]["SRC"]
        self.options = pkt["V"]["V"]["OPTS"]
        self.priority = pkt["V"]["V"]["PRIO"]
        self.d_router = pkt["V"]["V"]["DESIG"]
        self.bd_router = pkt["V"]["V"]["BDESIG"]

        # inactive timer is equal to dead interval
        self.inactive_timer_interval = self.ism.dead_interval

        self.ep = ExchangeProtocol(self)
        self.fp = FloodProtocol(self)

        # register all nsm events
        for nsmEvent in NSM_EVENT.keys():
            if nsmEvent == "NSM_PacketReceived":
                self.nsm[nsmEvent] = self._hello_received
            elif nsmEvent == "NSM_TwoWayReceived":
                self.nsm[nsmEvent] = self._two_way_or_exstart
            elif nsmEvent == "NSM_OneWayReceived":
                self.nsm[nsmEvent] = self._init
            elif nsmEvent == "NSM_NegotiationDone":
                self.nsm[nsmEvent] = self._exchange
            elif nsmEvent == "NSM_SeqNumberMismatch":
                self.nsm[nsmEvent] = self._seq_mismatch_or_bad_lsr
            elif nsmEvent == "NSM_ExchangeDone":
                self.nsm[nsmEvent] = self._loading
            elif nsmEvent == "NSM_BadLSReq":
                self.nsm[nsmEvent] = self._seq_mismatch_or_bad_lsr
            elif nsmEvent == "NSM_LoadingDone":
                self.nsm[nsmEvent] = self._full
            else:
                continue

    def fire(self, event):
        self.nsm[event]()

    def reset(self):
        self.change_nsm_state("NSM_Down")
        if self._dd_exstart_timer is not None:
            self._dd_exstart_timer.stop()
            del self._dd_exstart_timer
        if self.lsr_resend_timer is not None:
            self.lsr_resend_timer.stop()
            del self.lsr_resend_timer
        self._dd_exstart_timer = None
        self.lsr_resend_timer = None

        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        # Delete all LSA in LSDB
        self.ism.ai.oi.lsdb.empty_lsdb()

    def dead(self):
        neighborLock.acquire()
        self.reset()
        self.rtid = 0
        self.src = 0
        self.options = {"E": 0, "MC": 0, "L": 0, "NP": 0, "DC": 0, "O": 0, "DN": 0, "Q": 0}
        self.priority = 0
        self.d_router = 0
        self.bd_router = 0
        if self._inactive_timer is not None:
            self._inactive_timer.stop()
            del self._inactive_timer
            self._inactive_timer = None
        LOG.info("[ISM] Event: ISM_NeighborChange")
        self.ism.fire("ISM_NeighborChange")
        neighborLock.release()

    def _hello_received(self):
        if self.state == NSM_STATE["NSM_Down"]:
            self.change_nsm_state("NSM_Init")
            # start inactive timer
            if self._inactive_timer is None or self._inactive_timer.is_stop():
                self._inactive_timer = Timer(self.inactive_timer_interval, self.dead)
                self._inactive_timer.start()
                LOG.debug("[NSM] %s starts inactive timer." % util.int2ip(self.rtid))
            else:
                self._inactive_timer.reset()
        else:
            self._inactive_timer.reset()

    def _attempt(self):
        """
        Only for nbma network
        """
        self.change_nsm_state("NSM_Attempt")

    def _init(self):
        if self.state == NSM_STATE["NSM_Init"]:
            return
        self.change_nsm_state("NSM_Init")
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        if not self.lsr_resend_timer is None:
            self.lsr_resend_timer.stop()

    def _two_way_or_exstart(self):
        # whether become adjacent in broadcast, rfc 10.4
        if self.state == NSM_STATE["NSM_Init"] or self.state == NSM_STATE["NSM_TwoWay"]:
            # need to make adjacency
            if self.ism.bdrip == self.src or self.ism.drip == self.src or self.ism.link_type == "Point-to-Point":
                self.change_nsm_state("NSM_ExStart")

                # Generate dd_sum from each lsa list, and exception for virtual link and stub area.
                # But as a probe, we need not to send dd_sum.

                # start dd sync procedure
                self.dd_seqnum = int(time.time())
                self.dd_flags = 7  # set init bit, more bit, master bit
                self._send_dd()
                if self._dd_exstart_timer is None:
                    pass
                elif self._dd_exstart_timer.is_stop():
                    del self._dd_exstart_timer
                else:
                    self._dd_exstart_timer.stop()
                    del self._dd_exstart_timer
                self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
                self._dd_exstart_timer.start()

            # do not need to make adjacency
            else:
                if self.state == NSM_STATE["NSM_Init"]:
                    # change to 2-way state
                    self.change_nsm_state("NSM_TwoWay")
                else:
                    # stay in 2-way state
                    pass

    def _exchange(self):
        self.change_nsm_state("NSM_Exchange")
        self._dd_exstart_timer.stop()
        self.fp.set_ospf_header(self.ism.version, self.ism.area_id, self.ism.rid, self.ism.options)
        self.ep.exchange()

    def _loading(self):
        self.change_nsm_state("NSM_Loading")
        # start to send first lsr
        if len(self.ls_req) != 0:
            self._send_lsr(self.ls_req)
            self.lsr_resend_timer = Timer(self.ism.rxmt_interval, self._send_lsr, self.ls_req)
            self.lsr_resend_timer.start()
        else:
            self._full()

    def _full(self):
        self.change_nsm_state("NSM_Full")

    def _seq_mismatch_or_bad_lsr(self):
        LOG.warn("[NSM] %s sequence mismatch or bad LSR." % util.int2ip(self.rtid))
        # make sure that all these are clear
        self.ls_req = list()
        self.ls_rxmt = list()
        self.db_sum = list()
        self.last_recv = tuple()
        self.last_send = None
        self.last_send_ts = 0

        self.change_nsm_state("NSM_ExStart")
        self.dd_flags = 7  # set init bit, more bit, master bit
        self.dd_seqnum = int(time.time())
        self._send_dd()
        if self._dd_exstart_timer is None:
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        elif self._dd_exstart_timer.is_stop():
            del self._dd_exstart_timer
            self._dd_exstart_timer = Timer(self.ism.rxmt_interval, self._send_dd)
            self._dd_exstart_timer.start()
        else:
            self._dd_exstart_timer.reset()

    def _send_dd(self, lsa=None):

        self.ep.set_ospf_header(self.ism.version, self.ism.area_id, self.ism.rid, self.ism.options)
        self.ep.set_dd_options()
        self.ep.send_dd(self.ep.gen_dd(lsa))

    def _send_lsr(self, lsr):
        self.ep.send_lsr(self.ep.gen_lsr(lsr))

    def change_nsm_state(self, newstate):
        LOG.info("[NSM] %s change state to %s." % (util.int2ip(self.rtid), newstate))
        self.state = NSM_STATE[newstate]
Esempio n. 20
0
class ISM(object):

    def __init__(self, ai):
        self.state = ISM_STATE['ISM_Down']

        self.inf_trans_delay = 1  # TODO: how to set?
        self.prior = 0          # set probe priority 0 permanently
        self.drip = 0
        self.bdrip = 0
        self.neighbor = list()      # save all neighbors rid
        self.nbr_list = dict()       # save all neighbors' state(nsm), format: {nrid: nsm}
        self.output_cost = 0     # TODO: how to set?

        self.au_type = 0         # TODO: Auth not implement
        self.au_key = None       # TODO: Auth not implement

        self._hello_timer = None
        self._elect_timer = None
        self._lsa_age_timer = None

        self.lsa_age_step = 1     # check LSA age interval

        self.ai = ai

        self.version = 2
        self.rid = ai.oi.rid
        self.area_id = ai.area_id
        self.hello_interval = ai.oi.config['hello_interval']
        self.dead_interval = 4 * self.hello_interval
        self.ip_intf_addr = ai.oi.config['ip']
        self.ip_intf_mask = ai.oi.config['mask']
        self.link_type = ai.oi.config['link_type']
        self.options = ai.oi.config['options']
        self.rxmt_interval = ai.oi.config['rxmt_interval']
        self.mtu = ai.oi.config['mtu']

        # self.multiAreaCap = False
        # #rfc5185 multi-area adj support
        # if ai.oi.config.has_key('multiArea'):
        #     self.multiAreaCap = True
        #     self.multiArea = ai.oi.config['multiArea']

        self.hp = HelloProtocol(self)
        self.ism = dict()

        #register all ism events
        for ismevent in ISM_EVENT.keys():
            if ismevent == 'ISM_InterfaceUp':
                self.ism[ismevent] = self._interface_up
            elif ismevent == 'ISM_InterfaceDown':
                self.ism[ismevent] = self._down
            elif ismevent == 'ISM_BackupSeen':
                self.ism[ismevent] = self._dr_other
            elif ismevent == 'ISM_WaitTimer':
                self.ism[ismevent] = self._dr_other
            elif ismevent == 'ISM_NeighborChange':
                self.ism[ismevent] = self._nbr_change
            else:
                continue

        self.nbrDownFlag = False    # Whether neighbor down happened

    def fire(self, event):
        self.ism[event]()

    def _down(self):
        """
        To interface down state
        """
        if not self._hello_timer is None:
            self._hello_timer.stop()
            self._hello_timer = None
        if not self._lsa_age_timer is None:
            self._lsa_age_timer.stop()
            self._lsa_age_timer = None
        if not self._elect_timer is None:
            self._elect_timer.stop()
            self._elect_timer = None
        self.change_ism_state('ISM_Down')
        self.drip = 0
        self.bdrip = 0
        self.neighbor = list()
        self.nbr_list = dict()

    def _interface_up(self):
        """
        Handler for interface up event
        """
        #point to point link, go to point_to_point state directly
        if self.link_type == 'Point-to-Point':
            self._point_to_point()
        #broadcast link, go to dr other state
        elif self.link_type == 'Broadcast':
            self._waiting()
        else:
            LOG.error('[ISM] Wrong Link Type.')
            return

    def _waiting(self):
        self.change_ism_state('ISM_Waiting')
        self._begin_hello()
        self._hello_timer = Timer(self.hello_interval, self._begin_hello)
        self._hello_timer.start()
        self._elect_timer = Timer(self.dead_interval, self._elect_dr, once=True)
        self._elect_timer.start()

    def loopback(self):
        """
        not used in probe
        """
        self.change_ism_state('ISM_Loopback')
        pass

    def _point_to_point(self):
        """
        send hello in p2p or virtual link
        """
        self.change_ism_state('ISM_PointToPoint')
        self._begin_hello()
        self._hello_timer = Timer(self.hello_interval, self._begin_hello)
        self._hello_timer.start()
        #start a timer to check all lsa age per lsaAgeStep second
        #TODO: this implementation should be checked.
        if self._lsa_age_timer is None:
            self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
            self._lsa_age_timer.start()
            LOG.debug('[ISM] Start LSA age timer.')

    def _begin_hello(self):
        #start Hello Protocol
        self.hp.set_conf(
            self.version,
            self.hello_interval,
            self.dead_interval,
            self.rid,
            self.area_id,
            self.ip_intf_mask,
            self.options,
            self.link_type,
            self.drip,
            self.bdrip
        )
        self.hp.send_hello(self.hp.gen_hello())

    def _dr_other(self):
        """
        send hello in broadcast or nbma, not be dr
        """
        self.change_ism_state('ISM_DROther')
        if not self._elect_timer.is_stop():
            self._elect_timer.stop()
        #start a timer to check all lsa age per lsaAgeStep second
        #TODO: this implementation should be checked.
        if self._lsa_age_timer is None:
            self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
            self._lsa_age_timer.start()
            LOG.debug('[ISM] Start LSA age timer.')

    def _nbr_change(self):
        #Remove NSM which state is down
        tobe_removed = []
        for nrid in self.nbr_list:
            if self.nbr_list[nrid].state == NSM_STATE['NSM_Down']:
                tobe_removed.append(nrid)

        for rm in tobe_removed:
            if self.nbr_list[rm].src == self.drip:
                self.drip = 0
            if self.nbr_list[rm].src == self.bdrip:
                self.bdrip = 0
            del self.nbr_list[rm]
            self.neighbor.remove(rm)
            LOG.info('[ISM] Neighbor %s is deleted.' % int2ip(rm))

        if self.link_type == 'Broadcast':
            if not self._elect_timer.is_stop():
                self._elect_timer.stop()
            self._elect_dr()

    def backup(self):
        """
        being bdr, probe cannot be bdr
        """
        self.change_ism_state('ISM_Backup')
        pass

    def dr(self):
        """
        being dr, probe cannot be dr
        """
        self.change_ism_state('ISM_DR')
        pass

    def _elect_dr(self):
        """
        dr election algorithm
        """
        #TODO: unimplemented
        LOG.debug('[ISM] Election Finished.')
        if self.state == ISM_STATE['ISM_Waiting']:
            #Receive some cross-thread flag change, and then fire the event
            LOG.info('[ISM] Event: ISM_WaitTimer.')
            self.fire('ISM_WaitTimer')
        else:
            self._dr_other()

    def change_ism_state(self, newstate):
        LOG.info("[ISM] Change state to %s." % newstate)
        self.state = ISM_STATE[newstate]

    def _lsa_age(self):
        """
        Check all LSA age, and when LSA's age is MAXAGE, call aged handler
        """
        for lslist in self.ai.oi.lsdb.lsdb.values():
            tobe_removed = list()
            if len(lslist) == 0:
                continue
            else:
                self.ai.oi.lsdb.lsdb_lock.acquire()
                for lsa in lslist:
                    age = lslist[lsa]['H']['AGE']
                    dna = lslist[lsa]['H']['DNA']
                    lsa_ts = strptime(datetimeLock, lslist[lsa]['TIMESTAMP'])
                    now_age = abs(lsa_ts - datetime.datetime.now()).seconds + age

                    #Remove aged LSA, rfc chap. 14
                    if dna == 0 and now_age >= MAXAGE:
                        for nrid in self.nbr_list:
                            if len(self.nbr_list[nrid].ls_rxmt) == 0 and\
                               (self.nbr_list[nrid].state != NSM_STATE['NSM_Loading'] or
                                self.nbr_list[nrid].state != NSM_STATE['NSM_Exchange']):
                                tobe_removed.append(lsa)
                self.ai.oi.lsdb.lsdb_lock.release()
            tobe_removed = list(set(tobe_removed))

            if len(tobe_removed) != 0:
                LOG.info("[LSA] %s LSA(s) aged for reaching MAXAGE." % len(tobe_removed))
                self.ai.oi.lsdb.lsdb_lock.acquire()
                for rm in tobe_removed:
                    #Maybe we received aged LSA in flood, then the LSA will be deleted by flood. If this, pass it.
                    if rm not in lslist:
                        continue
                    del lslist[rm]
                self.ai.oi.lsdb.lsdb_lock.release()
Esempio n. 21
0
class ExchangeProtocol(OspfProtocol):
    def __init__(self, nsm):

        OspfProtocol.__init__(self)

        self.init = 0
        self.more = 0
        self.ms = 0
        self._rls_last_dd_timer = None

        self.nsm = nsm
        self.dst = ALL_SPF_ROUTER  # by default

        if self.nsm.ism.link_type == 'Broadcast':
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        elif self.nsm.ism.link_type == 'Point-to-Point':
            self.dst = util.int2ip(self.nsm.src)
        else:
            #TODO: if link type is virtual or others, set dst specific.
            pass

        #ospf socket
        self._sock = OspfSock()
        self._sock.bind(self.nsm.ism.ip_intf_addr)

    def __del__(self):
        self._sock.close()

    def set_dd_options(self):
        dolist = list(util.int2bin(self.nsm.dd_flags))
        self.init, self.more, self.ms = dolist[-3], dolist[-2], dolist[-1]

    def gen_dd(self, lsa=None):
        #In dd packet, the mtu is 1500 by default, but in virtual link, mtu is must set to 0
        #TODO: set virtual link mtu
        dd = DBDesc(
            mtu=self.nsm.ism.mtu,
            ddoptions=self.nsm.dd_flags,
            options=self.options,
            ddseq=self.nsm.dd_seqnum,
        )

        #TODO: unimplemented to send probe's dd
        if not lsa is None:
            pass

        ospf_packet = OSPF(
            v=self.version,
            type=2,  # 2 for dd
            area=self.area,
            len=len(dd) + len(OSPF()),
            router=self.rid,
            data=dd)

        return str(ospf_packet)

    def check_dd(self, pkt):
        """
        check received dd packet,
        return True means the packet is handled correctly,
        return False means the packet is not in right procedure
        """

        seq = pkt['V']['V']['DDSEQ']
        mss, i, m = pkt['V']['V']['MS'], pkt['V']['V']['INIT'], pkt['V']['V'][
            'MORE']
        r, mtu = pkt['V']['RID'], pkt['V']['V']['MTU']
        opt = pkt['V']['V']['OPTS']

        last_ospf_opt = self.nsm.options  # save current ospf options
        self.nsm.options = opt  # update nsm ospf options

        tmp_last_recv = None  # save last recv packet
        if self.nsm.last_recv != (seq, i, m, mss):
            tmp_last_recv = self.nsm.last_recv
            self.nsm.last_recv = (seq, i, m, mss)

        #check mtu, if neighbor's mtu greater than our interface, drop it
        if self.nsm.ism.mtu < mtu:
            LOG.warn('[Exchange] Deny for bigger MTU.')
            return False

        if self.nsm.state == NSM_STATE[
                'NSM_Down'] or self.nsm.state == NSM_STATE['NSM_Attempt']:
            LOG.warn('[Exchange] Deny for inappropriate state.')
            return False
        elif self.nsm.state == NSM_STATE['NSM_Init']:
            self.nsm.fire('NSM_TwoWayReceived')
            return False
        elif self.nsm.state == NSM_STATE['NSM_TwoWay']:
            LOG.warn('[Exchange] Ignore for inappropriate state.')
            return False

        elif self.nsm.state == NSM_STATE['NSM_ExStart']:
            #Negotiate DD exchange
            if mss == 1 and m == 1 and i == 1 and r > self.rid:
                self.init = 0
                self.more = 1
                self.ms = 0  # set myself slave, use master dd seq number

                self.nsm.dd_seqnum = pkt['V']['V']['DDSEQ']
                self.nsm.dd_flags = 4 * self.init + 2 * self.more + self.ms
                LOG.info('[NSM] Event: NSM_NegotiationDone')
                LOG.info('[NSM] We are slave.')
                self.nsm.fire('NSM_NegotiationDone')
                return True
            elif mss == 0 and i == 0 and r < self.rid:
                self.init = 0
                self.more = 1
                self.ms = 1  # set myself master, use my dd seq number
                self.nsm.dd_seqnum += 1

                self.nsm.dd_flags = 4 * self.init + 2 * self.more + self.ms
                LOG.info('[NSM] Event: NSM_NegotiationDone')
                LOG.info('[NSM] We are master.')
                self.nsm.fire('NSM_NegotiationDone')
                if m == 0:
                    self.nsm.fire('NSM_ExchangeDone')
                return True
            else:
                LOG.warn('[Exchange] Ignore for inappropriate dd options.')
                return False

        if self.nsm.state == NSM_STATE['NSM_Exchange']:

            if (seq, i, m, mss) == tmp_last_recv:
                if self.ms == 1:
                    LOG.warn('[Exchange] Duplicate DD packet, drop as master.')
                    return False
                else:
                    #retransmit the last send packet
                    LOG.warn(
                        '[Exchange] Duplicate DD packet, retransmit as slave.')
                    self.send_dd(self.nsm.last_send)
                    return False
            #check whether master/slave bit match or init bit is set unexpected
            if mss == self.ms or i == 1:
                LOG.warn('[Exchange] DD packet wrong options.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            #check whether ospf option is as same as the last received ospf packet
            if not last_ospf_opt is None:
                if last_ospf_opt != pkt['V']['V']['OPTS']:
                    LOG.warn(
                        '[Exchange] DD packet OSPF options are not same as the last received packet.'
                    )
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False

            #new recv dd packet is not last recv dd packet, get its lsa header
            if not self._get_lsa(pkt):
                #if there's some thing wrong in lsa header, fire SeqNumMismatch
                LOG.error('[Exchange] DD packet has wrong LSA header.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            #when more bit is 0, exchange stop, goto loading state
            if m == 0:
                if self.ms == 0:  # If we are slave, send dd as reply.
                    self.nsm.dd_seqnum = seq
                    #all pass checked, send my dd to neighbor
                    self.exchange()
                self.nsm.fire('NSM_ExchangeDone')
            else:
                #if probe is master, ddseq + 1; if slave, set ddseq to master ddseq
                if self.ms == 1:
                    self.nsm.dd_seqnum += 1
                else:
                    self.nsm.dd_seqnum = seq
                #all pass checked, send my dd to neighbor
                self.exchange()

            return True

        elif self.nsm.state == NSM_STATE[
                'NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:

            #check whether ospf option is as same as the last received ospf packet
            if not last_ospf_opt is None:
                if last_ospf_opt != pkt['V']['V']['OPTS']:
                    LOG.warn(
                        '[Exchange] DD packet OSPF options are not same as the last received packet.'
                    )
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False
            #Unexpected init bit
            if i == 1:
                LOG.error('[Exchange] Unexpected init bit in DD packet.')
                self.nsm.fire('NSM_SeqNumberMismatch')
                return False

            if self.ms == 1:
                LOG.error('[Exchange] Duplicate DD packet, drop as master.')
                return False
            else:
                #retransmit the last send packet
                if self.nsm.last_send is None:
                    LOG.error('[Exchange] Cannot retransmit last DD packet.')
                    self.nsm.fire('NSM_SeqNumberMismatch')
                    return False
                LOG.warn(
                    '[Exchange] Duplicate DD packet, retransmit as slave.')
                self.send_dd(self.nsm.last_send)
                return True
        else:
            pass

    def send_dd(self, pkt):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        self._sock.conn(self.dst)

        LOG.info('[Exchange] Send DD to %s.' % self.dst)
        self._sock.sendp(pkt)

        self.nsm.ism.ai.oi.stat.send_dd_count += 1
        self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

        if self.nsm.state == NSM_STATE[
                'NSM_Loading'] or self.nsm.state == NSM_STATE['NSM_Full']:
            self.nsm.last_send = pkt  # save the last sent packet

            #start a timer to wait dead interval to release the last recv packet
            if self._rls_last_dd_timer is None or self._rls_last_dd_timer.is_stop(
            ):
                self._rls_last_dd_timer = Timer(self.nsm.ism.dead_interval,
                                                self._rls_last_dd,
                                                once=True)
                self._rls_last_dd_timer.start()
            else:
                self._rls_last_dd_timer.reset()

    def _rls_last_dd(self):
        LOG.debug('[Exchange] Release last send DD packet.')
        self.nsm.last_send = None

    def _get_lsa(self, pkt):
        aid = pkt['V']['AID']
        lsa_hdr_list = pkt['V']['V']['LSAS']
        for lsah in lsa_hdr_list.keys():
            tp, lsid, adv, seq = lsa_hdr_list[lsah]['T'],\
                                 lsa_hdr_list[lsah]['LSID'],\
                                 lsa_hdr_list[lsah]['ADVRTR'],\
                                 lsa_hdr_list[lsah]['LSSEQNO'],\

            #generate lsa key according to lsa type.
            if tp == 5:
                #check if a type-5 lsa into a stub area, return false
                if self.nsm.ism.options['E'] == 0:
                    return False
                lsakey = (tp, lsid, adv)
            else:
                lsakey = (tp, aid, lsid, adv)

            lsalist = self.nsm.ism.ai.oi.lsdb.lookup_lsa_list(tp)
            if not lsalist is None:
                lsa = self.lookup_lsa(lsakey, lsalist)
                if lsa is None:
                    self.nsm.ls_req.append(lsakey)
                elif lsa['H']['LSSEQNO'] < seq:
                    #the lsa in dd is newer than lsa in the database
                    self.nsm.ls_req.append(lsakey)
                else:
                    continue
            #if did not find the lsa list
            else:
                LOG.error('[Exchange] Wrong LSA type in DD.')
                return False
        return True

    def exchange(self):
        #TODO: The probe always set more bit to 0 when exchange. Need to modify if we want to send probe's DD summary.
        lsa = self.nsm.db_sum
        tosend = lsa
        self.more = 0  # set more bit 0
        self.nsm.dd_flags = 4 * int(self.init) + 2 * int(self.more) + int(
            self.ms)
        LOG.debug('[Exchange] DD flag is %s.' % self.nsm.dd_flags)

        self.send_dd(self.gen_dd(tosend))

    def gen_lsr(self, rq):
        pkts = []
        maxlsa = 100
        more = True

        #In each LSR packet, it contains 100 lsa headers at max
        while more:
            if len(rq) - maxlsa > 0:
                lsas, rq = rq[:maxlsa], rq[maxlsa:]
            else:
                lsas = rq
                more = False

            lsrdata = []
            lsrlen = len(lsas) * len(LSR())

            for r in lsas:
                if r[0] == 5:
                    lsrdata.append(str(LSR(lstype=r[0], lsid=r[1], adv=r[2])))
                else:
                    lsrdata.append(str(LSR(lstype=r[0], lsid=r[2], adv=r[3])))

            ospf_packet = OSPF(
                v=self.version,
                type=3,  # 3 for lsr
                area=self.area,
                len=lsrlen + len(OSPF()),
                router=self.rid,
                data=''.join(lsrdata))

            pkts.append(str(ospf_packet))
        return pkts

    def send_lsr(self, pkts):
        if self.nsm.ism.link_type == 'Broadcast' and self.dst != self.nsm.ism.drip and self.dst != self.nsm.ism.bdrip:
            if self.nsm.ism.drip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.drip)
            if self.nsm.ism.bdrip == self.nsm.src:
                self.dst = util.int2ip(self.nsm.ism.bdrip)
        LOG.debug('[Exchange] Send LSR to %s.' % self.dst)
        self._sock.conn(self.dst)
        for p in pkts:
            self._sock.sendp(p)

            self.nsm.ism.ai.oi.stat.send_lsr_count += 1
            self.nsm.ism.ai.oi.stat.total_send_packet_count += 1

    def check_lsr(self, pkt):
        """
        The probe doesn't need to receive LSR
        """
        pass
Esempio n. 22
0
class ISM(object):
    def __init__(self, ai):
        self.state = ISM_STATE['ISM_Down']

        self.inf_trans_delay = 1  # TODO: how to set?
        self.prior = 0  # set probe priority 0 permanently
        self.drip = 0
        self.bdrip = 0
        self.neighbor = list()  # save all neighbors rid
        self.nbr_list = dict(
        )  # save all neighbors' state(nsm), format: {nrid: nsm}
        self.output_cost = 0  # TODO: how to set?

        self.au_type = 0  # TODO: Auth not implement
        self.au_key = None  # TODO: Auth not implement

        self._hello_timer = None
        self._elect_timer = None
        self._lsa_age_timer = None

        self.lsa_age_step = 1  # check LSA age interval

        self.ai = ai

        self.version = 2
        self.rid = ai.oi.rid
        self.area_id = ai.area_id
        self.hello_interval = ai.oi.config['hello_interval']
        self.dead_interval = 4 * self.hello_interval
        self.ip_intf_addr = ai.oi.config['ip']
        self.ip_intf_mask = ai.oi.config['mask']
        self.link_type = ai.oi.config['link_type']
        self.options = ai.oi.config['options']
        self.rxmt_interval = ai.oi.config['rxmt_interval']
        self.mtu = ai.oi.config['mtu']

        # self.multiAreaCap = False
        # #rfc5185 multi-area adj support
        # if ai.oi.config.has_key('multiArea'):
        #     self.multiAreaCap = True
        #     self.multiArea = ai.oi.config['multiArea']

        self.hp = HelloProtocol(self)
        self.ism = dict()

        #register all ism events
        for ismevent in ISM_EVENT.keys():
            if ismevent == 'ISM_InterfaceUp':
                self.ism[ismevent] = self._interface_up
            elif ismevent == 'ISM_InterfaceDown':
                self.ism[ismevent] = self._down
            elif ismevent == 'ISM_BackupSeen':
                self.ism[ismevent] = self._dr_other
            elif ismevent == 'ISM_WaitTimer':
                self.ism[ismevent] = self._dr_other
            elif ismevent == 'ISM_NeighborChange':
                self.ism[ismevent] = self._nbr_change
            else:
                continue

        self.nbrDownFlag = False  # Whether neighbor down happened

    def fire(self, event):
        self.ism[event]()

    def _down(self):
        """
        To interface down state
        """
        if not self._hello_timer is None:
            self._hello_timer.stop()
            self._hello_timer = None
        if not self._lsa_age_timer is None:
            self._lsa_age_timer.stop()
            self._lsa_age_timer = None
        if not self._elect_timer is None:
            self._elect_timer.stop()
            self._elect_timer = None
        self.change_ism_state('ISM_Down')
        self.drip = 0
        self.bdrip = 0
        self.neighbor = list()
        self.nbr_list = dict()

    def _interface_up(self):
        """
        Handler for interface up event
        """
        #point to point link, go to point_to_point state directly
        if self.link_type == 'Point-to-Point':
            self._point_to_point()
        #broadcast link, go to dr other state
        elif self.link_type == 'Broadcast':
            self._waiting()
        else:
            LOG.error('[ISM] Wrong Link Type.')
            return

    def _waiting(self):
        self.change_ism_state('ISM_Waiting')
        self._begin_hello()
        self._hello_timer = Timer(self.hello_interval, self._begin_hello)
        self._hello_timer.start()
        self._elect_timer = Timer(self.dead_interval,
                                  self._elect_dr,
                                  once=True)
        self._elect_timer.start()

    def loopback(self):
        """
        not used in probe
        """
        self.change_ism_state('ISM_Loopback')
        pass

    def _point_to_point(self):
        """
        send hello in p2p or virtual link
        """
        self.change_ism_state('ISM_PointToPoint')
        self._begin_hello()
        self._hello_timer = Timer(self.hello_interval, self._begin_hello)
        self._hello_timer.start()
        #start a timer to check all lsa age per lsaAgeStep second
        #TODO: this implementation should be checked.
        if self._lsa_age_timer is None:
            self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
            self._lsa_age_timer.start()
            LOG.debug('[ISM] Start LSA age timer.')

    def _begin_hello(self):
        #start Hello Protocol
        self.hp.set_conf(self.version, self.hello_interval, self.dead_interval,
                         self.rid, self.area_id, self.ip_intf_mask,
                         self.options, self.link_type, self.drip, self.bdrip)
        self.hp.send_hello(self.hp.gen_hello())

    def _dr_other(self):
        """
        send hello in broadcast or nbma, not be dr
        """
        self.change_ism_state('ISM_DROther')
        if not self._elect_timer.is_stop():
            self._elect_timer.stop()
        #start a timer to check all lsa age per lsaAgeStep second
        #TODO: this implementation should be checked.
        if self._lsa_age_timer is None:
            self._lsa_age_timer = Timer(self.lsa_age_step, self._lsa_age)
            self._lsa_age_timer.start()
            LOG.debug('[ISM] Start LSA age timer.')

    def _nbr_change(self):
        #Remove NSM which state is down
        tobe_removed = []
        for nrid in self.nbr_list:
            if self.nbr_list[nrid].state == NSM_STATE['NSM_Down']:
                tobe_removed.append(nrid)

        for rm in tobe_removed:
            if self.nbr_list[rm].src == self.drip:
                self.drip = 0
            if self.nbr_list[rm].src == self.bdrip:
                self.bdrip = 0
            del self.nbr_list[rm]
            self.neighbor.remove(rm)
            LOG.info('[ISM] Neighbor %s is deleted.' % int2ip(rm))

        if self.link_type == 'Broadcast':
            if not self._elect_timer.is_stop():
                self._elect_timer.stop()
            self._elect_dr()

    def backup(self):
        """
        being bdr, probe cannot be bdr
        """
        self.change_ism_state('ISM_Backup')
        pass

    def dr(self):
        """
        being dr, probe cannot be dr
        """
        self.change_ism_state('ISM_DR')
        pass

    def _elect_dr(self):
        """
        dr election algorithm
        """
        #TODO: unimplemented
        LOG.debug('[ISM] Election Finished.')
        if self.state == ISM_STATE['ISM_Waiting']:
            #Receive some cross-thread flag change, and then fire the event
            LOG.info('[ISM] Event: ISM_WaitTimer.')
            self.fire('ISM_WaitTimer')
        else:
            self._dr_other()

    def change_ism_state(self, newstate):
        LOG.info("[ISM] Change state to %s." % newstate)
        self.state = ISM_STATE[newstate]

    def _lsa_age(self):
        """
        Check all LSA age, and when LSA's age is MAXAGE, call aged handler
        """
        for lslist in self.ai.oi.lsdb.lsdb.values():
            tobe_removed = list()
            if len(lslist) == 0:
                continue
            else:
                self.ai.oi.lsdb.lsdb_lock.acquire()
                for lsa in lslist:
                    age = lslist[lsa]['H']['AGE']
                    dna = lslist[lsa]['H']['DNA']
                    lsa_ts = strptime(datetimeLock, lslist[lsa]['TIMESTAMP'])
                    now_age = abs(lsa_ts -
                                  datetime.datetime.now()).seconds + age

                    #Remove aged LSA, rfc chap. 14
                    if dna == 0 and now_age >= MAXAGE:
                        for nrid in self.nbr_list:
                            if len(self.nbr_list[nrid].ls_rxmt) == 0 and\
                               (self.nbr_list[nrid].state != NSM_STATE['NSM_Loading'] or
                                self.nbr_list[nrid].state != NSM_STATE['NSM_Exchange']):
                                tobe_removed.append(lsa)
                self.ai.oi.lsdb.lsdb_lock.release()
            tobe_removed = list(set(tobe_removed))

            if len(tobe_removed) != 0:
                LOG.info("[LSA] %s LSA(s) aged for reaching MAXAGE." %
                         len(tobe_removed))
                self.ai.oi.lsdb.lsdb_lock.acquire()
                for rm in tobe_removed:
                    #Maybe we received aged LSA in flood, then the LSA will be deleted by flood. If this, pass it.
                    if rm not in lslist:
                        continue
                    del lslist[rm]
                self.ai.oi.lsdb.lsdb_lock.release()