]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bfd_packet.c
bfdd: improve logging messages
[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 ssize_t mlen;
823
824 memset(port, 0, portlen);
825 memset(vrfname, 0, vrfnamelen);
826 memset(local, 0, sizeof(*local));
827 memset(peer, 0, sizeof(*peer));
828
829 mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
830 if (mlen == -1) {
831 if (errno != EAGAIN)
832 log_error("ipv4-recv: recv failed: %s",
833 strerror(errno));
834
835 return -1;
836 }
837
838 /* Get source address */
839 peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
840
841 /* Get and check TTL */
842 for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
843 cm = CMSG_NXTHDR(&msghdr6, cm)) {
844 if (cm->cmsg_level != IPPROTO_IPV6)
845 continue;
846
847 if (cm->cmsg_type == IPV6_HOPLIMIT) {
848 memcpy(&ttlval, CMSG_DATA(cm), 4);
849 if ((is_mhop == false) && (ttlval != BFD_TTL_VAL)) {
850 log_debug(
851 "ipv6-recv: invalid TTL from %s (expected %d, got %d flags %d)",
852 satostr(peer), ttlval, BFD_TTL_VAL,
853 msghdr.msg_flags);
854 return -1;
855 }
856 } else if (cm->cmsg_type == IPV6_PKTINFO) {
857 pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
858 if (pi6) {
859 local->sa_sin.sin_family = AF_INET6;
860 local->sa_sin6.sin6_addr = pi6->ipi6_addr;
861 fetch_portname_from_ifindex(pi6->ipi6_ifindex,
862 port, portlen);
863 }
864 }
865 }
866
867 return mlen;
868 }
869
870 static void bfd_sd_reschedule(int sd)
871 {
872 if (sd == bglobal.bg_shop) {
873 bglobal.bg_ev[0] = NULL;
874 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop,
875 &bglobal.bg_ev[0]);
876 } else if (sd == bglobal.bg_mhop) {
877 bglobal.bg_ev[1] = NULL;
878 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop,
879 &bglobal.bg_ev[1]);
880 } else if (sd == bglobal.bg_shop6) {
881 bglobal.bg_ev[2] = NULL;
882 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_shop6,
883 &bglobal.bg_ev[2]);
884 } else if (sd == bglobal.bg_mhop6) {
885 bglobal.bg_ev[3] = NULL;
886 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_mhop6,
887 &bglobal.bg_ev[3]);
888 } else if (sd == bglobal.bg_echo) {
889 bglobal.bg_ev[4] = NULL;
890 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_echo,
891 &bglobal.bg_ev[4]);
892 } else if (sd == bglobal.bg_vxlan) {
893 bglobal.bg_ev[5] = NULL;
894 thread_add_read(master, bfd_recv_cb, NULL, bglobal.bg_vxlan,
895 &bglobal.bg_ev[5]);
896 }
897 }
898
899 static void cp_debug(bool mhop, struct sockaddr_any *peer,
900 struct sockaddr_any *local, const char *port,
901 const char *vrf, const char *fmt, ...)
902 {
903 char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
904 va_list vl;
905
906 if (peer->sa_sin.sin_family)
907 snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
908 else
909 peerstr[0] = 0;
910
911 if (local->sa_sin.sin_family)
912 snprintf(localstr, sizeof(localstr), " local:%s",
913 satostr(local));
914 else
915 localstr[0] = 0;
916
917 if (port[0])
918 snprintf(portstr, sizeof(portstr), " port:%s", port);
919 else
920 portstr[0] = 0;
921
922 if (vrf[0])
923 snprintf(vrfstr, sizeof(vrfstr), " vrf:%s", port);
924 else
925 vrfstr[0] = 0;
926
927 va_start(vl, fmt);
928 vsnprintf(buf, sizeof(buf), fmt, vl);
929 va_end(vl);
930
931 log_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
932 mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
933 }
934
935 int bfd_recv_cb(struct thread *t)
936 {
937 int sd = THREAD_FD(t);
938 struct bfd_session *bfd;
939 struct bfd_pkt *cp;
940 bool is_mhop, is_vxlan;
941 ssize_t mlen = 0;
942 uint32_t oldEchoXmt_TO, oldXmtTime;
943 struct sockaddr_any local, peer;
944 char port[MAXNAMELEN + 1], vrfname[MAXNAMELEN + 1];
945 struct bfd_session_vxlan_info vxlan_info;
946
947 /* Schedule next read. */
948 bfd_sd_reschedule(sd);
949
950 /* Handle echo packets. */
951 if (sd == bglobal.bg_echo) {
952 ptm_bfd_process_echo_pkt(sd);
953 return 0;
954 }
955
956 /* Handle control packets. */
957 is_mhop = is_vxlan = false;
958 if (sd == bglobal.bg_shop || sd == bglobal.bg_mhop) {
959 is_mhop = sd == bglobal.bg_mhop;
960 mlen = bfd_recv_ipv4(sd, is_mhop, port, sizeof(port), vrfname,
961 sizeof(vrfname), &local, &peer);
962 } else if (sd == bglobal.bg_shop6 || sd == bglobal.bg_mhop6) {
963 is_mhop = sd == bglobal.bg_mhop6;
964 mlen = bfd_recv_ipv6(sd, is_mhop, port, sizeof(port), vrfname,
965 sizeof(vrfname), &local, &peer);
966 }
967 #if 0 /* TODO vxlan handling */
968 cp = ptm_bfd_process_vxlan_pkt(s, se, udata, &local_ifindex,
969 &sin, &vxlan_info, rx_pkt, &mlen);
970 if (!cp)
971 return -1;
972
973 is_vxlan = true;
974 /* keep in network-byte order */
975 peer.ip4_addr.s_addr = sin.sin_addr.s_addr;
976 peer.family = AF_INET;
977 strcpy(peer_addr, inet_ntoa(sin.sin_addr));
978 #endif
979
980 /* Implement RFC 5880 6.8.6 */
981 if (mlen < BFD_PKT_LEN) {
982 cp_debug(is_mhop, &peer, &local, port, vrfname,
983 "too small (%ld bytes)", mlen);
984 return 0;
985 }
986
987 /*
988 * Parse the control header for inconsistencies:
989 * - Invalid version;
990 * - Bad multiplier configuration;
991 * - Short packets;
992 * - Invalid discriminator;
993 */
994 cp = (struct bfd_pkt *)(msghdr.msg_iov->iov_base);
995 if (BFD_GETVER(cp->diag) != BFD_VERSION) {
996 cp_debug(is_mhop, &peer, &local, port, vrfname,
997 "bad version %d", BFD_GETVER(cp->diag));
998 return 0;
999 }
1000
1001 if (cp->detect_mult == 0) {
1002 cp_debug(is_mhop, &peer, &local, port, vrfname,
1003 "detect multiplier set to zero");
1004 return 0;
1005 }
1006
1007 if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
1008 cp_debug(is_mhop, &peer, &local, port, vrfname, "too small");
1009 return 0;
1010 }
1011
1012 if (cp->discrs.my_discr == 0) {
1013 cp_debug(is_mhop, &peer, &local, port, vrfname,
1014 "'my discriminator' is zero");
1015 return 0;
1016 }
1017
1018 /* Find the session that this packet belongs. */
1019 bfd = ptm_bfd_sess_find(cp, port, &peer, &local, vrfname, is_mhop);
1020 if (bfd == NULL) {
1021 cp_debug(is_mhop, &peer, &local, port, vrfname,
1022 "no session found");
1023 return 0;
1024 }
1025
1026 /* Handle VxLAN cases. */
1027 if (is_vxlan && !ptm_bfd_validate_vxlan_pkt(bfd, &vxlan_info))
1028 return 0;
1029
1030 bfd->stats.rx_ctrl_pkt++;
1031
1032 /*
1033 * Multi hop: validate packet TTL.
1034 * Single hop: set local address that received the packet.
1035 */
1036 if (is_mhop) {
1037 if ((BFD_TTL_VAL - bfd->mh_ttl) > ttlval) {
1038 cp_debug(is_mhop, &peer, &local, port, vrfname,
1039 "exceeded max hop count (expected %d, got %d)",
1040 bfd->mh_ttl, ttlval);
1041 return 0;
1042 }
1043 } else if (bfd->local_ip.sa_sin.sin_family == AF_UNSPEC) {
1044 bfd->local_ip = local;
1045 }
1046
1047 /*
1048 * If no interface was detected, save the interface where the
1049 * packet came in.
1050 */
1051 if (bfd->ifindex == 0)
1052 bfd->ifindex = ptm_bfd_fetch_ifindex(port);
1053
1054 /* Log remote discriminator changes. */
1055 if ((bfd->discrs.remote_discr != 0)
1056 && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
1057 cp_debug(is_mhop, &peer, &local, port, vrfname,
1058 "remote discriminator mismatch (expected %d, got %d)",
1059 bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
1060
1061 bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
1062
1063 /* If received the Final bit, the new values should take effect */
1064 if (bfd->polling && BFD_GETFBIT(cp->flags)) {
1065 bfd->timers.desired_min_tx = bfd->new_timers.desired_min_tx;
1066 bfd->timers.required_min_rx = bfd->new_timers.required_min_rx;
1067 bfd->new_timers.desired_min_tx = 0;
1068 bfd->new_timers.required_min_rx = 0;
1069 bfd->polling = 0;
1070 }
1071
1072 if (!bfd->demand_mode) {
1073 /* Compute detect time */
1074 bfd->detect_TO = cp->detect_mult
1075 * ((bfd->timers.required_min_rx
1076 > ntohl(cp->timers.desired_min_tx))
1077 ? bfd->timers.required_min_rx
1078 : ntohl(cp->timers.desired_min_tx));
1079 bfd->remote_detect_mult = cp->detect_mult;
1080 } else
1081 cp_debug(is_mhop, &peer, &local, port, vrfname,
1082 "unsupported demand mode");
1083
1084 /* Save remote diagnostics before state switch. */
1085 bfd->remote_diag = cp->diag & BFD_DIAGMASK;
1086
1087 /* State switch from section 6.8.6 */
1088 if (BFD_GETSTATE(cp->flags) == PTM_BFD_ADM_DOWN) {
1089 if (bfd->ses_state != PTM_BFD_DOWN)
1090 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1091 } else {
1092 switch (bfd->ses_state) {
1093 case (PTM_BFD_DOWN):
1094 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT)
1095 ptm_bfd_ses_up(bfd);
1096 else if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1097 bfd->ses_state = PTM_BFD_INIT;
1098 break;
1099 case (PTM_BFD_INIT):
1100 if (BFD_GETSTATE(cp->flags) == PTM_BFD_INIT
1101 || BFD_GETSTATE(cp->flags) == PTM_BFD_UP)
1102 ptm_bfd_ses_up(bfd);
1103 break;
1104 case (PTM_BFD_UP):
1105 if (BFD_GETSTATE(cp->flags) == PTM_BFD_DOWN)
1106 ptm_bfd_ses_dn(bfd, BFD_DIAGNEIGHDOWN);
1107 break;
1108 }
1109 }
1110
1111 /*
1112 * Handle echo packet status:
1113 * - Start echo packets if configured and permitted
1114 * (required_min_echo > 0);
1115 * - Stop echo packets if not allowed (required_min_echo == 0);
1116 * - Recalculate echo packet interval;
1117 */
1118 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO)) {
1119 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1120 if (!ntohl(cp->timers.required_min_echo)) {
1121 ptm_bfd_echo_stop(bfd, 1);
1122 } else {
1123 oldEchoXmt_TO = bfd->echo_xmt_TO;
1124 bfd->echo_xmt_TO =
1125 bfd->timers.required_min_echo;
1126 if (ntohl(cp->timers.required_min_echo)
1127 > bfd->echo_xmt_TO)
1128 bfd->echo_xmt_TO = ntohl(
1129 cp->timers.required_min_echo);
1130 if (oldEchoXmt_TO != bfd->echo_xmt_TO)
1131 ptm_bfd_echo_start(bfd);
1132 }
1133 } else if (ntohl(cp->timers.required_min_echo)) {
1134 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1135 if (ntohl(cp->timers.required_min_echo)
1136 > bfd->echo_xmt_TO)
1137 bfd->echo_xmt_TO =
1138 ntohl(cp->timers.required_min_echo);
1139 ptm_bfd_echo_start(bfd);
1140 }
1141 }
1142
1143 if (BFD_CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
1144 bfd->echo_xmt_TO = bfd->timers.required_min_echo;
1145 if (ntohl(cp->timers.required_min_echo) > bfd->echo_xmt_TO)
1146 bfd->echo_xmt_TO = ntohl(cp->timers.required_min_echo);
1147 }
1148
1149 /* Calculate new transmit time */
1150 oldXmtTime = bfd->xmt_TO;
1151 bfd->xmt_TO =
1152 (bfd->timers.desired_min_tx > ntohl(cp->timers.required_min_rx))
1153 ? bfd->timers.desired_min_tx
1154 : ntohl(cp->timers.required_min_rx);
1155
1156 /* If transmit time has changed, and too much time until next xmt,
1157 * restart
1158 */
1159 if (BFD_GETPBIT(cp->flags)) {
1160 ptm_bfd_xmt_TO(bfd, 1);
1161 } else if (oldXmtTime != bfd->xmt_TO) {
1162 /* XXX add some skid to this as well */
1163 ptm_bfd_start_xmt_timer(bfd, false);
1164 }
1165
1166 /* Restart detection timer (packet received) */
1167 if (!bfd->demand_mode)
1168 bfd_recvtimer_update(bfd);
1169
1170 /*
1171 * Save the timers and state sent by the remote end
1172 * for debugging and statistics.
1173 */
1174 if (BFD_GETFBIT(cp->flags)) {
1175 bfd->remote_timers.desired_min_tx =
1176 ntohl(cp->timers.desired_min_tx);
1177 bfd->remote_timers.required_min_rx =
1178 ntohl(cp->timers.required_min_rx);
1179 bfd->remote_timers.required_min_echo =
1180 ntohl(cp->timers.required_min_echo);
1181
1182 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bfd);
1183 }
1184
1185 return 0;
1186 }
1187
1188
1189 /*
1190 * Sockets creation.
1191 */
1192
1193
1194 /*
1195 * IPv4 sockets
1196 */
1197 int bp_set_ttl(int sd)
1198 {
1199 if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttlval, sizeof(ttlval)) == -1) {
1200 log_warning("%s: setsockopt(IP_TTL): %s", __func__,
1201 strerror(errno));
1202 return -1;
1203 }
1204
1205 return 0;
1206 }
1207
1208 int bp_set_tos(int sd)
1209 {
1210 if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tosval, sizeof(tosval)) == -1) {
1211 log_warning("%s: setsockopt(IP_TOS): %s", __func__,
1212 strerror(errno));
1213 return -1;
1214 }
1215
1216 return 0;
1217 }
1218
1219 static void bp_set_ipopts(int sd)
1220 {
1221 if (bp_set_ttl(sd) != 0)
1222 log_fatal("%s: TTL configuration failed", __func__);
1223
1224 if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1225 == -1)
1226 log_fatal("%s: setsockopt(IP_RECVTTL): %s", __func__,
1227 strerror(errno));
1228
1229 #ifdef BFD_LINUX
1230 int pktinfo = BFD_PKT_INFO_VAL;
1231 /* Figure out address and interface to do the peer matching. */
1232 if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1233 == -1)
1234 log_fatal("%s: setsockopt(IP_PKTINFO): %s", __func__,
1235 strerror(errno));
1236 #endif /* BFD_LINUX */
1237 #ifdef BFD_BSD
1238 int yes = 1;
1239
1240 /* Find out our address for peer matching. */
1241 if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
1242 log_fatal("%s: setsockopt(IP_RECVDSTADDR): %s", __func__,
1243 strerror(errno));
1244
1245 /* Find out interface where the packet came in. */
1246 if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
1247 log_fatal("%s: setsockopt_ipv4_ifindex: %s", __func__,
1248 strerror(errno));
1249 #endif /* BFD_BSD */
1250 }
1251
1252 static void bp_bind_ip(int sd, uint16_t port)
1253 {
1254 struct sockaddr_in sin;
1255
1256 memset(&sin, 0, sizeof(sin));
1257 sin.sin_family = AF_INET;
1258 sin.sin_addr.s_addr = htonl(INADDR_ANY);
1259 sin.sin_port = htons(port);
1260 if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
1261 log_fatal("%s: bind: %s", __func__, strerror(errno));
1262 }
1263
1264 int bp_udp_shop(void)
1265 {
1266 int sd;
1267
1268 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1269 if (sd == -1)
1270 log_fatal("%s: socket: %s", __func__, strerror(errno));
1271
1272 bp_set_ipopts(sd);
1273 bp_bind_ip(sd, BFD_DEFDESTPORT);
1274
1275 return sd;
1276 }
1277
1278 int bp_udp_mhop(void)
1279 {
1280 int sd;
1281
1282 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1283 if (sd == -1)
1284 log_fatal("%s: socket: %s", __func__, strerror(errno));
1285
1286 bp_set_ipopts(sd);
1287 bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1288
1289 return sd;
1290 }
1291
1292 int bp_peer_socket(struct bfd_peer_cfg *bpc)
1293 {
1294 int sd, pcount;
1295 struct sockaddr_in sin;
1296 static int srcPort = BFD_SRCPORTINIT;
1297
1298 sd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
1299 if (sd == -1) {
1300 log_error("ipv4-new: failed to create socket: %s",
1301 strerror(errno));
1302 return -1;
1303 }
1304
1305 if (!bpc->bpc_has_vxlan) {
1306 /* Set TTL to 255 for all transmitted packets */
1307 if (bp_set_ttl(sd) != 0) {
1308 close(sd);
1309 return -1;
1310 }
1311 }
1312
1313 /* Set TOS to CS6 for all transmitted packets */
1314 if (bp_set_tos(sd) != 0) {
1315 close(sd);
1316 return -1;
1317 }
1318
1319 /* dont bind-to-device incase of vxlan */
1320 if (!bpc->bpc_has_vxlan && bpc->bpc_has_localif) {
1321 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1322 close(sd);
1323 return -1;
1324 }
1325 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1326 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1327 close(sd);
1328 return -1;
1329 }
1330 }
1331
1332 /* Find an available source port in the proper range */
1333 memset(&sin, 0, sizeof(sin));
1334 sin = bpc->bpc_local.sa_sin;
1335 sin.sin_family = AF_INET;
1336 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1337 sin.sin_len = sizeof(sin);
1338 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1339 if (bpc->bpc_mhop || bpc->bpc_has_vxlan)
1340 sin.sin_addr = bpc->bpc_local.sa_sin.sin_addr;
1341 else
1342 sin.sin_addr.s_addr = INADDR_ANY;
1343
1344 pcount = 0;
1345 do {
1346 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1347 /* Searched all ports, none available */
1348 log_error("ipv4-new: failed to bind port: %s",
1349 strerror(errno));
1350 close(sd);
1351 return -1;
1352 }
1353 if (srcPort >= BFD_SRCPORTMAX)
1354 srcPort = BFD_SRCPORTINIT;
1355 sin.sin_port = htons(srcPort++);
1356 } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1357
1358 return sd;
1359 }
1360
1361
1362 /*
1363 * IPv6 sockets
1364 */
1365
1366 int bp_peer_socketv6(struct bfd_peer_cfg *bpc)
1367 {
1368 int sd, pcount, ifindex;
1369 struct sockaddr_in6 sin6;
1370 static int srcPort = BFD_SRCPORTINIT;
1371
1372 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1373 if (sd == -1) {
1374 log_error("ipv6-new: failed to create socket: %s",
1375 strerror(errno));
1376 return -1;
1377 }
1378
1379 if (!bpc->bpc_has_vxlan) {
1380 /* Set TTL to 255 for all transmitted packets */
1381 if (bp_set_ttlv6(sd) != 0) {
1382 close(sd);
1383 return -1;
1384 }
1385 }
1386
1387 /* Set TOS to CS6 for all transmitted packets */
1388 if (bp_set_tosv6(sd) != 0) {
1389 close(sd);
1390 return -1;
1391 }
1392
1393 /* Find an available source port in the proper range */
1394 memset(&sin6, 0, sizeof(sin6));
1395 sin6.sin6_family = AF_INET6;
1396 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1397 sin6.sin6_len = sizeof(sin6);
1398 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1399 sin6 = bpc->bpc_local.sa_sin6;
1400 if (sin6.sin6_family != AF_INET6) {
1401 #if 0 /* XXX what is this? */
1402 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1403 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1404 sin6.sin6_scope_id = ifindex;
1405 #endif
1406 } else if (bpc->bpc_has_localif) {
1407 ifindex = ptm_bfd_fetch_ifindex(bpc->bpc_localif);
1408 sin6.sin6_scope_id = ifindex;
1409 }
1410
1411 if (bpc->bpc_has_localif) {
1412 if (bp_bind_dev(sd, bpc->bpc_localif) != 0) {
1413 close(sd);
1414 return -1;
1415 }
1416 } else if (bpc->bpc_mhop && bpc->bpc_has_vrfname) {
1417 if (bp_bind_dev(sd, bpc->bpc_vrfname) != 0) {
1418 close(sd);
1419 return -1;
1420 }
1421 }
1422
1423 pcount = 0;
1424 do {
1425 if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1426 /* Searched all ports, none available */
1427 log_error("ipv6-new: failed to bind port: %s",
1428 strerror(errno));
1429 close(sd);
1430 return -1;
1431 }
1432 if (srcPort >= BFD_SRCPORTMAX)
1433 srcPort = BFD_SRCPORTINIT;
1434 sin6.sin6_port = htons(srcPort++);
1435 } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1436
1437 return sd;
1438 }
1439
1440 int bp_set_ttlv6(int sd)
1441 {
1442 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttlval,
1443 sizeof(ttlval))
1444 == -1) {
1445 log_warning("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__,
1446 strerror(errno));
1447 return -1;
1448 }
1449
1450 return 0;
1451 }
1452
1453 int bp_set_tosv6(int sd)
1454 {
1455 if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tosval, sizeof(tosval))
1456 == -1) {
1457 log_warning("%s: setsockopt(IPV6_TCLASS): %s", __func__,
1458 strerror(errno));
1459 return -1;
1460 }
1461
1462 return 0;
1463 }
1464
1465 static void bp_set_ipv6opts(int sd)
1466 {
1467 static int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1468 static int ipv6_only = BFD_IPV6_ONLY_VAL;
1469
1470 if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttlval,
1471 sizeof(ttlval))
1472 == -1)
1473 log_fatal("%s: setsockopt(IPV6_UNICAST_HOPS): %s", __func__,
1474 strerror(errno));
1475
1476 if (setsockopt_ipv6_hoplimit(sd, rcvttl) == -1)
1477 log_fatal("%s: setsockopt(IPV6_HOPLIMIT): %s", __func__,
1478 strerror(errno));
1479
1480 if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1481 log_fatal("%s: setsockopt(IPV6_PKTINFO): %s", __func__,
1482 strerror(errno));
1483
1484 if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1485 sizeof(ipv6_only))
1486 == -1)
1487 log_fatal("%s: setsockopt(IPV6_V6ONLY): %s", __func__,
1488 strerror(errno));
1489 }
1490
1491 static void bp_bind_ipv6(int sd, uint16_t port)
1492 {
1493 struct sockaddr_in6 sin6;
1494
1495 memset(&sin6, 0, sizeof(sin6));
1496 sin6.sin6_family = AF_INET6;
1497 sin6.sin6_addr = in6addr_any;
1498 sin6.sin6_port = htons(port);
1499 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1500 sin6.sin6_len = sizeof(sin6);
1501 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1502 if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1503 log_fatal("%s: bind: %s", __func__, strerror(errno));
1504 }
1505
1506 int bp_udp6_shop(void)
1507 {
1508 int sd;
1509
1510 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1511 if (sd == -1)
1512 log_fatal("%s: socket: %s", __func__, strerror(errno));
1513
1514 bp_set_ipv6opts(sd);
1515 bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1516
1517 return sd;
1518 }
1519
1520 int bp_udp6_mhop(void)
1521 {
1522 int sd;
1523
1524 sd = socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC);
1525 if (sd == -1)
1526 log_fatal("%s: socket: %s", __func__, strerror(errno));
1527
1528 bp_set_ipv6opts(sd);
1529 bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1530
1531 return sd;
1532 }