Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    def run(self):
        """
        Main thread
        """
        # gives an interface up message to ism
        self.area.interface.fire('ISM_InterfaceUp')
        if self.area.interface.state == ISM_STATE['ISM_Down']:
            LOG.error('[OSPF Instance] Interface up failed.')
            return

        # open a socket to listen ospf port to avoid sending icmp protocol unreachable
        self._sock = OspfSock()
        self._sock.bind_ospf_multicast_group(self.interface_mame)
        self._sock.add_ospf_multicast_group(self.local_ip)

        # bind signal handler to handle terminal signal
        signal.signal(signal.SIGTERM, self.term_handler)
        signal.signal(signal.SIGINT, self.term_handler)

        while True:
            (data, src) = self._sock.recv()
            (src_ip, port) = src
            if src_ip == self.local_ip:  # filter to drop all packets from self
                continue

            self.recv.ospf_handler(data, time.time())
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    def run(self):
        """
        Main thread
        """
        # gives an interface up message to ism
        self.area.interface.fire('ISM_InterfaceUp')
        if self.area.interface.state == ISM_STATE['ISM_Down']:
            LOG.error('[OSPF Instance] Interface up failed.')
            return

        # open a socket to listen ospf port to avoid sending icmp protocol unreachable
        self._sock = OspfSock()
        self._sock.bind_ospf_multicast_group(self.interface_mame)
        self._sock.add_ospf_multicast_group(self.local_ip)

        # bind signal handler to handle terminal signal
        signal.signal(signal.SIGTERM, self.term_handler)
        signal.signal(signal.SIGINT, self.term_handler)

        while True:
            (data, src) = self._sock.recv()
            (src_ip, port) = src
            if src_ip == self.local_ip:       # filter to drop all packets from self
                continue

            self.recv.ospf_handler(data, time.time())
Exemplo n.º 5
0
    def __init__(self, ism):

        OspfProtocol.__init__(self)

        self.netmask = 0
        self.link_type = None
        self.drip = 0
        self.bdrip = 0

        self.ism = ism
        self.nsm_list = ism.nbr_list

        #ospf socket
        self._sock = OspfSock()
        self._sock.bind(self.ism.ip_intf_addr)
        self._sock.conn(ALL_SPF_ROUTER)
Exemplo n.º 6
0
    def __init__(self, nsm):

        OspfProtocol.__init__(self)
        self.nsm = nsm

        #ospf socket
        self._sock = OspfSock()
        self._sock.bind(self.nsm.ism.ip_intf_addr)
Exemplo n.º 7
0
    def __init__(self, ism):

        OspfProtocol.__init__(self)

        self.netmask = 0
        self.link_type = None
        self.drip = 0
        self.bdrip = 0

        self.ism = ism
        self.nsm_list = ism.nbr_list

        #ospf socket
        self._sock = OspfSock()
        self._sock.bind(self.ism.ip_intf_addr)
        self._sock.conn(ALL_SPF_ROUTER)
