]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
Merge pull request #2985 from patrasar/Fix_1636
[mirror_frr.git] / bfdd / bfd_packet.c
1 /*********************************************************************
2 * Copyright 2017 Cumulus Networks, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * bfd_packet.c: implements the BFD protocol packet handling.
19 *
20 * Authors
21 * -------
22 * Shrijeet Mukherjee [shm@cumulusnetworks.com]
23 * Kanna Rajagopal [kanna@cumulusnetworks.com]
24 * Radhika Mahankali [Radhika@cumulusnetworks.com]
25 */
26
27 #include <zebra.h>
28
29 #ifdef BFD_LINUX
30 #include <linux/if_packet.h>
31 #endif /* BFD_LINUX */
32
33 #include <netinet/if_ether.h>
34 #include <netinet/udp.h>
35
36 #include "lib/sockopt.h"
37
38 #include "bfd.h"
39
40
41 /*
42 * Prototypes
43 */
44 static int ptm_bfd_process_echo_pkt(int s);
45 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
46 size_t datalen);
47
48 static void bfd_sd_reschedule(int sd);
49 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
50 char *port, size_t portlen, char *vrfname,
51 size_t vrfnamelen, struct sockaddr_any *local,
52 struct sockaddr_any *peer);
53 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
54 char *port, size_t portlen, char *vrfname,
55 size_t vrfnamelen, struct sockaddr_any *local,
56 struct sockaddr_any *peer);
57 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
58 struct sockaddr *to, socklen_t tolen);
59 int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr);
60
61 /* socket related prototypes */
62 static void bp_set_ipopts(int sd);
63 static void bp_bind_ip(int sd, uint16_t port);
64 static void bp_set_ipv6opts(int sd);
65 static void bp_bind_ipv6(int sd, uint16_t port);
66
67
68 /*
69 * Functions
70 */
71 int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
72 size_t datalen)
73 {
74 struct sockaddr *sa;
75 struct sockaddr_in sin;
76 struct sockaddr_in6 sin6;
77 socklen_t slen;
78 ssize_t rv;
79 int sd = -1;
80
81 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
82 memset(&sin6, 0, sizeof(sin6));
83 sin6.sin6_family = AF_INET6;
84 sin6.sin6_addr = bs->shop.peer.sa_sin6.sin6_addr;
85 sin6.sin6_port =
86 (port) ? *port
87 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
88 ? htons(BFD_DEF_MHOP_DEST_PORT)
89 : htons(BFD_DEFDESTPORT);
90
91 sd = bs->sock;
92 sa = (struct sockaddr *)&sin6;
93 slen = sizeof(sin6);
94 } else {
95 memset(&sin, 0, sizeof(sin));
96 sin.sin_family = AF_INET;
97 sin.sin_addr = bs->shop.peer.sa_sin.sin_addr;
98 sin.sin_port =
99 (port) ? *port
100 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
101 ? htons(BFD_DEF_MHOP_DEST_PORT)
102 : htons(BFD_DEFDESTPORT);
103
104 sd = bs->sock;
105 sa = (struct sockaddr *)&sin;
106 slen = sizeof(sin);
107 }
108
109 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
110 sa->sa_len = slen;
111 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
112 rv = sendto(sd, data, datalen, 0, sa, slen);
113 if (rv <= 0) {
114 log_debug("packet-send: send failure: %s", strerror(errno));
115 return -1;
116 }
117 if (rv < (ssize_t)datalen)
118 log_debug("packet-send: send partial", strerror(errno));
119
120 return 0;
121 }
122
123 void ptm_bfd_echo_snd(struct bfd_session *bfd)
124 {
125 struct sockaddr_any *sa;
126 socklen_t salen;
127 int sd;
128 struct bfd_echo_pkt bep;
129 struct sockaddr_in sin;
130 struct sockaddr_in6 sin6;
131
132 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
133 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
134
135 memset(&bep, 0, sizeof(bep));
136 bep.ver = BFD_ECHO_VERSION;
137 bep.len = BFD_ECHO_PKT_LEN;
138 bep.my_discr = htonl(bfd->discrs.my_discr);
139
140 sa = BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MH) ? &bfd->mhop.peer
141 : &bfd->shop.peer;
142 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
143 sd = bglobal.bg_echov6;
144 sin6 = sa->sa_sin6;
145 sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
146 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
147 sin6.sin6_len = sizeof(sin6);
148 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
149
150 sa = (struct sockaddr_any *)&sin6;
151 salen = sizeof(sin6);
152 } else {
153 sd = bglobal.bg_echo;
154 sin = sa->sa_sin;
155 sin.sin_port = htons(BFD_DEF_ECHO_PORT);
156 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
157 sin.sin_len = sizeof(sin);
158 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
159
160 sa = (struct sockaddr_any *)&sin;
161 salen = sizeof(sin);
162 }
163 if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep),
164 (struct sockaddr *)sa, salen)
165 == -1)
166 return;
167
168 bfd->stats.tx_echo_pkt++;
169 }
170
171 static int ptm_bfd_process_echo_pkt(int s)
172 {
173 struct bfd_session *bfd;
174 uint32_t my_discr = 0;
175 uint8_t ttl = 0;
176
177 /* Receive and parse echo packet. */
178 if (bp_bfd_echo_in(s, &ttl, &my_discr) == -1)
179 return 0;
180
181 /* Your discriminator not zero - use it to find session */
182 bfd = bfd_id_lookup(my_discr);
183 if (bfd == NULL) {
184 log_debug("echo-packet: no matching session (id:%u)", my_discr);
185 return -1;
186 }
187
188 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
189 log_debug("echo-packet: echo disabled [%s] (id:%u)",
190 bs_to_string(bfd), my_discr);
191 return -1;
192 }
193
194 bfd->stats.rx_echo_pkt++;
195
196 /* Compute detect time */
197 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
198
199 /* Update echo receive timeout. */
200 bfd_echo_recvtimer_update(bfd);
201
202 return 0;
203 }
204
205 void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
206 {
207 struct bfd_pkt cp;
208
209 /* Set fields according to section 6.5.7 */
210 cp.diag = bfd->local_diag;
211 BFD_SETVER(cp.diag, BFD_VERSION);
212 cp.flags = 0;
213 BFD_SETSTATE(cp.flags, bfd->ses_state);
214 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
215 BFD_SETPBIT(cp.flags, bfd->polling);
216 BFD_SETFBIT(cp.flags, fbit);
217 cp.detect_mult = bfd->detect_mult;
218 cp.len = BFD_PKT_LEN;
219 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
220 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
221 if (bfd->polling) {
222 cp.timers.desired_min_tx =
223 htonl(bfd->new_timers.desired_min_tx);
224 cp.timers.required_min_rx =
225 htonl(bfd->new_timers.required_min_rx);
226 } else {
227 cp.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
228 cp.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
229 }
230 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
231
232 if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
233 return;
234
235 bfd->stats.tx_ctrl_pkt++;
236 }
237
238 ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
239 char *port, size_t portlen, char *vrfname,
240 size_t vrfnamelen, struct sockaddr_any *local,
241 struct sockaddr_any *peer)
242 {
243 struct cmsghdr *cm;
244 int ifindex;
245 ssize_t mlen;
246 struct sockaddr_in msgaddr;
247 struct msghdr msghdr;
248 struct iovec iov[1];
249 uint8_t cmsgbuf[255];
250
251 port[0] = '\0';
252
253 /* Prepare the recvmsg params. */
254 iov[0].iov_base = msgbuf;
255 iov[0].iov_len = msgbuflen;
256
257 memset(&msghdr, 0, sizeof(msghdr));
258 msghdr.msg_name = &msgaddr;
259 msghdr.msg_namelen = sizeof(msgaddr);
260 msghdr.msg_iov = iov;
261 msghdr.msg_iovlen = 1;
262 msghdr.msg_control = cmsgbuf;
263 msghdr.msg_controllen = sizeof(cmsgbuf);
264
265 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
266 if (mlen == -1) {
267 if (errno != EAGAIN)
268 log_error("ipv4-recv: recv failed: %s",
269 strerror(errno));
270
271 return -1;
272 }
273
274 /* Get source address */
275 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
276
277 /* Get and check TTL */
278 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
279 cm = CMSG_NXTHDR(&msghdr, cm)) {
280 if (cm->cmsg_level != IPPROTO_IP)
281 continue;
282
283 switch (cm->cmsg_type) {
284 #ifdef BFD_LINUX
285 case IP_TTL: {
286 uint32_t ttlval;
287
288 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
289 if (ttlval > 255) {
290 log_debug("ipv4-recv: invalid TTL: %u", ttlval);
291 return -1;
292 }
293 *ttl = ttlval;
294 break;
295 }
296
297 case IP_PKTINFO: {
298 struct in_pktinfo *pi =
299 (struct in_pktinfo *)CMSG_DATA(cm);
300
301 if (pi == NULL)
302 break;
303
304 local->sa_sin.sin_family = AF_INET;
305 local->sa_sin.sin_addr = pi->ipi_addr;
306 fetch_portname_from_ifindex(pi->ipi_ifindex, port,
307 portlen);
308 break;
309 }
310 #endif /* BFD_LINUX */
311 #ifdef BFD_BSD
312 case IP_RECVTTL: {
313 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
314 break;
315 }
316
317 case IP_RECVDSTADDR: {
318 struct in_addr ia;
319
320 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
321 local->sa_sin.sin_family = AF_INET;
322 local->sa_sin.sin_addr = ia;
323 break;
324 }
325 #endif /* BFD_BSD */
326
327 default:
328 /*
329 * On *BSDs we expect to land here when skipping
330 * the IP_RECVIF header. It will be handled by
331 * getsockopt_ifindex() below.
332 */
333 /* NOTHING */
334 break;
335 }
336 }
337
338 /* OS agnostic way of getting interface name. */
339 if (port[0] == 0) {
340 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
341 if (ifindex > 0)
342 fetch_portname_from_ifindex(ifindex, port, portlen);
343 }
344
345 return mlen;
346 }
347
348 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
349 char *port, size_t portlen, char *vrfname,
350 size_t vrfnamelen, struct sockaddr_any *local,
351 struct sockaddr_any *peer)
352 {
353 struct cmsghdr *cm;
354 struct in6_pktinfo *pi6 = NULL;
355 int ifindex = 0;
356 ssize_t mlen;
357 uint32_t ttlval;
358 struct sockaddr_in6 msgaddr6;
359 struct msghdr msghdr6;
360 struct iovec iov[1];
361 uint8_t cmsgbuf6[255];
362
363 /* Prepare the recvmsg params. */
364 iov[0].iov_base = msgbuf;
365 iov[0].iov_len = msgbuflen;
366
367 memset(&msghdr6, 0, sizeof(msghdr6));
368 msghdr6.msg_name = &msgaddr6;
369 msghdr6.msg_namelen = sizeof(msgaddr6);
370 msghdr6.msg_iov = iov;
371 msghdr6.msg_iovlen = 1;
372 msghdr6.msg_control = cmsgbuf6;
373 msghdr6.msg_controllen = sizeof(cmsgbuf6);
374
375 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
376 if (mlen == -1) {
377 if (errno != EAGAIN)
378 log_error("ipv6-recv: recv failed: %s",
379 strerror(errno));
380
381 return -1;
382 }
383
384 /* Get source address */
385 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
386
387 /* Get and check TTL */
388 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
389 cm = CMSG_NXTHDR(&msghdr6, cm)) {
390 if (cm->cmsg_level != IPPROTO_IPV6)
391 continue;
392
393 if (cm->cmsg_type == IPV6_HOPLIMIT) {
394 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
395 if (ttlval > 255) {
396 log_debug("ipv6-recv: invalid TTL: %u", ttlval);
397 return -1;
398 }
399
400 *ttl = ttlval;
401 } else if (cm->cmsg_type == IPV6_PKTINFO) {
402 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
403 if (pi6) {
404 local->sa_sin.sin_family = AF_INET6;
405 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
406 fetch_portname_from_ifindex(pi6->ipi6_ifindex,
407 port, portlen);
408 ifindex = pi6->ipi6_ifindex;
409 }
410 }
411 }
412
413 /* Set scope ID for link local addresses. */
414 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
415 peer->sa_sin6.sin6_scope_id = ifindex;
416 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
417 local->sa_sin6.sin6_scope_id = ifindex;
418
419 return mlen;
420 }
421
422 static void bfd_sd_reschedule(int sd)
423 {
424 if (sd == bglobal.bg_shop) {
425 THREAD_OFF(bglobal.bg_ev[0]);
426 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
427 &bglobal.bg_ev[0]);
428 } else if (sd == bglobal.bg_mhop) {
429 THREAD_OFF(bglobal.bg_ev[1]);
430 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
431 &bglobal.bg_ev[1]);
432 } else if (sd == bglobal.bg_shop6) {
433 THREAD_OFF(bglobal.bg_ev[2]);
434 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
435 &bglobal.bg_ev[2]);
436 } else if (sd == bglobal.bg_mhop6) {
437 THREAD_OFF(bglobal.bg_ev[3]);
438 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
439 &bglobal.bg_ev[3]);
440 } else if (sd == bglobal.bg_echo) {
441 THREAD_OFF(bglobal.bg_ev[4]);
442 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
443 &bglobal.bg_ev[4]);
444 } else if (sd == bglobal.bg_echov6) {
445 THREAD_OFF(bglobal.bg_ev[5]);
446 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
447 &bglobal.bg_ev[5]);
448 }
449 }
450
451 static void cp_debug(bool mhop, struct sockaddr_any *peer,
452 struct sockaddr_any *local, const char *port,
453 const char *vrf, const char *fmt, ...)
454 {
455 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
456 va_list vl;
457
458 if (peer->sa_sin.sin_family)
459 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
460 else
461 peerstr[0] = 0;
462
463 if (local->sa_sin.sin_family)
464 snprintf(localstr, sizeof(localstr), " local:%s",
465 satostr(local));
466 else
467 localstr[0] = 0;
468
469 if (port[0])
470 snprintf(portstr, sizeof(portstr), " port:%s", port);
471 else
472 portstr[0] = 0;
473
474 if (vrf[0])
475 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
476 else
477 vrfstr[0] = 0;
478
479 va_start(vl, fmt);
480 vsnprintf(buf, sizeof(buf), fmt, vl);
481 va_end(vl);
482
483 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
484 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
485 }
486
487 int bfd_recv_cb(struct thread *t)
488 {
489 int sd = THREAD_FD(t);
490 struct bfd_session *bfd;
491 struct bfd_pkt *cp;
492 bool is_mhop;
493 ssize_t mlen = 0;
494 uint32_t oldEchoXmt_TO, oldXmtTime;
495 uint8_t ttl;
496 struct sockaddr_any local, peer;
497 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
498 uint8_t msgbuf[1516];
499
500 /* Schedule next read. */
501 bfd_sd_reschedule(sd);
502
503 /* Handle echo packets. */
504 if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) {
505 ptm_bfd_process_echo_pkt(sd);
506 return 0;
507 }
508
509 /* Sanitize input/output. */
510 memset(port, 0, sizeof(port));
511 memset(vrfname, 0, sizeof(vrfname));
512 memset(&local, 0, sizeof(local));
513 memset(&peer, 0, sizeof(peer));
514
515 /* Handle control packets. */
516 is_mhop = false;
517 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
518 is_mhop = sd == bglobal.bg_mhop;
519 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, port,
520 sizeof(port), vrfname, sizeof(vrfname),
521 &local, &peer);
522 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
523 is_mhop = sd == bglobal.bg_mhop6;
524 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, port,
525 sizeof(port), vrfname, sizeof(vrfname),
526 &local, &peer);
527 }
528
529 /* Implement RFC 5880 6.8.6 */
530 if (mlen < BFD_PKT_LEN) {
531 cp_debug(is_mhop, &peer, &local, port, vrfname,
532 "too small (%ld bytes)", mlen);
533 return 0;
534 }
535
536 /* Validate packet TTL. */
537 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
538 cp_debug(is_mhop, &peer, &local, port, vrfname,
539 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
540 return 0;
541 }
542
543 /*
544 * Parse the control header for inconsistencies:
545 * - Invalid version;
546 * - Bad multiplier configuration;
547 * - Short packets;
548 * - Invalid discriminator;
549 */
550 cp = (struct bfd_pkt *)(msgbuf);
551 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
552 cp_debug(is_mhop, &peer, &local, port, vrfname,
553 "bad version %d", BFD_GETVER(cp->diag));
554 return 0;
555 }
556
557 if (cp->detect_mult == 0) {
558 cp_debug(is_mhop, &peer, &local, port, vrfname,
559 "detect multiplier set to zero");
560 return 0;
561 }
562
563 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
564 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
565 return 0;
566 }
567
568 if (cp->discrs.my_discr == 0) {
569 cp_debug(is_mhop, &peer, &local, port, vrfname,
570 "'my discriminator' is zero");
571 return 0;
572 }
573
574 /* Find the session that this packet belongs. */
575 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
576 if (bfd == NULL) {
577 cp_debug(is_mhop, &peer, &local, port, vrfname,
578 "no session found");
579 return 0;
580 }
581
582 bfd->stats.rx_ctrl_pkt++;
583
584 /*
585 * Multi hop: validate packet TTL.
586 * Single hop: set local address that received the packet.
587 */
588 if (is_mhop) {
589 if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
590 cp_debug(is_mhop, &peer, &local, port, vrfname,
591 "exceeded max hop count (expected %d, got %d)",
592 bfd->mh_ttl, BFD_TTL_VAL);
593 return 0;
594 }
595 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
596 bfd->local_ip = local;
597 }
598
599 /*
600 * If no interface was detected, save the interface where the
601 * packet came in.
602 */
603 if (bfd->ifindex == 0)
604 bfd->ifindex = ptm_bfd_fetch_ifindex(port);
605
606 /* Log remote discriminator changes. */
607 if ((bfd->discrs.remote_discr != 0)
608 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
609 cp_debug(is_mhop, &peer, &local, port, vrfname,
610 "remote discriminator mismatch (expected %d, got %d)",
611 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
612
613 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
614
615 /* If received the Final bit, the new values should take effect */
616 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
617 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
618 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
619 bfd->new_timers.desired_min_tx = 0;
620 bfd->new_timers.required_min_rx = 0;
621 bfd->polling = 0;
622 }
623
624 if (!bfd->demand_mode) {
625 /* Compute detect time */
626 bfd->detect_TO = cp->detect_mult
627 * ((bfd->timers.required_min_rx
628 > ntohl(cp->timers.desired_min_tx))
629 ? bfd->timers.required_min_rx
630 : ntohl(cp->timers.desired_min_tx));
631 bfd->remote_detect_mult = cp->detect_mult;
632 } else
633 cp_debug(is_mhop, &peer, &local, port, vrfname,
634 "unsupported demand mode");
635
636 /* Save remote diagnostics before state switch. */
637 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
638
639 /* State switch from section 6.8.6 */
640 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
641 if (bfd->ses_state != PTM_BFD_DOWN)
642 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
643 } else {
644 switch (bfd->ses_state) {
645 case (PTM_BFD_DOWN):
646 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
647 ptm_bfd_ses_up(bfd);
648 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
649 bfd->ses_state = PTM_BFD_INIT;
650 break;
651 case (PTM_BFD_INIT):
652 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
653 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
654 ptm_bfd_ses_up(bfd);
655 break;
656 case (PTM_BFD_UP):
657 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
658 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
659 break;
660 }
661 }
662
663 /*
664 * Handle echo packet status:
665 * - Start echo packets if configured and permitted
666 * (required_min_echo > 0);
667 * - Stop echo packets if not allowed (required_min_echo == 0);
668 * - Recalculate echo packet interval;
669 */
670 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
671 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
672 if (!ntohl(cp->timers.required_min_echo)) {
673 ptm_bfd_echo_stop(bfd, 1);
674 } else {
675 oldEchoXmt_TO = bfd->echo_xmt_TO;
676 bfd->echo_xmt_TO =
677 bfd->timers.required_min_echo;
678 if (ntohl(cp->timers.required_min_echo)
679 > bfd->echo_xmt_TO)
680 bfd->echo_xmt_TO = ntohl(
681 cp->timers.required_min_echo);
682 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
683 ptm_bfd_echo_start(bfd);
684 }
685 } else if (ntohl(cp->timers.required_min_echo)) {
686 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
687 if (ntohl(cp->timers.required_min_echo)
688 > bfd->echo_xmt_TO)
689 bfd->echo_xmt_TO =
690 ntohl(cp->timers.required_min_echo);
691 ptm_bfd_echo_start(bfd);
692 }
693 }
694
695 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
696 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
697 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
698 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
699 }
700
701 /* Calculate new transmit time */
702 oldXmtTime = bfd->xmt_TO;
703 bfd->xmt_TO =
704 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
705 ? bfd->timers.desired_min_tx
706 : ntohl(cp->timers.required_min_rx);
707
708 /* If transmit time has changed, and too much time until next xmt,
709 * restart
710 */
711 if (BFD_GETPBIT(cp->flags)) {
712 ptm_bfd_xmt_TO(bfd, 1);
713 } else if (oldXmtTime != bfd->xmt_TO) {
714 /* XXX add some skid to this as well */
715 ptm_bfd_start_xmt_timer(bfd, false);
716 }
717
718 /* Restart detection timer (packet received) */
719 if (!bfd->demand_mode)
720 bfd_recvtimer_update(bfd);
721
722 /*
723 * Save the timers and state sent by the remote end
724 * for debugging and statistics.
725 */
726 if (BFD_GETFBIT(cp->flags)) {
727 bfd->remote_timers.desired_min_tx =
728 ntohl(cp->timers.desired_min_tx);
729 bfd->remote_timers.required_min_rx =
730 ntohl(cp->timers.required_min_rx);
731 bfd->remote_timers.required_min_echo =
732 ntohl(cp->timers.required_min_echo);
733
734 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
735 }
736
737 return 0;
738 }
739
740 /*
741 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
742 * the packet is looped back or returns the my discriminator ID along
743 * with the TTL.
744 *
745 * Returns -1 on error or loopback or 0 on success.
746 */
747 int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
748 {
749 struct bfd_echo_pkt *bep;
750 ssize_t rlen;
751 struct sockaddr_any local, peer;
752 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
753 uint8_t msgbuf[1516];
754
755 if (sd == bglobal.bg_echo)
756 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, port,
757 sizeof(port), vrfname, sizeof(vrfname),
758 &local, &peer);
759 else
760 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, port,
761 sizeof(port), vrfname, sizeof(vrfname),
762 &local, &peer);
763
764 /* Short packet, better not risk reading it. */
765 if (rlen < (ssize_t)sizeof(*bep)) {
766 cp_debug(false, &peer, &local, port, vrfname,
767 "small echo packet");
768 return -1;
769 }
770
771 /* Test for loopback. */
772 if (*ttl == BFD_TTL_VAL) {
773 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
774 (struct sockaddr *)&peer,
775 (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin)
776 : sizeof(peer.sa_sin6));
777 return -1;
778 }
779
780 /* Read my discriminator from BFD Echo packet. */
781 bep = (struct bfd_echo_pkt *)msgbuf;
782 *my_discr = ntohl(bep->my_discr);
783 if (*my_discr == 0) {
784 cp_debug(false, &peer, &local, port, vrfname,
785 "invalid echo packet discriminator (zero)");
786 return -1;
787 }
788
789 return 0;
790 }
791
792 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
793 struct sockaddr *to, socklen_t tolen)
794 {
795 struct cmsghdr *cmsg;
796 ssize_t wlen;
797 int ttlval = ttl;
798 bool is_ipv6 = to->sa_family == AF_INET6;
799 struct msghdr msg;
800 struct iovec iov[1];
801 uint8_t msgctl[255];
802
803 /* Prepare message data. */
804 iov[0].iov_base = data;
805 iov[0].iov_len = datalen;
806
807 memset(&msg, 0, sizeof(msg));
808 memset(msgctl, 0, sizeof(msgctl));
809 msg.msg_name = to;
810 msg.msg_namelen = tolen;
811 msg.msg_iov = iov;
812 msg.msg_iovlen = 1;
813
814 /* Prepare the packet TTL information. */
815 if (ttl > 0) {
816 /* Use ancillary data. */
817 msg.msg_control = msgctl;
818 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
819
820 /* Configure the ancillary data. */
821 cmsg = CMSG_FIRSTHDR(&msg);
822 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
823 if (is_ipv6) {
824 cmsg->cmsg_level = IPPROTO_IPV6;
825 cmsg->cmsg_type = IPV6_HOPLIMIT;
826 } else {
827 #if BFD_LINUX
828 cmsg->cmsg_level = IPPROTO_IP;
829 cmsg->cmsg_type = IP_TTL;
830 #else
831 /* FreeBSD does not support TTL in ancillary data. */
832 msg.msg_control = NULL;
833 msg.msg_controllen = 0;
834
835 bp_set_ttl(sd, ttl);
836 #endif /* BFD_BSD */
837 }
838 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
839 }
840
841 /* Send echo back. */
842 wlen = sendmsg(sd, &msg, 0);
843 if (wlen <= 0) {
844 log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno));
845 return -1;
846 } else if (wlen < (ssize_t)datalen) {
847 log_debug("udp-send: partial send: %ld expected %ld", wlen,
848 datalen);
849 return -1;
850 }
851
852 return 0;
853 }
854
855
856 /*
857 * Sockets creation.
858 */
859
860
861 /*
862 * IPv4 sockets
863 */
864 int bp_set_ttl(int sd, uint8_t value)
865 {
866 int ttl = value;
867
868 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
869 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value,
870 strerror(errno));
871 return -1;
872 }
873
874 return 0;
875 }
876
877 int bp_set_tos(int sd, uint8_t value)
878 {
879 int tos = value;
880
881 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
882 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value,
883 strerror(errno));
884 return -1;
885 }
886
887 return 0;
888 }
889
890 static void bp_set_ipopts(int sd)
891 {
892 int rcvttl = BFD_RCV_TTL_VAL;
893
894 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
895 log_fatal("set-ipopts: TTL configuration failed");
896
897 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
898 == -1)
899 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
900 strerror(errno));
901
902 #ifdef BFD_LINUX
903 int pktinfo = BFD_PKT_INFO_VAL;
904
905 /* Figure out address and interface to do the peer matching. */
906 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
907 == -1)
908 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo,
909 strerror(errno));
910 #endif /* BFD_LINUX */
911 #ifdef BFD_BSD
912 int yes = 1;
913
914 /* Find out our address for peer matching. */
915 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
916 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes,
917 strerror(errno));
918
919 /* Find out interface where the packet came in. */
920 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
921 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
922 strerror(errno));
923 #endif /* BFD_BSD */
924 }
925
926 static void bp_bind_ip(int sd, uint16_t port)
927 {
928 struct sockaddr_in sin;
929
930 memset(&sin, 0, sizeof(sin));
931 sin.sin_family = AF_INET;
932 sin.sin_addr.s_addr = htonl(INADDR_ANY);
933 sin.sin_port = htons(port);
934 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
935 log_fatal("bind-ip: bind: %s", strerror(errno));
936 }
937
938 int bp_udp_shop(void)
939 {
940 int sd;
941
942 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
943 if (sd == -1)
944 log_fatal("udp-shop: socket: %s", strerror(errno));
945
946 bp_set_ipopts(sd);
947 bp_bind_ip(sd, BFD_DEFDESTPORT);
948
949 return sd;
950 }
951
952 int bp_udp_mhop(void)
953 {
954 int sd;
955
956 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
957 if (sd == -1)
958 log_fatal("udp-mhop: socket: %s", strerror(errno));
959
960 bp_set_ipopts(sd);
961 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
962
963 return sd;
964 }
965
966 int bp_peer_socket(struct bfd_peer_cfg *bpc)
967 {
968 int sd, pcount;
969 struct sockaddr_in sin;
970 static int srcPort = BFD_SRCPORTINIT;
971
972 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
973 if (sd == -1) {
974 log_error("ipv4-new: failed to create socket: %s",
975 strerror(errno));
976 return -1;
977 }
978
979 /* Set TTL to 255 for all transmitted packets */
980 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
981 close(sd);
982 return -1;
983 }
984
985 /* Set TOS to CS6 for all transmitted packets */
986 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
987 close(sd);
988 return -1;
989 }
990
991 if (bpc->bpc_has_localif) {
992 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
993 close(sd);
994 return -1;
995 }
996 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
997 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
998 close(sd);
999 return -1;
1000 }
1001 }
1002
1003 /* Find an available source port in the proper range */
1004 memset(&sin, 0, sizeof(sin));
1005 sin = bpc->bpc_local.sa_sin;
1006 sin.sin_family = AF_INET;
1007 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1008 sin.sin_len = sizeof(sin);
1009 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1010 if (bpc->bpc_mhop)
1011 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1012 else
1013 sin.sin_addr.s_addr = INADDR_ANY;
1014
1015 pcount = 0;
1016 do {
1017 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1018 /* Searched all ports, none available */
1019 log_error("ipv4-new: failed to bind port: %s",
1020 strerror(errno));
1021 close(sd);
1022 return -1;
1023 }
1024 if (srcPort >= BFD_SRCPORTMAX)
1025 srcPort = BFD_SRCPORTINIT;
1026 sin.sin_port = htons(srcPort++);
1027 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1028
1029 return sd;
1030 }
1031
1032
1033 /*
1034 * IPv6 sockets
1035 */
1036
1037 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1038 {
1039 int sd, pcount, ifindex;
1040 struct sockaddr_in6 sin6;
1041 static int srcPort = BFD_SRCPORTINIT;
1042
1043 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1044 if (sd == -1) {
1045 log_error("ipv6-new: failed to create socket: %s",
1046 strerror(errno));
1047 return -1;
1048 }
1049
1050 /* Set TTL to 255 for all transmitted packets */
1051 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1052 close(sd);
1053 return -1;
1054 }
1055
1056 /* Set TOS to CS6 for all transmitted packets */
1057 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1058 close(sd);
1059 return -1;
1060 }
1061
1062 /* Find an available source port in the proper range */
1063 memset(&sin6, 0, sizeof(sin6));
1064 sin6.sin6_family = AF_INET6;
1065 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1066 sin6.sin6_len = sizeof(sin6);
1067 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1068 sin6 = bpc->bpc_local.sa_sin6;
1069 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1070 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1071 sin6.sin6_scope_id = ifindex;
1072
1073 if (bpc->bpc_has_localif) {
1074 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1075 close(sd);
1076 return -1;
1077 }
1078 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1079 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1080 close(sd);
1081 return -1;
1082 }
1083 }
1084
1085 pcount = 0;
1086 do {
1087 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1088 /* Searched all ports, none available */
1089 log_error("ipv6-new: failed to bind port: %s",
1090 strerror(errno));
1091 close(sd);
1092 return -1;
1093 }
1094 if (srcPort >= BFD_SRCPORTMAX)
1095 srcPort = BFD_SRCPORTINIT;
1096 sin6.sin6_port = htons(srcPort++);
1097 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1098
1099 return sd;
1100 }
1101
1102 int bp_set_ttlv6(int sd, uint8_t value)
1103 {
1104 int ttl = value;
1105
1106 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1107 == -1) {
1108 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1109 value, strerror(errno));
1110 return -1;
1111 }
1112
1113 return 0;
1114 }
1115
1116 int bp_set_tosv6(int sd, uint8_t value)
1117 {
1118 int tos = value;
1119
1120 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1121 == -1) {
1122 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1123 strerror(errno));
1124 return -1;
1125 }
1126
1127 return 0;
1128 }
1129
1130 static void bp_set_ipv6opts(int sd)
1131 {
1132 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1133 int ipv6_only = BFD_IPV6_ONLY_VAL;
1134
1135 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1136 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1137 BFD_TTL_VAL, strerror(errno));
1138
1139 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1140 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1141 BFD_RCV_TTL_VAL, strerror(errno));
1142
1143 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1144 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1145 ipv6_pktinfo, strerror(errno));
1146
1147 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1148 sizeof(ipv6_only))
1149 == -1)
1150 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1151 ipv6_only, strerror(errno));
1152 }
1153
1154 static void bp_bind_ipv6(int sd, uint16_t port)
1155 {
1156 struct sockaddr_in6 sin6;
1157
1158 memset(&sin6, 0, sizeof(sin6));
1159 sin6.sin6_family = AF_INET6;
1160 sin6.sin6_addr = in6addr_any;
1161 sin6.sin6_port = htons(port);
1162 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1163 sin6.sin6_len = sizeof(sin6);
1164 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1165 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1166 log_fatal("bind-ipv6: bind: %s", strerror(errno));
1167 }
1168
1169 int bp_udp6_shop(void)
1170 {
1171 int sd;
1172
1173 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1174 if (sd == -1)
1175 log_fatal("udp6-shop: socket: %s", strerror(errno));
1176
1177 bp_set_ipv6opts(sd);
1178 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1179
1180 return sd;
1181 }
1182
1183 int bp_udp6_mhop(void)
1184 {
1185 int sd;
1186
1187 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1188 if (sd == -1)
1189 log_fatal("udp6-mhop: socket: %s", strerror(errno));
1190
1191 bp_set_ipv6opts(sd);
1192 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1193
1194 return sd;
1195 }
1196
1197 int bp_echo_socket(void)
1198 {
1199 int s;
1200
1201 s = socket(AF_INET, SOCK_DGRAM, 0);
1202 if (s == -1)
1203 log_fatal("echo-socket: socket: %s", strerror(errno));
1204
1205 bp_set_ipopts(s);
1206 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1207
1208 return s;
1209 }
1210
1211 int bp_echov6_socket(void)
1212 {
1213 int s;
1214
1215 s = socket(AF_INET6, SOCK_DGRAM, 0);
1216 if (s == -1)
1217 log_fatal("echov6-socket: socket: %s", strerror(errno));
1218
1219 bp_set_ipv6opts(s);
1220 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1221
1222 return s;
1223 }