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 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 __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 __init__(self, nsm): OspfProtocol.__init__(self) self.nsm = nsm #ospf socket self._sock = OspfSock() self._sock.bind(self.nsm.ism.ip_intf_addr)
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 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')
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 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)
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')
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)
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)