ospf: bird stuck in R state

Yury Shevchuk bird at hole.botik.ru
Thu Dec 18 15:53:50 CET 2008


Hi :)

The following is a bird bug with some investigation but no good
solution yet.  Could bird developers please take a look and advise?

Thanks,

-- sizif

We are running ospf with bird 1.0.12 and from time to time discover
bird stuck in R state:

# ps auxw | grep bird
root     14269 56.9  0.0  3088 2064 ?        Rs   Dec16 690:56 /usr/sbin/bird

I have approached the process with gdb and have found the following:

0x0807b742 in s_merge (from=0x80e0e54, to=0x80d118c) at slists.c:35
(gdb) bt
#0  0x0807b742 in s_merge (from=0x80e0e54, to=0x80d118c) at slists.c:35
#1  0x0807b6e3 in s_rem_node (n=0x80e0e54) at slists.c:125
#2  0x08066fdb in lsa_install_new (lsa=0xbf983c80, body=0x80c8fc0,
    oa=0x8096340) at /home/sizif/bird/./proto/ospf/lsalib.c:459
#3  0x0806568a in ospf_lsupd_receive (ps=0x80b56e4, ifa=0x8099570,
    n=0x80b3bf8) at /home/sizif/bird/./proto/ospf/lsupd.c:487
#4  0x08060553 in ospf_rx_hook (sk=0x80b4310, size=64)
    at /home/sizif/bird/./proto/ospf/packet.c:346
#5  0x08074c27 in sk_read (s=0x80b4310) at io.c:1212
#6  0x080751af in io_loop () at io.c:1378
#7  0x080779eb in main (argc=3, argv=0xbf983fd4) at main.c:458

Here is the spot in s_merge where we cycle:

   /* Really merging */
   while (g->next)
>    g = g->next;

(gdb) p /x *g
$1 = {prev = 0x80e0e54, null = 0x0, next = 0x80ab1d0, node = 0x0}
(gdb) display /x g
$2 = 0x80ab1d0

That is, the list of slist readers is trivially cycled.  0x80ab1d0 is
the address of n->dbsi field of one of the neighbors:

(gdb) p /x *(struct ospf_neighbor *)0x80ab190
$31 = {n = {next = 0x80abc38, prev = 0x80aa6e8}, pool = 0x80ab160,
  ifa = 0x8099570, state = 0x4, inactim = 0x80ab848, imms = {byte = 0x2,
    bit = {ms = 0x0, m = 0x1, i = 0x0, padding = 0x0}}, dds = 0xcf4d0c79,
  ddr = 0xb846f866, myimms = {byte = 0x7, bit = {ms = 0x1, m = 0x1, i = 0x1,
      padding = 0x0}}, rid = 0xc0a84909, ip = 0xc0a84909, priority = 0xa,
  options = 0x2, dr = 0xc0a8491e, bdr = 0xc0a84930, adj = 0x0, dbsi = {
    prev = 0x80e0e54, null = 0x0, next = 0x80ab1d0, node = 0x0}, lsrql = {
    head = 0x80ab1e4, null = 0x0, tail = 0x80ab1e0,
    tail_readers = 0x80ab1f4}, lsrqh = 0x80ab8b8, lsrqi = {prev = 0x80ab1e4,
    null = 0x0, next = 0x0, node = 0x80ab1e4}, lsrtl = {head = 0x80e9f8c,
    null = 0x0, tail = 0x80e9e4c, tail_readers = 0x80ab214}, lsrti = {
    prev = 0x80ab208, null = 0x0, next = 0x0, node = 0x80ab208},
  lsrth = 0x80aba50, ldbdes = 0x80ab268, rxmt_timer = 0x80ab878, ackl = {{
      head = 0x80ab234, null = 0x0, tail = 0x80ab230}, {head = 0x80ab240,
      null = 0x0, tail = 0x80ab23c}}, ackd_timer = 0x80abbd8, csn = 0x0}

Another interesing observation is that both nodes given as arguments
to s_merge have identical readers field:

#1  0x0807b6e3 in s_rem_node (n=0x80e0e54) at slists.c:125
(gdb) p /x *n
$38 = {next = 0x80d118c, prev = 0x809a7b4, readers = 0x80ab1d0}
(gdb) p /x *(snode *)0x80d118c
$40 = {next = 0x80e0f44, prev = 0x809a7b4, readers = 0x80ab1d0}
(gdb) p /x *(snode *)0x80e0f44
$41 = {next = 0x80bcdec, prev = 0x80d118c, readers = 0x0}
(gdb) p /x *(snode *)0x80bcdec
$42 = {next = 0x80b6db4, prev = 0x80e0f44, readers = 0x0}
(gdb) p /x *(snode *)0x80b6db4
$43 = {next = 0x80e1174, prev = 0x80bcdec, readers = 0x0}
(gdb) p /x *n
$44 = {next = 0x80d118c, prev = 0x809a7b4, readers = 0x80ab1d0}
(gdb) p /x *(snode *)0x809a7b4
$45 = {next = 0x80d118c, prev = 0x809a764, readers = 0x0}
(gdb) p /x *(snode *)0x809a764
$46 = {next = 0x809a7b4, prev = 0x809a714, readers = 0x0}

How can an iterator be referenced by more than one slist node?  It
looks like someone did s_put(&(n->dbsi), ...) while the previous
iteration has not been completed.

The s_get...s_put pair in ospf_dbdes_send seems sound and safe.
I think the culprit is ospf_neigh_sm who does s_init(&(n->dbsi)) when
entering NEIGHBOR_EXCHANGE:

  case INM_NEGDONE:
    if (n->state == NEIGHBOR_EXSTART)
    {
      neigh_chstate(n, NEIGHBOR_EXCHANGE);
      s_init(&(n->dbsi), &po->lsal);

The s_init does good when the neighbor enters NEIGHBOR_EXCHANGE for
the first time.  But if the neighbor enters NEIGHBOR_EXCHANGE again
without finishing the n->dbsi run through po->lsal, we will get
exactly the effect we observe: n->dbsi relinked to the head node of
po->lsal without unlinking it from the node it currently points to.

And a neighbor can indeed leave NEIGHBOR_EXCHANGE with unfinished run
through po->lsal for a number of reasons, with INM_SEQMIS or
INM_BADLSREQ.

One solution I can think of is to keep n->dbsi in s_put state
*always*, except for a short s_get...s_put fragment in ospf_dbdes_send
where there are no state transitions.  Do initial s_init(&(n->dbsi))
in ospf_neighbor_new.  Do s_get before s_init in the ospf_neigh_sm
fragment cited above.  Do s_put unconditionally in ospf_dbdes_send.

Your thoughts?



More information about the Bird-users mailing list