Exemplo n.º 8
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
Exemplo n.º 9
0
class HelloProtocol(OspfProtocol):

    def __init__(self, ism):

        OspfProtocol.__init__(self)

        self.netmask = 0
        self.link_type = None
        self.drip = 0
        self.bdrip = 0

        self.ism = ism
        self.nsm_list = ism.nbr_list

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

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

    def set_conf(self, v, hi, di, r, a, m, o, t, dr, bdr):
        self.version = v
        self.hello_interval = hi
        self.dead_interval = di
        self.area = util.ip2int(a)
        self.rid = util.ip2int(r)
        self.netmask = util.ip2int(m)
        self.options = self.convert_options_to_int(o)
        self.link_type = t
        self.drip = dr
        self.bdrip = bdr

    def send_hello(self, pkt):
        LOG.debug('[Hello] Send Hello.')
        self._sock.sendp(pkt)
        self.ism.ai.oi.stat.send_hello_count += 1
        self.ism.ai.oi.stat.total_send_packet_count += 1

    def gen_hello(self):
        hello = Hello(
            hellointerval=self.hello_interval,
            deadinterval=self.dead_interval,
            mask=self.netmask,
            options=self.options,
            router=self.drip,
            backup=self.bdrip
        )
        for nbr in self.ism.neighbor:
            hello.data += str(HelloNeighbor(neighbor=nbr))

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

        return str(ospf_packet)

    def check_hello(self, pkt):
        """
        check neighbor hello contents match or not
        """
        ver = pkt['V']['VER']
        nrid = pkt['V']['RID']
        aid = pkt['V']['AID']
        net = pkt['V']['V']['NETMASK']
        hi = pkt['V']['V']['HELLO']
        di = pkt['V']['V']['DEAD']
        opt = pkt['V']['V']['OPTS']

        if ver != self.version:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'VERSION_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.version, ver]
            # ))
            LOG.warn('[Hello] Router %s version %d mismatch.' % (util.int2ip(nrid), ver))
            return False

        #area check has be handled in ospf receiver
        # if aid != self.area:
        #     self.ism.ai.oi.msgHandler.record_event(Event.str_event(
        #         'AREA_MISMATCH',
        #         self.ism.ai.oi.processId,
        #         [util.int2ip(nrid), util.int2ip(self.area), util.int2ip(aid)]
        #     ))
        #     LOG.warn('[Hello] Router %s area ID %s mismatch.' % (util.int2ip(nrid), util.int2ip(aid)))
        #     return False

        if hi != self.hello_interval:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'HELLO_TIMER_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.helloInterval, hi]
            # ))
            LOG.warn('[Hello] Router %s hello interval %d mismatch.' % (util.int2ip(nrid), hi))
            return False
        if di != self.dead_interval:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'DEAD_TIMER_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.deadInterval, di]
            # ))
            LOG.warn('[Hello] Router %s dead interval %d mismatch.' % (util.int2ip(nrid), di))
            return False
        if self.link_type != 'Point-to-Point' and self.link_type != 'Virtual':
        #If linkType is p2p or virtual link, ignore this check
            if net != self.netmask:
                # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
                #     'NETMASK_MISMATCH',
                #     self.ism.ai.oi.processId,
                #     [util.int2ip(nrid), util.int2ip(self.netmask), util.int2ip(net)]
                # ))
                LOG.warn('[Hello] Router %s netmask %s mismatch.' % (util.int2ip(nrid), util.int2ip(net)))
                return False

        if self.options >> 1 & 0x1 != opt['E']:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'OPT_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.options, opt]
            # ))
            LOG.warn('[Hello] Router %s E-bit option %s mismatch.' % (util.int2ip(nrid), opt['E']))
            return False
        if self.options >> 3 & 0x1 != opt['NP']:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'OPT_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.options, opt]
            # ))
            LOG.warn('[Hello] Router %s NP-bit option %s mismatch.' % (util.int2ip(nrid), opt['NP']))
            return False
        #TODO: check other options

        #Pass check, add to active neighbor
        if not nrid in self.ism.neighbor:
            LOG.info('[Hello] Add new active neighbor %s.' % util.int2ip(nrid))
            self.ism.neighbor.append(nrid)

            #Meanwhile, create an NSM for this router.
            if not nrid in self.nsm_list:
                self.nsm_list[nrid] = NSM(self.ism, nrid, pkt)

        LOG.debug('[NSM] %s Event: NSM_PacketReceived.' % util.int2ip(nrid))
        self.nsm_list[nrid].fire('NSM_PacketReceived')
        return True

    def check_active_router(self, pkt):
        active_nrid = pkt['V']['V']['NBORS']
        nrid = pkt['V']['RID']

        if self.rid in active_nrid:
            LOG.debug('[NSM] %s Event: NSM_TwoWayReceived.' % util.int2ip(nrid))
            self.nsm_list[nrid].fire('NSM_TwoWayReceived')
            return True
        else:
            LOG.debug('[NSM] %s Event: NSM_OneWayReceived' % util.int2ip(nrid))
            LOG.debug('[NSM] %s state is %s.' % (util.int2ip(nrid), self.nsm_list[nrid].state))
            if self.nsm_list[nrid].state >= NSM_STATE['NSM_TwoWay']:
                #This is not in RFC. When probe received hello without itself, make adj down.
                LOG.info('[NSM] One way state. Reset adjacency with router %s.' % util.int2ip(nrid))
                self.nsm_list[nrid].reset()
            self.nsm_list[nrid].fire('NSM_OneWayReceived')
            return False

    def get_dr_bdr(self, pkt):
        nrid = pkt['V']['RID']
        LOG.debug('[NSM] %s state is %s.' % (util.int2ip(nrid), self.nsm_list[nrid].state))
        if self.nsm_list[nrid].state < NSM_STATE['NSM_TwoWay']:
            return
        else:
            srcip = pkt['H']['SRC']
            dr = pkt['V']['V']['DESIG']
            bdr = pkt['V']['V']['BDESIG']
            prio = pkt['V']['V']['PRIO']

            if self.drip != dr:
                if dr == srcip or bdr == srcip:
                    self.drip, self.bdrip = dr, bdr
                    self.ism.drip, self.ism.bdrip = dr, bdr
                    LOG.debug('[ISM] DR/BDR found: %s, %s.'
                                   % (util.int2ip(self.ism.drip), util.int2ip(self.ism.bdrip)))

                    LOG.info('[ISM] Event: ISM_BackupSeen.')
                    self.ism.fire('ISM_BackupSeen')
