]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
Merge pull request #2875 from opensourcerouting/fabricd
[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 /* Prepare the recvmsg params. */
252 iov[0].iov_base = msgbuf;
253 iov[0].iov_len = msgbuflen;
254
255 memset(&msghdr, 0, sizeof(msghdr));
256 msghdr.msg_name = &msgaddr;
257 msghdr.msg_namelen = sizeof(msgaddr);
258 msghdr.msg_iov = iov;
259 msghdr.msg_iovlen = 1;
260 msghdr.msg_control = cmsgbuf;
261 msghdr.msg_controllen = sizeof(cmsgbuf);
262
263 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
264 if (mlen == -1) {
265 if (errno != EAGAIN)
266 log_error("ipv4-recv: recv failed: %s",
267 strerror(errno));
268
269 return -1;
270 }
271
272 /* Get source address */
273 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
274
275 /* Get and check TTL */
276 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
277 cm = CMSG_NXTHDR(&msghdr, cm)) {
278 if (cm->cmsg_level != IPPROTO_IP)
279 continue;
280
281 switch (cm->cmsg_type) {
282 #ifdef BFD_LINUX
283 case IP_TTL: {
284 uint32_t ttlval;
285
286 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
287 if (ttlval > 255) {
288 log_debug("ipv4-recv: invalid TTL: %u", ttlval);
289 return -1;
290 }
291 *ttl = ttlval;
292 break;
293 }
294
295 case IP_PKTINFO: {
296 struct in_pktinfo *pi =
297 (struct in_pktinfo *)CMSG_DATA(cm);
298
299 if (pi == NULL)
300 break;
301
302 local->sa_sin.sin_family = AF_INET;
303 local->sa_sin.sin_addr = pi->ipi_addr;
304 fetch_portname_from_ifindex(pi->ipi_ifindex, port,
305 portlen);
306 break;
307 }
308 #endif /* BFD_LINUX */
309 #ifdef BFD_BSD
310 case IP_RECVTTL: {
311 memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
312 break;
313 }
314
315 case IP_RECVDSTADDR: {
316 struct in_addr ia;
317
318 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
319 local->sa_sin.sin_family = AF_INET;
320 local->sa_sin.sin_addr = ia;
321 break;
322 }
323 #endif /* BFD_BSD */
324
325 default:
326 /*
327 * On *BSDs we expect to land here when skipping
328 * the IP_RECVIF header. It will be handled by
329 * getsockopt_ifindex() below.
330 */
331 /* NOTHING */
332 break;
333 }
334 }
335
336 /* OS agnostic way of getting interface name. */
337 if (port[0] == 0) {
338 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
339 if (ifindex > 0)
340 fetch_portname_from_ifindex(ifindex, port, portlen);
341 }
342
343 return mlen;
344 }
345
346 ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
347 char *port, size_t portlen, char *vrfname,
348 size_t vrfnamelen, struct sockaddr_any *local,
349 struct sockaddr_any *peer)
350 {
351 struct cmsghdr *cm;
352 struct in6_pktinfo *pi6 = NULL;
353 int ifindex = 0;
354 ssize_t mlen;
355 uint32_t ttlval;
356 struct sockaddr_in6 msgaddr6;
357 struct msghdr msghdr6;
358 struct iovec iov[1];
359 uint8_t cmsgbuf6[255];
360
361 /* Prepare the recvmsg params. */
362 iov[0].iov_base = msgbuf;
363 iov[0].iov_len = msgbuflen;
364
365 memset(&msghdr6, 0, sizeof(msghdr6));
366 msghdr6.msg_name = &msgaddr6;
367 msghdr6.msg_namelen = sizeof(msgaddr6);
368 msghdr6.msg_iov = iov;
369 msghdr6.msg_iovlen = 1;
370 msghdr6.msg_control = cmsgbuf6;
371 msghdr6.msg_controllen = sizeof(cmsgbuf6);
372
373 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
374 if (mlen == -1) {
375 if (errno != EAGAIN)
376 log_error("ipv6-recv: recv failed: %s",
377 strerror(errno));
378
379 return -1;
380 }
381
382 /* Get source address */
383 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
384
385 /* Get and check TTL */
386 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
387 cm = CMSG_NXTHDR(&msghdr6, cm)) {
388 if (cm->cmsg_level != IPPROTO_IPV6)
389 continue;
390
391 if (cm->cmsg_type == IPV6_HOPLIMIT) {
392 memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
393 if (ttlval > 255) {
394 log_debug("ipv6-recv: invalid TTL: %u", ttlval);
395 return -1;
396 }
397
398 *ttl = ttlval;
399 } else if (cm->cmsg_type == IPV6_PKTINFO) {
400 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
401 if (pi6) {
402 local->sa_sin.sin_family = AF_INET6;
403 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
404 fetch_portname_from_ifindex(pi6->ipi6_ifindex,
405 port, portlen);
406 ifindex = pi6->ipi6_ifindex;
407 }
408 }
409 }
410
411 /* Set scope ID for link local addresses. */
412 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
413 peer->sa_sin6.sin6_scope_id = ifindex;
414 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
415 local->sa_sin6.sin6_scope_id = ifindex;
416
417 return mlen;
418 }
419
420 static void bfd_sd_reschedule(int sd)
421 {
422 if (sd == bglobal.bg_shop) {
423 THREAD_OFF(bglobal.bg_ev[0]);
424 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
425 &bglobal.bg_ev[0]);
426 } else if (sd == bglobal.bg_mhop) {
427 THREAD_OFF(bglobal.bg_ev[1]);
428 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
429 &bglobal.bg_ev[1]);
430 } else if (sd == bglobal.bg_shop6) {
431 THREAD_OFF(bglobal.bg_ev[2]);
432 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
433 &bglobal.bg_ev[2]);
434 } else if (sd == bglobal.bg_mhop6) {
435 THREAD_OFF(bglobal.bg_ev[3]);
436 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
437 &bglobal.bg_ev[3]);
438 } else if (sd == bglobal.bg_echo) {
439 THREAD_OFF(bglobal.bg_ev[4]);
440 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
441 &bglobal.bg_ev[4]);
442 } else if (sd == bglobal.bg_echov6) {
443 THREAD_OFF(bglobal.bg_ev[5]);
444 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echov6,
445 &bglobal.bg_ev[5]);
446 }
447 }
448
449 static void cp_debug(bool mhop, struct sockaddr_any *peer,
450 struct sockaddr_any *local, const char *port,
451 const char *vrf, const char *fmt, ...)
452 {
453 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
454 va_list vl;
455
456 if (peer->sa_sin.sin_family)
457 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
458 else
459 peerstr[0] = 0;
460
461 if (local->sa_sin.sin_family)
462 snprintf(localstr, sizeof(localstr), " local:%s",
463 satostr(local));
464 else
465 localstr[0] = 0;
466
467 if (port[0])
468 snprintf(portstr, sizeof(portstr), " port:%s", port);
469 else
470 portstr[0] = 0;
471
472 if (vrf[0])
473 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
474 else
475 vrfstr[0] = 0;
476
477 va_start(vl, fmt);
478 vsnprintf(buf, sizeof(buf), fmt, vl);
479 va_end(vl);
480
481 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
482 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
483 }
484
485 int bfd_recv_cb(struct thread *t)
486 {
487 int sd = THREAD_FD(t);
488 struct bfd_session *bfd;
489 struct bfd_pkt *cp;
490 bool is_mhop;
491 ssize_t mlen = 0;
492 uint32_t oldEchoXmt_TO, oldXmtTime;
493 uint8_t ttl;
494 struct sockaddr_any local, peer;
495 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
496 uint8_t msgbuf[1516];
497
498 /* Schedule next read. */
499 bfd_sd_reschedule(sd);
500
501 /* Handle echo packets. */
502 if (sd == bglobal.bg_echo || sd == bglobal.bg_echov6) {
503 ptm_bfd_process_echo_pkt(sd);
504 return 0;
505 }
506
507 /* Sanitize input/output. */
508 memset(port, 0, sizeof(port));
509 memset(vrfname, 0, sizeof(vrfname));
510 memset(&local, 0, sizeof(local));
511 memset(&peer, 0, sizeof(peer));
512
513 /* Handle control packets. */
514 is_mhop = false;
515 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
516 is_mhop = sd == bglobal.bg_mhop;
517 mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, port,
518 sizeof(port), vrfname, sizeof(vrfname),
519 &local, &peer);
520 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
521 is_mhop = sd == bglobal.bg_mhop6;
522 mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, port,
523 sizeof(port), vrfname, sizeof(vrfname),
524 &local, &peer);
525 }
526
527 /* Implement RFC 5880 6.8.6 */
528 if (mlen < BFD_PKT_LEN) {
529 cp_debug(is_mhop, &peer, &local, port, vrfname,
530 "too small (%ld bytes)", mlen);
531 return 0;
532 }
533
534 /* Validate packet TTL. */
535 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
536 cp_debug(is_mhop, &peer, &local, port, vrfname,
537 "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
538 return 0;
539 }
540
541 /*
542 * Parse the control header for inconsistencies:
543 * - Invalid version;
544 * - Bad multiplier configuration;
545 * - Short packets;
546 * - Invalid discriminator;
547 */
548 cp = (struct bfd_pkt *)(msgbuf);
549 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
550 cp_debug(is_mhop, &peer, &local, port, vrfname,
551 "bad version %d", BFD_GETVER(cp->diag));
552 return 0;
553 }
554
555 if (cp->detect_mult == 0) {
556 cp_debug(is_mhop, &peer, &local, port, vrfname,
557 "detect multiplier set to zero");
558 return 0;
559 }
560
561 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
562 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
563 return 0;
564 }
565
566 if (cp->discrs.my_discr == 0) {
567 cp_debug(is_mhop, &peer, &local, port, vrfname,
568 "'my discriminator' is zero");
569 return 0;
570 }
571
572 /* Find the session that this packet belongs. */
573 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
574 if (bfd == NULL) {
575 cp_debug(is_mhop, &peer, &local, port, vrfname,
576 "no session found");
577 return 0;
578 }
579
580 bfd->stats.rx_ctrl_pkt++;
581
582 /*
583 * Multi hop: validate packet TTL.
584 * Single hop: set local address that received the packet.
585 */
586 if (is_mhop) {
587 if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) {
588 cp_debug(is_mhop, &peer, &local, port, vrfname,
589 "exceeded max hop count (expected %d, got %d)",
590 bfd->mh_ttl, BFD_TTL_VAL);
591 return 0;
592 }
593 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
594 bfd->local_ip = local;
595 }
596
597 /*
598 * If no interface was detected, save the interface where the
599 * packet came in.
600 */
601 if (bfd->ifindex == 0)
602 bfd->ifindex = ptm_bfd_fetch_ifindex(port);
603
604 /* Log remote discriminator changes. */
605 if ((bfd->discrs.remote_discr != 0)
606 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
607 cp_debug(is_mhop, &peer, &local, port, vrfname,
608 "remote discriminator mismatch (expected %d, got %d)",
609 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
610
611 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
612
613 /* If received the Final bit, the new values should take effect */
614 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
615 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
616 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
617 bfd->new_timers.desired_min_tx = 0;
618 bfd->new_timers.required_min_rx = 0;
619 bfd->polling = 0;
620 }
621
622 if (!bfd->demand_mode) {
623 /* Compute detect time */
624 bfd->detect_TO = cp->detect_mult
625 * ((bfd->timers.required_min_rx
626 > ntohl(cp->timers.desired_min_tx))
627 ? bfd->timers.required_min_rx
628 : ntohl(cp->timers.desired_min_tx));
629 bfd->remote_detect_mult = cp->detect_mult;
630 } else
631 cp_debug(is_mhop, &peer, &local, port, vrfname,
632 "unsupported demand mode");
633
634 /* Save remote diagnostics before state switch. */
635 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
636
637 /* State switch from section 6.8.6 */
638 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
639 if (bfd->ses_state != PTM_BFD_DOWN)
640 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
641 } else {
642 switch (bfd->ses_state) {
643 case (PTM_BFD_DOWN):
644 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
645 ptm_bfd_ses_up(bfd);
646 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
647 bfd->ses_state = PTM_BFD_INIT;
648 break;
649 case (PTM_BFD_INIT):
650 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
651 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
652 ptm_bfd_ses_up(bfd);
653 break;
654 case (PTM_BFD_UP):
655 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
656 ptm_bfd_ses_dn(bfd, BD_NEIGHBOR_DOWN);
657 break;
658 }
659 }
660
661 /*
662 * Handle echo packet status:
663 * - Start echo packets if configured and permitted
664 * (required_min_echo > 0);
665 * - Stop echo packets if not allowed (required_min_echo == 0);
666 * - Recalculate echo packet interval;
667 */
668 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
669 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
670 if (!ntohl(cp->timers.required_min_echo)) {
671 ptm_bfd_echo_stop(bfd, 1);
672 } else {
673 oldEchoXmt_TO = bfd->echo_xmt_TO;
674 bfd->echo_xmt_TO =
675 bfd->timers.required_min_echo;
676 if (ntohl(cp->timers.required_min_echo)
677 > bfd->echo_xmt_TO)
678 bfd->echo_xmt_TO = ntohl(
679 cp->timers.required_min_echo);
680 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
681 ptm_bfd_echo_start(bfd);
682 }
683 } else if (ntohl(cp->timers.required_min_echo)) {
684 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
685 if (ntohl(cp->timers.required_min_echo)
686 > bfd->echo_xmt_TO)
687 bfd->echo_xmt_TO =
688 ntohl(cp->timers.required_min_echo);
689 ptm_bfd_echo_start(bfd);
690 }
691 }
692
693 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
694 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
695 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
696 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
697 }
698
699 /* Calculate new transmit time */
700 oldXmtTime = bfd->xmt_TO;
701 bfd->xmt_TO =
702 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
703 ? bfd->timers.desired_min_tx
704 : ntohl(cp->timers.required_min_rx);
705
706 /* If transmit time has changed, and too much time until next xmt,
707 * restart
708 */
709 if (BFD_GETPBIT(cp->flags)) {
710 ptm_bfd_xmt_TO(bfd, 1);
711 } else if (oldXmtTime != bfd->xmt_TO) {
712 /* XXX add some skid to this as well */
713 ptm_bfd_start_xmt_timer(bfd, false);
714 }
715
716 /* Restart detection timer (packet received) */
717 if (!bfd->demand_mode)
718 bfd_recvtimer_update(bfd);
719
720 /*
721 * Save the timers and state sent by the remote end
722 * for debugging and statistics.
723 */
724 if (BFD_GETFBIT(cp->flags)) {
725 bfd->remote_timers.desired_min_tx =
726 ntohl(cp->timers.desired_min_tx);
727 bfd->remote_timers.required_min_rx =
728 ntohl(cp->timers.required_min_rx);
729 bfd->remote_timers.required_min_echo =
730 ntohl(cp->timers.required_min_echo);
731
732 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
733 }
734
735 return 0;
736 }
737
738 /*
739 * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
740 * the packet is looped back or returns the my discriminator ID along
741 * with the TTL.
742 *
743 * Returns -1 on error or loopback or 0 on success.
744 */
745 int bp_bfd_echo_in(int sd, uint8_t *ttl, uint32_t *my_discr)
746 {
747 struct bfd_echo_pkt *bep;
748 ssize_t rlen;
749 struct sockaddr_any local, peer;
750 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
751 uint8_t msgbuf[1516];
752
753 if (sd == bglobal.bg_echo)
754 rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, port,
755 sizeof(port), vrfname, sizeof(vrfname),
756 &local, &peer);
757 else
758 rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, port,
759 sizeof(port), vrfname, sizeof(vrfname),
760 &local, &peer);
761
762 /* Short packet, better not risk reading it. */
763 if (rlen < (ssize_t)sizeof(*bep)) {
764 cp_debug(false, &peer, &local, port, vrfname,
765 "small echo packet");
766 return -1;
767 }
768
769 /* Test for loopback. */
770 if (*ttl == BFD_TTL_VAL) {
771 bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
772 (struct sockaddr *)&peer,
773 (sd == bglobal.bg_echo) ? sizeof(peer.sa_sin)
774 : sizeof(peer.sa_sin6));
775 return -1;
776 }
777
778 /* Read my discriminator from BFD Echo packet. */
779 bep = (struct bfd_echo_pkt *)msgbuf;
780 *my_discr = ntohl(bep->my_discr);
781 if (*my_discr == 0) {
782 cp_debug(false, &peer, &local, port, vrfname,
783 "invalid echo packet discriminator (zero)");
784 return -1;
785 }
786
787 return 0;
788 }
789
790 int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
791 struct sockaddr *to, socklen_t tolen)
792 {
793 struct cmsghdr *cmsg;
794 ssize_t wlen;
795 int ttlval = ttl;
796 bool is_ipv6 = to->sa_family == AF_INET6;
797 struct msghdr msg;
798 struct iovec iov[1];
799 uint8_t msgctl[255];
800
801 /* Prepare message data. */
802 iov[0].iov_base = data;
803 iov[0].iov_len = datalen;
804
805 memset(&msg, 0, sizeof(msg));
806 memset(msgctl, 0, sizeof(msgctl));
807 msg.msg_name = to;
808 msg.msg_namelen = tolen;
809 msg.msg_iov = iov;
810 msg.msg_iovlen = 1;
811
812 /* Prepare the packet TTL information. */
813 if (ttl > 0) {
814 /* Use ancillary data. */
815 msg.msg_control = msgctl;
816 msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
817
818 /* Configure the ancillary data. */
819 cmsg = CMSG_FIRSTHDR(&msg);
820 cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
821 if (is_ipv6) {
822 cmsg->cmsg_level = IPPROTO_IPV6;
823 cmsg->cmsg_type = IPV6_HOPLIMIT;
824 } else {
825 #if BFD_LINUX
826 cmsg->cmsg_level = IPPROTO_IP;
827 cmsg->cmsg_type = IP_TTL;
828 #else
829 /* FreeBSD does not support TTL in ancillary data. */
830 msg.msg_control = NULL;
831 msg.msg_controllen = 0;
832
833 bp_set_ttl(sd, ttl);
834 #endif /* BFD_BSD */
835 }
836 memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
837 }
838
839 /* Send echo back. */
840 wlen = sendmsg(sd, &msg, 0);
841 if (wlen <= 0) {
842 log_debug("udp-send: loopback failure: (%d) %s", errno, strerror(errno));
843 return -1;
844 } else if (wlen < (ssize_t)datalen) {
845 log_debug("udp-send: partial send: %ld expected %ld", wlen,
846 datalen);
847 return -1;
848 }
849
850 return 0;
851 }
852
853
854 /*
855 * Sockets creation.
856 */
857
858
859 /*
860 * IPv4 sockets
861 */
862 int bp_set_ttl(int sd, uint8_t value)
863 {
864 int ttl = value;
865
866 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
867 log_warning("set-ttl: setsockopt(IP_TTL, %d): %s", value,
868 strerror(errno));
869 return -1;
870 }
871
872 return 0;
873 }
874
875 int bp_set_tos(int sd, uint8_t value)
876 {
877 int tos = value;
878
879 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
880 log_warning("set-tos: setsockopt(IP_TOS, %d): %s", value,
881 strerror(errno));
882 return -1;
883 }
884
885 return 0;
886 }
887
888 static void bp_set_ipopts(int sd)
889 {
890 int rcvttl = BFD_RCV_TTL_VAL;
891
892 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
893 log_fatal("set-ipopts: TTL configuration failed");
894
895 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
896 == -1)
897 log_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
898 strerror(errno));
899
900 #ifdef BFD_LINUX
901 int pktinfo = BFD_PKT_INFO_VAL;
902
903 /* Figure out address and interface to do the peer matching. */
904 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
905 == -1)
906 log_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s", pktinfo,
907 strerror(errno));
908 #endif /* BFD_LINUX */
909 #ifdef BFD_BSD
910 int yes = 1;
911
912 /* Find out our address for peer matching. */
913 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
914 log_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s", yes,
915 strerror(errno));
916
917 /* Find out interface where the packet came in. */
918 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
919 log_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
920 strerror(errno));
921 #endif /* BFD_BSD */
922 }
923
924 static void bp_bind_ip(int sd, uint16_t port)
925 {
926 struct sockaddr_in sin;
927
928 memset(&sin, 0, sizeof(sin));
929 sin.sin_family = AF_INET;
930 sin.sin_addr.s_addr = htonl(INADDR_ANY);
931 sin.sin_port = htons(port);
932 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
933 log_fatal("bind-ip: bind: %s", strerror(errno));
934 }
935
936 int bp_udp_shop(void)
937 {
938 int sd;
939
940 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
941 if (sd == -1)
942 log_fatal("udp-shop: socket: %s", strerror(errno));
943
944 bp_set_ipopts(sd);
945 bp_bind_ip(sd, BFD_DEFDESTPORT);
946
947 return sd;
948 }
949
950 int bp_udp_mhop(void)
951 {
952 int sd;
953
954 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
955 if (sd == -1)
956 log_fatal("udp-mhop: socket: %s", strerror(errno));
957
958 bp_set_ipopts(sd);
959 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
960
961 return sd;
962 }
963
964 int bp_peer_socket(struct bfd_peer_cfg *bpc)
965 {
966 int sd, pcount;
967 struct sockaddr_in sin;
968 static int srcPort = BFD_SRCPORTINIT;
969
970 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
971 if (sd == -1) {
972 log_error("ipv4-new: failed to create socket: %s",
973 strerror(errno));
974 return -1;
975 }
976
977 /* Set TTL to 255 for all transmitted packets */
978 if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
979 close(sd);
980 return -1;
981 }
982
983 /* Set TOS to CS6 for all transmitted packets */
984 if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
985 close(sd);
986 return -1;
987 }
988
989 if (bpc->bpc_has_localif) {
990 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
991 close(sd);
992 return -1;
993 }
994 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
995 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
996 close(sd);
997 return -1;
998 }
999 }
1000
1001 /* Find an available source port in the proper range */
1002 memset(&sin, 0, sizeof(sin));
1003 sin = bpc->bpc_local.sa_sin;
1004 sin.sin_family = AF_INET;
1005 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1006 sin.sin_len = sizeof(sin);
1007 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1008 if (bpc->bpc_mhop)
1009 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1010 else
1011 sin.sin_addr.s_addr = INADDR_ANY;
1012
1013 pcount = 0;
1014 do {
1015 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1016 /* Searched all ports, none available */
1017 log_error("ipv4-new: failed to bind port: %s",
1018 strerror(errno));
1019 close(sd);
1020 return -1;
1021 }
1022 if (srcPort >= BFD_SRCPORTMAX)
1023 srcPort = BFD_SRCPORTINIT;
1024 sin.sin_port = htons(srcPort++);
1025 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1026
1027 return sd;
1028 }
1029
1030
1031 /*
1032 * IPv6 sockets
1033 */
1034
1035 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1036 {
1037 int sd, pcount, ifindex;
1038 struct sockaddr_in6 sin6;
1039 static int srcPort = BFD_SRCPORTINIT;
1040
1041 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1042 if (sd == -1) {
1043 log_error("ipv6-new: failed to create socket: %s",
1044 strerror(errno));
1045 return -1;
1046 }
1047
1048 /* Set TTL to 255 for all transmitted packets */
1049 if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1050 close(sd);
1051 return -1;
1052 }
1053
1054 /* Set TOS to CS6 for all transmitted packets */
1055 if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1056 close(sd);
1057 return -1;
1058 }
1059
1060 /* Find an available source port in the proper range */
1061 memset(&sin6, 0, sizeof(sin6));
1062 sin6.sin6_family = AF_INET6;
1063 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1064 sin6.sin6_len = sizeof(sin6);
1065 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1066 sin6 = bpc->bpc_local.sa_sin6;
1067 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1068 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1069 sin6.sin6_scope_id = ifindex;
1070
1071 if (bpc->bpc_has_localif) {
1072 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1073 close(sd);
1074 return -1;
1075 }
1076 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1077 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1078 close(sd);
1079 return -1;
1080 }
1081 }
1082
1083 pcount = 0;
1084 do {
1085 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1086 /* Searched all ports, none available */
1087 log_error("ipv6-new: failed to bind port: %s",
1088 strerror(errno));
1089 close(sd);
1090 return -1;
1091 }
1092 if (srcPort >= BFD_SRCPORTMAX)
1093 srcPort = BFD_SRCPORTINIT;
1094 sin6.sin6_port = htons(srcPort++);
1095 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1096
1097 return sd;
1098 }
1099
1100 int bp_set_ttlv6(int sd, uint8_t value)
1101 {
1102 int ttl = value;
1103
1104 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1105 == -1) {
1106 log_warning("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1107 value, strerror(errno));
1108 return -1;
1109 }
1110
1111 return 0;
1112 }
1113
1114 int bp_set_tosv6(int sd, uint8_t value)
1115 {
1116 int tos = value;
1117
1118 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1119 == -1) {
1120 log_warning("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1121 strerror(errno));
1122 return -1;
1123 }
1124
1125 return 0;
1126 }
1127
1128 static void bp_set_ipv6opts(int sd)
1129 {
1130 int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1131 int ipv6_only = BFD_IPV6_ONLY_VAL;
1132
1133 if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1134 log_fatal("set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1135 BFD_TTL_VAL, strerror(errno));
1136
1137 if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1138 log_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1139 BFD_RCV_TTL_VAL, strerror(errno));
1140
1141 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1142 log_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1143 ipv6_pktinfo, strerror(errno));
1144
1145 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1146 sizeof(ipv6_only))
1147 == -1)
1148 log_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1149 ipv6_only, strerror(errno));
1150 }
1151
1152 static void bp_bind_ipv6(int sd, uint16_t port)
1153 {
1154 struct sockaddr_in6 sin6;
1155
1156 memset(&sin6, 0, sizeof(sin6));
1157 sin6.sin6_family = AF_INET6;
1158 sin6.sin6_addr = in6addr_any;
1159 sin6.sin6_port = htons(port);
1160 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1161 sin6.sin6_len = sizeof(sin6);
1162 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1163 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1164 log_fatal("bind-ipv6: bind: %s", strerror(errno));
1165 }
1166
1167 int bp_udp6_shop(void)
1168 {
1169 int sd;
1170
1171 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1172 if (sd == -1)
1173 log_fatal("udp6-shop: socket: %s", strerror(errno));
1174
1175 bp_set_ipv6opts(sd);
1176 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1177
1178 return sd;
1179 }
1180
1181 int bp_udp6_mhop(void)
1182 {
1183 int sd;
1184
1185 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1186 if (sd == -1)
1187 log_fatal("udp6-mhop: socket: %s", strerror(errno));
1188
1189 bp_set_ipv6opts(sd);
1190 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1191
1192 return sd;
1193 }
1194
1195 int bp_echo_socket(void)
1196 {
1197 int s;
1198
1199 s = socket(AF_INET, SOCK_DGRAM, 0);
1200 if (s == -1)
1201 log_fatal("echo-socket: socket: %s", strerror(errno));
1202
1203 bp_set_ipopts(s);
1204 bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1205
1206 return s;
1207 }
1208
1209 int bp_echov6_socket(void)
1210 {
1211 int s;
1212
1213 s = socket(AF_INET6, SOCK_DGRAM, 0);
1214 if (s == -1)
1215 log_fatal("echov6-socket: socket: %s", strerror(errno));
1216
1217 bp_set_ipv6opts(s);
1218 bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1219
1220 return s;
1221 }