]>
Commit | Line | Data |
---|---|---|
6a8d3f1c OZ |
1 | /* |
2 | * BIRD -- Bidirectional Forwarding Detection (BFD) | |
3 | * | |
4 | * Can be freely distributed and used under the terms of the GNU GPL. | |
5 | */ | |
bf139664 OZ |
6 | |
7 | #include "bfd.h" | |
8 | ||
9 | ||
6a8d3f1c OZ |
10 | #define HASH_ID_KEY(n) n->loc_id |
11 | #define HASH_ID_NEXT(n) n->next_id | |
12 | #define HASH_ID_EQ(a,b) (a == b) | |
13 | #define HASH_ID_FN(k) (k) | |
bf139664 | 14 | |
6a8d3f1c OZ |
15 | #define HASH_IP_KEY(n) n->addr |
16 | #define HASH_IP_NEXT(n) n->next_ip | |
17 | #define HASH_IP_EQ(a,b) ipa_equal(a,b) | |
18 | #define HASH_IP_FN(k) ipa_hash(k) | |
bf139664 | 19 | |
6a8d3f1c | 20 | static inline void bfd_notify_kick(struct bfd_proto *p); |
bf139664 | 21 | |
6a8d3f1c OZ |
22 | static void |
23 | bfd_session_update_state(struct bfd_session *s, uint state, uint diag) | |
bf139664 | 24 | { |
6a8d3f1c OZ |
25 | struct bfd_proto *p = s->bfd; |
26 | int notify; | |
bf139664 | 27 | |
6a8d3f1c OZ |
28 | if (s->loc_state == state) |
29 | return; | |
bf139664 | 30 | |
6a8d3f1c OZ |
31 | //TRACE(D_EVENTS, "Session changed %I %d %d", s->addr, state, diag); |
32 | debug("STATE %I %d %d %d\n", s->addr, s->loc_state, state, diag); | |
33 | ||
34 | bfd_lock_sessions(p); | |
35 | s->loc_state = state; | |
36 | s->loc_diag = diag; | |
bf139664 | 37 | |
6a8d3f1c OZ |
38 | notify = !NODE_VALID(&s->n); |
39 | if (notify) | |
40 | add_tail(&p->notify_list, &s->n); | |
41 | bfd_unlock_sessions(p); | |
bf139664 | 42 | |
6a8d3f1c OZ |
43 | if (notify) |
44 | bfd_notify_kick(p); | |
bf139664 OZ |
45 | } |
46 | ||
6a8d3f1c OZ |
47 | static void |
48 | bfd_session_timeout(struct bfd_session *s) | |
bf139664 | 49 | { |
6a8d3f1c OZ |
50 | s->rem_state = BFD_STATE_DOWN; |
51 | s->rem_id = 0; | |
52 | s->rem_min_tx_int = 0; | |
53 | s->rem_min_rx_int = 1; | |
54 | s->rem_demand_mode = 0; | |
55 | s->rem_detect_mult = 0; | |
bf139664 | 56 | |
6a8d3f1c | 57 | bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_TIMEOUT); |
bf139664 OZ |
58 | } |
59 | ||
60 | static void | |
6a8d3f1c | 61 | bfd_session_update_tx_interval(struct bfd_session *s) |
bf139664 | 62 | { |
6a8d3f1c OZ |
63 | u32 tx_int = MAX(s->des_min_tx_int, s->rem_min_rx_int); |
64 | u32 tx_int_l = tx_int - (tx_int / 4); // 75 % | |
65 | u32 tx_int_h = tx_int - (tx_int / 10); // 90 % | |
bf139664 | 66 | |
6a8d3f1c OZ |
67 | s->tx_timer->recurrent = tx_int_l; |
68 | s->tx_timer->randomize = tx_int_h - tx_int_l; | |
bf139664 | 69 | |
6a8d3f1c OZ |
70 | /* Do not set timer if no previous event */ |
71 | if (!s->last_tx) | |
72 | return; | |
bf139664 | 73 | |
6a8d3f1c OZ |
74 | /* Set timer relative to last tx_timer event */ |
75 | tm2_set(s->tx_timer, s->last_tx + tx_int_l); | |
bf139664 OZ |
76 | } |
77 | ||
78 | static void | |
6a8d3f1c | 79 | bfd_session_update_detection_time(struct bfd_session *s, int kick) |
bf139664 | 80 | { |
6a8d3f1c | 81 | btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult; |
bf139664 | 82 | |
6a8d3f1c OZ |
83 | if (kick) |
84 | s->last_rx = current_time(); | |
bf139664 | 85 | |
6a8d3f1c OZ |
86 | if (!s->last_rx) |
87 | return; | |
bf139664 | 88 | |
6a8d3f1c | 89 | tm2_set(s->hold_timer, s->last_rx + timeout); |
bf139664 OZ |
90 | } |
91 | ||
92 | static void | |
93 | bfd_session_control_tx_timer(struct bfd_session *s) | |
94 | { | |
95 | if (!s->opened) | |
96 | goto stop; | |
97 | ||
98 | if (s->passive && (s->rem_id == 0)) | |
99 | goto stop; | |
100 | ||
101 | if (s->rem_demand_mode && | |
102 | !s->poll_active && | |
103 | (s->loc_state == BFD_STATE_UP) && | |
104 | (s->rem_state == BFD_STATE_UP)) | |
105 | goto stop; | |
106 | ||
107 | if (s->rem_min_rx_int == 0) | |
108 | goto stop; | |
109 | ||
110 | /* So TX timer should run */ | |
111 | if (tm2_active(s->tx_timer)) | |
112 | return; | |
113 | ||
114 | tm2_start(s->tx_timer, 0); | |
115 | return; | |
116 | ||
117 | stop: | |
118 | tm2_stop(s->tx_timer); | |
119 | s->last_tx = 0; | |
120 | } | |
121 | ||
122 | static void | |
bf139664 OZ |
123 | bfd_session_request_poll(struct bfd_session *s, u8 request) |
124 | { | |
125 | s->poll_scheduled |= request; | |
126 | ||
127 | if (s->poll_active) | |
128 | return; | |
129 | ||
130 | s->poll_active = s->poll_scheduled; | |
131 | s->poll_scheduled = 0; | |
132 | bfd_send_ctl(s->bfd, s, 0); | |
133 | } | |
134 | ||
6a8d3f1c | 135 | static void |
bf139664 OZ |
136 | bfd_session_terminate_poll(struct bfd_session *s) |
137 | { | |
138 | u8 poll_done = s->poll_active & ~s->poll_scheduled; | |
139 | ||
140 | if (poll_done & BFD_POLL_TX) | |
141 | s->des_min_tx_int = s->des_min_tx_new; | |
142 | ||
143 | if (poll_done & BFD_POLL_RX) | |
144 | s->req_min_rx_int = s->req_min_rx_new; | |
145 | ||
146 | s->poll_active = 0; | |
147 | ||
148 | /* Timers are updated by caller - bfd_session_process_ctl() */ | |
149 | ||
6a8d3f1c | 150 | // xxx_restart_poll(); |
bf139664 OZ |
151 | } |
152 | ||
153 | void | |
6a8d3f1c | 154 | bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int) |
bf139664 OZ |
155 | { |
156 | if (s->poll_active && (flags & BFD_FLAG_FINAL)) | |
157 | bfd_session_terminate_poll(s); | |
158 | ||
6a8d3f1c | 159 | if ((s->des_min_tx_int != old_tx_int) || (s->rem_min_rx_int != old_rx_int)) |
bf139664 OZ |
160 | bfd_session_update_tx_interval(s); |
161 | ||
162 | bfd_session_update_detection_time(s, 1); | |
163 | ||
164 | /* Update session state */ | |
165 | int next_state = 0; | |
166 | int diag = BFD_DIAG_NOTHING; | |
167 | ||
168 | switch (s->loc_state) | |
169 | { | |
170 | case BFD_STATE_ADMIN_DOWN: | |
171 | return; | |
172 | ||
173 | case BFD_STATE_DOWN: | |
174 | if (s->rem_state == BFD_STATE_DOWN) next_state = BFD_STATE_INIT; | |
175 | else if (s->rem_state == BFD_STATE_INIT) next_state = BFD_STATE_UP; | |
176 | break; | |
177 | ||
178 | case BFD_STATE_INIT: | |
179 | if (s->rem_state == BFD_STATE_ADMIN_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN; | |
180 | else if (s->rem_state >= BFD_STATE_INIT) next_state = BFD_STATE_UP; | |
181 | break; | |
182 | ||
183 | case BFD_STATE_UP: | |
184 | if (s->rem_state <= BFD_STATE_DOWN) next_state = BFD_STATE_DOWN, diag = BFD_DIAG_NEIGHBOR_DOWN; | |
185 | break; | |
186 | } | |
187 | ||
188 | if (next_state) | |
189 | bfd_session_update_state(s, next_state, diag); | |
190 | ||
191 | bfd_session_control_tx_timer(s); | |
192 | ||
193 | if (flags & BFD_FLAG_POLL) | |
6a8d3f1c | 194 | bfd_send_ctl(s->bfd, s, 1); |
bf139664 OZ |
195 | } |
196 | ||
bf139664 OZ |
197 | static void |
198 | bfd_session_set_min_tx(struct bfd_session *s, u32 val) | |
199 | { | |
200 | /* Note that des_min_tx_int <= des_min_tx_new */ | |
201 | ||
202 | if (val == s->des_min_tx_new) | |
203 | return; | |
204 | ||
205 | s->des_min_tx_new = val; | |
206 | ||
207 | /* Postpone timer update if des_min_tx_int increases and the session is up */ | |
208 | if ((s->loc_state != BFD_STATE_UP) || (val < s->des_min_tx_int)) | |
209 | { | |
210 | s->des_min_tx_int = val; | |
211 | bfd_session_update_tx_interval(s); | |
212 | } | |
213 | ||
214 | bfd_session_request_poll(s, BFD_POLL_TX); | |
215 | } | |
216 | ||
217 | static void | |
218 | bfd_session_set_min_rx(struct bfd_session *s, u32 val) | |
219 | { | |
220 | /* Note that req_min_rx_int >= req_min_rx_new */ | |
221 | ||
222 | if (val == s->req_min_rx_new) | |
223 | return; | |
224 | ||
225 | s->req_min_rx_new = val; | |
226 | ||
227 | /* Postpone timer update if req_min_rx_int decreases and the session is up */ | |
228 | if ((s->loc_state != BFD_STATE_UP) || (val > s->req_min_rx_int)) | |
229 | { | |
230 | s->req_min_rx_int = val; | |
231 | bfd_session_update_detection_time(s, 0); | |
232 | } | |
233 | ||
234 | bfd_session_request_poll(s, BFD_POLL_RX); | |
235 | } | |
236 | ||
6a8d3f1c OZ |
237 | struct bfd_session * |
238 | bfd_find_session_by_id(struct bfd_proto *p, u32 id) | |
239 | { | |
240 | return HASH_FIND(p->session_hash_id, HASH_ID, id); | |
241 | } | |
242 | ||
243 | struct bfd_session * | |
244 | bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr) | |
245 | { | |
246 | return HASH_FIND(p->session_hash_ip, HASH_IP, addr); | |
247 | } | |
248 | ||
249 | static void | |
250 | bfd_tx_timer_hook(timer2 *t) | |
251 | { | |
252 | struct bfd_session *s = t->data; | |
253 | ||
254 | s->last_tx = current_time(); | |
255 | // debug("TX %d\n", (s32) (s->last_tx TO_MS)); | |
256 | bfd_send_ctl(s->bfd, s, 0); | |
257 | } | |
258 | ||
259 | static void | |
260 | bfd_hold_timer_hook(timer2 *t) | |
261 | { | |
262 | bfd_session_timeout(t->data); | |
263 | } | |
264 | ||
265 | static u32 | |
266 | bfd_get_free_id(struct bfd_proto *p) | |
267 | { | |
268 | u32 id; | |
269 | for (id = random_u32(); 1; id++) | |
270 | if (id && !bfd_find_session_by_id(p, id)) | |
271 | break; | |
272 | ||
273 | return id; | |
274 | } | |
275 | ||
276 | static struct bfd_session * | |
277 | bfd_add_session(struct bfd_proto *p, ip_addr addr, struct bfd_session_config *opts) | |
278 | { | |
279 | birdloop_enter(p->loop); | |
280 | ||
281 | struct bfd_session *s = sl_alloc(p->session_slab); | |
282 | bzero(s, sizeof(struct bfd_session)); | |
283 | ||
284 | s->addr = addr; | |
285 | s->loc_id = bfd_get_free_id(p); | |
286 | debug("XXX INS1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr); | |
287 | HASH_INSERT(p->session_hash_id, HASH_ID, s); | |
288 | debug("XXX INS2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count); | |
289 | HASH_INSERT(p->session_hash_ip, HASH_IP, s); | |
290 | debug("XXX INS3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count); | |
291 | s->bfd = p; | |
292 | ||
293 | /* Initialization of state variables - see RFC 5880 6.8.1 */ | |
294 | s->loc_state = BFD_STATE_DOWN; | |
295 | s->rem_state = BFD_STATE_DOWN; | |
296 | s->des_min_tx_int = s->des_min_tx_new = opts->min_tx_int; // XXX opts->idle_tx_int; | |
297 | s->req_min_rx_int = s->req_min_rx_new = opts->min_rx_int; | |
298 | s->rem_min_rx_int = 1; | |
299 | s->detect_mult = opts->multiplier; | |
300 | s->passive = opts->passive; | |
301 | ||
302 | s->tx_timer = tm2_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0); | |
303 | s->hold_timer = tm2_new_init(p->tpool, bfd_hold_timer_hook, s, 0, 0); | |
304 | bfd_session_update_tx_interval(s); | |
305 | ||
306 | birdloop_leave(p->loop); | |
307 | ||
308 | return s; | |
309 | } | |
310 | ||
311 | static void | |
312 | bfd_open_session(struct bfd_proto *p, struct bfd_session *s, ip_addr local, struct iface *ifa) | |
313 | { | |
314 | birdloop_enter(p->loop); | |
315 | ||
316 | s->bsock = bfd_get_socket(p, local, ifa); | |
317 | // s->local = local; | |
318 | // s->iface = ifa; | |
319 | s->opened = 1; | |
320 | ||
321 | bfd_session_control_tx_timer(s); | |
322 | ||
323 | birdloop_leave(p->loop); | |
324 | } | |
325 | ||
326 | static void | |
327 | bfd_close_session(struct bfd_proto *p, struct bfd_session *s) | |
328 | { | |
329 | birdloop_enter(p->loop); | |
330 | ||
331 | bfd_free_socket(s->bsock); | |
332 | s->bsock = NULL; | |
333 | // s->local = IPA_NONE; | |
334 | // s->iface = NULL; | |
335 | s->opened = 0; | |
336 | ||
337 | bfd_session_update_state(s, BFD_STATE_DOWN, BFD_DIAG_PATH_DOWN); | |
338 | bfd_session_control_tx_timer(s); | |
339 | ||
340 | birdloop_leave(p->loop); | |
341 | } | |
342 | ||
343 | static void | |
344 | bfd_remove_session(struct bfd_proto *p, struct bfd_session *s) | |
345 | { | |
346 | birdloop_enter(p->loop); | |
347 | ||
348 | bfd_free_socket(s->bsock); | |
349 | ||
350 | rfree(s->tx_timer); | |
351 | rfree(s->hold_timer); | |
352 | ||
353 | debug("XXX REM1 %d %d %u %I\n", p->session_hash_id.count, p->session_hash_ip.count, s->loc_id, s->addr); | |
354 | HASH_REMOVE(p->session_hash_id, HASH_ID, s); | |
355 | debug("XXX REM2 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count); | |
356 | HASH_REMOVE(p->session_hash_ip, HASH_IP, s); | |
357 | debug("XXX REM3 %d %d\n", p->session_hash_id.count, p->session_hash_ip.count); | |
358 | ||
359 | sl_free(p->session_slab, s); | |
360 | ||
361 | birdloop_leave(p->loop); | |
362 | } | |
363 | ||
364 | static void | |
365 | bfd_configure_session(struct bfd_proto *p, struct bfd_session *s, | |
366 | struct bfd_session_config *opts) | |
367 | { | |
368 | birdloop_enter(p->loop); | |
369 | ||
370 | // XXX opts->idle_tx_int; | |
371 | ||
372 | bfd_session_set_min_tx(s, opts->min_tx_int); | |
373 | bfd_session_set_min_rx(s, opts->min_rx_int); | |
374 | s->detect_mult = opts->multiplier; | |
375 | s->passive = opts->passive; | |
376 | ||
377 | bfd_session_control_tx_timer(s); | |
378 | ||
379 | birdloop_leave(p->loop); | |
380 | } | |
381 | ||
bf139664 OZ |
382 | static void |
383 | bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) | |
384 | { | |
385 | n->session = bfd_add_session(p, n->addr, n->opts); | |
386 | ||
387 | if (n->opts->multihop) | |
388 | { | |
389 | bfd_open_session(p, n->session, n->local, NULL); | |
390 | return; | |
391 | } | |
392 | ||
393 | struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, NEF_STICKY); | |
394 | if (!nb) | |
395 | { | |
396 | log(L_ERR "%s: Invalid remote address %I%J", p->p.name, n->addr, n->iface); | |
397 | return; | |
398 | } | |
399 | ||
400 | if (nb->data) | |
401 | { | |
402 | log(L_ERR "%s: Duplicate remote address %I", p->p.name, n->addr); | |
403 | return; | |
404 | } | |
405 | ||
406 | nb->data = n->session; | |
407 | ||
408 | if (nb->scope > 0) | |
409 | bfd_open_session(p, n->session, nb->iface->addr->ip, nb->iface); | |
410 | else | |
6a8d3f1c | 411 | TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", n->addr, n->iface); |
bf139664 OZ |
412 | } |
413 | ||
414 | static void | |
415 | bfd_stop_neighbor(struct bfd_proto *p, struct bfd_neighbor *n) | |
416 | { | |
417 | if (!n->opts->multihop) | |
418 | { | |
419 | struct neighbor *nb = neigh_find2(&p->p, &n->addr, n->iface, 0); | |
420 | if (nb) | |
421 | nb->data = NULL; | |
422 | } | |
423 | ||
424 | bfd_remove_session(p, n->session); | |
425 | } | |
426 | ||
bf139664 OZ |
427 | static void |
428 | bfd_neigh_notify(struct neighbor *nb) | |
429 | { | |
430 | struct bfd_proto *p = (struct bfd_proto *) nb->proto; | |
431 | struct bfd_session *s = nb->data; | |
432 | ||
433 | if (!s) | |
434 | return; | |
435 | ||
436 | if ((nb->scope > 0) && !s->opened) | |
437 | bfd_open_session(p, s, nb->iface->addr->ip, nb->iface); | |
438 | ||
439 | if ((nb->scope <= 0) && s->opened) | |
440 | bfd_close_session(p, s); | |
441 | } | |
442 | ||
443 | ||
6a8d3f1c OZ |
444 | /* This core notify code should be replaced after main loop transition to birdloop */ |
445 | ||
446 | int pipe(int pipefd[2]); | |
447 | void pipe_drain(int fd); | |
448 | void pipe_kick(int fd); | |
449 | ||
450 | static int | |
451 | bfd_notify_hook(sock *sk, int len) | |
452 | { | |
453 | struct bfd_proto *p = sk->data; | |
454 | struct bfd_session *s; | |
455 | list tmp_list; | |
456 | ||
457 | pipe_drain(sk->fd); | |
458 | ||
459 | bfd_lock_sessions(p); | |
460 | init_list(&tmp_list); | |
461 | add_tail_list(&tmp_list, &p->notify_list); | |
462 | init_list(&p->notify_list); | |
463 | bfd_unlock_sessions(p); | |
464 | ||
465 | WALK_LIST_FIRST(s, tmp_list) | |
466 | { | |
467 | bfd_lock_sessions(p); | |
468 | rem2_node(&s->n); | |
469 | bfd_unlock_sessions(p); | |
470 | ||
471 | // XXX do something | |
472 | TRACE(D_EVENTS, "Notify: session changed %I %d %d", s->addr, s->loc_state, s->loc_diag); | |
473 | } | |
474 | ||
475 | return 0; | |
476 | } | |
477 | ||
478 | static inline void | |
479 | bfd_notify_kick(struct bfd_proto *p) | |
480 | { | |
481 | pipe_kick(p->notify_ws->fd); | |
482 | } | |
483 | ||
484 | static void | |
485 | bfd_noterr_hook(sock *sk, int err) | |
486 | { | |
487 | struct bfd_proto *p = sk->data; | |
488 | log(L_ERR "%s: Notify socket error: %m", p->p.name, err); | |
489 | } | |
490 | ||
491 | static void | |
492 | bfd_notify_init(struct bfd_proto *p) | |
493 | { | |
494 | int pfds[2]; | |
495 | sock *sk; | |
496 | ||
497 | int rv = pipe(pfds); | |
498 | if (rv < 0) | |
499 | die("pipe: %m"); | |
500 | ||
501 | sk = sk_new(p->p.pool); | |
502 | sk->type = SK_MAGIC; | |
503 | sk->rx_hook = bfd_notify_hook; | |
504 | sk->err_hook = bfd_noterr_hook; | |
505 | sk->fd = pfds[0]; | |
506 | sk->data = p; | |
507 | if (sk_open(sk) < 0) | |
508 | die("bfd: sk_open failed"); | |
509 | p->notify_rs = sk; | |
510 | ||
511 | /* The write sock is not added to any event loop */ | |
512 | sk = sk_new(p->p.pool); | |
513 | sk->type = SK_MAGIC; | |
514 | sk->fd = pfds[1]; | |
515 | sk->data = p; | |
516 | sk->flags = SKF_THREAD; | |
517 | if (sk_open(sk) < 0) | |
518 | die("bfd: sk_open failed"); | |
519 | p->notify_ws = sk; | |
520 | } | |
521 | ||
bf139664 OZ |
522 | |
523 | static struct proto * | |
524 | bfd_init(struct proto_config *c) | |
525 | { | |
526 | struct proto *p = proto_new(c, sizeof(struct bfd_proto)); | |
527 | ||
6a8d3f1c | 528 | p->neigh_notify = bfd_neigh_notify; |
bf139664 OZ |
529 | |
530 | return p; | |
531 | } | |
532 | ||
533 | static int | |
534 | bfd_start(struct proto *P) | |
535 | { | |
536 | struct bfd_proto *p = (struct bfd_proto *) P; | |
537 | struct bfd_config *cf = (struct bfd_config *) (P->cf); | |
538 | ||
6a8d3f1c OZ |
539 | p->loop = birdloop_new(P->pool); |
540 | p->tpool = rp_new(NULL, "BFD thread root"); | |
541 | pthread_spin_init(&p->lock, PTHREAD_PROCESS_PRIVATE); | |
542 | ||
bf139664 | 543 | p->session_slab = sl_new(P->pool, sizeof(struct bfd_session)); |
6a8d3f1c OZ |
544 | HASH_INIT(p->session_hash_id, P->pool, 4); |
545 | HASH_INIT(p->session_hash_ip, P->pool, 4); | |
546 | ||
547 | init_list(&p->sock_list); | |
548 | ||
549 | ||
550 | birdloop_mask_wakeups(p->loop); | |
bf139664 | 551 | |
6a8d3f1c OZ |
552 | init_list(&p->notify_list); |
553 | bfd_notify_init(p); | |
554 | ||
555 | birdloop_enter(p->loop); | |
556 | p->rx_1 = bfd_open_rx_sk(p, 0); | |
557 | p->rx_m = bfd_open_rx_sk(p, 1); | |
558 | birdloop_leave(p->loop); | |
bf139664 OZ |
559 | |
560 | struct bfd_neighbor *n; | |
6a8d3f1c | 561 | WALK_LIST(n, cf->neigh_list) |
bf139664 OZ |
562 | bfd_start_neighbor(p, n); |
563 | ||
6a8d3f1c OZ |
564 | birdloop_unmask_wakeups(p->loop); |
565 | ||
bf139664 OZ |
566 | return PS_UP; |
567 | } | |
568 | ||
569 | ||
570 | static int | |
571 | bfd_shutdown(struct proto *P) | |
572 | { | |
573 | struct bfd_proto *p = (struct bfd_proto *) P; | |
574 | ||
575 | return PS_DOWN; | |
576 | } | |
577 | ||
578 | static inline int | |
579 | bfd_same_neighbor(struct bfd_neighbor *x, struct bfd_neighbor *y) | |
580 | { | |
581 | return ipa_equal(x->addr, y->addr) && ipa_equal(x->local, y->local) && | |
582 | (x->iface == y->iface) && (x->opts->multihop == y->opts->multihop); | |
583 | } | |
584 | ||
585 | static void | |
586 | bfd_match_neighbor(struct bfd_proto *p, struct bfd_neighbor *on, struct bfd_config *new) | |
587 | { | |
588 | struct bfd_neighbor *nn; | |
589 | ||
6a8d3f1c | 590 | WALK_LIST(nn, new->neigh_list) |
bf139664 OZ |
591 | if (bfd_same_neighbor(nn, on)) |
592 | { | |
593 | nn->session = on->session; | |
6a8d3f1c | 594 | bfd_configure_session(p, nn->session, nn->opts); |
bf139664 OZ |
595 | return; |
596 | } | |
597 | ||
598 | bfd_stop_neighbor(p, on); | |
599 | } | |
600 | ||
601 | static int | |
602 | bfd_reconfigure(struct proto *P, struct proto_config *c) | |
603 | { | |
604 | struct bfd_proto *p = (struct bfd_proto *) P; | |
605 | struct bfd_config *old = (struct bfd_config *) (P->cf); | |
606 | struct bfd_config *new = (struct bfd_config *) c; | |
607 | struct bfd_neighbor *n; | |
608 | ||
6a8d3f1c OZ |
609 | birdloop_mask_wakeups(p->loop); |
610 | ||
611 | WALK_LIST(n, old->neigh_list) | |
bf139664 OZ |
612 | bfd_match_neighbor(p, n, new); |
613 | ||
6a8d3f1c | 614 | WALK_LIST(n, new->neigh_list) |
bf139664 OZ |
615 | if (!n->session) |
616 | bfd_start_neighbor(p, n); | |
617 | ||
6a8d3f1c OZ |
618 | birdloop_unmask_wakeups(p->loop); |
619 | ||
bf139664 OZ |
620 | return 1; |
621 | } | |
622 | ||
623 | static void | |
624 | bfd_copy_config(struct proto_config *dest, struct proto_config *src) | |
625 | { | |
626 | struct bfd_config *d = (struct bfd_config *) dest; | |
6a8d3f1c | 627 | // struct bfd_config *s = (struct bfd_config *) src; |
bf139664 | 628 | |
6a8d3f1c OZ |
629 | /* We clean up neigh_list, ifaces are non-sharable */ |
630 | init_list(&d->neigh_list); | |
bf139664 | 631 | |
bf139664 OZ |
632 | } |
633 | ||
6a8d3f1c OZ |
634 | void |
635 | bfd_show_sessions(struct proto *P) | |
636 | { | |
637 | struct bfd_proto *p = (struct bfd_proto *) P; | |
638 | uint state, diag; | |
639 | u32 tx_int, timeout; | |
640 | const char *ifname; | |
641 | ||
642 | if (p->p.proto_state != PS_UP) | |
643 | { | |
644 | cli_msg(-1013, "%s: is not up", p->p.name); | |
645 | cli_msg(0, ""); | |
646 | return; | |
647 | } | |
648 | ||
649 | cli_msg(-1013, "%s:", p->p.name); | |
650 | cli_msg(-1013, "%-12s\t%s\t%s\t%s\t%s", "Router IP", "Iface", | |
651 | "State", "TX Int", "Timeout"); | |
652 | ||
653 | debug("XXX WALK %d %d\n", p->session_hash_id.count, p->session_hash_ip.count); | |
654 | ||
655 | HASH_WALK(p->session_hash_id, next_id, s) | |
656 | { | |
657 | // FIXME this is unsafe | |
658 | state = s->loc_state; | |
659 | diag = s->loc_diag; | |
660 | ifname = (s->bsock && s->bsock->sk->iface) ? s->bsock->sk->iface->name : "---"; | |
661 | tx_int = (MAX(s->des_min_tx_int, s->rem_min_rx_int) TO_MS); | |
662 | timeout = (MAX(s->req_min_rx_int, s->rem_min_tx_int) TO_MS) * s->rem_detect_mult; | |
663 | ||
664 | cli_msg(-1013, "%I\t%s\t%d %d\t%u\t%u", | |
665 | s->addr, ifname, state, diag, tx_int, timeout); | |
666 | } | |
667 | HASH_WALK_END; | |
668 | ||
669 | cli_msg(0, ""); | |
670 | } | |
671 | ||
672 | ||
bf139664 OZ |
673 | struct protocol proto_bfd = { |
674 | .name = "BFD", | |
675 | .template = "bfd%d", | |
676 | .init = bfd_init, | |
677 | .start = bfd_start, | |
678 | .shutdown = bfd_shutdown, | |
679 | .reconfigure = bfd_reconfigure, | |
680 | .copy_config = bfd_copy_config, | |
681 | }; |