Exemplo n.º 10
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
Exemplo n.º 11
0
class OspfInstance(object):
    """
    OSPF instance skeleton class
    """

    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(OspfInstance, cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self, config):

        self.start_time = util.current_time()
        self.area_list = dict()
        self.backbone = None        # unused
        self.virtual_link = None     # unused
        self.external_router = list()    # unused

        self.as_external_lsa = dict()     # type-5 lsa list
        self.nssa_lsa = dict()           # type-7 lsa list
        self.opaque11_lsa = dict()       # type-11 lsa list

        self.config = config

        self.rid = self.config['router_id']
        self.interface_mame = self.config['interface']
        self.local_ip = self.config['ip']

        # Check config parameters legality
        if self.config['hello_interval'] < 1 or self.config['hello_interval'] > 65535:
            LOG.critical('[OSPF Instance] Hello interval is beyond the limitation.')
            return
        try:
            self.config['options'] = OspfProtocol.parse_options_config(self.config['options'])
        except Exception:
            LOG.critical('[OSPF Instance] Options is illegal.')
            return

        # The probe only connect to one area, so area list has no meaning.
        self.area = OspfArea(self)
        self.area_list[self.area.area_id] = self.area

        self.lsdb = OspfLsdb(self)

        self.recv = OspfReceiver(self.area.interface, self.area.interface.nbr_list, self.config['packet_display'])
        self._sock = None   # socket for receiving ospf packets

        # Statistics
        self.stat = OspfStat()

    def run(self):
        """
        Main thread
        """
        # gives an interface up message to ism
        self.area.interface.fire('ISM_InterfaceUp')
        if self.area.interface.state == ISM_STATE['ISM_Down']:
            LOG.error('[OSPF Instance] Interface up failed.')
            return

        # open a socket to listen ospf port to avoid sending icmp protocol unreachable
        self._sock = OspfSock()
        self._sock.bind_ospf_multicast_group(self.interface_mame)
        self._sock.add_ospf_multicast_group(self.local_ip)

        # bind signal handler to handle terminal signal
        signal.signal(signal.SIGTERM, self.term_handler)
        signal.signal(signal.SIGINT, self.term_handler)

        while True:
            (data, src) = self._sock.recv()
            (src_ip, port) = src
            if src_ip == self.local_ip:       # filter to drop all packets from self
                continue

            self.recv.ospf_handler(data, time.time())

    def term_handler(self, a, b):
        LOG.debug('[OSPF Instance] Signal %s is received.' % str(a))
        self.exit()

    def exit(self):
        self._sock.drop_ospf_multicast_group(self.local_ip)
        self._sock.close()
        LOG.info('[OSPF Instance] Program exits.')
        exit(0)
