]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
bfdd: fix IPv6 peers using link-local address
[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 * Definitions
42 */
43
44 /* iov for BFD control frames */
45 #define CMSG_HDR_LEN sizeof(struct cmsghdr)
46 #define CMSG_TTL_LEN (CMSG_HDR_LEN + sizeof(uint32_t))
47 #define CMSG_IN_PKT_INFO_LEN (CMSG_HDR_LEN + sizeof(struct in_pktinfo) + 4)
48 #define CMSG_IN6_PKT_INFO_LEN \
49 (CMSG_HDR_LEN + sizeof(struct in6_addr) + sizeof(int) + 4)
50
51 struct bfd_raw_echo_pkt {
52 #ifdef BFD_LINUX
53 struct iphdr ip;
54 #endif /* BFD_LINUX */
55 #ifdef BFD_BSD
56 struct ip ip;
57 #endif /* BFD_BSD */
58 struct udphdr udp;
59 struct bfd_echo_pkt data;
60 };
61
62 #if 0 /* TODO: VxLAN support. */
63 struct bfd_raw_ctrl_pkt {
64 struct iphdr ip;
65 struct udphdr udp;
66 struct bfd_pkt data;
67 };
68 #endif
69
70 struct vxlan_hdr {
71 uint32_t flags;
72 uint32_t vnid;
73 };
74
75 #define IP_ECHO_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
76 #define UDP_ECHO_PKT_LEN (UDP_HDR_LEN + BFD_ECHO_PKT_LEN)
77 #define IP_CTRL_PKT_LEN (IP_HDR_LEN + UDP_HDR_LEN + BFD_PKT_LEN)
78 #define UDP_CTRL_PKT_LEN (UDP_HDR_LEN + BFD_PKT_LEN)
79
80 static uint8_t msgbuf[BFD_PKT_LEN];
81 static struct iovec msgiov = {&(msgbuf[0]), sizeof(msgbuf)};
82 static uint8_t cmsgbuf[255];
83
84 static struct sockaddr_in msgaddr;
85 static struct msghdr msghdr = {(void *)&msgaddr, sizeof(msgaddr), &msgiov, 1,
86 (void *)&cmsgbuf, sizeof(cmsgbuf), 0};
87
88 static uint8_t cmsgbuf6[255];
89
90 static struct sockaddr_in6 msgaddr6;
91 static struct msghdr msghdr6 = {(void *)&msgaddr6, sizeof(msgaddr6), &msgiov, 1,
92 (void *)&cmsgbuf6, sizeof(cmsgbuf6), 0};
93
94 static int ttlval = BFD_TTL_VAL;
95 static int tosval = BFD_TOS_VAL;
96 static int rcvttl = BFD_RCV_TTL_VAL;
97
98 /*
99 * Prototypes
100 */
101 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session *bfd);
102 static void ptm_bfd_echo_pkt_create(struct bfd_session *bfd);
103 static int ptm_bfd_echo_loopback(uint8_t *pkt, int pkt_len, struct sockaddr *ss,
104 socklen_t sslen);
105 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session *bfd, int fbit);
106 static int ptm_bfd_process_echo_pkt(int s);
107 static bool
108 ptm_bfd_validate_vxlan_pkt(struct bfd_session *bfd,
109 struct bfd_session_vxlan_info *vxlan_info);
110
111 static void bfd_sd_reschedule(int sd);
112 static ssize_t bfd_recv_ipv4(int sd, bool is_mhop, char *port, size_t portlen,
113 char *vrfname, size_t vrfnamelen,
114 struct sockaddr_any *local,
115 struct sockaddr_any *peer);
116 static ssize_t bfd_recv_ipv6(int sd, bool is_mhop, char *port, size_t portlen,
117 char *vrfname, size_t vrfnamelen,
118 struct sockaddr_any *local,
119 struct sockaddr_any *peer);
120
121 /* socket related prototypes */
122 static void bp_set_ipopts(int sd);
123 static void bp_bind_ip(int sd, uint16_t port);
124 static void bp_set_ipv6opts(int sd);
125 static void bp_bind_ipv6(int sd, uint16_t port);
126
127
128 /*
129 * Functions
130 */
131 uint16_t checksum(uint16_t *buf, int len)
132 {
133 int nbytes = len;
134 int sum = 0;
135 uint16_t csum = 0;
136 int size = sizeof(uint16_t);
137
138 while (nbytes > 1) {
139 sum += *buf++;
140 nbytes -= size;
141 }
142
143 if (nbytes == 1) {
144 *(uint8_t *)(&csum) = *(uint8_t *)buf;
145 sum += csum;
146 }
147
148 sum = (sum >> 16) + (sum & 0xFFFF);
149 sum += (sum >> 16);
150 csum = ~sum;
151 return csum;
152 }
153
154 static uint16_t ptm_bfd_gen_IP_ID(struct bfd_session *bfd)
155 {
156 return (++bfd->ip_id);
157 }
158
159 static int _ptm_bfd_send(struct bfd_session *bs, bool use_layer2,
160 uint16_t *port, const void *data, size_t datalen)
161 {
162 struct sockaddr *sa;
163 struct sockaddr_in sin;
164 struct sockaddr_in6 sin6;
165 #ifdef BFD_LINUX
166 struct sockaddr_ll dll;
167 #endif /* BFD_LINUX */
168 socklen_t slen;
169 ssize_t rv;
170 int sd = -1;
171
172 if (use_layer2) {
173 #ifdef BFD_LINUX
174 memset(&dll, 0, sizeof(dll));
175 dll.sll_family = AF_PACKET;
176 dll.sll_protocol = htons(ETH_P_IP);
177 memcpy(dll.sll_addr, bs->peer_mac, ETHERNET_ADDRESS_LENGTH);
178 dll.sll_halen = htons(ETHERNET_ADDRESS_LENGTH);
179 dll.sll_ifindex = bs->ifindex;
180
181 sd = bglobal.bg_echo;
182 sa = (struct sockaddr *)&dll;
183 slen = sizeof(dll);
184 #else
185 /*
186 * TODO: implement layer 2 send for *BSDs. This is
187 * needed for VxLAN.
188 */
189 log_warning("packet-send: not implemented");
190 return -1;
191 #endif
192 } else if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
193 memset(&sin6, 0, sizeof(sin6));
194 sin6.sin6_family = AF_INET6;
195 sin6.sin6_addr = bs->shop.peer.sa_sin6.sin6_addr;
196 sin6.sin6_port =
197 (port) ? *port
198 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
199 ? htons(BFD_DEF_MHOP_DEST_PORT)
200 : htons(BFD_DEFDESTPORT);
201
202 sd = bs->sock;
203 sa = (struct sockaddr *)&sin6;
204 slen = sizeof(sin6);
205 } else {
206 memset(&sin, 0, sizeof(sin));
207 sin.sin_family = AF_INET;
208 sin.sin_addr = bs->shop.peer.sa_sin.sin_addr;
209 sin.sin_port =
210 (port) ? *port
211 : (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
212 ? htons(BFD_DEF_MHOP_DEST_PORT)
213 : htons(BFD_DEFDESTPORT);
214
215 sd = bs->sock;
216 sa = (struct sockaddr *)&sin;
217 slen = sizeof(sin);
218 }
219
220 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
221 sa->sa_len = slen;
222 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
223 rv = sendto(sd, data, datalen, 0, sa, slen);
224 if (rv <= 0) {
225 log_debug("packet-send: send failure: %s", strerror(errno));
226 return -1;
227 }
228 if (rv < (ssize_t)datalen)
229 log_debug("packet-send: send partial", strerror(errno));
230
231 return 0;
232 }
233
234 static void ptm_bfd_echo_pkt_create(struct bfd_session *bfd)
235 {
236 struct bfd_raw_echo_pkt ep;
237 uint8_t *pkt = bfd->echo_pkt;
238
239 memset(&ep, 0, sizeof(ep));
240 memset(bfd->echo_pkt, 0, sizeof(bfd->echo_pkt));
241
242 /* Construct ethernet header information */
243 memcpy(pkt, bfd->peer_mac, ETHERNET_ADDRESS_LENGTH);
244 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
245 memcpy(pkt, bfd->local_mac, ETHERNET_ADDRESS_LENGTH);
246 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
247 #ifdef BFD_LINUX
248 pkt[0] = ETH_P_IP / 256;
249 pkt[1] = ETH_P_IP % 256;
250 #endif /* BFD_LINUX */
251 #ifdef BFD_BSD
252 pkt[0] = ETHERTYPE_IP / 256;
253 pkt[1] = ETHERTYPE_IP % 256;
254 #endif /* BFD_BSD */
255 pkt += 2;
256
257 /* Construct IP header information */
258 #ifdef BFD_LINUX
259 ep.ip.version = 4;
260 ep.ip.ihl = 5;
261 ep.ip.tos = 0;
262 ep.ip.tot_len = htons(IP_ECHO_PKT_LEN);
263 ep.ip.id = htons(ptm_bfd_gen_IP_ID(bfd));
264 ep.ip.frag_off = 0;
265 ep.ip.ttl = BFD_TTL_VAL;
266 ep.ip.protocol = IPPROTO_UDP;
267 ep.ip.saddr = bfd->local_ip.sa_sin.sin_addr.s_addr;
268 ep.ip.daddr = bfd->shop.peer.sa_sin.sin_addr.s_addr;
269 ep.ip.check = checksum((uint16_t *)&ep.ip, IP_HDR_LEN);
270 #endif /* BFD_LINUX */
271 #ifdef BFD_BSD
272 ep.ip.ip_v = 4;
273 ep.ip.ip_hl = 5;
274 ep.ip.ip_tos = 0;
275 ep.ip.ip_len = htons(IP_ECHO_PKT_LEN);
276 ep.ip.ip_id = htons(ptm_bfd_gen_IP_ID(bfd));
277 ep.ip.ip_off = 0;
278 ep.ip.ip_ttl = BFD_TTL_VAL;
279 ep.ip.ip_p = IPPROTO_UDP;
280 ep.ip.ip_src = bfd->local_ip.sa_sin.sin_addr;
281 ep.ip.ip_dst = bfd->shop.peer.sa_sin.sin_addr;
282 ep.ip.ip_sum = checksum((uint16_t *)&ep.ip, IP_HDR_LEN);
283 #endif /* BFD_BSD */
284
285 /* Construct UDP header information */
286 #ifdef BFD_LINUX
287 ep.udp.source = htons(BFD_DEF_ECHO_PORT);
288 ep.udp.dest = htons(BFD_DEF_ECHO_PORT);
289 ep.udp.len = htons(UDP_ECHO_PKT_LEN);
290 #endif /* BFD_LINUX */
291 #ifdef BFD_BSD
292 ep.udp.uh_sport = htons(BFD_DEF_ECHO_PORT);
293 ep.udp.uh_dport = htons(BFD_DEF_ECHO_PORT);
294 ep.udp.uh_ulen = htons(UDP_ECHO_PKT_LEN);
295 #endif /* BFD_BSD */
296
297 /* Construct Echo packet information */
298 ep.data.ver = BFD_ECHO_VERSION;
299 ep.data.len = BFD_ECHO_PKT_LEN;
300 ep.data.my_discr = htonl(bfd->discrs.my_discr);
301 #ifdef BFD_LINUX
302 ep.udp.check =
303 #endif /* BFD_LINUX */
304 #ifdef BFD_BSD
305 ep.udp.uh_sum =
306 #endif /* BFD_BSD */
307 udp4_checksum(&ep.ip, (uint8_t *)&ep.udp,
308 UDP_ECHO_PKT_LEN);
309
310 memcpy(pkt, &ep, sizeof(ep));
311 }
312
313 void ptm_bfd_echo_snd(struct bfd_session *bfd)
314 {
315 struct bfd_raw_echo_pkt *ep;
316 bool use_layer2 = false;
317 const void *pkt;
318 size_t pktlen;
319 uint16_t port = htons(BFD_DEF_ECHO_PORT);
320
321 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
322 ptm_bfd_echo_pkt_create(bfd);
323 BFD_SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
324 } else {
325 /* just update the checksum and ip Id */
326 ep = (struct bfd_raw_echo_pkt *)(bfd->echo_pkt + ETH_HDR_LEN);
327 #ifdef BFD_LINUX
328 ep->ip.id = htons(ptm_bfd_gen_IP_ID(bfd));
329 ep->ip.check = 0;
330 ep->ip.check = checksum((uint16_t *)&ep->ip, IP_HDR_LEN);
331 #endif /* BFD_LINUX */
332 #ifdef BFD_BSD
333 ep->ip.ip_id = htons(ptm_bfd_gen_IP_ID(bfd));
334 ep->ip.ip_sum = 0;
335 ep->ip.ip_sum = checksum((uint16_t *)&ep->ip, IP_HDR_LEN);
336 #endif /* BFD_BSD */
337 }
338
339 if (use_layer2) {
340 pkt = bfd->echo_pkt;
341 pktlen = BFD_ECHO_PKT_TOT_LEN;
342 } else {
343 pkt = &bfd->echo_pkt[ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN];
344 pktlen = BFD_ECHO_PKT_TOT_LEN
345 - (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN);
346 }
347
348 if (_ptm_bfd_send(bfd, use_layer2, &port, pkt, pktlen) != 0) {
349 log_debug("echo-packet: send failure: %s", strerror(errno));
350 return;
351 }
352
353 bfd->stats.tx_echo_pkt++;
354 }
355
356 static int ptm_bfd_echo_loopback(uint8_t *pkt, int pkt_len, struct sockaddr *ss,
357 socklen_t sslen)
358 {
359 #ifdef BFD_LINUX
360 struct bfd_raw_echo_pkt *ep =
361 (struct bfd_raw_echo_pkt *)(pkt + ETH_HDR_LEN);
362 uint8_t temp_mac[ETHERNET_ADDRESS_LENGTH];
363 uint32_t temp_ip;
364 struct ethhdr *eth = (struct ethhdr *)pkt;
365
366 /* swap the mac addresses */
367 memcpy(temp_mac, eth->h_source, ETHERNET_ADDRESS_LENGTH);
368 memcpy(eth->h_source, eth->h_dest, ETHERNET_ADDRESS_LENGTH);
369 memcpy(eth->h_dest, temp_mac, ETHERNET_ADDRESS_LENGTH);
370
371 /* swap ip addresses */
372 temp_ip = ep->ip.saddr;
373 ep->ip.saddr = ep->ip.daddr;
374 ep->ip.daddr = temp_ip;
375
376 ep->ip.ttl = ep->ip.ttl - 1;
377 ep->ip.check = 0;
378 ep->ip.check = checksum((uint16_t *)ep, IP_HDR_LEN);
379 #endif /* BFD_LINUX */
380 #ifdef BFD_BSD_FILTER
381 struct bfd_raw_echo_pkt_t *ep =
382 (struct bfd_raw_echo_pkt *)(pkt + ETH_HDR_LEN);
383 uint8_t temp_mac[ETHERNET_ADDRESS_LENGTH];
384 struct in_addr temp_ip;
385 struct ether_header *ether = (struct ether_header *)pkt;
386
387 /*
388 * TODO: this is not yet implemented and requires BPF code for
389 * OmniOS, NetBSD and FreeBSD9.
390 */
391
392 /* swap the mac addresses */
393 memcpy(temp_mac, ether->ether_shost, ETHERNET_ADDRESS_LENGTH);
394 memcpy(ether->ether_shost, ether->ether_dhost, ETHERNET_ADDRESS_LENGTH);
395 memcpy(ether->ether_dhost, temp_mac, ETHERNET_ADDRESS_LENGTH);
396
397 /* swap ip addresses */
398 temp_ip = ep->ip.ip_src;
399 ep->ip.ip_src = ep->ip.ip_dst;
400 ep->ip.ip_dst = temp_ip;
401
402 ep->ip.ip_ttl = ep->ip.ip_ttl - 1;
403 ep->ip.ip_sum = 0;
404 ep->ip.ip_sum = checksum((uint16_t *)ep, IP_HDR_LEN);
405 #endif /* BFD_BSD_FILTER */
406
407 if (sendto(bglobal.bg_echo, pkt, pkt_len, 0, ss, sslen) < 0) {
408 log_debug("echo-loopback: send failure: %s", strerror(errno));
409 return -1;
410 }
411
412 return 0;
413 }
414
415 static void ptm_bfd_vxlan_pkt_snd(struct bfd_session *bfd
416 __attribute__((__unused__)),
417 int fbit __attribute__((__unused__)))
418 {
419 #if 0 /* TODO: VxLAN support. */
420 struct bfd_raw_ctrl_pkt cp;
421 uint8_t vxlan_pkt[BFD_VXLAN_PKT_TOT_LEN];
422 uint8_t *pkt = vxlan_pkt;
423 struct sockaddr_in sin;
424 struct vxlan_hdr *vhdr;
425
426 memset(vxlan_pkt, 0, sizeof(vxlan_pkt));
427 memset(&cp, 0, sizeof(cp));
428
429 /* Construct VxLAN header information */
430 vhdr = (struct vxlan_hdr *)pkt;
431 vhdr->flags = htonl(0x08000000);
432 vhdr->vnid = htonl(bfd->vxlan_info.vnid << 8);
433 pkt += VXLAN_HDR_LEN;
434
435 /* Construct ethernet header information */
436 memcpy(pkt, bfd->vxlan_info.peer_dst_mac, ETHERNET_ADDRESS_LENGTH);
437 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
438 memcpy(pkt, bfd->vxlan_info.local_dst_mac, ETHERNET_ADDRESS_LENGTH);
439 pkt = pkt + ETHERNET_ADDRESS_LENGTH;
440 pkt[0] = ETH_P_IP / 256;
441 pkt[1] = ETH_P_IP % 256;
442 pkt += 2;
443
444 /* Construct IP header information */
445 cp.ip.version = 4;
446 cp.ip.ihl = 5;
447 cp.ip.tos = 0;
448 cp.ip.tot_len = htons(IP_CTRL_PKT_LEN);
449 cp.ip.id = ptm_bfd_gen_IP_ID(bfd);
450 cp.ip.frag_off = 0;
451 cp.ip.ttl = BFD_TTL_VAL;
452 cp.ip.protocol = IPPROTO_UDP;
453 cp.ip.daddr = bfd->vxlan_info.peer_dst_ip.s_addr;
454 cp.ip.saddr = bfd->vxlan_info.local_dst_ip.s_addr;
455 cp.ip.check = checksum((uint16_t *)&cp.ip, IP_HDR_LEN);
456
457 /* Construct UDP header information */
458 cp.udp.source = htons(BFD_DEFDESTPORT);
459 cp.udp.dest = htons(BFD_DEFDESTPORT);
460 cp.udp.len = htons(UDP_CTRL_PKT_LEN);
461
462 /* Construct BFD control packet information */
463 cp.data.diag = bfd->local_diag;
464 BFD_SETVER(cp.data.diag, BFD_VERSION);
465 BFD_SETSTATE(cp.data.flags, bfd->ses_state);
466 BFD_SETDEMANDBIT(cp.data.flags, BFD_DEF_DEMAND);
467 BFD_SETPBIT(cp.data.flags, bfd->polling);
468 BFD_SETFBIT(cp.data.flags, fbit);
469 cp.data.detect_mult = bfd->detect_mult;
470 cp.data.len = BFD_PKT_LEN;
471 cp.data.discrs.my_discr = htonl(bfd->discrs.my_discr);
472 cp.data.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
473 cp.data.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
474 cp.data.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
475 cp.data.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
476
477 cp.udp.check =
478 udp4_checksum(&cp.ip, (uint8_t *)&cp.udp, UDP_CTRL_PKT_LEN);
479
480 memcpy(pkt, &cp, sizeof(cp));
481 sin.sin_family = AF_INET;
482 sin.sin_addr = bfd->shop.peer.sa_sin.sin_addr;
483 sin.sin_port = htons(4789);
484
485 if (sendto(bfd->sock, vxlan_pkt, BFD_VXLAN_PKT_TOT_LEN, 0,
486 (struct sockaddr *)&sin, sizeof(struct sockaddr_in))
487 < 0) {
488 ERRLOG("Error sending vxlan bfd pkt: %s", strerror(errno));
489 } else {
490 bfd->stats.tx_ctrl_pkt++;
491 }
492 #endif
493 }
494
495 static int ptm_bfd_process_echo_pkt(int s)
496 {
497 uint32_t my_discr = 0;
498 struct sockaddr_storage ss;
499 socklen_t sslen = sizeof(ss);
500 uint8_t rx_pkt[BFD_RX_BUF_LEN];
501 ssize_t pkt_len = sizeof(rx_pkt);
502 struct bfd_session *bfd;
503 #ifdef BFD_LINUX
504 struct bfd_raw_echo_pkt *ep;
505
506 /*
507 * valgrind: memset() ss so valgrind doesn't complain about
508 * uninitialized memory.
509 */
510 memset(&ss, 0, sizeof(ss));
511 pkt_len = recvfrom(s, rx_pkt, sizeof(rx_pkt), MSG_DONTWAIT,
512 (struct sockaddr *)&ss, &sslen);
513 if (pkt_len <= 0) {
514 if (errno != EAGAIN)
515 log_error("echo-packet: read failure: %s",
516 strerror(errno));
517
518 return -1;
519 }
520
521 /* Check if we have at least the basic headers to send back. */
522 if (pkt_len < BFD_ECHO_PKT_TOT_LEN) {
523 log_debug("echo-packet: too short (got %ld, expected %d)",
524 pkt_len, BFD_ECHO_PKT_TOT_LEN);
525 return -1;
526 }
527
528 ep = (struct bfd_raw_echo_pkt *)(rx_pkt + ETH_HDR_LEN);
529 /* if TTL = 255, assume that the received echo packet has
530 * to be looped back
531 */
532 if (ep->ip.ttl == BFD_TTL_VAL)
533 return ptm_bfd_echo_loopback(rx_pkt, pkt_len,
534 (struct sockaddr *)&ss,
535 sizeof(struct sockaddr_ll));
536
537 my_discr = ntohl(ep->data.my_discr);
538 if (ep->data.my_discr == 0) {
539 log_debug("echo-packet: 'my discriminator' is zero");
540 return -1;
541 }
542 #endif /* BFD_LINUX */
543 #ifdef BFD_BSD
544 int rv;
545 uint8_t ttl;
546
547 /*
548 * bsd_echo_sock_read() already treats invalid TTL values and
549 * zeroed discriminators.
550 */
551 rv = bsd_echo_sock_read(s, rx_pkt, &pkt_len, &ss, &sslen, &ttl,
552 &my_discr);
553 if (rv == -1)
554 return -1;
555
556 if (ttl == BFD_TTL_VAL)
557 return ptm_bfd_echo_loopback(rx_pkt, pkt_len,
558 (struct sockaddr *)&ss, sslen);
559 #endif /* BFD_BSD */
560
561 /* Your discriminator not zero - use it to find session */
562 bfd = bfd_id_lookup(my_discr);
563 if (bfd == NULL) {
564 log_debug("echo-packet: no matching session (id:%u)", my_discr);
565 return -1;
566 }
567
568 if (!BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
569 log_debug("echo-packet: echo disabled [%s]", my_discr,
570 bs_to_string(bfd));
571 return -1;
572 }
573
574 bfd->stats.rx_echo_pkt++;
575
576 /* Compute detect time */
577 bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
578
579 /* Update echo receive timeout. */
580 bfd_echo_recvtimer_update(bfd);
581
582 return 0;
583 }
584
585 void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
586 {
587 struct bfd_pkt cp;
588
589 /* if the BFD session is for VxLAN tunnel, then construct and
590 * send bfd raw packet
591 */
592 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_VXLAN)) {
593 ptm_bfd_vxlan_pkt_snd(bfd, fbit);
594 return;
595 }
596
597 /* Set fields according to section 6.5.7 */
598 cp.diag = bfd->local_diag;
599 BFD_SETVER(cp.diag, BFD_VERSION);
600 cp.flags = 0;
601 BFD_SETSTATE(cp.flags, bfd->ses_state);
602 BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
603 BFD_SETPBIT(cp.flags, bfd->polling);
604 BFD_SETFBIT(cp.flags, fbit);
605 cp.detect_mult = bfd->detect_mult;
606 cp.len = BFD_PKT_LEN;
607 cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
608 cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
609 if (bfd->polling) {
610 cp.timers.desired_min_tx =
611 htonl(bfd->new_timers.desired_min_tx);
612 cp.timers.required_min_rx =
613 htonl(bfd->new_timers.required_min_rx);
614 } else {
615 cp.timers.desired_min_tx = htonl(bfd->timers.desired_min_tx);
616 cp.timers.required_min_rx = htonl(bfd->timers.required_min_rx);
617 }
618 cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo);
619
620 if (_ptm_bfd_send(bfd, false, NULL, &cp, BFD_PKT_LEN) != 0)
621 return;
622
623 bfd->stats.tx_ctrl_pkt++;
624 }
625
626 #if 0 /* TODO VxLAN Support */
627 static struct bfd_pkt *
628 ptm_bfd_process_vxlan_pkt(int s, ptm_sockevent_e se, void *udata, int *ifindex,
629 struct sockaddr_in *sin,
630 struct bfd_session_vxlan_info_t *vxlan_info,
631 uint8_t *rx_pkt, int *mlen)
632 {
633 struct sockaddr_ll sll;
634 uint32_t from_len = sizeof(struct sockaddr_ll);
635 struct bfd_raw_ctrl_pkt *cp;
636 uint8_t *pkt = rx_pkt;
637 struct iphdr *iph;
638 struct ethhdr *inner_ethh;
639
640 *mlen = recvfrom(s, rx_pkt, BFD_RX_BUF_LEN, MSG_DONTWAIT,
641 (struct sockaddr *)&sll, &from_len);
642
643 if (*mlen < 0) {
644 if (errno != EAGAIN)
645 ERRLOG("Error receiving from BFD Vxlan socket %d: %m",
646 s);
647 return NULL;
648 }
649
650 iph = (struct iphdr *)(pkt + ETH_HDR_LEN);
651 pkt = pkt + ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN;
652 vxlan_info->vnid = ntohl(*((int *)(pkt + 4)));
653 vxlan_info->vnid = vxlan_info->vnid >> 8;
654
655 pkt = pkt + VXLAN_HDR_LEN;
656 inner_ethh = (struct ethhdr *)pkt;
657
658 cp = (struct bfd_raw_ctrl_pkt *)(pkt + ETH_HDR_LEN);
659
660 /* Discard the non BFD packets */
661 if (ntohs(cp->udp.dest) != BFD_DEFDESTPORT)
662 return NULL;
663
664 *ifindex = sll.sll_ifindex;
665 sin->sin_addr.s_addr = iph->saddr;
666 sin->sin_port = ntohs(cp->udp.dest);
667
668 vxlan_info->local_dst_ip.s_addr = cp->ip.daddr;
669 memcpy(vxlan_info->local_dst_mac, inner_ethh->h_dest,
670 ETHERNET_ADDRESS_LENGTH);
671
672 return &cp->data;
673 }
674 #endif /* VxLAN */
675
676 static bool
677 ptm_bfd_validate_vxlan_pkt(struct bfd_session *bfd,
678 struct bfd_session_vxlan_info *vxlan_info)
679 {
680 if (bfd->vxlan_info.check_tnl_key && (vxlan_info->vnid != 0)) {
681 log_error("vxlan-packet: vnid not zero: %d", vxlan_info->vnid);
682 return false;
683 }
684
685 if (bfd->vxlan_info.local_dst_ip.s_addr
686 != vxlan_info->local_dst_ip.s_addr) {
687 log_error("vxlan-packet: wrong inner destination",
688 inet_ntoa(vxlan_info->local_dst_ip));
689 return false;
690 }
691
692 if (memcmp(bfd->vxlan_info.local_dst_mac, vxlan_info->local_dst_mac,
693 ETHERNET_ADDRESS_LENGTH)) {
694 log_error(
695 "vxlan-packet: wrong inner mac: %02x:%02x:%02x:%02x:%02x:%02x",
696 vxlan_info->local_dst_mac[0],
697 vxlan_info->local_dst_mac[1],
698 vxlan_info->local_dst_mac[2],
699 vxlan_info->local_dst_mac[3],
700 vxlan_info->local_dst_mac[4],
701 vxlan_info->local_dst_mac[5]);
702 return false;
703 }
704
705 return true;
706 }
707
708 static ssize_t bfd_recv_ipv4(int sd, bool is_mhop, char *port, size_t portlen,
709 char *vrfname, size_t vrfnamelen,
710 struct sockaddr_any *local,
711 struct sockaddr_any *peer)
712 {
713 struct cmsghdr *cm;
714 int ifindex;
715 ssize_t mlen;
716
717 memset(port, 0, portlen);
718 memset(vrfname, 0, vrfnamelen);
719 memset(local, 0, sizeof(*local));
720 memset(peer, 0, sizeof(*peer));
721
722 mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
723 if (mlen == -1) {
724 if (errno != EAGAIN)
725 log_error("ipv4-recv: recv failed: %s",
726 strerror(errno));
727
728 return -1;
729 }
730
731 /* Get source address */
732 peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
733
734 /* Get and check TTL */
735 for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
736 cm = CMSG_NXTHDR(&msghdr, cm)) {
737 if (cm->cmsg_level != IPPROTO_IP)
738 continue;
739
740 switch (cm->cmsg_type) {
741 #ifdef BFD_LINUX
742 case IP_TTL: {
743 uint32_t ttl;
744
745 memcpy(&ttl, CMSG_DATA(cm), sizeof(ttl));
746 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
747 log_debug(
748 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
749 satostr(peer), ttl, BFD_TTL_VAL,
750 msghdr.msg_flags);
751 return -1;
752 }
753 break;
754 }
755
756 case IP_PKTINFO: {
757 struct in_pktinfo *pi =
758 (struct in_pktinfo *)CMSG_DATA(cm);
759
760 if (pi == NULL)
761 break;
762
763 local->sa_sin.sin_family = AF_INET;
764 local->sa_sin.sin_addr = pi->ipi_addr;
765 fetch_portname_from_ifindex(pi->ipi_ifindex, port,
766 portlen);
767 break;
768 }
769 #endif /* BFD_LINUX */
770 #ifdef BFD_BSD
771 case IP_RECVTTL: {
772 uint8_t ttl;
773
774 memcpy(&ttl, CMSG_DATA(cm), sizeof(ttl));
775 if ((is_mhop == false) && (ttl != BFD_TTL_VAL)) {
776 log_debug(
777 "ipv4-recv: invalid TTL from %s (expected %d, got %d flags %d)",
778 satostr(peer), ttl, BFD_TTL_VAL,
779 msghdr.msg_flags);
780 return -1;
781 }
782 break;
783 }
784
785 case IP_RECVDSTADDR: {
786 struct in_addr ia;
787
788 memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
789 local->sa_sin.sin_family = AF_INET;
790 local->sa_sin.sin_addr = ia;
791 break;
792 }
793 #endif /* BFD_BSD */
794
795 default:
796 /*
797 * On *BSDs we expect to land here when skipping
798 * the IP_RECVIF header. It will be handled by
799 * getsockopt_ifindex() below.
800 */
801 /* NOTHING */
802 break;
803 }
804 }
805
806 /* OS agnostic way of getting interface name. */
807 if (port[0] == 0) {
808 ifindex = getsockopt_ifindex(AF_INET, &msghdr);
809 if (ifindex > 0)
810 fetch_portname_from_ifindex(ifindex, port, portlen);
811 }
812
813 return mlen;
814 }
815
816 ssize_t bfd_recv_ipv6(int sd, bool is_mhop, char *port, size_t portlen,
817 char *vrfname, size_t vrfnamelen,
818 struct sockaddr_any *local, struct sockaddr_any *peer)
819 {
820 struct cmsghdr *cm;
821 struct in6_pktinfo *pi6 = NULL;
822 int ifindex = 0;
823 ssize_t mlen;
824
825 memset(port, 0, portlen);
826 memset(vrfname, 0, vrfnamelen);
827 memset(local, 0, sizeof(*local));
828 memset(peer, 0, sizeof(*peer));
829
830 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
831 if (mlen == -1) {
832 if (errno != EAGAIN)
833 log_error("ipv4-recv: recv failed: %s",
834 strerror(errno));
835
836 return -1;
837 }
838
839 /* Get source address */
840 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
841
842 /* Get and check TTL */
843 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
844 cm = CMSG_NXTHDR(&msghdr6, cm)) {
845 if (cm->cmsg_level != IPPROTO_IPV6)
846 continue;
847
848 if (cm->cmsg_type == IPV6_HOPLIMIT) {
849 memcpy(&ttlval, CMSG_DATA(cm), 4);
850 if ((is_mhop == false) && (ttlval != BFD_TTL_VAL)) {
851 log_debug(
852 "ipv6-recv: invalid TTL from %s (expected %d, got %d flags %d)",
853 satostr(peer), ttlval, BFD_TTL_VAL,
854 msghdr.msg_flags);
855 return -1;
856 }
857 } else if (cm->cmsg_type == IPV6_PKTINFO) {
858 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
859 if (pi6) {
860 local->sa_sin.sin_family = AF_INET6;
861 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
862 fetch_portname_from_ifindex(pi6->ipi6_ifindex,
863 port, portlen);
864 ifindex = pi6->ipi6_ifindex;
865 }
866 }
867 }
868
869 /* Set scope ID for link local addresses. */
870 if (IN6_IS_ADDR_LINKLOCAL(&peer->sa_sin6.sin6_addr))
871 peer->sa_sin6.sin6_scope_id = ifindex;
872 if (IN6_IS_ADDR_LINKLOCAL(&local->sa_sin6.sin6_addr))
873 local->sa_sin6.sin6_scope_id = ifindex;
874
875 return mlen;
876 }
877
878 static void bfd_sd_reschedule(int sd)
879 {
880 if (sd == bglobal.bg_shop) {
881 bglobal.bg_ev[0] = NULL;
882 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
883 &bglobal.bg_ev[0]);
884 } else if (sd == bglobal.bg_mhop) {
885 bglobal.bg_ev[1] = NULL;
886 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
887 &bglobal.bg_ev[1]);
888 } else if (sd == bglobal.bg_shop6) {
889 bglobal.bg_ev[2] = NULL;
890 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
891 &bglobal.bg_ev[2]);
892 } else if (sd == bglobal.bg_mhop6) {
893 bglobal.bg_ev[3] = NULL;
894 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
895 &bglobal.bg_ev[3]);
896 } else if (sd == bglobal.bg_echo) {
897 bglobal.bg_ev[4] = NULL;
898 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
899 &bglobal.bg_ev[4]);
900 } else if (sd == bglobal.bg_vxlan) {
901 bglobal.bg_ev[5] = NULL;
902 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_vxlan,
903 &bglobal.bg_ev[5]);
904 }
905 }
906
907 static void cp_debug(bool mhop, struct sockaddr_any *peer,
908 struct sockaddr_any *local, const char *port,
909 const char *vrf, const char *fmt, ...)
910 {
911 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
912 va_list vl;
913
914 if (peer->sa_sin.sin_family)
915 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
916 else
917 peerstr[0] = 0;
918
919 if (local->sa_sin.sin_family)
920 snprintf(localstr, sizeof(localstr), " local:%s",
921 satostr(local));
922 else
923 localstr[0] = 0;
924
925 if (port[0])
926 snprintf(portstr, sizeof(portstr), " port:%s", port);
927 else
928 portstr[0] = 0;
929
930 if (vrf[0])
931 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
932 else
933 vrfstr[0] = 0;
934
935 va_start(vl, fmt);
936 vsnprintf(buf, sizeof(buf), fmt, vl);
937 va_end(vl);
938
939 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
940 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
941 }
942
943 int bfd_recv_cb(struct thread *t)
944 {
945 int sd = THREAD_FD(t);
946 struct bfd_session *bfd;
947 struct bfd_pkt *cp;
948 bool is_mhop, is_vxlan;
949 ssize_t mlen = 0;
950 uint32_t oldEchoXmt_TO, oldXmtTime;
951 struct sockaddr_any local, peer;
952 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
953 struct bfd_session_vxlan_info vxlan_info;
954
955 /* Schedule next read. */
956 bfd_sd_reschedule(sd);
957
958 /* Handle echo packets. */
959 if (sd == bglobal.bg_echo) {
960 ptm_bfd_process_echo_pkt(sd);
961 return 0;
962 }
963
964 /* Handle control packets. */
965 is_mhop = is_vxlan = false;
966 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
967 is_mhop = sd == bglobal.bg_mhop;
968 mlen = bfd_recv_ipv4(sd, is_mhop, port, sizeof(port), vrfname,
969 sizeof(vrfname), &local, &peer);
970 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
971 is_mhop = sd == bglobal.bg_mhop6;
972 mlen = bfd_recv_ipv6(sd, is_mhop, port, sizeof(port), vrfname,
973 sizeof(vrfname), &local, &peer);
974 }
975 #if 0 /* TODO vxlan handling */
976 cp = ptm_bfd_process_vxlan_pkt(s, se, udata, &local_ifindex,
977 &sin, &vxlan_info, rx_pkt, &mlen);
978 if (!cp)
979 return -1;
980
981 is_vxlan = true;
982 /* keep in network-byte order */
983 peer.ip4_addr.s_addr = sin.sin_addr.s_addr;
984 peer.family = AF_INET;
985 strcpy(peer_addr, inet_ntoa(sin.sin_addr));
986 #endif
987
988 /* Implement RFC 5880 6.8.6 */
989 if (mlen < BFD_PKT_LEN) {
990 cp_debug(is_mhop, &peer, &local, port, vrfname,
991 "too small (%ld bytes)", mlen);
992 return 0;
993 }
994
995 /*
996 * Parse the control header for inconsistencies:
997 * - Invalid version;
998 * - Bad multiplier configuration;
999 * - Short packets;
1000 * - Invalid discriminator;
1001 */
1002 cp = (struct bfd_pkt *)(msghdr.msg_iov->iov_base);
1003 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
1004 cp_debug(is_mhop, &peer, &local, port, vrfname,
1005 "bad version %d", BFD_GETVER(cp->diag));
1006 return 0;
1007 }
1008
1009 if (cp->detect_mult == 0) {
1010 cp_debug(is_mhop, &peer, &local, port, vrfname,
1011 "detect multiplier set to zero");
1012 return 0;
1013 }
1014
1015 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
1016 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
1017 return 0;
1018 }
1019
1020 if (cp->discrs.my_discr == 0) {
1021 cp_debug(is_mhop, &peer, &local, port, vrfname,
1022 "'my discriminator' is zero");
1023 return 0;
1024 }
1025
1026 /* Find the session that this packet belongs. */
1027 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
1028 if (bfd == NULL) {
1029 cp_debug(is_mhop, &peer, &local, port, vrfname,
1030 "no session found");
1031 return 0;
1032 }
1033
1034 /* Handle VxLAN cases. */
1035 if (is_vxlan && !ptm_bfd_validate_vxlan_pkt(bfd, &vxlan_info))
1036 return 0;
1037
1038 bfd->stats.rx_ctrl_pkt++;
1039
1040 /*
1041 * Multi hop: validate packet TTL.
1042 * Single hop: set local address that received the packet.
1043 */
1044 if (is_mhop) {
1045 if ((BFD_TTL_VAL - bfd->mh_ttl) > ttlval) {
1046 cp_debug(is_mhop, &peer, &local, port, vrfname,
1047 "exceeded max hop count (expected %d, got %d)",
1048 bfd->mh_ttl, ttlval);
1049 return 0;
1050 }
1051 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
1052 bfd->local_ip = local;
1053 }
1054
1055 /*
1056 * If no interface was detected, save the interface where the
1057 * packet came in.
1058 */
1059 if (bfd->ifindex == 0)
1060 bfd->ifindex = ptm_bfd_fetch_ifindex(port);
1061
1062 /* Log remote discriminator changes. */
1063 if ((bfd->discrs.remote_discr != 0)
1064 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
1065 cp_debug(is_mhop, &peer, &local, port, vrfname,
1066 "remote discriminator mismatch (expected %d, got %d)",
1067 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
1068
1069 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
1070
1071 /* If received the Final bit, the new values should take effect */
1072 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
1073 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
1074 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
1075 bfd->new_timers.desired_min_tx = 0;
1076 bfd->new_timers.required_min_rx = 0;
1077 bfd->polling = 0;
1078 }
1079
1080 if (!bfd->demand_mode) {
1081 /* Compute detect time */
1082 bfd->detect_TO = cp->detect_mult
1083 * ((bfd->timers.required_min_rx
1084 > ntohl(cp->timers.desired_min_tx))
1085 ? bfd->timers.required_min_rx
1086 : ntohl(cp->timers.desired_min_tx));
1087 bfd->remote_detect_mult = cp->detect_mult;
1088 } else
1089 cp_debug(is_mhop, &peer, &local, port, vrfname,
1090 "unsupported demand mode");
1091
1092 /* Save remote diagnostics before state switch. */
1093 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
1094
1095 /* State switch from section 6.8.6 */
1096 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
1097 if (bfd->ses_state != PTM_BFD_DOWN)
1098 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1099 } else {
1100 switch (bfd->ses_state) {
1101 case (PTM_BFD_DOWN):
1102 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
1103 ptm_bfd_ses_up(bfd);
1104 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1105 bfd->ses_state = PTM_BFD_INIT;
1106 break;
1107 case (PTM_BFD_INIT):
1108 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
1109 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
1110 ptm_bfd_ses_up(bfd);
1111 break;
1112 case (PTM_BFD_UP):
1113 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1114 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1115 break;
1116 }
1117 }
1118
1119 /*
1120 * Handle echo packet status:
1121 * - Start echo packets if configured and permitted
1122 * (required_min_echo > 0);
1123 * - Stop echo packets if not allowed (required_min_echo == 0);
1124 * - Recalculate echo packet interval;
1125 */
1126 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
1127 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1128 if (!ntohl(cp->timers.required_min_echo)) {
1129 ptm_bfd_echo_stop(bfd, 1);
1130 } else {
1131 oldEchoXmt_TO = bfd->echo_xmt_TO;
1132 bfd->echo_xmt_TO =
1133 bfd->timers.required_min_echo;
1134 if (ntohl(cp->timers.required_min_echo)
1135 > bfd->echo_xmt_TO)
1136 bfd->echo_xmt_TO = ntohl(
1137 cp->timers.required_min_echo);
1138 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
1139 ptm_bfd_echo_start(bfd);
1140 }
1141 } else if (ntohl(cp->timers.required_min_echo)) {
1142 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1143 if (ntohl(cp->timers.required_min_echo)
1144 > bfd->echo_xmt_TO)
1145 bfd->echo_xmt_TO =
1146 ntohl(cp->timers.required_min_echo);
1147 ptm_bfd_echo_start(bfd);
1148 }
1149 }
1150
1151 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1152 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1153 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
1154 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
1155 }
1156
1157 /* Calculate new transmit time */
1158 oldXmtTime = bfd->xmt_TO;
1159 bfd->xmt_TO =
1160 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
1161 ? bfd->timers.desired_min_tx
1162 : ntohl(cp->timers.required_min_rx);
1163
1164 /* If transmit time has changed, and too much time until next xmt,
1165 * restart
1166 */
1167 if (BFD_GETPBIT(cp->flags)) {
1168 ptm_bfd_xmt_TO(bfd, 1);
1169 } else if (oldXmtTime != bfd->xmt_TO) {
1170 /* XXX add some skid to this as well */
1171 ptm_bfd_start_xmt_timer(bfd, false);
1172 }
1173
1174 /* Restart detection timer (packet received) */
1175 if (!bfd->demand_mode)
1176 bfd_recvtimer_update(bfd);
1177
1178 /*
1179 * Save the timers and state sent by the remote end
1180 * for debugging and statistics.
1181 */
1182 if (BFD_GETFBIT(cp->flags)) {
1183 bfd->remote_timers.desired_min_tx =
1184 ntohl(cp->timers.desired_min_tx);
1185 bfd->remote_timers.required_min_rx =
1186 ntohl(cp->timers.required_min_rx);
1187 bfd->remote_timers.required_min_echo =
1188 ntohl(cp->timers.required_min_echo);
1189
1190 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
1191 }
1192
1193 return 0;
1194 }
1195
1196
1197 /*
1198 * Sockets creation.
1199 */
1200
1201
1202 /*
1203 * IPv4 sockets
1204 */
1205 int bp_set_ttl(int sd)
1206 {
1207 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttlval, sizeof(ttlval)) == -1) {
1208 log_warning("%s: setsockopt(IP_TTL): %s", __func__,
1209 strerror(errno));
1210 return -1;
1211 }
1212
1213 return 0;
1214 }
1215
1216 int bp_set_tos(int sd)
1217 {
1218 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tosval, sizeof(tosval)) == -1) {
1219 log_warning("%s: setsockopt(IP_TOS): %s", __func__,
1220 strerror(errno));
1221 return -1;
1222 }
1223
1224 return 0;
1225 }
1226
1227 static void bp_set_ipopts(int sd)
1228 {
1229 if (bp_set_ttl(sd) != 0)
1230 log_fatal("%s: TTL configuration failed", __func__);
1231
1232 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1233 == -1)
1234 log_fatal("%s: setsockopt(IP_RECVTTL): %s", __func__,
1235 strerror(errno));
1236
1237 #ifdef BFD_LINUX
1238 int pktinfo = BFD_PKT_INFO_VAL;
1239 /* Figure out address and interface to do the peer matching. */
1240 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1241 == -1)
1242 log_fatal("%s: setsockopt(IP_PKTINFO): %s", __func__,
1243 strerror(errno));
1244 #endif /* BFD_LINUX */
1245 #ifdef BFD_BSD
1246 int yes = 1;
1247
1248 /* Find out our address for peer matching. */
1249 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
1250 log_fatal("%s: setsockopt(IP_RECVDSTADDR): %s", __func__,
1251 strerror(errno));
1252
1253 /* Find out interface where the packet came in. */
1254 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
1255 log_fatal("%s: setsockopt_ipv4_ifindex: %s", __func__,
1256 strerror(errno));
1257 #endif /* BFD_BSD */
1258 }
1259
1260 static void bp_bind_ip(int sd, uint16_t port)
1261 {
1262 struct sockaddr_in sin;
1263
1264 memset(&sin, 0, sizeof(sin));
1265 sin.sin_family = AF_INET;
1266 sin.sin_addr.s_addr = htonl(INADDR_ANY);
1267 sin.sin_port = htons(port);
1268 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
1269 log_fatal("%s: bind: %s", __func__, strerror(errno));
1270 }
1271
1272 int bp_udp_shop(void)
1273 {
1274 int sd;
1275
1276 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1277 if (sd == -1)
1278 log_fatal("%s: socket: %s", __func__, strerror(errno));
1279
1280 bp_set_ipopts(sd);
1281 bp_bind_ip(sd, BFD_DEFDESTPORT);
1282
1283 return sd;
1284 }
1285
1286 int bp_udp_mhop(void)
1287 {
1288 int sd;
1289
1290 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1291 if (sd == -1)
1292 log_fatal("%s: socket: %s", __func__, strerror(errno));
1293
1294 bp_set_ipopts(sd);
1295 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1296
1297 return sd;
1298 }
1299
1300 int bp_peer_socket(struct bfd_peer_cfg *bpc)
1301 {
1302 int sd, pcount;
1303 struct sockaddr_in sin;
1304 static int srcPort = BFD_SRCPORTINIT;
1305
1306 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1307 if (sd == -1) {
1308 log_error("ipv4-new: failed to create socket: %s",
1309 strerror(errno));
1310 return -1;
1311 }
1312
1313 if (!bpc->bpc_has_vxlan) {
1314 /* Set TTL to 255 for all transmitted packets */
1315 if (bp_set_ttl(sd) != 0) {
1316 close(sd);
1317 return -1;
1318 }
1319 }
1320
1321 /* Set TOS to CS6 for all transmitted packets */
1322 if (bp_set_tos(sd) != 0) {
1323 close(sd);
1324 return -1;
1325 }
1326
1327 /* dont bind-to-device incase of vxlan */
1328 if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif) {
1329 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1330 close(sd);
1331 return -1;
1332 }
1333 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1334 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1335 close(sd);
1336 return -1;
1337 }
1338 }
1339
1340 /* Find an available source port in the proper range */
1341 memset(&sin, 0, sizeof(sin));
1342 sin = bpc->bpc_local.sa_sin;
1343 sin.sin_family = AF_INET;
1344 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1345 sin.sin_len = sizeof(sin);
1346 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1347 if (bpc->bpc_mhop || bpc->bpc_has_vxlan)
1348 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1349 else
1350 sin.sin_addr.s_addr = INADDR_ANY;
1351
1352 pcount = 0;
1353 do {
1354 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1355 /* Searched all ports, none available */
1356 log_error("ipv4-new: failed to bind port: %s",
1357 strerror(errno));
1358 close(sd);
1359 return -1;
1360 }
1361 if (srcPort >= BFD_SRCPORTMAX)
1362 srcPort = BFD_SRCPORTINIT;
1363 sin.sin_port = htons(srcPort++);
1364 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1365
1366 return sd;
1367 }
1368
1369
1370 /*
1371 * IPv6 sockets
1372 */
1373
1374 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1375 {
1376 int sd, pcount, ifindex;
1377 struct sockaddr_in6 sin6;
1378 static int srcPort = BFD_SRCPORTINIT;
1379
1380 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1381 if (sd == -1) {
1382 log_error("ipv6-new: failed to create socket: %s",
1383 strerror(errno));
1384 return -1;
1385 }
1386
1387 if (!bpc->bpc_has_vxlan) {
1388 /* Set TTL to 255 for all transmitted packets */
1389 if (bp_set_ttlv6(sd) != 0) {
1390 close(sd);
1391 return -1;
1392 }
1393 }
1394
1395 /* Set TOS to CS6 for all transmitted packets */
1396 if (bp_set_tosv6(sd) != 0) {
1397 close(sd);
1398 return -1;
1399 }
1400
1401 /* Find an available source port in the proper range */
1402 memset(&sin6, 0, sizeof(sin6));
1403 sin6.sin6_family = AF_INET6;
1404 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1405 sin6.sin6_len = sizeof(sin6);
1406 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1407 sin6 = bpc->bpc_local.sa_sin6;
1408 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1409 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1410 sin6.sin6_scope_id = ifindex;
1411
1412 if (bpc->bpc_has_localif) {
1413 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1414 close(sd);
1415 return -1;
1416 }
1417 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1418 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1419 close(sd);
1420 return -1;
1421 }
1422 }
1423
1424 pcount = 0;
1425 do {
1426 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1427 /* Searched all ports, none available */
1428 log_error("ipv6-new: failed to bind port: %s",
1429 strerror(errno));
1430 close(sd);
1431 return -1;
1432 }
1433 if (srcPort >= BFD_SRCPORTMAX)
1434 srcPort = BFD_SRCPORTINIT;
1435 sin6.sin6_port = htons(srcPort++);
1436 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1437
1438 return sd;
1439 }
1440
1441 int bp_set_ttlv6(int sd)
1442 {
1443 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttlval,
1444 sizeof(ttlval))
1445 == -1) {
1446 log_warning("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__,
1447 strerror(errno));
1448 return -1;
1449 }
1450
1451 return 0;
1452 }
1453
1454 int bp_set_tosv6(int sd)
1455 {
1456 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tosval, sizeof(tosval))
1457 == -1) {
1458 log_warning("%s: setsockopt(IPV6_TCLASS): %s", __func__,
1459 strerror(errno));
1460 return -1;
1461 }
1462
1463 return 0;
1464 }
1465
1466 static void bp_set_ipv6opts(int sd)
1467 {
1468 static int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1469 static int ipv6_only = BFD_IPV6_ONLY_VAL;
1470
1471 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttlval,
1472 sizeof(ttlval))
1473 == -1)
1474 log_fatal("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__,
1475 strerror(errno));
1476
1477 if (setsockopt_ipv6_hoplimit(sd, rcvttl) == -1)
1478 log_fatal("%s: setsockopt(IPV6_HOPLIMIT): %s", __func__,
1479 strerror(errno));
1480
1481 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1482 log_fatal("%s: setsockopt(IPV6_PKTINFO): %s", __func__,
1483 strerror(errno));
1484
1485 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1486 sizeof(ipv6_only))
1487 == -1)
1488 log_fatal("%s: setsockopt(IPV6_V6ONLY): %s", __func__,
1489 strerror(errno));
1490 }
1491
1492 static void bp_bind_ipv6(int sd, uint16_t port)
1493 {
1494 struct sockaddr_in6 sin6;
1495
1496 memset(&sin6, 0, sizeof(sin6));
1497 sin6.sin6_family = AF_INET6;
1498 sin6.sin6_addr = in6addr_any;
1499 sin6.sin6_port = htons(port);
1500 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1501 sin6.sin6_len = sizeof(sin6);
1502 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1503 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1504 log_fatal("%s: bind: %s", __func__, strerror(errno));
1505 }
1506
1507 int bp_udp6_shop(void)
1508 {
1509 int sd;
1510
1511 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1512 if (sd == -1)
1513 log_fatal("%s: socket: %s", __func__, strerror(errno));
1514
1515 bp_set_ipv6opts(sd);
1516 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1517
1518 return sd;
1519 }
1520
1521 int bp_udp6_mhop(void)
1522 {
1523 int sd;
1524
1525 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1526 if (sd == -1)
1527 log_fatal("%s: socket: %s", __func__, strerror(errno));
1528
1529 bp_set_ipv6opts(sd);
1530 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1531
1532 return sd;
1533 }