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