Exemplo n.º 12
0
class HelloProtocol(OspfProtocol):
    def __init__(self, ism):

        OspfProtocol.__init__(self)

        self.netmask = 0
        self.link_type = None
        self.drip = 0
        self.bdrip = 0

        self.ism = ism
        self.nsm_list = ism.nbr_list

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

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

    def set_conf(self, v, hi, di, r, a, m, o, t, dr, bdr):
        self.version = v
        self.hello_interval = hi
        self.dead_interval = di
        self.area = util.ip2int(a)
        self.rid = util.ip2int(r)
        self.netmask = util.ip2int(m)
        self.options = self.convert_options_to_int(o)
        self.link_type = t
        self.drip = dr
        self.bdrip = bdr

    def send_hello(self, pkt):
        LOG.debug('[Hello] Send Hello.')
        self._sock.sendp(pkt)
        self.ism.ai.oi.stat.send_hello_count += 1
        self.ism.ai.oi.stat.total_send_packet_count += 1

    def gen_hello(self):
        hello = Hello(hellointerval=self.hello_interval,
                      deadinterval=self.dead_interval,
                      mask=self.netmask,
                      options=self.options,
                      router=self.drip,
                      backup=self.bdrip)
        for nbr in self.ism.neighbor:
            hello.data += str(HelloNeighbor(neighbor=nbr))

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

        return str(ospf_packet)

    def check_hello(self, pkt):
        """
        check neighbor hello contents match or not
        """
        ver = pkt['V']['VER']
        nrid = pkt['V']['RID']
        aid = pkt['V']['AID']
        net = pkt['V']['V']['NETMASK']
        hi = pkt['V']['V']['HELLO']
        di = pkt['V']['V']['DEAD']
        opt = pkt['V']['V']['OPTS']

        if ver != self.version:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'VERSION_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.version, ver]
            # ))
            LOG.warn('[Hello] Router %s version %d mismatch.' %
                     (util.int2ip(nrid), ver))
            return False

        #area check has be handled in ospf receiver
        # if aid != self.area:
        #     self.ism.ai.oi.msgHandler.record_event(Event.str_event(
        #         'AREA_MISMATCH',
        #         self.ism.ai.oi.processId,
        #         [util.int2ip(nrid), util.int2ip(self.area), util.int2ip(aid)]
        #     ))
        #     LOG.warn('[Hello] Router %s area ID %s mismatch.' % (util.int2ip(nrid), util.int2ip(aid)))
        #     return False

        if hi != self.hello_interval:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'HELLO_TIMER_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.helloInterval, hi]
            # ))
            LOG.warn('[Hello] Router %s hello interval %d mismatch.' %
                     (util.int2ip(nrid), hi))
            return False
        if di != self.dead_interval:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'DEAD_TIMER_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.deadInterval, di]
            # ))
            LOG.warn('[Hello] Router %s dead interval %d mismatch.' %
                     (util.int2ip(nrid), di))
            return False
        if self.link_type != 'Point-to-Point' and self.link_type != 'Virtual':
            #If linkType is p2p or virtual link, ignore this check
            if net != self.netmask:
                # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
                #     'NETMASK_MISMATCH',
                #     self.ism.ai.oi.processId,
                #     [util.int2ip(nrid), util.int2ip(self.netmask), util.int2ip(net)]
                # ))
                LOG.warn('[Hello] Router %s netmask %s mismatch.' %
                         (util.int2ip(nrid), util.int2ip(net)))
                return False

        if self.options >> 1 & 0x1 != opt['E']:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'OPT_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.options, opt]
            # ))
            LOG.warn('[Hello] Router %s E-bit option %s mismatch.' %
                     (util.int2ip(nrid), opt['E']))
            return False
        if self.options >> 3 & 0x1 != opt['NP']:
            # self.ism.ai.oi.msgHandler.record_event(Event.str_event(
            #     'OPT_MISMATCH',
            #     self.ism.ai.oi.processId,
            #     [util.int2ip(nrid), self.options, opt]
            # ))
            LOG.warn('[Hello] Router %s NP-bit option %s mismatch.' %
                     (util.int2ip(nrid), opt['NP']))
            return False
        #TODO: check other options

        #Pass check, add to active neighbor
        if not nrid in self.ism.neighbor:
            LOG.info('[Hello] Add new active neighbor %s.' % util.int2ip(nrid))
            self.ism.neighbor.append(nrid)

            #Meanwhile, create an NSM for this router.
            if not nrid in self.nsm_list:
                self.nsm_list[nrid] = NSM(self.ism, nrid, pkt)

        LOG.debug('[NSM] %s Event: NSM_PacketReceived.' % util.int2ip(nrid))
        self.nsm_list[nrid].fire('NSM_PacketReceived')
        return True

    def check_active_router(self, pkt):
        active_nrid = pkt['V']['V']['NBORS']
        nrid = pkt['V']['RID']

        if self.rid in active_nrid:
            LOG.debug('[NSM] %s Event: NSM_TwoWayReceived.' %
                      util.int2ip(nrid))
            self.nsm_list[nrid].fire('NSM_TwoWayReceived')
            return True
        else:
            LOG.debug('[NSM] %s Event: NSM_OneWayReceived' % util.int2ip(nrid))
            LOG.debug('[NSM] %s state is %s.' %
                      (util.int2ip(nrid), self.nsm_list[nrid].state))
            if self.nsm_list[nrid].state >= NSM_STATE['NSM_TwoWay']:
                #This is not in RFC. When probe received hello without itself, make adj down.
                LOG.info(
                    '[NSM] One way state. Reset adjacency with router %s.' %
                    util.int2ip(nrid))
                self.nsm_list[nrid].reset()
            self.nsm_list[nrid].fire('NSM_OneWayReceived')
            return False

    def get_dr_bdr(self, pkt):
        nrid = pkt['V']['RID']
        LOG.debug('[NSM] %s state is %s.' %
                  (util.int2ip(nrid), self.nsm_list[nrid].state))
        if self.nsm_list[nrid].state < NSM_STATE['NSM_TwoWay']:
            return
        else:
            srcip = pkt['H']['SRC']
            dr = pkt['V']['V']['DESIG']
            bdr = pkt['V']['V']['BDESIG']
            prio = pkt['V']['V']['PRIO']

            if self.drip != dr:
                if dr == srcip or bdr == srcip:
                    self.drip, self.bdrip = dr, bdr
                    self.ism.drip, self.ism.bdrip = dr, bdr
                    LOG.debug('[ISM] DR/BDR found: %s, %s.' % (util.int2ip(
                        self.ism.drip), util.int2ip(self.ism.bdrip)))

                    LOG.info('[ISM] Event: ISM_BackupSeen.')
                    self.ism.fire('ISM_BackupSeen')
