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