1 /* $OpenBSD: bfd.c,v 1.80 2023/08/03 09:49:08 mvs Exp $ */
2
3 /*
4 * Copyright (c) 2016-2018 Peter Hessler <phessler@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Support for Bi-directional Forwarding Detection (RFC 5880 / 5881)
21 */
22
23 #include <sys/param.h>
24 #include <sys/errno.h>
25
26 #include <sys/task.h>
27 #include <sys/pool.h>
28 #include <sys/socket.h>
29 #include <sys/socketvar.h>
30 #include <sys/stdint.h>
31 #include <sys/systm.h>
32
33 #include <net/if.h>
34 #include <net/if_var.h>
35 #include <net/route.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38
39 #include <net/bfd.h>
40
41 /*
42 * RFC 5880 Page 7
43 * The Mandatory Section of a BFD Control packet has the following
44 * format:
45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 * | My Discriminator |
49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 * | Your Discriminator |
51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 * | Desired Min TX Interval |
53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * | Required Min RX Interval |
55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56 * | Required Min Echo RX Interval |
57 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58 *
59 *
60 * An optional Authentication Section MAY be present:
61 * 0 1 2 3
62 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
63 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 * | Auth Type | Auth Len | Authentication Data... |
65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 *
67 */
68
69 /* BFD on-wire format */
70 struct bfd_header {
71 uint8_t bfd_ver_diag;
72 uint8_t bfd_sta_flags;
73
74 uint8_t bfd_detect_multi; /* detection time multiplier */
75 uint8_t bfd_length; /* in bytes */
76 uint32_t bfd_my_discriminator; /* From this system */
77 uint32_t bfd_your_discriminator; /* Received */
78 uint32_t bfd_desired_min_tx_interval; /* in microseconds */
79 uint32_t bfd_required_min_rx_interval; /* in microseconds */
80 uint32_t bfd_required_min_echo_interval; /* in microseconds */
81 } __packed;
82
83 /* optional authentication on-wire format */
84 struct bfd_auth_header {
85 uint8_t bfd_auth_type;
86 uint8_t bfd_auth_len;
87 uint16_t bfd_auth_data;
88 } __packed;
89
90 #define BFD_VERSION 1 /* RFC 5880 Page 6 */
91 #define BFD_VER(x) (((x) & 0xe0) >> 5)
92 #define BFD_DIAG(x) ((x) & 0x1f)
93 #define BFD_STATE(x) (((x) & 0xc0) >> 6)
94 #define BFD_FLAGS(x) ((x) & 0x3f)
95 #define BFD_HDRLEN 24 /* RFC 5880 Page 37 */
96 #define BFD_AUTH_SIMPLE_LEN 16 + 3 /* RFC 5880 Page 10 */
97 #define BFD_AUTH_MD5_LEN 24 /* RFC 5880 Page 11 */
98 #define BFD_AUTH_SHA1_LEN 28 /* RFC 5880 Page 12 */
99
100 /* Diagnostic Code (RFC 5880 Page 8) */
101 #define BFD_DIAG_NONE 0
102 #define BFD_DIAG_EXPIRED 1
103 #define BFD_DIAG_ECHO_FAILED 2
104 #define BFD_DIAG_NEIGHBOR_SIGDOWN 3
105 #define BFD_DIAG_FIB_RESET 4
106 #define BFD_DIAG_PATH_DOWN 5
107 #define BFD_DIAG_CONCAT_PATH_DOWN 6
108 #define BFD_DIAG_ADMIN_DOWN 7
109 #define BFD_DIAG_CONCAT_REVERSE_DOWN 8
110
111 /* State (RFC 5880 Page 8) */
112 #define BFD_STATE_ADMINDOWN 0
113 #define BFD_STATE_DOWN 1
114 #define BFD_STATE_INIT 2
115 #define BFD_STATE_UP 3
116
117 /* Flags (RFC 5880 Page 8) */
118 #define BFD_FLAG_P 0x20
119 #define BFD_FLAG_F 0x10
120 #define BFD_FLAG_C 0x08
121 #define BFD_FLAG_A 0x04
122 #define BFD_FLAG_D 0x02
123 #define BFD_FLAG_M 0x01
124
125
126 /* Auth Type (RFC 5880 Page 10) */
127 #define BFD_AUTH_TYPE_RESERVED 0
128 #define BFD_AUTH_TYPE_SIMPLE 1
129 #define BFD_AUTH_KEYED_MD5 2
130 #define BFD_AUTH_METICULOUS_MD5 3
131 #define BFD_AUTH_KEYED_SHA1 4
132 #define BFD_AUTH_METICULOUS_SHA1 5
133
134 #define BFD_UDP_PORT_CONTROL 3784
135 #define BFD_UDP_PORT_ECHO 3785
136
137 #define BFD_SECOND 1000000 /* 1,000,000 us == 1 second */
138 /* We currently tick every 10ms, so force a minimum that can be handled */
139 #define BFD_MINIMUM 50000 /* 50,000 us == 50 ms */
140
141
142 struct pool bfd_pool, bfd_pool_neigh, bfd_pool_time;
143 struct taskq *bfdtq;
144
145
146 struct bfd_config *bfd_lookup(struct rtentry *);
147 void bfddestroy(void);
148
149 struct socket *bfd_listener(struct bfd_config *, unsigned int);
150 struct socket *bfd_sender(struct bfd_config *, unsigned int);
151 void bfd_input(struct bfd_config *, struct mbuf *);
152 void bfd_set_state(struct bfd_config *, unsigned int);
153
154 int bfd_send(struct bfd_config *, struct mbuf *);
155 void bfd_send_control(void *);
156
157 void bfd_start_task(void *);
158 void bfd_send_task(void *);
159 void bfd_upcall_task(void *);
160 void bfd_clear_task(void *);
161 void bfd_error(struct bfd_config *);
162 void bfd_timeout_rx(void *);
163 void bfd_timeout_tx(void *);
164
165 void bfd_upcall(struct socket *, caddr_t, int);
166 void bfd_senddown(struct bfd_config *);
167 void bfd_reset(struct bfd_config *);
168 void bfd_set_uptime(struct bfd_config *);
169
170 void bfd_debug(struct bfd_config *);
171
172 TAILQ_HEAD(bfd_queue, bfd_config) bfd_queue;
173
174 /*
175 * allocate a new bfd session
176 */
177 int
bfdset(struct rtentry * rt)178 bfdset(struct rtentry *rt)
179 {
180 struct bfd_config *bfd;
181
182 /* at the moment it is not allowed to run BFD on indirect routes */
183 if (ISSET(rt->rt_flags, RTF_GATEWAY) || !ISSET(rt->rt_flags, RTF_HOST))
184 return (EINVAL);
185
186 /* Do our necessary memory allocations upfront */
187 bfd = pool_get(&bfd_pool, PR_WAITOK | PR_ZERO);
188
189 /* make sure we don't already have this setup */
190 if (bfd_lookup(rt) != NULL) {
191 pool_put(&bfd_pool, bfd);
192 return (EADDRINUSE);
193 }
194
195 bfd->bc_neighbor = pool_get(&bfd_pool_neigh, PR_WAITOK | PR_ZERO);
196 bfd->bc_time = pool_get(&bfd_pool_time, PR_WAITOK | PR_ZERO);
197
198 bfd->bc_rt = rt;
199 rtref(bfd->bc_rt); /* we depend on this route not going away */
200
201 getmicrotime(bfd->bc_time);
202 bfd_reset(bfd);
203 bfd->bc_neighbor->bn_ldiscr = arc4random();
204
205 if (!timeout_initialized(&bfd->bc_timo_rx))
206 timeout_set(&bfd->bc_timo_rx, bfd_timeout_rx, bfd);
207 if (!timeout_initialized(&bfd->bc_timo_tx))
208 timeout_set(&bfd->bc_timo_tx, bfd_timeout_tx, bfd);
209
210 task_set(&bfd->bc_bfd_task, bfd_start_task, bfd);
211 task_set(&bfd->bc_clear_task, bfd_clear_task, bfd);
212
213 task_add(bfdtq, &bfd->bc_bfd_task);
214
215 TAILQ_INSERT_TAIL(&bfd_queue, bfd, bc_entry);
216 bfd_set_state(bfd, BFD_STATE_DOWN);
217
218 return (0);
219 }
220
221 /*
222 * remove and free a bfd session
223 */
224 void
bfdclear(struct rtentry * rt)225 bfdclear(struct rtentry *rt)
226 {
227 struct bfd_config *bfd;
228
229 if ((bfd = bfd_lookup(rt)) == NULL)
230 return;
231
232 task_add(bfdtq, &bfd->bc_clear_task);
233 }
234
235 void
bfd_clear_task(void * arg)236 bfd_clear_task(void *arg)
237 {
238 struct bfd_config *bfd = (struct bfd_config *)arg;
239 struct rtentry *rt = bfd->bc_rt;
240
241 timeout_del(&bfd->bc_timo_rx);
242 timeout_del(&bfd->bc_timo_tx);
243 task_del(bfdtq, &bfd->bc_upcall_task);
244 task_del(bfdtq, &bfd->bc_bfd_send_task);
245
246 TAILQ_REMOVE(&bfd_queue, bfd, bc_entry);
247
248 /* inform our neighbor */
249 bfd_senddown(bfd);
250
251 rt->rt_flags &= ~RTF_BFD;
252 if (bfd->bc_so) {
253 /* remove upcall before calling soclose or it will be called */
254 bfd->bc_so->so_upcall = NULL;
255 soclose(bfd->bc_so, MSG_DONTWAIT);
256 }
257 if (bfd->bc_soecho) {
258 bfd->bc_soecho->so_upcall = NULL;
259 soclose(bfd->bc_soecho, MSG_DONTWAIT);
260 }
261 if (bfd->bc_sosend)
262 soclose(bfd->bc_sosend, MSG_DONTWAIT);
263
264 rtfree(bfd->bc_rt);
265 bfd->bc_rt = NULL;
266
267 pool_put(&bfd_pool_time, bfd->bc_time);
268 pool_put(&bfd_pool_neigh, bfd->bc_neighbor);
269 pool_put(&bfd_pool, bfd);
270 }
271
272 /*
273 * Create and initialize the global bfd framework
274 */
275 void
bfdinit(void)276 bfdinit(void)
277 {
278 pool_init(&bfd_pool, sizeof(struct bfd_config), 0,
279 IPL_SOFTNET, 0, "bfd_config", NULL);
280 pool_init(&bfd_pool_neigh, sizeof(struct bfd_neighbor), 0,
281 IPL_SOFTNET, 0, "bfd_config_peer", NULL);
282 pool_init(&bfd_pool_time, sizeof(struct timeval), 0,
283 IPL_SOFTNET, 0, "bfd_config_time", NULL);
284
285 bfdtq = taskq_create("bfd", 1, IPL_SOFTNET, 0);
286 if (bfdtq == NULL)
287 panic("unable to create BFD taskq");
288
289 TAILQ_INIT(&bfd_queue);
290 }
291
292 /*
293 * Destroy all bfd sessions and remove the tasks
294 *
295 */
296 void
bfddestroy(void)297 bfddestroy(void)
298 {
299 struct bfd_config *bfd;
300
301 /* inform our neighbor we are rebooting */
302 while ((bfd = TAILQ_FIRST(&bfd_queue))) {
303 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_FIB_RESET;
304 bfdclear(bfd->bc_rt);
305 }
306
307 taskq_barrier(bfdtq);
308 taskq_destroy(bfdtq);
309 pool_destroy(&bfd_pool_time);
310 pool_destroy(&bfd_pool_neigh);
311 pool_destroy(&bfd_pool);
312 }
313
314 /*
315 * Return the matching bfd
316 */
317 struct bfd_config *
bfd_lookup(struct rtentry * rt)318 bfd_lookup(struct rtentry *rt)
319 {
320 struct bfd_config *bfd;
321
322 TAILQ_FOREACH(bfd, &bfd_queue, bc_entry) {
323 if (bfd->bc_rt == rt)
324 return (bfd);
325 }
326 return (NULL);
327 }
328
329 struct sockaddr *
bfd2sa(struct rtentry * rt,struct sockaddr_bfd * sa_bfd)330 bfd2sa(struct rtentry *rt, struct sockaddr_bfd *sa_bfd)
331 {
332 struct bfd_config *bfd;
333
334 bfd = bfd_lookup(rt);
335
336 if (bfd == NULL)
337 return (NULL);
338
339 memset(sa_bfd, 0, sizeof(*sa_bfd));
340 sa_bfd->bs_len = sizeof(*sa_bfd);
341 sa_bfd->bs_family = bfd->bc_rt->rt_dest->sa_family;
342
343 sa_bfd->bs_mode = bfd->bc_mode;
344 sa_bfd->bs_mintx = bfd->bc_mintx;
345 sa_bfd->bs_minrx = bfd->bc_minrx;
346 sa_bfd->bs_minecho = bfd->bc_minecho;
347 sa_bfd->bs_multiplier = bfd->bc_multiplier;
348
349 sa_bfd->bs_uptime = bfd->bc_time->tv_sec;
350 sa_bfd->bs_lastuptime = bfd->bc_lastuptime;
351 sa_bfd->bs_state = bfd->bc_state;
352 sa_bfd->bs_remotestate = bfd->bc_neighbor->bn_rstate;
353 sa_bfd->bs_laststate = bfd->bc_laststate;
354 sa_bfd->bs_error = bfd->bc_error;
355
356 sa_bfd->bs_localdiscr = bfd->bc_neighbor->bn_ldiscr;
357 sa_bfd->bs_localdiag = bfd->bc_neighbor->bn_ldiag;
358 sa_bfd->bs_remotediscr = bfd->bc_neighbor->bn_rdiscr;
359 sa_bfd->bs_remotediag = bfd->bc_neighbor->bn_rdiag;
360
361 return ((struct sockaddr *)sa_bfd);
362 }
363
364 /*
365 * End of public interfaces.
366 *
367 * Everything below this line should not be used outside of this file.
368 */
369
370 /*
371 * Task to listen and kick off the bfd process
372 */
373 void
bfd_start_task(void * arg)374 bfd_start_task(void *arg)
375 {
376 struct bfd_config *bfd = (struct bfd_config *)arg;
377
378 /* start listeners */
379 bfd->bc_so = bfd_listener(bfd, BFD_UDP_PORT_CONTROL);
380 if (!bfd->bc_so)
381 printf("bfd_listener(%d) failed\n",
382 BFD_UDP_PORT_CONTROL);
383 bfd->bc_soecho = bfd_listener(bfd, BFD_UDP_PORT_ECHO);
384 if (!bfd->bc_soecho)
385 printf("bfd_listener(%d) failed\n",
386 BFD_UDP_PORT_ECHO);
387
388 /* start sending */
389 bfd->bc_sosend = bfd_sender(bfd, BFD_UDP_PORT_CONTROL);
390 if (bfd->bc_sosend) {
391 task_set(&bfd->bc_bfd_send_task, bfd_send_task, bfd);
392 task_add(bfdtq, &bfd->bc_bfd_send_task);
393 }
394
395 task_set(&bfd->bc_upcall_task, bfd_upcall_task, bfd);
396
397 return;
398 }
399
400 void
bfd_send_task(void * arg)401 bfd_send_task(void *arg)
402 {
403 struct bfd_config *bfd = (struct bfd_config *)arg;
404 struct rtentry *rt = bfd->bc_rt;
405
406 if (ISSET(rt->rt_flags, RTF_UP)) {
407 bfd_send_control(bfd);
408 } else {
409 if (bfd->bc_neighbor->bn_lstate > BFD_STATE_DOWN) {
410 bfd->bc_error++;
411 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_PATH_DOWN;
412 bfd_reset(bfd);
413 bfd_set_state(bfd, BFD_STATE_DOWN);
414 }
415 }
416 //rtm_bfd(bfd);
417
418 /* re-add 70%-90% jitter to our transmits, rfc 5880 6.8.7 */
419 timeout_add_usec(&bfd->bc_timo_tx,
420 bfd->bc_mintx * (arc4random_uniform(20) + 70) / 100);
421 }
422
423 /*
424 * Setup a bfd listener socket
425 */
426 struct socket *
bfd_listener(struct bfd_config * bfd,unsigned int port)427 bfd_listener(struct bfd_config *bfd, unsigned int port)
428 {
429 struct proc *p = curproc;
430 struct rtentry *rt = bfd->bc_rt;
431 struct sockaddr *src = rt->rt_ifa->ifa_addr;
432 struct sockaddr *dst = rt_key(rt);
433 struct sockaddr *sa;
434 struct sockaddr_in *sin;
435 struct sockaddr_in6 *sin6;
436 struct socket *so;
437 struct mbuf *m = NULL, *mopt = NULL;
438 int *ip, error;
439
440 /* sa_family and sa_len must be equal */
441 if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len)
442 return (NULL);
443
444 error = socreate(dst->sa_family, &so, SOCK_DGRAM, 0);
445 if (error) {
446 printf("%s: socreate error %d\n",
447 __func__, error);
448 return (NULL);
449 }
450
451 MGET(mopt, M_WAIT, MT_SOOPTS);
452 mopt->m_len = sizeof(int);
453 ip = mtod(mopt, int *);
454 *ip = MAXTTL;
455 error = sosetopt(so, IPPROTO_IP, IP_MINTTL, mopt);
456 m_freem(mopt);
457 if (error) {
458 printf("%s: sosetopt error %d\n",
459 __func__, error);
460 goto close;
461 }
462
463 MGET(m, M_WAIT, MT_SONAME);
464 m->m_len = src->sa_len;
465 sa = mtod(m, struct sockaddr *);
466 memcpy(sa, src, src->sa_len);
467 switch(sa->sa_family) {
468 case AF_INET:
469 sin = (struct sockaddr_in *)sa;
470 sin->sin_port = htons(port);
471 break;
472 case AF_INET6:
473 sin6 = (struct sockaddr_in6 *)sa;
474 sin6->sin6_port = htons(port);
475 break;
476 default:
477 break;
478 }
479
480 solock(so);
481 error = sobind(so, m, p);
482 sounlock(so);
483 if (error) {
484 printf("%s: sobind error %d\n",
485 __func__, error);
486 goto close;
487 }
488 so->so_upcallarg = (caddr_t)bfd;
489 so->so_upcall = bfd_upcall;
490
491 m_free(m);
492
493 return (so);
494
495 close:
496 m_free(m);
497 soclose(so, MSG_DONTWAIT);
498
499 return (NULL);
500 }
501
502 /*
503 * Setup the bfd sending process
504 */
505 struct socket *
bfd_sender(struct bfd_config * bfd,unsigned int port)506 bfd_sender(struct bfd_config *bfd, unsigned int port)
507 {
508 struct socket *so;
509 struct rtentry *rt = bfd->bc_rt;
510 struct proc *p = curproc;
511 struct mbuf *m = NULL, *mopt = NULL;
512 struct sockaddr *src = rt->rt_ifa->ifa_addr;
513 struct sockaddr *dst = rt_key(rt);
514 struct sockaddr *sa;
515 struct sockaddr_in6 *sin6;
516 struct sockaddr_in *sin;
517 int error, *ip;
518
519 /* sa_family and sa_len must be equal */
520 if (src->sa_family != dst->sa_family || src->sa_len != dst->sa_len)
521 return (NULL);
522
523 error = socreate(dst->sa_family, &so, SOCK_DGRAM, 0);
524
525 if (error)
526 return (NULL);
527
528 MGET(mopt, M_WAIT, MT_SOOPTS);
529 mopt->m_len = sizeof(int);
530 ip = mtod(mopt, int *);
531 *ip = IP_PORTRANGE_HIGH;
532 error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
533 m_freem(mopt);
534 if (error) {
535 printf("%s: sosetopt error %d\n",
536 __func__, error);
537 goto close;
538 }
539
540 MGET(mopt, M_WAIT, MT_SOOPTS);
541 mopt->m_len = sizeof(int);
542 ip = mtod(mopt, int *);
543 *ip = MAXTTL;
544 error = sosetopt(so, IPPROTO_IP, IP_TTL, mopt);
545 m_freem(mopt);
546 if (error) {
547 printf("%s: sosetopt error %d\n",
548 __func__, error);
549 goto close;
550 }
551
552 MGET(mopt, M_WAIT, MT_SOOPTS);
553 mopt->m_len = sizeof(int);
554 ip = mtod(mopt, int *);
555 *ip = IPTOS_PREC_INTERNETCONTROL;
556 error = sosetopt(so, IPPROTO_IP, IP_TOS, mopt);
557 m_freem(mopt);
558 if (error) {
559 printf("%s: sosetopt error %d\n",
560 __func__, error);
561 goto close;
562 }
563
564 MGET(m, M_WAIT, MT_SONAME);
565 m->m_len = src->sa_len;
566 sa = mtod(m, struct sockaddr *);
567 memcpy(sa, src, src->sa_len);
568 switch(sa->sa_family) {
569 case AF_INET:
570 sin = (struct sockaddr_in *)sa;
571 sin->sin_port = 0;
572 break;
573 case AF_INET6:
574 sin6 = (struct sockaddr_in6 *)sa;
575 sin6->sin6_port = 0;
576 break;
577 default:
578 break;
579 }
580
581 solock(so);
582 error = sobind(so, m, p);
583 sounlock(so);
584 if (error) {
585 printf("%s: sobind error %d\n",
586 __func__, error);
587 goto close;
588 }
589
590 memcpy(sa, dst, dst->sa_len);
591 switch(sa->sa_family) {
592 case AF_INET:
593 sin = (struct sockaddr_in *)sa;
594 sin->sin_port = ntohs(port);
595 break;
596 case AF_INET6:
597 sin6 = (struct sockaddr_in6 *)sa;
598 sin6->sin6_port = ntohs(port);
599 break;
600 default:
601 break;
602 }
603
604 solock(so);
605 error = soconnect(so, m);
606 sounlock(so);
607 if (error && error != ECONNREFUSED) {
608 printf("%s: soconnect error %d\n",
609 __func__, error);
610 goto close;
611 }
612
613 m_free(m);
614
615 return (so);
616
617 close:
618 m_free(m);
619 soclose(so, MSG_DONTWAIT);
620
621 return (NULL);
622 }
623
624 /*
625 * Will be called per-received packet
626 */
627 void
bfd_upcall(struct socket * so,caddr_t arg,int waitflag)628 bfd_upcall(struct socket *so, caddr_t arg, int waitflag)
629 {
630 struct bfd_config *bfd = (struct bfd_config *)arg;
631
632 bfd->bc_upcallso = so;
633 task_add(bfdtq, &bfd->bc_upcall_task);
634 }
635
636 void
bfd_upcall_task(void * arg)637 bfd_upcall_task(void *arg)
638 {
639 struct bfd_config *bfd = (struct bfd_config *)arg;
640 struct socket *so = bfd->bc_upcallso;
641 struct mbuf *m;
642 struct uio uio;
643 int flags, error;
644
645 uio.uio_procp = NULL;
646 do {
647 uio.uio_resid = so->so_rcv.sb_cc;
648 flags = MSG_DONTWAIT;
649 error = soreceive(so, NULL, &uio, &m, NULL, &flags, 0);
650 if (error && error != EAGAIN) {
651 bfd_error(bfd);
652 return;
653 }
654 if (m != NULL)
655 bfd_input(bfd, m);
656 } while (so->so_rcv.sb_cc);
657
658 bfd->bc_upcallso = NULL;
659
660 return;
661 }
662
663 void
bfd_error(struct bfd_config * bfd)664 bfd_error(struct bfd_config *bfd)
665 {
666 if (bfd->bc_state <= BFD_STATE_DOWN)
667 return;
668
669 if (++bfd->bc_error >= bfd->bc_neighbor->bn_mult) {
670 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_EXPIRED;
671 bfd_reset(bfd);
672 if (bfd->bc_state > BFD_STATE_DOWN)
673 bfd_set_state(bfd, BFD_STATE_DOWN);
674 }
675 }
676
677 void
bfd_timeout_tx(void * v)678 bfd_timeout_tx(void *v)
679 {
680 struct bfd_config *bfd = v;
681 task_add(bfdtq, &bfd->bc_bfd_send_task);
682 }
683
684 /*
685 * Triggers when we do not receive a valid packet in time
686 */
687 void
bfd_timeout_rx(void * v)688 bfd_timeout_rx(void *v)
689 {
690 struct bfd_config *bfd = v;
691
692 if (bfd->bc_state > BFD_STATE_DOWN) {
693 bfd_error(bfd);
694 rtm_bfd(bfd);
695 }
696
697 timeout_add_usec(&bfd->bc_timo_rx, bfd->bc_minrx);
698 }
699
700 /*
701 * Tell our neighbor that we are going down
702 */
703 void
bfd_senddown(struct bfd_config * bfd)704 bfd_senddown(struct bfd_config *bfd)
705 {
706 /* If we are down, return early */
707 if (bfd->bc_state < BFD_STATE_INIT)
708 return;
709
710 if (bfd->bc_neighbor->bn_ldiag == 0)
711 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_ADMIN_DOWN;
712
713 bfd_set_state(bfd, BFD_STATE_ADMINDOWN);
714 bfd_send_control(bfd);
715
716 return;
717 }
718
719 /*
720 * Clean a BFD peer to defaults
721 */
722 void
bfd_reset(struct bfd_config * bfd)723 bfd_reset(struct bfd_config *bfd)
724 {
725 /* Clean */
726 bfd->bc_neighbor->bn_rdiscr = 0;
727 bfd->bc_neighbor->bn_demand = 0;
728 bfd->bc_neighbor->bn_rdemand = 0;
729 bfd->bc_neighbor->bn_authtype = 0;
730 bfd->bc_neighbor->bn_rauthseq = 0;
731 bfd->bc_neighbor->bn_lauthseq = 0;
732 bfd->bc_neighbor->bn_authseqknown = 0;
733 bfd->bc_neighbor->bn_ldiag = 0;
734
735 bfd->bc_mode = BFD_MODE_ASYNC;
736 bfd->bc_state = BFD_STATE_DOWN;
737
738 /* rfc5880 6.8.18 */
739 bfd->bc_neighbor->bn_lstate = BFD_STATE_DOWN;
740 bfd->bc_neighbor->bn_rstate = BFD_STATE_DOWN;
741 bfd->bc_neighbor->bn_mintx = BFD_SECOND;
742 bfd->bc_neighbor->bn_req_minrx = BFD_SECOND;
743 bfd->bc_neighbor->bn_rminrx = 1;
744 bfd->bc_neighbor->bn_mult = 3;
745
746 bfd->bc_mintx = bfd->bc_neighbor->bn_mintx;
747 bfd->bc_minrx = bfd->bc_neighbor->bn_req_minrx;
748 bfd->bc_multiplier = bfd->bc_neighbor->bn_mult;
749 bfd->bc_minecho = 0; //XXX - BFD_SECOND;
750
751 bfd_set_uptime(bfd);
752
753 return;
754 }
755
756 void
bfd_input(struct bfd_config * bfd,struct mbuf * m)757 bfd_input(struct bfd_config *bfd, struct mbuf *m)
758 {
759 struct bfd_header *peer;
760 struct bfd_auth_header *auth;
761 struct mbuf *mp, *mp0;
762 unsigned int ver, diag = BFD_DIAG_NONE, state, flags;
763 int offp;
764
765 mp = m_pulldown(m, 0, sizeof(*peer), &offp);
766
767 if (mp == NULL)
768 return;
769 peer = (struct bfd_header *)(mp->m_data + offp);
770
771 /* We only support BFD Version 1 */
772 if (( ver = BFD_VER(peer->bfd_ver_diag)) != 1)
773 goto discard;
774
775 diag = BFD_DIAG(peer->bfd_ver_diag);
776 state = BFD_STATE(peer->bfd_sta_flags);
777 flags = BFD_FLAGS(peer->bfd_sta_flags);
778
779 if (peer->bfd_length + offp > mp->m_len) {
780 printf("%s: bad len %d != %d\n", __func__,
781 peer->bfd_length + offp, mp->m_len);
782 goto discard;
783 }
784
785 if (peer->bfd_detect_multi == 0)
786 goto discard;
787 if (flags & BFD_FLAG_M)
788 goto discard;
789 if (ntohl(peer->bfd_my_discriminator) == 0)
790 goto discard;
791 if (ntohl(peer->bfd_your_discriminator) == 0 &&
792 BFD_STATE(peer->bfd_sta_flags) > BFD_STATE_DOWN)
793 goto discard;
794 if ((ntohl(peer->bfd_your_discriminator) != 0) &&
795 (ntohl(peer->bfd_your_discriminator) !=
796 bfd->bc_neighbor->bn_ldiscr)) {
797 bfd_error(bfd);
798 goto discard;
799 }
800
801 if ((flags & BFD_FLAG_A) && bfd->bc_neighbor->bn_authtype == 0)
802 goto discard;
803 if (!(flags & BFD_FLAG_A) && bfd->bc_neighbor->bn_authtype != 0)
804 goto discard;
805 if (flags & BFD_FLAG_A) {
806 mp0 = m_pulldown(mp, 0, sizeof(*auth), &offp);
807 if (mp0 == NULL)
808 goto discard;
809 auth = (struct bfd_auth_header *)(mp0->m_data + offp);
810 #if 0
811 if (bfd_process_auth(bfd, auth) != 0) {
812 m_free(mp0);
813 goto discard;
814 }
815 #endif
816 }
817
818 bfd->bc_neighbor->bn_rdiscr = ntohl(peer->bfd_my_discriminator);
819 bfd->bc_neighbor->bn_rstate = state;
820 bfd->bc_neighbor->bn_rdemand = (flags & BFD_FLAG_D);
821 bfd->bc_poll = (flags & BFD_FLAG_F);
822
823 /* Local change to the algorithm, we don't accept below 50ms */
824 if (ntohl(peer->bfd_required_min_rx_interval) < BFD_MINIMUM)
825 goto discard;
826 /*
827 * Local change to the algorithm, we can't use larger than signed
828 * 32bits for a timeout.
829 * That is Too Long(tm) anyways.
830 */
831 if (ntohl(peer->bfd_required_min_rx_interval) > INT32_MAX)
832 goto discard;
833 bfd->bc_neighbor->bn_rminrx =
834 ntohl(peer->bfd_required_min_rx_interval);
835 bfd->bc_minrx = bfd->bc_neighbor->bn_req_minrx;
836
837 bfd->bc_neighbor->bn_mintx =
838 htonl(peer->bfd_desired_min_tx_interval);
839 if (bfd->bc_neighbor->bn_lstate != BFD_STATE_UP)
840 bfd->bc_neighbor->bn_mintx = BFD_SECOND;
841
842 bfd->bc_neighbor->bn_req_minrx =
843 ntohl(peer->bfd_required_min_rx_interval);
844
845 /* rfc5880 6.8.7 */
846 bfd->bc_mintx = max(bfd->bc_neighbor->bn_rminrx,
847 bfd->bc_neighbor->bn_mintx);
848
849 /* According the to pseudo-code RFC 5880 page 34 */
850 if (bfd->bc_state == BFD_STATE_ADMINDOWN)
851 goto discard;
852 if (bfd->bc_neighbor->bn_rstate == BFD_STATE_ADMINDOWN) {
853 if (bfd->bc_neighbor->bn_lstate != BFD_STATE_DOWN) {
854 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_NEIGHBOR_SIGDOWN;
855 bfd_set_state(bfd, BFD_STATE_DOWN);
856 }
857 } else if (bfd->bc_neighbor->bn_lstate == BFD_STATE_DOWN) {
858 if (bfd->bc_neighbor->bn_rstate == BFD_STATE_DOWN)
859 bfd_set_state(bfd, BFD_STATE_INIT);
860 else if (bfd->bc_neighbor->bn_rstate == BFD_STATE_INIT) {
861 bfd->bc_neighbor->bn_ldiag = 0;
862 bfd_set_state(bfd, BFD_STATE_UP);
863 }
864 } else if (bfd->bc_neighbor->bn_lstate == BFD_STATE_INIT) {
865 if (bfd->bc_neighbor->bn_rstate >= BFD_STATE_INIT) {
866 bfd->bc_neighbor->bn_ldiag = 0;
867 bfd_set_state(bfd, BFD_STATE_UP);
868 } else {
869 goto discard;
870 }
871 } else {
872 if (bfd->bc_neighbor->bn_rstate == BFD_STATE_DOWN) {
873 bfd->bc_neighbor->bn_ldiag = BFD_DIAG_NEIGHBOR_SIGDOWN;
874 bfd_set_state(bfd, BFD_STATE_DOWN);
875 goto discard;
876 }
877 }
878
879 if (bfd->bc_neighbor->bn_lstate == BFD_STATE_UP) {
880 bfd->bc_neighbor->bn_ldiag = 0;
881 bfd->bc_neighbor->bn_demand = 1;
882 bfd->bc_neighbor->bn_rdemand = (flags & BFD_FLAG_D);
883 }
884
885 bfd->bc_error = 0;
886
887 discard:
888 bfd->bc_neighbor->bn_rdiag = diag;
889 m_free(m);
890
891 timeout_add_usec(&bfd->bc_timo_rx, bfd->bc_minrx);
892
893 return;
894 }
895
896 void
bfd_set_state(struct bfd_config * bfd,unsigned int state)897 bfd_set_state(struct bfd_config *bfd, unsigned int state)
898 {
899 struct ifnet *ifp;
900 struct rtentry *rt = bfd->bc_rt;
901
902 ifp = if_get(rt->rt_ifidx);
903 if (ifp == NULL) {
904 printf("%s: cannot find interface index %u\n",
905 __func__, rt->rt_ifidx);
906 bfd->bc_error++;
907 bfd_reset(bfd);
908 return;
909 }
910
911 bfd->bc_neighbor->bn_lstate = state;
912 if (state > BFD_STATE_ADMINDOWN)
913 bfd->bc_neighbor->bn_ldiag = 0;
914
915 if (!rtisvalid(rt))
916 bfd->bc_neighbor->bn_lstate = BFD_STATE_DOWN;
917
918 switch (state) {
919 case BFD_STATE_ADMINDOWN:
920 bfd->bc_laststate = bfd->bc_state;
921 /* FALLTHROUGH */
922 case BFD_STATE_DOWN:
923 if (bfd->bc_state == BFD_STATE_UP) {
924 bfd->bc_laststate = bfd->bc_state;
925 bfd_set_uptime(bfd);
926 }
927 break;
928 case BFD_STATE_INIT:
929 bfd->bc_laststate = bfd->bc_state;
930 break;
931 case BFD_STATE_UP:
932 bfd->bc_laststate =
933 bfd->bc_state == BFD_STATE_INIT ?
934 bfd->bc_laststate : bfd->bc_state;
935 bfd_set_uptime(bfd);
936 break;
937 }
938
939 bfd->bc_state = state;
940 rtm_bfd(bfd);
941 if_put(ifp);
942
943 return;
944 }
945
946 void
bfd_set_uptime(struct bfd_config * bfd)947 bfd_set_uptime(struct bfd_config *bfd)
948 {
949 struct timeval tv;
950
951 getmicrotime(&tv);
952 bfd->bc_lastuptime = tv.tv_sec - bfd->bc_time->tv_sec;
953 memcpy(bfd->bc_time, &tv, sizeof(tv));
954 }
955
956 void
bfd_send_control(void * x)957 bfd_send_control(void *x)
958 {
959 struct bfd_config *bfd = x;
960 struct mbuf *m;
961 struct bfd_header *h;
962 int error, len;
963
964 MGETHDR(m, M_WAIT, MT_DATA);
965 MCLGET(m, M_WAIT);
966
967 len = BFD_HDRLEN;
968 m->m_len = m->m_pkthdr.len = len;
969 h = mtod(m, struct bfd_header *);
970
971 memset(h, 0xff, sizeof(*h)); /* canary */
972
973 h->bfd_ver_diag = ((BFD_VERSION << 5) | (bfd->bc_neighbor->bn_ldiag));
974 h->bfd_sta_flags = (bfd->bc_state << 6);
975 h->bfd_detect_multi = bfd->bc_neighbor->bn_mult;
976 h->bfd_length = BFD_HDRLEN;
977 h->bfd_my_discriminator = htonl(bfd->bc_neighbor->bn_ldiscr);
978 h->bfd_your_discriminator = htonl(bfd->bc_neighbor->bn_rdiscr);
979
980 h->bfd_desired_min_tx_interval =
981 htonl(bfd->bc_neighbor->bn_mintx);
982 h->bfd_required_min_rx_interval =
983 htonl(bfd->bc_neighbor->bn_req_minrx);
984 h->bfd_required_min_echo_interval = htonl(bfd->bc_minecho);
985
986 error = bfd_send(bfd, m);
987
988 if (error) {
989 bfd_error(bfd);
990 if (!(error == EHOSTDOWN || error == ECONNREFUSED)) {
991 printf("%s: %u\n", __func__, error);
992 }
993 }
994 }
995
996 int
bfd_send(struct bfd_config * bfd,struct mbuf * m)997 bfd_send(struct bfd_config *bfd, struct mbuf *m)
998 {
999 return(sosend(bfd->bc_sosend, NULL, NULL, m, NULL, MSG_DONTWAIT));
1000 }
1001
1002 /*
1003 * Print debug information about this bfd instance
1004 */
1005 void
bfd_debug(struct bfd_config * bfd)1006 bfd_debug(struct bfd_config *bfd)
1007 {
1008 struct rtentry *rt = bfd->bc_rt;
1009 struct timeval tv;
1010 char buf[64];
1011
1012 printf("dest: %s ", sockaddr_ntop(rt_key(rt), buf, sizeof(buf)));
1013 printf("src: %s ", sockaddr_ntop(rt->rt_ifa->ifa_addr, buf,
1014 sizeof(buf)));
1015 printf("\n");
1016 printf("\t");
1017 printf("session state: %u ", bfd->bc_state);
1018 printf("mode: %u ", bfd->bc_mode);
1019 printf("error: %u ", bfd->bc_error);
1020 printf("minrx: %u ", bfd->bc_minrx);
1021 printf("mintx: %u ", bfd->bc_mintx);
1022 printf("multiplier: %u ", bfd->bc_multiplier);
1023 printf("\n");
1024 printf("\t");
1025 printf("local session state: %u ", bfd->bc_neighbor->bn_lstate);
1026 printf("local diag: %u ", bfd->bc_neighbor->bn_ldiag);
1027 printf("\n");
1028 printf("\t");
1029 printf("remote discriminator: %u ", bfd->bc_neighbor->bn_rdiscr);
1030 printf("local discriminator: %u ", bfd->bc_neighbor->bn_ldiscr);
1031 printf("\n");
1032 printf("\t");
1033 printf("remote session state: %u ", bfd->bc_neighbor->bn_rstate);
1034 printf("remote diag: %u ", bfd->bc_neighbor->bn_rdiag);
1035 printf("remote min rx: %u ", bfd->bc_neighbor->bn_rminrx);
1036 printf("\n");
1037 printf("\t");
1038 printf("last state: %u ", bfd->bc_laststate);
1039 getmicrotime(&tv);
1040 printf("uptime %llds ", tv.tv_sec - bfd->bc_time->tv_sec);
1041 printf("time started %lld.%06ld ", bfd->bc_time->tv_sec,
1042 bfd->bc_time->tv_usec);
1043 printf("last uptime %llds ", bfd->bc_lastuptime);
1044 printf("\n");
1045 }
1046