Exemplo n.º 13
0
class FloodProtocol(OspfProtocol):

    def __init__(self, nsm):

        OspfProtocol.__init__(self)
        self.nsm = nsm

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

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

    def check_lsu(self, pkt):
        """
        Check LSU according to RFC chap. 13
        """
        if self.nsm.state == NSM_STATE['NSM_Exchange'] or\
           self.nsm.state == NSM_STATE['NSM_Loading'] or\
           self.nsm.state == NSM_STATE['NSM_Full']:

            #check lsa area
            aid = pkt['V']['AID']
            if aid != util.ip2int(self.nsm.ism.area_id):
                LOG.warn('[Flood] LSU from wrong area %s.' % util.int2ip(aid))
                return

            lsas = pkt['V']['V']['LSAS']
            uniack = []

            for lsa in lsas:
                #check lsa chksum, step 1, this step has done in ospfParser
                #check allowed lsa type, step 2
                tp = lsas[lsa]['H']['T']
                if tp not in ALLOW_LS_TYPE:
                    LOG.warn('[Flood] Nonsupport LS type.')
                    continue
                else:
                    lslist = self.nsm.ism.ai.oi.lsdb.lookup_lsa_list(tp)

                #if type=5, check area type. if area is stub, drop it. step 3.
                if tp == 5 and self.nsm.options['E'] == 0:
                    LOG.warn('[Flood] Type-5 LSA in stub area, drop it.')
                    continue
                #if type=7, check option.
                if tp == 7 and self.nsm.options['NP'] == 0:
                    LOG.warn('[Flood] Type-7 LSA in not NSSA area, drop it.')
                    continue
                if tp in [9, 10, 11] and self.nsm.options['O'] == 0:
                    LOG.warn('[Flood] Opaque LSA not supported, drop it.')
                    continue
                #if type=11, check area type. if area is stub, drop it. step 3.
                if tp == 11 and self.nsm.options['E'] == 0:
                    LOG.warn('[Flood] Type-11 LSA in stub area, drop it.')
                    continue

                #check lsu age, step 4
                lsid, adv = lsas[lsa]['H']['LSID'], lsas[lsa]['H']['ADVRTR']
                #generate lsa key,
                # for type-5, ls key is (type, lsid, adv-rt),
                # for other types, ls key is (type, areaid, lsid, adv-rt)
                if tp == 5:
                    ls = (tp, lsid, adv)
                else:
                    ls = (tp, aid, lsid, adv)
                age = lsas[lsa]['H']['AGE']

                if age == MAXAGE and\
                   ls not in lslist.keys() and\
                   self.nsm.state != NSM_STATE['NSM_Exchange'] and\
                   self.nsm.state != NSM_STATE['NSM_Loading']:

                    #send lsack and continue to handle next lsa
                    LOG.info('[Flood] Received MAX age LSA %s. Send unicast LSAck.' % str(ls))

                    self._sock.conn(util.int2ip(self.nsm.src))
                    lsahdr = {1: lsas[lsa]}
                    self.send_lsack(self.gen_lsack(lsahdr))
                    continue

                #step 5
                exist_lsa = self.lookup_lsa(ls, lslist)

                if exist_lsa is None or self.judge_new_lsa(exist_lsa['H'], lsas[lsa]['H']) == lsas[lsa]['H']:
                    #check whether this lsa is added in lslist in MinLSArrival, if yes, drop it.
                    if (exist_lsa is not None) and self.judge_new_lsa(exist_lsa['H'], lsas[lsa]['H']) == lsas[lsa]['H']:
                        #step 5a
                        exist_lsa_timestamp = util.strptime(datetimeLock, exist_lsa['TIMESTAMP'])
                        if abs((exist_lsa_timestamp - datetime.datetime.now()).seconds) < MIN_LS_ARRIVAL:
                            LOG.debug('[Flood] LSA received in MinLSArrival.')
                            continue
                        #TODO: flood this lsa to subset of the interfaces. step 5b
                        else:
                            pass

                    #remove this lsa in all neighbors' ls_rxmt. step 5c
                    for rid in self.nsm.ism.hp.nsm_list:
                        if self.nsm.ism.hp.nsm_list[rid].ls_rxmt.count(lsas[lsa]) > 0:
                            self.nsm.ism.hp.nsm_list[rid].ls_rxmt.remove(lsas[lsa])

                    #add timestamp and add this lsa to ls list. step 5d
                    lsas[lsa]['TIMESTAMP'] = util.current_time_str()
                    lsas[lsa]['AREA'] = aid
                    self.nsm.ism.ai.oi.lsdb.lsdb_lock.acquire()
                    if ls in lslist and age == MAXAGE:
                        #if the age is MAXAGE, delete this lsa in the list. Attention: this is not the rule in rfc.
                        LOG.info('[Flood] Received LSA %s of MAXAGE. Delete it in LSDB.' % str(ls))
                        del lslist[ls]
                    else:
                        lslist[ls] = lsas[lsa]
                    self.nsm.ism.ai.oi.lsdb.lsdb_lock.release()

                    #remove the lsa in ls_req if it exists in it. Attention: this is not the rule in rfc.
                    if ls in self.nsm.ls_req:
                        self.nsm.ls_req.remove(ls)
                        if len(self.nsm.ls_req) == 0 and not self.nsm.lsr_resend_timer.is_stop():
                            self.nsm.lsr_resend_timer.stop()
                            #send lsack to neighbor
                            #self.send_lsack(self.gen_lsack(lsas))
                            #change nsm to full state
                            if self.nsm.state != NSM_STATE['NSM_Full']:
                                self.nsm.fire('NSM_LoadingDone')
                                continue

                    #TODO: maybe need to send lsack to receiver. step 5e
                    #if the lsa is self-originated, may need to do some special work. step 5f
                    #As a probe, it will has no self-originated LSA. Skip this.

                #If lsa exists and the local one is newer or equal to the received lsa.
                else:
                    #if this lsa is in ls_req, throws a BadLSReq. step 6
                    if ls in self.nsm.ls_req:
                        LOG.info('[NSM] Event: NSM_BadLSReq')
                        self.nsm.fire('NSM_BadLSReq')
                        return
                    #if the existLSA is equal to this lsa, do as follow. step 7
                    if not self.judge_new_lsa(exist_lsa['H'], lsas[lsa]['H']):
                        #step 7a
                        if lsas[lsa] in self.nsm.ls_rxmt:
                            #implied acknowledgment
                            self.nsm.ls_rxmt.remove(lsas[lsa])
                        #need to send lsack to neighbor, step 7b
                        else:
                            uniack.append(lsas[lsa])
                        continue

                    #if the existLSA is more recent, do as follow. step 8
                    elif self.judge_new_lsa(exist_lsa['H'], lsas[lsa]['H']) == exist_lsa['H']:
                        if exist_lsa['H']['AGE'] == MAXAGE and exist_lsa['H']['LSSEQNO'] == MAX_SEQ_NO:
                            LOG.warn('[Flood] MaxSequenceNumber LSA, drop it.')
                        else:
                            #Send a lsu to the neighbor if it didn't be sent in MinLSArrival,
                            # This not necessary in probe, so we send lsack to neighbor.
                            pass

            #send lsack after handling all lsa
            else:
                if len(self.nsm.ls_req) > 0:
                    LOG.debug('[Exchange] Still have %s LSA(s) to request.' % len(self.nsm.ls_req))
                if len(uniack) != 0:
                    LOG.debug('[Flood] Send LSAck to %s.' % util.int2ip(self.nsm.src))
                    uniacks = {}
                    for indexack in range(0, len(uniack)):
                        uniacks[indexack + 1] = uniack[indexack]
                    self._sock.conn(util.int2ip(self.nsm.src))
                    self.send_lsack(self.gen_lsack(uniacks))

                if len(lsas) != 0:
                    if self.nsm.ism.link_type == 'Broadcast':
                        dst = ALL_D_ROUTER
                    elif self.nsm.ism.link_type == 'Point-to-Point':
                        dst = ALL_SPF_ROUTER
                    else:
                        dst = ALL_SPF_ROUTER
                    LOG.debug('[Flood] Send multicast LSAck to %s.' % dst)
                    self._sock.conn(dst)
                    self.send_lsack(self.gen_lsack(lsas))
        else:
            LOG.warn('[Flood] NSM is under Exchange state, drop this LSU.')
            return

    def check_lsack(self, pkt):
        pass

    def send_lsack(self, pkt):
        self._sock.sendp(pkt)

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

    def gen_lsack(self, lsas):
        lsackdata = []
        for lsa in lsas.keys():
            hdr = lsas[lsa]['H']
            lsackdata.append(str(LSAHeader(
                age=hdr['AGE'],
                options=self.convert_options_to_int(hdr['OPTS']),
                type=hdr['T'],
                id=hdr['LSID'],
                adv=hdr['ADVRTR'],
                seq=hdr['LSSEQNO'],
                sum=hdr['CKSUM'],
                len=hdr['L']
            )))

        ospfdata = ''.join(lsackdata)
        ospf_packet = OSPF(
            v=self.version,
            type=5,             # 5 for lsack
            area=self.area,
            len=len(ospfdata)+len(OSPF()),
            router=self.rid,
            data=ospfdata
        )
        return str(ospf_packet)
