diff options
Diffstat (limited to 'proto/ospf/hello.c')
-rw-r--r-- | proto/ospf/hello.c | 549 |
1 files changed, 288 insertions, 261 deletions
diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index e8bce09f..3aeb9f9a 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -2,6 +2,8 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip <feela@network.cz> + * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2009--2014 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -9,25 +11,26 @@ #include "ospf.h" -#ifdef OSPFv2 -struct ospf_hello_packet +struct ospf_hello2_packet { - struct ospf_packet ospf_packet; - ip_addr netmask; + struct ospf_packet hdr; + union ospf_auth auth; + + u32 netmask; u16 helloint; u8 options; u8 priority; u32 deadint; u32 dr; u32 bdr; -}; -#endif + u32 neighbors[]; +}; -#ifdef OSPFv3 -struct ospf_hello_packet +struct ospf_hello3_packet { - struct ospf_packet ospf_packet; + struct ospf_packet hdr; + u32 iface_id; u8 priority; u8 options3; @@ -37,286 +40,92 @@ struct ospf_hello_packet u16 deadint; u32 dr; u32 bdr; -}; -#endif - - -void -ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, - struct ospf_neighbor *n, ip_addr faddr) -{ - struct proto_ospf *po = ifa->oa->po; - struct proto *p = &po->proto; - char *beg = "OSPF: Bad HELLO packet from "; - unsigned int size, i, twoway, peers; - u32 tmp; - u32 *pnrid; - - size = ntohs(ps_i->length); - if (size < sizeof(struct ospf_hello_packet)) - { - log(L_ERR "%s%I - too short (%u B)", beg, faddr, size); - return; - } - - struct ospf_hello_packet *ps = (void *) ps_i; - - OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname); - -#ifdef OSPFv2 - ip_addr mask = ps->netmask; - ipa_ntoh(mask); - if ((ifa->type != OSPF_IT_VLINK) && - (ifa->type != OSPF_IT_PTP) && - !ipa_equal(mask, ipa_mkmask(ifa->addr->pxlen))) - { - log(L_ERR "%s%I - netmask mismatch (%I)", beg, faddr, mask); - return; - } -#endif - - tmp = ntohs(ps->helloint); - if (tmp != ifa->helloint) - { - log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, tmp); - return; - } - -#ifdef OSPFv2 - tmp = ntohl(ps->deadint); -#else /* OSPFv3 */ - tmp = ntohs(ps->deadint); -#endif - if (tmp != ifa->deadint) - { - log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, tmp); - return; - } - - /* Check whether bits E, N match */ - if ((ps->options ^ ifa->oa->options) & (OPT_E | OPT_N)) - { - log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, ps->options); - return; - } - -#ifdef OSPFv2 - if (n && (n->rid != ntohl(ps_i->routerid))) - { - OSPF_TRACE(D_EVENTS, - "Neighbor %I has changed router id from %R to %R.", - n->ip, n->rid, ntohl(ps_i->routerid)); - ospf_neigh_remove(n); - n = NULL; - } -#endif - - if (!n) - { - if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) - { - struct nbma_node *nn = find_nbma_node(ifa, faddr); - if (!nn && ifa->strictnbma) - { - log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname); - return; - } - - if (nn && (ifa->type == OSPF_IT_NBMA) && - (((ps->priority == 0) && nn->eligible) || - ((ps->priority > 0) && !nn->eligible))) - { - log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname); - return; - } - - if (nn) - nn->found = 1; - } - - OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); - - n = ospf_neighbor_new(ifa); - - n->rid = ntohl(ps_i->routerid); - n->ip = faddr; - n->dr = ntohl(ps->dr); - n->bdr = ntohl(ps->bdr); - n->priority = ps->priority; -#ifdef OSPFv3 - n->iface_id = ntohl(ps->iface_id); -#endif - - if (n->ifa->cf->bfd) - ospf_neigh_update_bfd(n, n->ifa->bfd); - } -#ifdef OSPFv3 /* NOTE: this could also be relevant for OSPFv2 on PtP ifaces */ - else if (!ipa_equal(faddr, n->ip)) - { - OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr); - n->ip = faddr; - } -#endif - - ospf_neigh_sm(n, INM_HELLOREC); - - pnrid = (u32 *) ((struct ospf_hello_packet *) (ps + 1)); - - peers = (size - sizeof(struct ospf_hello_packet))/ sizeof(u32); - - twoway = 0; - for (i = 0; i < peers; i++) - { - if (ntohl(pnrid[i]) == po->router_id) - { - DBG("%s: Twoway received from %I\n", p->name, faddr); - ospf_neigh_sm(n, INM_2WAYREC); - twoway = 1; - break; - } - } - - if (!twoway) - ospf_neigh_sm(n, INM_1WAYREC); - - u32 olddr = n->dr; - u32 oldbdr = n->bdr; - u32 oldpriority = n->priority; -#ifdef OSPFv3 - u32 oldiface_id = n->iface_id; -#endif - - n->dr = ntohl(ps->dr); - n->bdr = ntohl(ps->bdr); - n->priority = ps->priority; -#ifdef OSPFv3 - n->iface_id = ntohl(ps->iface_id); -#endif - - - /* Check priority change */ - if (n->state >= NEIGHBOR_2WAY) - { -#ifdef OSPFv2 - u32 neigh = ipa_to_u32(n->ip); -#else /* OSPFv3 */ - u32 neigh = n->rid; -#endif - - if (n->priority != oldpriority) - ospf_iface_sm(ifa, ISM_NEICH); - -#ifdef OSPFv3 - if (n->iface_id != oldiface_id) - ospf_iface_sm(ifa, ISM_NEICH); -#endif - - /* Neighbor is declaring itself ad DR and there is no BDR */ - if ((n->dr == neigh) && (n->bdr == 0) - && (n->state != NEIGHBOR_FULL)) - ospf_iface_sm(ifa, ISM_BACKS); - - /* Neighbor is declaring itself as BDR */ - if ((n->bdr == neigh) && (n->state != NEIGHBOR_FULL)) - ospf_iface_sm(ifa, ISM_BACKS); - - /* Neighbor is newly declaring itself as DR or BDR */ - if (((n->dr == neigh) && (n->dr != olddr)) - || ((n->bdr == neigh) && (n->bdr != oldbdr))) - ospf_iface_sm(ifa, ISM_NEICH); - - /* Neighbor is no more declaring itself as DR or BDR */ - if (((olddr == neigh) && (n->dr != olddr)) - || ((oldbdr == neigh) && (n->bdr != oldbdr))) - ospf_iface_sm(ifa, ISM_NEICH); - } + u32 neighbors[]; +}; - if (ifa->type == OSPF_IT_NBMA) - { - if ((ifa->priority == 0) && (n->priority > 0)) - ospf_hello_send(n->ifa, OHS_HELLO, n); - } - ospf_neigh_sm(n, INM_HELLOREC); -} void -ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) +ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) { - struct ospf_hello_packet *pkt; - struct ospf_packet *op; - struct proto *p; + struct ospf_proto *p = ifa->oa->po; + struct ospf_packet *pkt; struct ospf_neighbor *neigh, *n1; - u16 length; - int i; struct nbma_node *nb; + u32 *neighbors; + uint length; + int i, max; if (ifa->state <= OSPF_IS_LOOP) return; if (ifa->stub) - return; /* Don't send any packet on stub iface */ + return; - p = (struct proto *) (ifa->oa->po); - DBG("%s: Hello/Poll timer fired on interface %s with IP %I\n", - p->name, ifa->ifname, ifa->addr->ip); - /* Now we should send a hello packet */ pkt = ospf_tx_buffer(ifa); - op = &pkt->ospf_packet; - - /* Now fill ospf_hello header */ ospf_pkt_fill_hdr(ifa, pkt, HELLO_P); -#ifdef OSPFv2 - pkt->netmask = ipa_mkmask(ifa->addr->pxlen); - ipa_hton(pkt->netmask); - if ((ifa->type == OSPF_IT_VLINK) || - ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) - pkt->netmask = IPA_NONE; -#endif - - pkt->helloint = ntohs(ifa->helloint); - pkt->priority = ifa->priority; - -#ifdef OSPFv3 - pkt->iface_id = htonl(ifa->iface_id); - - pkt->options3 = ifa->oa->options >> 16; - pkt->options2 = ifa->oa->options >> 8; -#endif - pkt->options = ifa->oa->options; - -#ifdef OSPFv2 - pkt->deadint = htonl(ifa->deadint); - pkt->dr = htonl(ipa_to_u32(ifa->drip)); - pkt->bdr = htonl(ipa_to_u32(ifa->bdrip)); -#else /* OSPFv3 */ - pkt->deadint = htons(ifa->deadint); - pkt->dr = htonl(ifa->drid); - pkt->bdr = htonl(ifa->bdrid); -#endif + if (ospf_is_v2(p)) + { + struct ospf_hello2_packet *ps = (void *) pkt; + + if ((ifa->type == OSPF_IT_VLINK) || + ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) + ps->netmask = 0; + else + ps->netmask = htonl(u32_mkmask(ifa->addr->pxlen)); + + ps->helloint = ntohs(ifa->helloint); + ps->options = ifa->oa->options; + ps->priority = ifa->priority; + ps->deadint = htonl(ifa->deadint); + ps->dr = htonl(ipa_to_u32(ifa->drip)); + ps->bdr = htonl(ipa_to_u32(ifa->bdrip)); + + length = sizeof(struct ospf_hello2_packet); + neighbors = ps->neighbors; + } + else + { + struct ospf_hello3_packet *ps = (void *) pkt; + + ps->iface_id = htonl(ifa->iface_id); + ps->priority = ifa->priority; + ps->options3 = ifa->oa->options >> 16; + ps->options2 = ifa->oa->options >> 8; + ps->options = ifa->oa->options; + ps->helloint = ntohs(ifa->helloint); + ps->deadint = htons(ifa->deadint); + ps->dr = htonl(ifa->drid); + ps->bdr = htonl(ifa->bdrid); + + length = sizeof(struct ospf_hello3_packet); + neighbors = ps->neighbors; + } - /* Fill all neighbors */ i = 0; + max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32); + /* Fill all neighbors */ if (kind != OHS_SHUTDOWN) { - u32 *pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet)); WALK_LIST(neigh, ifa->neigh_list) { - if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_maxsize(ifa)) + if (i == max) { - log(L_WARN "%s: Too many neighbors on interface %s", p->name, ifa->ifname); + log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname); break; } - *(pp + i) = htonl(neigh->rid); + neighbors[i] = htonl(neigh->rid); i++; } } - length = sizeof(struct ospf_hello_packet) + i * sizeof(u32); - op->length = htons(length); + length += i * sizeof(u32); + pkt->length = htons(length); + + OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname); switch(ifa->type) { @@ -334,7 +143,7 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) int to_all = ifa->state > OSPF_IS_DROTHER; int me_elig = ifa->priority > 0; - + if (kind == OHS_POLL) /* Poll timer */ { WALK_LIST(nb, ifa->nbma_list) @@ -369,8 +178,226 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) break; default: - bug("Bug in ospf_hello_send()"); + bug("Bug in ospf_send_hello()"); } +} - OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname); + +void +ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, + struct ospf_neighbor *n, ip_addr faddr) +{ + struct ospf_proto *p = ifa->oa->po; + char *beg = "OSPF: Bad HELLO packet from "; + u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr; + u8 rcv_options, rcv_priority; + u32 *neighbors; + u32 neigh_count; + uint plen, i; + + /* RFC 2328 10.5 */ + + OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname); + + plen = ntohs(pkt->length); + + if (ospf_is_v2(p)) + { + struct ospf_hello2_packet *ps = (void *) pkt; + + if (plen < sizeof(struct ospf_hello2_packet)) + { + log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); + return; + } + + rcv_iface_id = 0; + rcv_helloint = ntohs(ps->helloint); + rcv_deadint = ntohl(ps->deadint); + rcv_dr = ntohl(ps->dr); + rcv_bdr = ntohl(ps->bdr); + rcv_options = ps->options; + rcv_priority = ps->priority; + + int pxlen = u32_masklen(ntohl(ps->netmask)); + if ((ifa->type != OSPF_IT_VLINK) && + (ifa->type != OSPF_IT_PTP) && + (pxlen != ifa->addr->pxlen)) + { + log(L_ERR "%s%I - prefix length mismatch (%d)", beg, faddr, pxlen); + return; + } + + neighbors = ps->neighbors; + neigh_count = (plen - sizeof(struct ospf_hello2_packet)) / sizeof(u32); + } + else /* OSPFv3 */ + { + struct ospf_hello3_packet *ps = (void *) pkt; + + if (plen < sizeof(struct ospf_hello3_packet)) + { + log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen); + return; + } + + rcv_iface_id = ntohl(ps->iface_id); + rcv_helloint = ntohs(ps->helloint); + rcv_deadint = ntohs(ps->deadint); + rcv_dr = ntohl(ps->dr); + rcv_bdr = ntohl(ps->bdr); + rcv_options = ps->options; + rcv_priority = ps->priority; + + neighbors = ps->neighbors; + neigh_count = (plen - sizeof(struct ospf_hello3_packet)) / sizeof(u32); + } + + if (rcv_helloint != ifa->helloint) + { + log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, rcv_helloint); + return; + } + + if (rcv_deadint != ifa->deadint) + { + log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, rcv_deadint); + return; + } + + /* Check whether bits E, N match */ + if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N)) + { + log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, rcv_options); + return; + } + + /* Check consistency of existing neighbor entry */ + if (n) + { + uint t = ifa->type; + if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP))) + { + /* Neighbor identified by IP address; Router ID may change */ + if (n->rid != ntohl(pkt->routerid)) + { + OSPF_TRACE(D_EVENTS, "Neighbor %I has changed Router ID from %R to %R", + n->ip, n->rid, ntohl(pkt->routerid)); + ospf_neigh_remove(n); + n = NULL; + } + } + else /* OSPFv3 or OSPFv2/PtP */ + { + /* Neighbor identified by Router ID; IP address may change */ + if (!ipa_equal(faddr, n->ip)) + { + OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr); + n->ip = faddr; + } + } + } + + if (!n) + { + if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP)) + { + struct nbma_node *nn = find_nbma_node(ifa, faddr); + + if (!nn && ifa->strictnbma) + { + log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname); + return; + } + + if (nn && (ifa->type == OSPF_IT_NBMA) && + (((rcv_priority == 0) && nn->eligible) || + ((rcv_priority > 0) && !nn->eligible))) + { + log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname); + return; + } + + if (nn) + nn->found = 1; + } + + OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname); + + n = ospf_neighbor_new(ifa); + + n->rid = ntohl(pkt->routerid); + n->ip = faddr; + n->dr = rcv_dr; + n->bdr = rcv_bdr; + n->priority = rcv_priority; + n->iface_id = rcv_iface_id; + + if (n->ifa->cf->bfd) + ospf_neigh_update_bfd(n, n->ifa->bfd); + } + + u32 n_id = ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; + + u32 old_dr = n->dr; + u32 old_bdr = n->bdr; + u32 old_priority = n->priority; + u32 old_iface_id = n->iface_id; + + n->dr = rcv_dr; + n->bdr = rcv_bdr; + n->priority = rcv_priority; + n->iface_id = rcv_iface_id; + + + /* Update inactivity timer */ + ospf_neigh_sm(n, INM_HELLOREC); + + /* RFC 2328 9.5.1 - non-eligible routers reply to hello on NBMA nets */ + if (ifa->type == OSPF_IT_NBMA) + if ((ifa->priority == 0) && (n->priority > 0)) + ospf_send_hello(n->ifa, OHS_HELLO, n); + + + /* Examine list of neighbors */ + for (i = 0; i < neigh_count; i++) + if (neighbors[i] == htonl(p->router_id)) + goto found_self; + + ospf_neigh_sm(n, INM_1WAYREC); + return; + + found_self: + ospf_neigh_sm(n, INM_2WAYREC); + + + if (n->iface_id != old_iface_id) + { + /* If neighbor is DR, also update cached DR interface ID */ + if (ifa->drid == n->rid) + ifa->dr_iface_id = n->iface_id; + + /* RFC 5340 4.4.3 Event 4 - change of neighbor's interface ID */ + ospf_notify_rt_lsa(ifa->oa); + + /* Missed in RFC 5340 4.4.3 Event 4 - (Px-)Net-LSA uses iface_id to ref Link-LSAs */ + ospf_notify_net_lsa(ifa); + } + + if (ifa->state == OSPF_IS_WAITING) + { + /* Neighbor is declaring itself DR (and there is no BDR) or as BDR */ + if (((n->dr == n_id) && (n->bdr == 0)) || (n->bdr == n_id)) + ospf_iface_sm(ifa, ISM_BACKS); + } + else if (ifa->state >= OSPF_IS_DROTHER) + { + /* Neighbor changed priority or started/stopped declaring itself as DR/BDR */ + if ((n->priority != old_priority) || + ((n->dr == n_id) && (old_dr != n_id)) || + ((n->dr != n_id) && (old_dr == n_id)) || + ((n->bdr == n_id) && (old_bdr != n_id)) || + ((n->bdr != n_id) && (old_bdr == n_id))) + ospf_iface_sm(ifa, ISM_NEICH); + } } |