bird/proto/ospf/hello.c
Ondrej Zajicek 8df02847e8 Fixes a compatibility issue in OSPFv2 PtP links.
BIRD used zero netmask in hello packets on all PtP links, not just on
unnumbered ones. This patch fixes it and adds option 'ptp netmask'
for overriding the default behavior.

Thanks to Alexander V. Chernikov for the original patch.
2013-04-17 13:06:40 +02:00

360 lines
8.1 KiB
C

/*
* BIRD -- OSPF
*
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "ospf.h"
#ifdef OSPFv2
struct ospf_hello_packet
{
struct ospf_packet ospf_packet;
ip_addr netmask;
u16 helloint;
u8 options;
u8 priority;
u32 deadint;
u32 dr;
u32 bdr;
};
#endif
#ifdef OSPFv3
struct ospf_hello_packet
{
struct ospf_packet ospf_packet;
u32 iface_id;
u8 priority;
u8 options3;
u8 options2;
u8 options;
u16 helloint;
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%s", faddr,
(ifa->type == OSPF_IT_VLINK ? "vlink-" : ""), ifa->iface->name);
#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;
}
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->iface->name);
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->iface->name);
return;
}
if (nn)
nn->found = 1;
}
OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr,
ifa->iface->name);
n = ospf_neighbor_new(ifa);
n->rid = ntohl(((struct ospf_packet *) ps)->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
}
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);
}
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)
{
struct ospf_hello_packet *pkt;
struct ospf_packet *op;
struct proto *p;
struct ospf_neighbor *neigh, *n1;
u16 length;
int i;
struct nbma_node *nb;
if (ifa->state <= OSPF_IS_LOOP)
return;
if (ifa->stub)
return; /* Don't send any packet on stub iface */
p = (struct proto *) (ifa->oa->po);
DBG("%s: Hello/Poll timer fired on interface %s with IP %I\n",
p->name, ifa->iface->name, 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
/* Fill all neighbors */
i = 0;
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_bufsize(ifa))
{
log(L_WARN "%s: Too many neighbors on interface %s", p->name, ifa->iface->name);
break;
}
*(pp + i) = htonl(neigh->rid);
i++;
}
}
length = sizeof(struct ospf_hello_packet) + i * sizeof(u32);
op->length = htons(length);
switch(ifa->type)
{
case OSPF_IT_BCAST:
case OSPF_IT_PTP:
ospf_send_to_all(ifa);
break;
case OSPF_IT_NBMA:
if (dirn) /* Response to received hello */
{
ospf_send_to(ifa, dirn->ip);
break;
}
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)
if (!nb->found && (to_all || (me_elig && nb->eligible)))
ospf_send_to(ifa, nb->ip);
}
else /* Hello timer */
{
WALK_LIST(n1, ifa->neigh_list)
if (to_all || (me_elig && (n1->priority > 0)) ||
(n1->rid == ifa->drid) || (n1->rid == ifa->bdrid))
ospf_send_to(ifa, n1->ip);
}
break;
case OSPF_IT_PTMP:
WALK_LIST(n1, ifa->neigh_list)
ospf_send_to(ifa, n1->ip);
WALK_LIST(nb, ifa->nbma_list)
if (!nb->found)
ospf_send_to(ifa, nb->ip);
/* If there is no other target, we also send HELLO packet to the other end */
if (ipa_nonzero(ifa->addr->opposite) && !ifa->strictnbma &&
EMPTY_LIST(ifa->neigh_list) && EMPTY_LIST(ifa->nbma_list))
ospf_send_to(ifa, ifa->addr->opposite);
break;
case OSPF_IT_VLINK:
ospf_send_to(ifa, ifa->vip);
break;
default:
bug("Bug in ospf_hello_send()");
}
OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s%s",
(ifa->type == OSPF_IT_VLINK ? "vlink-" : ""), ifa->iface->name);
}