1 /*
2 * BIRD -- OSPF
3 *
4 * (c) 1999--2005 Ondrej Filip <feela@network.cz>
5 * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
6 * (c) 2009--2014 CZ.NIC z.s.p.o.
7 *
8 * Can be freely distributed and used under the terms of the GNU GPL.
9 */
10
11 #include "ospf.h"
12 #include "nest/password.h"
13 #include "lib/md5.h"
14 #include "lib/mac.h"
15 #include "lib/socket.h"
16
17 const char * const ospf_pkt_names[] = {
18 [HELLO_P] = "HELLO",
19 [DBDES_P] = "DBDES",
20 [LSREQ_P] = "LSREQ",
21 [LSUPD_P] = "LSUPD",
22 [LSACK_P] = "LSACK",
23 };
24
25 void
ospf_pkt_fill_hdr(struct ospf_iface * ifa,void * buf,u8 h_type)26 ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
27 {
28 struct ospf_proto *p = ifa->oa->po;
29 struct ospf_packet *pkt;
30
31 pkt = (struct ospf_packet *) buf;
32
33 pkt->version = ospf_get_version(p);
34 pkt->type = h_type;
35 pkt->length = htons(ospf_pkt_maxsize(ifa));
36 pkt->routerid = htonl(p->router_id);
37 pkt->areaid = htonl(ifa->oa->areaid);
38 pkt->checksum = 0;
39 pkt->instance_id = ifa->instance_id;
40 pkt->autype = ospf_is_v2(p) ? ifa->autype : 0;
41 }
42
43 /* We assume OSPFv2 in ospf_pkt_finalize() */
44 static void
ospf_pkt_finalize2(struct ospf_iface * ifa,struct ospf_packet * pkt,uint * plen)45 ospf_pkt_finalize2(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen)
46 {
47 struct ospf_proto *p = ifa->oa->po;
48 struct password_item *pass = NULL;
49 union ospf_auth2 *auth = (void *) (pkt + 1);
50 memset(auth, 0, sizeof(union ospf_auth2));
51
52 /* Compatibility note: auth may contain anything if autype is
53 none, but nonzero values do not work with Mikrotik OSPF */
54
55 pkt->checksum = 0;
56 pkt->autype = ifa->autype;
57
58 switch (ifa->autype)
59 {
60 case OSPF_AUTH_SIMPLE:
61 pass = password_find(ifa->passwords, 1);
62 if (!pass)
63 {
64 log(L_ERR "No suitable password found for authentication");
65 return;
66 }
67 strncpy(auth->password, pass->password, sizeof(auth->password));
68 /* fallthrough */
69
70 case OSPF_AUTH_NONE:
71 {
72 void *body = (void *) (auth + 1);
73 uint blen = *plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth2);
74 pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL);
75 }
76 break;
77
78 case OSPF_AUTH_CRYPT:
79 pass = password_find(ifa->passwords, 0);
80 if (!pass)
81 {
82 log(L_ERR "%s: No suitable password found for authentication", p->p.name);
83 return;
84 }
85
86 /* Perhaps use random value to prevent replay attacks after
87 reboot when system does not have independent RTC? */
88 if (!ifa->csn)
89 {
90 ifa->csn = (u32) (current_real_time() TO_S);
91 ifa->csn_use = current_time();
92 }
93
94 /* We must have sufficient delay between sending a packet and increasing
95 CSN to prevent reordering of packets (in a network) with different CSNs */
96 if ((current_time() - ifa->csn_use) > 1 S)
97 ifa->csn++;
98
99 ifa->csn_use = current_time();
100
101 uint auth_len = mac_type_length(pass->alg);
102 byte *auth_tail = ((byte *) pkt + *plen);
103 *plen += auth_len;
104
105 ASSERT(*plen < ifa->sk->tbsize);
106
107 auth->c32.zero = 0;
108 auth->c32.keyid = pass->id;
109 auth->c32.len = auth_len;
110 auth->c32.csn = htonl(ifa->csn);
111
112 /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */
113 if (pass->alg < ALG_HMAC)
114 strncpy(auth_tail, pass->password, auth_len);
115 else
116 memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
117
118 mac_fill(pass->alg, pass->password, pass->length, (byte *) pkt, *plen, auth_tail);
119 break;
120
121 default:
122 bug("Unknown authentication type");
123 }
124 }
125
126 /*
127 * Return an extra packet size that should be added to a final packet size
128 */
129 static void
ospf_pkt_finalize3(struct ospf_iface * ifa,struct ospf_packet * pkt,uint * plen,ip_addr src)130 ospf_pkt_finalize3(struct ospf_iface *ifa, struct ospf_packet *pkt, uint *plen, ip_addr src)
131 {
132 struct ospf_proto *p = ifa->oa->po;
133 struct ospf_auth3 *auth = (void *) ((byte *) pkt + *plen);
134
135 pkt->checksum = 0;
136 pkt->autype = 0;
137
138 if (ifa->autype != OSPF_AUTH_CRYPT)
139 return;
140
141 struct password_item *pass = password_find(ifa->passwords, 0);
142 if (!pass)
143 {
144 log(L_ERR "%s: No suitable password found for authentication", p->p.name);
145 return;
146 }
147
148 /* FIXME: Ensure persistence */
149 p->csn64++;
150
151 uint mac_len = mac_type_length(pass->alg);
152 uint auth_len = sizeof(struct ospf_auth3) + mac_len;
153 *plen += auth_len;
154
155 ASSERT(*plen < ifa->sk->tbsize);
156
157 memset(auth, 0, sizeof(struct ospf_auth3));
158 auth->type = htons(OSPF3_AUTH_HMAC);
159 auth->length = htons(auth_len);
160 auth->reserved = 0;
161 auth->sa_id = htons(pass->id);
162 put_u64(&auth->csn, p->csn64);
163
164 /* Initialize with src IP address padded with HMAC_MAGIC */
165 put_ip6(auth->data, ipa_to_ip6(src));
166 memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
167
168 /* Attach OSPFv3 Cryptographic Protocol ID to the key */
169 uint pass_len = pass->length + 2;
170 byte *pass_key = alloca(pass_len);
171 memcpy(pass_key, pass->password, pass->length);
172 put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
173
174 mac_fill(pass->alg, pass_key, pass_len, (byte *) pkt, *plen, auth->data);
175 }
176
177
178 static int
ospf_pkt_checkauth2(struct ospf_neighbor * n,struct ospf_iface * ifa,struct ospf_packet * pkt,uint len)179 ospf_pkt_checkauth2(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len)
180 {
181 struct ospf_proto *p = ifa->oa->po;
182 union ospf_auth2 *auth = (void *) (pkt + 1);
183 struct password_item *pass = NULL;
184 const char *err_dsc = NULL;
185 uint err_val = 0;
186
187 uint plen = ntohs(pkt->length);
188 u8 autype = pkt->autype;
189
190 if (autype != ifa->autype)
191 DROP("authentication method mismatch", autype);
192
193 switch (autype)
194 {
195 case OSPF_AUTH_NONE:
196 return 1;
197
198 case OSPF_AUTH_SIMPLE:
199 pass = password_find(ifa->passwords, 1);
200 if (!pass)
201 DROP1("no password found");
202
203 if (!password_verify(pass, auth->password, sizeof(auth->password)))
204 DROP("wrong password", pass->id);
205
206 return 1;
207
208 case OSPF_AUTH_CRYPT:
209 pass = password_find_by_id(ifa->passwords, auth->c32.keyid);
210 if (!pass)
211 DROP("no suitable password found", auth->c32.keyid);
212
213 uint auth_len = mac_type_length(pass->alg);
214
215 if (plen + auth->c32.len > len)
216 DROP("packet length mismatch", len);
217
218 if (auth->c32.len != auth_len)
219 DROP("wrong authentication length", auth->c32.len);
220
221 u32 rcv_csn = ntohl(auth->c32.csn);
222 if (n && (rcv_csn < n->csn))
223 // DROP("lower sequence number", rcv_csn);
224 {
225 /* We want to report both new and old CSN */
226 LOG_PKT_AUTH("Authentication failed for nbr %R on %s - "
227 "lower sequence number (rcv %u, old %u)",
228 n->rid, ifa->ifname, rcv_csn, n->csn);
229 return 0;
230 }
231
232 byte *auth_tail = ((byte *) pkt) + plen;
233 byte *auth_data = alloca(auth_len);
234 memcpy(auth_data, auth_tail, auth_len);
235
236 /* Append key for keyed hash, append padding for HMAC (RFC 5709 3.3) */
237 if (pass->alg < ALG_HMAC)
238 strncpy(auth_tail, pass->password, auth_len);
239 else
240 memset32(auth_tail, HMAC_MAGIC, auth_len / 4);
241
242 if (!mac_verify(pass->alg, pass->password, pass->length,
243 (byte *) pkt, plen + auth_len, auth_data))
244 DROP("wrong authentication code", pass->id);
245
246 if (n)
247 n->csn = rcv_csn;
248
249 return 1;
250
251 default:
252 bug("Unknown authentication type");
253 }
254
255 drop:
256 LOG_PKT_AUTH("Authentication failed for nbr %R on %s - %s (%u)",
257 (n ? n->rid : ntohl(pkt->routerid)), ifa->ifname, err_dsc, err_val);
258
259 return 0;
260 }
261
262 static int
ospf_pkt_checkauth3(struct ospf_neighbor * n,struct ospf_iface * ifa,struct ospf_packet * pkt,uint len,ip_addr src)263 ospf_pkt_checkauth3(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, uint len, ip_addr src)
264 {
265 struct ospf_proto *p = ifa->oa->po;
266 const char *err_dsc = NULL;
267 uint err_val = 0;
268
269 uint plen = ntohs(pkt->length);
270 uint opts, lls_present, auth_present;
271
272 /*
273 * When autentication is not enabled, ignore the trailer. This is different
274 * from OSPFv2, but it is necessary in order to support migration modes. Note
275 * that regular authenticated packets do not have valid checksum and will be
276 * dropped by OS on non-authenticated ifaces.
277 */
278 if (ifa->autype != OSPF_AUTH_CRYPT)
279 return 1;
280
281 switch(pkt->type)
282 {
283 case HELLO_P:
284 opts = ospf_hello3_options(pkt);
285 lls_present = opts & OPT_L_V3;
286 auth_present = opts & OPT_AT;
287 break;
288
289 case DBDES_P:
290 opts = ospf_dbdes3_options(pkt);
291 lls_present = opts & OPT_L_V3;
292 auth_present = opts & OPT_AT;
293 break;
294
295 default:
296 lls_present = 0;
297 auth_present = n->options & OPT_AT;
298 }
299
300 if (!auth_present)
301 DROP1("missing authentication trailer");
302
303 if (lls_present)
304 {
305 if ((plen + sizeof(struct ospf_lls)) > len)
306 DROP("packet length mismatch", len);
307
308 struct ospf_lls *lls = (void *) ((byte *) pkt + plen);
309 plen += ntohs(lls->length);
310 }
311
312 if ((plen + sizeof(struct ospf_auth3)) > len)
313 DROP("packet length mismatch", len);
314
315 struct ospf_auth3 *auth = (void *) ((byte *) pkt + plen);
316
317 uint rcv_auth_type = ntohs(auth->type);
318 if (rcv_auth_type != OSPF3_AUTH_HMAC)
319 DROP("authentication method mismatch", rcv_auth_type);
320
321 uint rcv_auth_len = ntohs(auth->length);
322 if (plen + rcv_auth_len > len)
323 DROP("packet length mismatch", len);
324
325 uint rcv_key_id = ntohs(auth->sa_id);
326 struct password_item *pass = password_find_by_id(ifa->passwords, rcv_key_id);
327 if (!pass)
328 DROP("no suitable password found", rcv_key_id);
329
330 uint mac_len = mac_type_length(pass->alg);
331 if (rcv_auth_len != (sizeof(struct ospf_auth3) + mac_len))
332 DROP("wrong authentication length", rcv_auth_len);
333
334 uint pt = pkt->type - 1;
335 u64 rcv_csn = get_u64(&auth->csn);
336 if (n && (rcv_csn <= n->csn64[pt]))
337 {
338 /* We want to report both new and old CSN */
339 LOG_PKT_AUTH("Authentication failed for nbr %R on %s - "
340 "lower sequence number (rcv %u, old %u)",
341 n->rid, ifa->ifname, (uint) rcv_csn, (uint) n->csn64[pt]);
342 return 0;
343 }
344
345 /* Save the received authentication data */
346 byte *auth_data = alloca(mac_len);
347 memcpy(auth_data, auth->data, mac_len);
348
349 /* Initialize with src IP address padded with HMAC_MAGIC */
350 put_ip6(auth->data, ipa_to_ip6(src));
351 memset32(auth->data + 16, HMAC_MAGIC, (mac_len - 16) / 4);
352
353 /* Attach OSPFv3 Cryptographic Protocol ID to the key */
354 uint pass_len = pass->length + 2;
355 byte *pass_key = alloca(pass_len);
356 memcpy(pass_key, pass->password, pass->length);
357 put_u16(pass_key + pass->length, OSPF3_CRYPTO_ID);
358
359 if (!mac_verify(pass->alg, pass_key, pass_len,
360 (byte *) pkt, plen + rcv_auth_len, auth_data))
361 DROP("wrong authentication code", pass->id);
362
363 if (n)
364 n->csn64[pt] = rcv_csn;
365
366 return 1;
367
368 drop:
369 LOG_PKT_AUTH("Authentication failed for nbr %R on %s - %s (%u)",
370 (n ? n->rid : ntohl(pkt->routerid)), ifa->ifname, err_dsc, err_val);
371
372 return 0;
373 }
374
375 /**
376 * ospf_rx_hook
377 * @sk: socket we received the packet.
378 * @len: length of the packet
379 *
380 * This is the entry point for messages from neighbors. Many checks (like
381 * authentication, checksums, size) are done before the packet is passed to
382 * non generic functions.
383 */
384 int
ospf_rx_hook(sock * sk,uint len)385 ospf_rx_hook(sock *sk, uint len)
386 {
387 /* We want just packets from sk->iface. Unfortunately, on BSD we cannot filter
388 out other packets at kernel level and we receive all packets on all sockets */
389 if (sk->lifindex != sk->iface->index)
390 return 1;
391
392 DBG("OSPF: RX hook called (iface %s, src %I, dst %I)\n",
393 sk->iface->name, sk->faddr, sk->laddr);
394
395 /* Initially, the packet is associated with the 'master' iface */
396 struct ospf_iface *ifa = sk->data;
397 struct ospf_proto *p = ifa->oa->po;
398 const char *err_dsc = NULL;
399 uint err_val = 0;
400
401 /* Should not happen */
402 if (ifa->state <= OSPF_IS_LOOP)
403 return 1;
404
405 int src_local, dst_local, dst_mcast;
406 src_local = ipa_in_netX(sk->faddr, &ifa->addr->prefix);
407 dst_local = ipa_equal(sk->laddr, ifa->addr->ip);
408 dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers);
409
410 if (ospf_is_v2(p))
411 {
412 /* First, we eliminate packets with strange address combinations.
413 * In OSPFv2, they might be for other ospf_ifaces (with different IP
414 * prefix) on the same real iface, so we don't log it. We enforce
415 * that (src_local || dst_local), therefore we are eliminating all
416 * such cases.
417 */
418 if (dst_mcast && !src_local)
419 return 1;
420 if (!dst_mcast && !dst_local)
421 return 1;
422
423 /* Ignore my own broadcast packets */
424 if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip))
425 return 1;
426 }
427 else
428 {
429 /* In OSPFv3, src_local and dst_local mean link-local.
430 * RFC 5340 says that local (non-vlink) packets use
431 * link-local src address, but does not enforce it. Strange.
432 */
433 if (dst_mcast && !src_local)
434 LOG_PKT_WARN("Multicast packet received from non-link-local %I via %s",
435 sk->faddr, ifa->ifname);
436 }
437
438 /* Second, we check packet length, checksum, and the protocol version */
439 struct ospf_packet *pkt = (void *) sk_rx_buffer(sk, &len);
440
441
442 if (pkt == NULL)
443 DROP("bad IP header", len);
444
445 if (len < sizeof(struct ospf_packet))
446 DROP("too short", len);
447
448 if (pkt->version != ospf_get_version(p))
449 DROP("version mismatch", pkt->version);
450
451 uint plen = ntohs(pkt->length);
452 uint hlen = sizeof(struct ospf_packet) + (ospf_is_v2(p) ? sizeof(union ospf_auth2) : 0);
453 if ((plen < hlen) || ((plen % 4) != 0))
454 DROP("invalid length", plen);
455
456 if (sk->flags & SKF_TRUNCATED)
457 {
458 /* If we have dynamic buffers and received truncated message, we expand RX buffer */
459
460 uint bs = plen + 256;
461 bs = BIRD_ALIGN(bs, 1024);
462
463 if (!ifa->cf->rx_buffer && (bs > sk->rbsize))
464 sk_set_rbsize(sk, bs);
465
466 DROP("truncated", plen);
467 }
468
469 if (plen > len)
470 DROP("length mismatch", plen);
471
472 if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT))
473 {
474 void *body = ((void *) pkt) + hlen;
475 uint blen = plen - hlen;
476
477 if (!ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL))
478 DROP("invalid checksum", ntohs(pkt->checksum));
479 }
480
481 /* Third, we resolve associated iface and handle vlinks. */
482
483 u32 areaid = ntohl(pkt->areaid);
484 u32 rid = ntohl(pkt->routerid);
485 u8 instance_id = pkt->instance_id;
486
487 if (areaid == ifa->oa->areaid)
488 {
489 /* Matching area ID */
490
491 if (instance_id != ifa->instance_id)
492 return 1;
493
494 /* It is real iface, source should be local (in OSPFv2) */
495 if (ospf_is_v2(p) && !src_local)
496 DROP1("strange source address");
497
498 goto found;
499 }
500 else if ((areaid == 0) && !dst_mcast)
501 {
502 /* Backbone area ID and possible vlink packet */
503
504 if ((p->areano == 1) || !oa_is_ext(ifa->oa))
505 return 1;
506
507 struct ospf_iface *iff = NULL;
508 WALK_LIST(iff, p->iface_list)
509 {
510 if ((iff->type == OSPF_IT_VLINK) &&
511 (iff->voa == ifa->oa) &&
512 (iff->instance_id == instance_id) &&
513 (iff->vid == rid))
514 {
515 /* Vlink should be UP */
516 if (iff->state != OSPF_IS_PTP)
517 return 1;
518
519 ifa = iff;
520 goto found;
521 }
522 }
523
524 /*
525 * Cannot find matching vlink. It is either misconfigured vlink; NBMA or
526 * PtMP with misconfigured area ID, or packet for some other instance (that
527 * is possible even if instance_id == ifa->instance_id, because it may be
528 * also vlink packet in the other instance, which is different namespace).
529 */
530
531 return 1;
532 }
533 else
534 {
535 /* Non-matching area ID but cannot be vlink packet */
536
537 if (instance_id != ifa->instance_id)
538 return 1;
539
540 DROP("area mismatch", areaid);
541 }
542
543
544 found:
545 if (ifa->stub) /* This shouldn't happen */
546 return 1;
547
548 if (ipa_equal(sk->laddr, ifa->des_routers) && (ifa->sk_dr == 0))
549 return 1;
550
551 /* TTL check must be done after instance dispatch */
552 if (ifa->check_ttl && (sk->rcv_ttl < 255))
553 DROP("wrong TTL", sk->rcv_ttl);
554
555 if (rid == p->router_id)
556 DROP1("my own router ID");
557
558 if (rid == 0)
559 DROP1("zero router ID");
560
561 /* Check packet type here, ospf_pkt_checkauth3() expects valid values */
562 if (pkt->type < HELLO_P || pkt->type > LSACK_P)
563 DROP("invalid packet type", pkt->type);
564
565 /* In OSPFv2, neighbors are identified by either IP or Router ID, based on network type */
566 uint t = ifa->type;
567 struct ospf_neighbor *n;
568 if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
569 n = find_neigh_by_ip(ifa, sk->faddr);
570 else
571 n = find_neigh(ifa, rid);
572
573 if (!n && (pkt->type != HELLO_P))
574 {
575 OSPF_TRACE(D_PACKETS, "Non-HELLO packet received from unknown nbr %R on %s, src %I",
576 rid, ifa->ifname, sk->faddr);
577 return 1;
578 }
579
580 /* We need to ignore out-of-state packets before ospf_pkt_checkauth3() */
581 if ((pkt->type > DBDES_P) && (n->state < NEIGHBOR_EXCHANGE))
582 {
583 OSPF_TRACE(D_PACKETS, "%s packet ignored - lesser state than Exchange",
584 ospf_pkt_names[pkt->type]);
585 return 1;
586 }
587
588 /* ospf_pkt_checkauthX() has its own error logging */
589 if ((ospf_is_v2(p) ?
590 !ospf_pkt_checkauth2(n, ifa, pkt, len) :
591 !ospf_pkt_checkauth3(n, ifa, pkt, len, sk->faddr)))
592 return 1;
593
594 switch (pkt->type)
595 {
596 case HELLO_P:
597 ospf_receive_hello(pkt, ifa, n, sk->faddr);
598 break;
599
600 case DBDES_P:
601 ospf_receive_dbdes(pkt, ifa, n);
602 break;
603
604 case LSREQ_P:
605 ospf_receive_lsreq(pkt, ifa, n);
606 break;
607
608 case LSUPD_P:
609 ospf_receive_lsupd(pkt, ifa, n);
610 break;
611
612 case LSACK_P:
613 ospf_receive_lsack(pkt, ifa, n);
614 break;
615 };
616 return 1;
617
618 drop:
619 LOG_PKT("Bad packet from %I via %s - %s (%u)",
620 sk->faddr, ifa->ifname, err_dsc, err_val);
621
622 return 1;
623 }
624
625 /*
626 void
627 ospf_tx_hook(sock * sk)
628 {
629 struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
630 // struct proto *p = (struct proto *) (ifa->oa->p);
631 log(L_ERR "OSPF: TX hook called on %s", ifa->ifname);
632 }
633 */
634
635 void
ospf_err_hook(sock * sk,int err)636 ospf_err_hook(sock * sk, int err)
637 {
638 struct ospf_iface *ifa = (struct ospf_iface *) (sk->data);
639 struct ospf_proto *p = ifa->oa->po;
640 log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->ifname, err);
641 }
642
643 void
ospf_verr_hook(sock * sk,int err)644 ospf_verr_hook(sock *sk, int err)
645 {
646 struct ospf_proto *p = (struct ospf_proto *) (sk->data);
647 log(L_ERR "%s: Vlink socket error: %M", p->p.name, err);
648 }
649
650 void
ospf_send_to(struct ospf_iface * ifa,ip_addr dst)651 ospf_send_to(struct ospf_iface *ifa, ip_addr dst)
652 {
653 sock *sk = ifa->sk;
654 struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf;
655 uint plen = ntohs(pkt->length);
656
657 if (ospf_is_v2(ifa->oa->po))
658 ospf_pkt_finalize2(ifa, pkt, &plen);
659 else
660 ospf_pkt_finalize3(ifa, pkt, &plen, sk->saddr);
661
662 int done = sk_send_to(sk, plen, dst, 0);
663 if (!done)
664 log(L_WARN "OSPF: TX queue full on %s", ifa->ifname);
665 }
666
667 void
ospf_send_to_agt(struct ospf_iface * ifa,u8 state)668 ospf_send_to_agt(struct ospf_iface *ifa, u8 state)
669 {
670 struct ospf_neighbor *n;
671
672 WALK_LIST(n, ifa->neigh_list)
673 if (n->state >= state)
674 ospf_send_to(ifa, n->ip);
675 }
676
677 void
ospf_send_to_bdr(struct ospf_iface * ifa)678 ospf_send_to_bdr(struct ospf_iface *ifa)
679 {
680 if (ipa_nonzero2(ifa->drip))
681 ospf_send_to(ifa, ifa->drip);
682 if (ipa_nonzero2(ifa->bdrip))
683 ospf_send_to(ifa, ifa->bdrip);
684 }
685