hello.c 7.92 KB
Newer Older
1
2
3
/*
 *	BIRD -- OSPF
 *
Ondřej Filip's avatar
Ondřej Filip committed
4
 *	(c) 1999--2004 Ondrej Filip <feela@network.cz>
5
6
7
8
9
10
11
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#include "ospf.h"

void
12
ospf_hello_receive(struct ospf_hello_packet *ps,
13
		   struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr)
14
{
15
  u32 *pnrid;
16
  ip_addr olddr, oldbdr;
Ondřej Filip's avatar
Ondřej Filip committed
17
  ip_addr mask;
18
  char *beg = "Bad OSPF hello packet from ", *rec = " received: ";
19
  struct proto *p = (struct proto *) ifa->oa->po;
Ondřej Filip's avatar
Ondřej Filip committed
20
  unsigned int size = ntohs(ps->ospf_packet.length), i, twoway, oldpriority, eligible = 0, peers;
21

22
  OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s%s", faddr,
23
      (ifa->type == OSPF_IT_VLINK ? "vlink-" : ""), ifa->iface->name);
24
  mask = ps->netmask;
Ondřej Filip's avatar
Ondřej Filip committed
25
  ipa_ntoh(mask);
Ondřej Filip's avatar
Ondřej Filip committed
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  if (ifa->type != OSPF_IT_VLINK)
    {
      char *msg = L_WARN "Received HELLO packet %s (%I) is inconsistent "
	"with the primary address of interface %s.";

      if ((ifa->type != OSPF_IT_PTP) &&
	  !ipa_equal(mask, ipa_mkmask(ifa->iface->addr->pxlen)))
	{
	  if (!n) log(msg, "netmask", mask, ifa->iface->name);
	  return;
	}

      /* This check is not specified in RFC 2328, but it is needed
       * to handle the case when there is more IP networks on one
       * physical network (which is not handled in RFC 2328).
       * We allow OSPF on primary IP address only and ignore HELLO packets
       * with secondary addresses (which are sent for example by Quagga.
       */
      if ((ifa->iface->addr->flags & IA_UNNUMBERED) ?
	  !ipa_equal(faddr, ifa->iface->addr->opposite) :
	  !ipa_equal(ipa_and(faddr,mask), ifa->iface->addr->prefix))
	{
	  if (!n) log(msg, "address", faddr, ifa->iface->name);
	  return;
	}
    }
53
54

  if (ntohs(ps->helloint) != ifa->helloint)
55
  {
56
    log(L_ERR "%s%I%shello interval mismatch (%d).", beg, faddr, rec,
Ondřej Filip's avatar
Ondřej Filip committed
57
	ntohs(ps->helloint));
58
59
60
    return;
  }

61
  if (ntohl(ps->deadint) != ifa->dead)
62
  {
Ondřej Filip's avatar
Ondřej Filip committed
63
64
    log(L_ERR "%s%I%sdead interval mismatch (%d).", beg, faddr, rec,
	ntohl(ps->deadint));
65
66
67
    return;
  }

68
  if (ps->options != ifa->oa->opt.byte)
69
  {
Ondřej Filip's avatar
Ondřej Filip committed
70
    log(L_ERR "%s%I%soptions mismatch (0x%x).", beg, faddr, rec, ps->options);
71
72
73
    return;
  }

74
  if (!n)
75
  {
76
    if ((ifa->type == OSPF_IT_NBMA))
77
78
    {
      struct nbma_node *nn;
79
      int found = 0;
80

81
      WALK_LIST(nn, ifa->nbma_list)
82
      {
83
	if (ipa_equal(faddr, nn->ip))
84
	{
85
	  found = 1;
86
87
88
	  break;
	}
      }
89
      if ((found == 0) && (ifa->strictnbma))
90
      {
91
92
	log(L_WARN "Ignoring new neighbor: %I on %s.", faddr,
	    ifa->iface->name);
93
94
	return;
      }
95
      if (found)
96
      {
97
98
99
100
101
102
	eligible = nn->eligible;
	if (((ps->priority == 0) && eligible)
	    || ((ps->priority > 0) && (eligible == 0)))
	{
	  log(L_ERR "Eligibility mismatch for neighbor: %I on %s",
	      faddr, ifa->iface->name);
103
	  return;
104
	}
105
106
      }
    }
Ondřej Filip's avatar
Ondřej Filip committed
107
    OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s.", faddr,
108
	       ifa->iface->name);
109
110
111

    n = ospf_neighbor_new(ifa);

112
    n->rid = ntohl(((struct ospf_packet *) ps)->routerid);
113
114
    n->ip = faddr;
    n->dr = ps->dr;
Ondřej Filip's avatar
Ondřej Filip committed
115
    ipa_ntoh(n->dr);
116
    n->bdr = ps->bdr;
