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