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