Exemplo n.º 14
0
class OspfInstance(object):
    """
    OSPF instance skeleton class
    """

    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(OspfInstance,
                                  cls).__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self, config):

        self.start_time = util.current_time()
        self.area_list = dict()
        self.backbone = None  # unused
        self.virtual_link = None  # unused
        self.external_router = list()  # unused

        self.as_external_lsa = dict()  # type-5 lsa list
        self.nssa_lsa = dict()  # type-7 lsa list
        self.opaque11_lsa = dict()  # type-11 lsa list

        self.config = config

        self.rid = self.config['router_id']
        self.interface_mame = self.config['interface']
        self.local_ip = self.config['ip']

        # Check config parameters legality
        if self.config['hello_interval'] < 1 or self.config[
                'hello_interval'] > 65535:
            LOG.critical(
                '[OSPF Instance] Hello interval is beyond the limitation.')
            return
        try:
            self.config['options'] = OspfProtocol.parse_options_config(
                self.config['options'])
        except Exception:
            LOG.critical('[OSPF Instance] Options is illegal.')
            return

        # The probe only connect to one area, so area list has no meaning.
        self.area = OspfArea(self)
        self.area_list[self.area.area_id] = self.area

        self.lsdb = OspfLsdb(self)

        self.recv = OspfReceiver(self.area.interface,
                                 self.area.interface.nbr_list,
                                 self.config['packet_display'])
        self._sock = None  # socket for receiving ospf packets

        # Statistics
        self.stat = OspfStat()

    def run(self):
        """
        Main thread
        """
        # gives an interface up message to ism
        self.area.interface.fire('ISM_InterfaceUp')
        if self.area.interface.state == ISM_STATE['ISM_Down']:
            LOG.error('[OSPF Instance] Interface up failed.')
            return

        # open a socket to listen ospf port to avoid sending icmp protocol unreachable
        self._sock = OspfSock()
        self._sock.bind_ospf_multicast_group(self.interface_mame)
        self._sock.add_ospf_multicast_group(self.local_ip)

        # bind signal handler to handle terminal signal
        signal.signal(signal.SIGTERM, self.term_handler)
        signal.signal(signal.SIGINT, self.term_handler)

        while True:
            (data, src) = self._sock.recv()
            (src_ip, port) = src
            if src_ip == self.local_ip:  # filter to drop all packets from self
                continue

            self.recv.ospf_handler(data, time.time())

    def term_handler(self, a, b):
        LOG.debug('[OSPF Instance] Signal %s is received.' % str(a))
        self.exit()

    def exit(self):
        self._sock.drop_ospf_multicast_group(self.local_ip)
        self._sock.close()
        LOG.info('[OSPF Instance] Program exits.')
        exit(0)