Ondřej Filip's avatar
Ondřej Filip committed
117
    ipa_ntoh(n->bdr);
118
119
    n->priority = ps->priority;
    n->options = ps->options;
120
121
122
  }
  ospf_neigh_sm(n, INM_HELLOREC);

123
  pnrid = (u32 *) ((struct ospf_hello_packet *) (ps + 1));
124

Ondřej Filip's avatar
Ondřej Filip committed
125
126
  peers = (size - sizeof(struct ospf_hello_packet))/ sizeof(u32);

127
  twoway = 0;
Ondřej Filip's avatar
Ondřej Filip committed
128
  for (i = 0; i < peers; i++)
129
  {
130
    if (ntohl(*(pnrid + i)) == p->cf->global->router_id)
131
    {
Ondřej Filip's avatar
Ondřej Filip committed
132
      DBG("%s: Twoway received from %I\n", p->name, faddr);
133
      ospf_neigh_sm(n, INM_2WAYREC);
134
      twoway = 1;
135
136
137
138
      break;
    }
  }

139
140
  if (!twoway)
    ospf_neigh_sm(n, INM_1WAYREC);
141

142
  olddr = n->dr;
143
  n->dr = ipa_ntoh(ps->dr);
144
  oldbdr = n->bdr;
145
  n->bdr = ipa_ntoh(ps->bdr);
146
147
148
  oldpriority = n->priority;
  n->priority = ps->priority;

149
  /* Check priority change */
150
  if (n->state >= NEIGHBOR_2WAY)
151
  {
152
    if (n->priority != oldpriority)
Ondřej Filip's avatar
Ondřej Filip committed
153
      ospf_iface_sm(ifa, ISM_NEICH);
154

155
    /* Router is declaring itself ad DR and there is no BDR */
156
    if (ipa_equal(n->ip, n->dr) && (ipa_to_u32(n->bdr) == 0)
157
	&& (n->state != NEIGHBOR_FULL))
Ondřej Filip's avatar
Ondřej Filip committed
158
      ospf_iface_sm(ifa, ISM_BACKS);
159

160
    /* Neighbor is declaring itself as BDR */
161
    if (ipa_equal(n->ip, n->bdr) && (n->state != NEIGHBOR_FULL))
Ondřej Filip's avatar
Ondřej Filip committed
162
      ospf_iface_sm(ifa, ISM_BACKS);
163
164

    /* Neighbor is newly declaring itself as DR or BDR */
165
166
    if ((ipa_equal(n->ip, n->dr) && (!ipa_equal(n->dr, olddr)))
	|| (ipa_equal(n->ip, n->bdr) && (!ipa_equal(n->bdr, oldbdr))))
Ondřej Filip's avatar
Ondřej Filip committed
167
      ospf_iface_sm(ifa, ISM_NEICH);
168
169

    /* Neighbor is no more declaring itself as DR or BDR */
170
171
    if ((ipa_equal(n->ip, olddr) && (!ipa_equal(n->dr, olddr)))
	|| (ipa_equal(n->ip, oldbdr) && (!ipa_equal(n->bdr, oldbdr))))
Ondřej Filip's avatar
Ondřej Filip committed
172
      ospf_iface_sm(ifa, ISM_NEICH);
173
174
  }

175
  if (ifa->type == OSPF_IT_NBMA)
176
  {
177
178
    if ((ifa->priority == 0) && (n->priority > 0))
      ospf_hello_send(NULL, 0, n);
179
  }
180
181
182
  ospf_neigh_sm(n, INM_HELLOREC);
}

183
void
184
ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn)
185
186
187
188
189
{
  struct ospf_iface *ifa;
  struct ospf_hello_packet *pkt;
  struct ospf_packet *op;
  struct proto *p;
Ondřej Filip's avatar
Ondřej Filip committed
190
  struct ospf_neighbor *neigh, *n1;
191
192
  u16 length;
  u32 *pp;
Ondřej Filip's avatar
Ondřej Filip committed
193
194
  int i, send;
  struct nbma_node *nb;
195

196
197
198
199
  if (timer == NULL)
    ifa = dirn->ifa;
  else
    ifa = (struct ospf_iface *) timer->data;
200

Ondřej Filip's avatar
Ondřej Filip committed
201
202
203
  if (ifa->state == OSPF_IS_DOWN)
    return;

204
205
  if (ifa->stub)
    return;			/* Don't send any packet on stub iface */
206

207
  p = (struct proto *) (ifa->oa->po);
208
  DBG("%s: Hello/Poll timer fired on interface %s.\n",
209
      p->name, ifa->iface->name);
210
211
  /* Now we should send a hello packet */
  /* First a common packet header */
Ondřej Filip's avatar
Ondřej Filip committed
212
  if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_VLINK))
