/* * BIRD -- OSPF * * (c) 1999 Ondrej Filip * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" char *ospf_is[]={ "down", "loop", "waiting", "point-to-point", "drother", "backup", "dr" }; char *ospf_ism[]={ "interface up", "wait timer fired", "backup seen", "neighbor change", "loop indicated", "unloop indicated", "interface down"}; char *ospf_it[]={ "broadcast", "nbma", "point-to-point", "virtual link" }; void iface_chstate(struct ospf_iface *ifa, u8 state) { struct proto_ospf *po=ifa->proto; struct proto *p=&po->proto; u8 oldstate; if(ifa->state!=state) { OSPF_TRACE(D_EVENTS, "Changing state of iface: %s from \"%s\" into \"%s\".", ifa->iface->name, ospf_is[ifa->state], ospf_is[state]); oldstate=ifa->state; ifa->state=state; if(ifa->iface->flags & IF_MULTICAST) { if((state==OSPF_IS_BACKUP)||(state==OSPF_IS_DR)) { if((ifa->dr_sk==NULL)&&(ifa->type!=OSPF_IT_NBMA)) { DBG("%s: Adding new multicast socket for (B)DR\n", p->name); ifa->dr_sk=sk_new(p->pool); ifa->dr_sk->type=SK_IP_MC; ifa->dr_sk->dport=OSPF_PROTO; ifa->dr_sk->saddr=AllDRouters; ifa->dr_sk->daddr=AllDRouters; ifa->dr_sk->tos=IP_PREC_INTERNET_CONTROL; ifa->dr_sk->ttl=1; ifa->dr_sk->rx_hook=ospf_rx_hook; ifa->dr_sk->tx_hook=ospf_tx_hook; ifa->dr_sk->err_hook=ospf_err_hook; ifa->dr_sk->iface=ifa->iface; ifa->dr_sk->rbsize=ifa->iface->mtu; ifa->dr_sk->tbsize=ifa->iface->mtu; ifa->dr_sk->data=(void *)ifa; if(sk_open(ifa->dr_sk)!=0) { DBG("%s: SK_OPEN: new? mc open failed.\n", p->name); } } } else { if(ifa->dr_sk!=NULL) { rfree(ifa->dr_sk); ifa->dr_sk=NULL; } } if((oldstate==OSPF_IS_DR)&&(ifa->nlsa!=NULL)) { ifa->nlsa->lsa.age=LSA_MAXAGE; if(state>=OSPF_IS_WAITING) { net_flush_lsa(ifa->nlsa,po,ifa->oa); } if(can_flush_lsa(ifa->oa)) flush_lsa(ifa->nlsa,ifa->oa); ifa->nlsa=NULL; } } } } void downint(struct ospf_iface *ifa) { struct ospf_neighbor *n,*nx; struct proto *p=&ifa->proto->proto; struct proto_ospf *po=ifa->proto; WALK_LIST_DELSAFE(n,nx,ifa->neigh_list) { OSPF_TRACE(D_EVENTS, "Removing neighbor %I", n->ip); ospf_neigh_remove(n); } rem_node(NODE ifa); if(ifa->hello_sk!=NULL) { rfree(ifa->hello_sk); } if(ifa->dr_sk!=NULL) { rfree(ifa->dr_sk); } if(ifa->ip_sk!=NULL) { rfree(ifa->ip_sk); } if(ifa->wait_timer!=NULL) { tm_stop(ifa->wait_timer); rfree(ifa->wait_timer); } if(ifa->hello_timer!=NULL) { tm_stop(ifa->hello_timer); rfree(ifa->hello_timer); } rfree(ifa->lock); mb_free(ifa); } void ospf_int_sm(struct ospf_iface *ifa, int event) { struct proto *p=(struct proto *)(ifa->proto); struct proto_ospf *po=ifa->proto; struct ospf_area *oa=ifa->oa; OSPF_TRACE(D_EVENTS, "SM on iface %s. Event is \"%s\".", ifa->iface->name, ospf_ism[event]); switch(event) { case ISM_UP: if(ifa->state==OSPF_IS_DOWN) { /* Now, nothing should be adjacent */ restart_hellotim(ifa); if((ifa->type==OSPF_IT_PTP) || (ifa->type==OSPF_IT_VLINK)) { iface_chstate(ifa, OSPF_IS_PTP); } else { if(ifa->priority==0) { iface_chstate(ifa, OSPF_IS_DROTHER); } else { iface_chstate(ifa, OSPF_IS_WAITING); restart_waittim(ifa); } } addifa_rtlsa(ifa); } schedule_rt_lsa(ifa->oa); break; case ISM_BACKS: case ISM_WAITF: if(ifa->state==OSPF_IS_WAITING) { bdr_election(ifa ,p); } break; case ISM_NEICH: if((ifa->state==OSPF_IS_DROTHER) || (ifa->state==OSPF_IS_DR) || (ifa->state==OSPF_IS_BACKUP)) { bdr_election(ifa ,p); schedule_rt_lsa(ifa->oa); } break; case ISM_DOWN: iface_chstate(ifa, OSPF_IS_DOWN); downint(ifa); schedule_rt_lsa(oa); break; case ISM_LOOP: /* Useless? */ iface_chstate(ifa, OSPF_IS_LOOP); downint(ifa); schedule_rt_lsa(ifa->oa); break; case ISM_UNLOOP: iface_chstate(ifa, OSPF_IS_DOWN); schedule_rt_lsa(ifa->oa); break; default: bug("%s: ISM - Unknown event?",p->name); break; } } sock * ospf_open_mc_socket(struct ospf_iface *ifa) { sock *mcsk; struct proto *p; p=(struct proto *)(ifa->proto); mcsk=sk_new(p->pool); mcsk->type=SK_IP_MC; mcsk->dport=OSPF_PROTO; mcsk->saddr=AllSPFRouters; mcsk->daddr=AllSPFRouters; mcsk->tos=IP_PREC_INTERNET_CONTROL; mcsk->ttl=1; mcsk->rx_hook=ospf_rx_hook; mcsk->tx_hook=ospf_tx_hook; mcsk->err_hook=ospf_err_hook; mcsk->iface=ifa->iface; mcsk->rbsize=ifa->iface->mtu; mcsk->tbsize=ifa->iface->mtu; mcsk->data=(void *)ifa; if(sk_open(mcsk)!=0) { DBG("%s: SK_OPEN: mc open failed.\n",p->name); return(NULL); } DBG("%s: SK_OPEN: mc opened.\n",p->name); return(mcsk); } sock * ospf_open_ip_socket(struct ospf_iface *ifa) { sock *ipsk; struct proto *p; p=(struct proto *)(ifa->proto); ipsk=sk_new(p->pool); ipsk->type=SK_IP; ipsk->dport=OSPF_PROTO; ipsk->saddr=ifa->iface->addr->ip; ipsk->tos=IP_PREC_INTERNET_CONTROL; ipsk->ttl=1; ipsk->rx_hook=ospf_rx_hook; ipsk->tx_hook=ospf_tx_hook; ipsk->err_hook=ospf_err_hook; ipsk->iface=ifa->iface; ipsk->rbsize=ifa->iface->mtu; ipsk->tbsize=ifa->iface->mtu; ipsk->data=(void *)ifa; if(sk_open(ipsk)!=0) { DBG("%s: SK_OPEN: ip open failed.\n",p->name); return(NULL); } DBG("%s: SK_OPEN: ip opened.\n",p->name); return(ipsk); } /* Of course, it's NOT true now */ u8 ospf_iface_clasify(struct iface *ifa, struct proto *p) { DBG("%s: Iface flags=%x.\n", p->name, ifa->flags); if((ifa->flags & (IF_MULTIACCESS|IF_MULTICAST))== (IF_MULTIACCESS|IF_MULTICAST)) { DBG("%s: Clasifying BCAST.\n", p->name); return OSPF_IT_BCAST; } if((ifa->flags & (IF_MULTIACCESS|IF_MULTICAST))== IF_MULTIACCESS) { DBG("%s: Clasifying NBMA.\n", p->name); return OSPF_IT_NBMA; } DBG("%s: Clasifying P-T-P.\n", p->name); return OSPF_IT_PTP; } struct ospf_iface* find_iface(struct proto_ospf *p, struct iface *what) { struct ospf_iface *i; WALK_LIST (i, p->iface_list) if ((i)->iface == what) return i; return NULL; } void ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface) { struct proto_ospf *po=(struct proto_ospf *)p; struct ospf_config *c=(struct ospf_config *)(p->cf); struct ospf_area_config *ac; struct ospf_iface_patt *ip=NULL; struct ospf_iface *ifa; struct object_lock *lock; DBG("%s: If notify called\n", p->name); if (iface->flags & IF_IGNORE) return; if(flags & IF_CHANGE_UP) { WALK_LIST(ac, c->area_list) { if(ip=(struct ospf_iface_patt *) iface_patt_match(&ac->patt_list, iface)) break; } if(ip) { OSPF_TRACE(D_EVENTS, "Using interface %s.", iface->name); lock = olock_new( p->pool ); lock->addr = AllSPFRouters; lock->type = OBJLOCK_IP; lock->port = OSPF_PROTO; lock->iface = iface; lock->data = p; lock->hook = ospf_ifa_add; olock_acquire(lock); } } if(flags & IF_CHANGE_DOWN) { if((ifa=find_iface((struct proto_ospf *)p, iface))!=NULL) { OSPF_TRACE(D_EVENTS, "Killing interface %s.", iface->name); ospf_int_sm(ifa, ISM_DOWN); } } if(flags & IF_CHANGE_MTU) { if((ifa=find_iface((struct proto_ospf *)p, iface))!=NULL) { OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s.", iface->name); /* FIXME: change MTU */ } } } void ospf_iface_info(struct ospf_iface *ifa) { int x; cli_msg(-1015,"Interface \"%s\":", ifa->iface->name); cli_msg(-1015,"\tArea: %I (%u)", ifa->oa->areaid, ifa->oa->areaid); cli_msg(-1015,"\tType: %s", ospf_it[ifa->type]); cli_msg(-1015,"\tState: %s", ospf_is[ifa->state]); cli_msg(-1015,"\tPriority: %u", ifa->priority); cli_msg(-1015,"\tCost: %u", ifa->cost); cli_msg(-1015,"\tHello timer: %u", ifa->helloint); cli_msg(-1015,"\tWait timer: %u", ifa->waitint); cli_msg(-1015,"\tDead timer: %u", ifa->deadc*ifa->helloint); cli_msg(-1015,"\tRetransmit timer: %u", ifa->rxmtint); if((ifa->type==OSPF_IT_BCAST)||(ifa->type==OSPF_IT_NBMA)) { cli_msg(-1015,"\tDesigned router (ID): %I", ifa->drid); cli_msg(-1015,"\tDesigned router (IP): %I", ifa->drip); cli_msg(-1015,"\tBackup designed router (ID): %I", ifa->bdrid); cli_msg(-1015,"\tBackup designed router (IP): %I", ifa->bdrip); } } void ospf_ifa_add(struct object_lock *lock) { struct proto_ospf *po=lock->data; struct iface *iface=lock->iface; struct proto *p=&po->proto; struct nbma_node *nbma,*nb; u8 i; sock *mcsk; struct ospf_iface *ifa; struct ospf_config *c=(struct ospf_config *)(p->cf); struct ospf_area_config *ac; struct ospf_iface_patt *ip=NULL; WALK_LIST(ac, c->area_list) { if(ip=(struct ospf_iface_patt *) iface_patt_match(&ac->patt_list, iface)) break; } if(!ip) { bug("After lock I cannot find pattern."); } ifa=mb_allocz(p->pool, sizeof(struct ospf_iface)); ifa->proto=po; ifa->iface=iface; ifa->an=ac->areaid; ifa->cost=ip->cost; ifa->rxmtint=ip->rxmtint; ifa->inftransdelay=ip->inftransdelay; ifa->priority=ip->priority; ifa->helloint=ip->helloint; ifa->waitint=ip->waitint; ifa->deadc=ip->deadc; ifa->autype=ip->autype; memcpy(ifa->aukey,ip->password,8); ifa->options=2; /* FIXME what options? */ if(ip->type==OSPF_IT_UNDEF) ifa->type=ospf_iface_clasify(ifa->iface, (struct proto *)ifa->proto); else ifa->type=ip->type; if(ifa->type!=OSPF_IT_NBMA) { if((ifa->hello_sk=ospf_open_mc_socket(ifa))==NULL) { log("%s: Huh? could not open mc socket on interface %s?", p->name, iface->name); mb_free(ifa); log("%s: Ignoring this interface.", p->name); rfree(lock); return; } ifa->dr_sk=NULL; } if((ifa->ip_sk=ospf_open_ip_socket(ifa))==NULL) { log("%s: Huh? could not open ip socket on interface %s?", p->name, iface->name); mb_free(ifa); log("%s: Ignoring this interface", p->name); rfree(lock); return; } ifa->lock = lock; init_list(&ifa->neigh_list); init_list(&ifa->nbma_list); WALK_LIST(nb,ip->nbma_list) { nbma=mb_alloc(p->pool,sizeof(struct nbma_node)); nbma->ip=nb->ip; add_tail(&ifa->nbma_list, NODE nbma); } /* Add hello timer */ ifa->hello_timer=tm_new(p->pool); ifa->hello_timer->data=ifa; ifa->hello_timer->randomize=0; ifa->hello_timer->hook=hello_timer_hook; ifa->hello_timer->recurrent=ifa->helloint; DBG("%s: Installing hello timer. (%u)\n", p->name, ifa->helloint); ifa->wait_timer=tm_new(p->pool); ifa->wait_timer->data=ifa; ifa->wait_timer->randomize=0; ifa->wait_timer->hook=wait_timer_hook; ifa->wait_timer->recurrent=0; DBG("%s: Installing wait timer. (%u)\n", p->name, ifa->waitint); add_tail(&((struct proto_ospf *)p)->iface_list, NODE ifa); ifa->state=OSPF_IS_DOWN; ospf_int_sm(ifa, ISM_UP); }