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