213
  {
Ondřej Filip's avatar
Ondřej Filip committed
214
    pkt = (struct ospf_hello_packet *) (ifa->ip_sk->tbuf);
Ondřej Filip's avatar
Ondřej Filip committed
215
  }
216
  else
Ondřej Filip's avatar
Ondřej Filip committed
217
  {
Ondřej Filip's avatar
Ondřej Filip committed
218
    pkt = (struct ospf_hello_packet *) (ifa->hello_sk->tbuf);
Ondřej Filip's avatar
Ondřej Filip committed
219
  }
220

Ondřej Filip's avatar
Ondřej Filip committed
221
  /* Now fill ospf_hello header */
222
  op = (struct ospf_packet *) pkt;
223

224
  ospf_pkt_fill_hdr(ifa, pkt, HELLO_P);
Ondřej Filip's avatar
Ondřej Filip committed
225

226
  pkt->netmask = ipa_mkmask(ifa->iface->addr->pxlen);
Ondřej Filip's avatar
Ondřej Filip committed
227
  ipa_hton(pkt->netmask);
228
229
  if ((ifa->type == OSPF_IT_VLINK) || (ifa->type == OSPF_IT_PTP))
    pkt->netmask = IPA_NONE;
230
  pkt->helloint = ntohs(ifa->helloint);
231
  pkt->options = ifa->oa->opt.byte;
232
  pkt->priority = ifa->priority;
233
  pkt->deadint = htonl(ifa->dead);
234
  pkt->dr = ifa->drip;
Ondřej Filip's avatar
Ondřej Filip committed
235
  ipa_hton(pkt->dr);
236
  pkt->bdr = ifa->bdrip;
Ondřej Filip's avatar
Ondřej Filip committed
237
  ipa_hton(pkt->bdr);
Ondřej Filip's avatar
Ondřej Filip committed
238
239

  /* Fill all neighbors */
240
241
242
  i = 0;
  pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet));
  WALK_LIST(neigh, ifa->neigh_list)
Ondřej Filip's avatar
Ondřej Filip committed
243
  {
244
245
246
247
248
    if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_maxsize(ifa))
    {
      OSPF_TRACE(D_PACKETS, "Too many neighbors on the interface!");
      break;
    }
249
    *(pp + i) = htonl(neigh->rid);
Ondřej Filip's avatar
Ondřej Filip committed
250
251
252
    i++;
  }

253
254
  length = sizeof(struct ospf_hello_packet) + i * sizeof(u32);
  op->length = htons(length);
Ondřej Filip's avatar
Ondřej Filip committed
255

Ondřej Filip's avatar
Ondřej Filip committed
256
  switch(ifa->type)
Ondřej Filip's avatar
Ondřej Filip committed
257
  {
Ondřej Filip's avatar
Ondřej Filip committed
258
259
    case OSPF_IT_NBMA:
      if (timer == NULL)		/* Response to received hello */
Ondřej Filip's avatar
Ondřej Filip committed
260
      {
261
        ospf_send_to(ifa->ip_sk, dirn->ip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
262
      }
Ondřej Filip's avatar
Ondřej Filip committed
263
      else
264
      {
Ondřej Filip's avatar
Ondřej Filip committed
265
266
267
268
269
270
271
272
273
274
275
276
        int toall = 0;
        int meeli = 0;
        if (ifa->state > OSPF_IS_DROTHER)
          toall = 1;
        if (ifa->priority > 0)
          meeli = 1;
 
        WALK_LIST(nb, ifa->nbma_list)
        {
          send = 1;
          WALK_LIST(n1, ifa->neigh_list)
          {
277
            if (ipa_equal(nb->ip, n1->ip))
Ondřej Filip's avatar
Ondřej Filip committed
278
279
280
281
282
283
284
285
            {
              send = 0;
              break;
            }
          }
          if ((poll == 1) && (send))
          {
            if (toall || (meeli && nb->eligible))
286
              ospf_send_to(ifa->ip_sk, nb->ip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
287
288
289
290
291
292
293
294
          }
        }
        if (poll == 0)
        {
          WALK_LIST(n1, ifa->neigh_list)
          {
            if (toall || (n1->rid == ifa->drid) || (n1->rid == ifa->bdrid) ||
                (meeli && (n1->priority > 0)))
295
              ospf_send_to(ifa->ip_sk, n1->ip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
296
297
          }
        }
298
      }
Ondřej Filip's avatar
Ondřej Filip committed
299
300
      break;
    case OSPF_IT_VLINK:
301
      ospf_send_to(ifa->ip_sk, ifa->vip, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
302
303
      break;
    default:
304
      ospf_send_to(ifa->hello_sk, IPA_NONE, ifa);
Ondřej Filip's avatar
Ondřej Filip committed
305
  }
306
307
308

  OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s%s",
	     (ifa->type == OSPF_IT_VLINK ? "vlink-" : ""), ifa->iface->name);
309
}