Line data Source code
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 : #include "lib/checksum.h"
38 : #include "lib/network.h"
39 :
40 : #include "bfd.h"
41 :
42 : /*
43 : * Prototypes
44 : */
45 : static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s);
46 : int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
47 : size_t datalen);
48 :
49 : static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd);
50 : ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
51 : ifindex_t *ifindex, struct sockaddr_any *local,
52 : struct sockaddr_any *peer);
53 : ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
54 : ifindex_t *ifindex, struct sockaddr_any *local,
55 : struct sockaddr_any *peer);
56 : int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
57 : struct sockaddr *to, socklen_t tolen);
58 : int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
59 : uint32_t *my_discr, uint64_t *my_rtt);
60 : #ifdef BFD_LINUX
61 : ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
62 : uint8_t *ttl, ifindex_t *ifindex,
63 : struct sockaddr_any *local, struct sockaddr_any *peer);
64 : void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
65 : struct sockaddr_any *peer, struct interface *ifp);
66 : int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
67 : struct bfd_session *bfd);
68 : ssize_t bfd_recv_fp_echo(int sd, uint8_t *msgbuf, size_t msgbuflen,
69 : uint8_t *ttl, ifindex_t *ifindex,
70 : struct sockaddr_any *local, struct sockaddr_any *peer);
71 : #endif
72 :
73 : /* socket related prototypes */
74 : static void bp_set_ipopts(int sd);
75 : static void bp_bind_ip(int sd, uint16_t port);
76 : static void bp_set_ipv6opts(int sd);
77 : static void bp_bind_ipv6(int sd, uint16_t port);
78 :
79 :
80 : /*
81 : * Functions
82 : */
83 42 : int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data,
84 : size_t datalen)
85 : {
86 42 : struct sockaddr *sa;
87 42 : struct sockaddr_in sin;
88 42 : struct sockaddr_in6 sin6;
89 42 : socklen_t slen;
90 42 : ssize_t rv;
91 42 : int sd = -1;
92 :
93 42 : if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6)) {
94 0 : memset(&sin6, 0, sizeof(sin6));
95 0 : sin6.sin6_family = AF_INET6;
96 0 : memcpy(&sin6.sin6_addr, &bs->key.peer, sizeof(sin6.sin6_addr));
97 0 : if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
98 0 : sin6.sin6_scope_id = bs->ifp->ifindex;
99 :
100 0 : sin6.sin6_port =
101 : (port) ? *port
102 0 : : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
103 : ? htons(BFD_DEF_MHOP_DEST_PORT)
104 0 : : htons(BFD_DEFDESTPORT);
105 :
106 0 : sd = bs->sock;
107 0 : sa = (struct sockaddr *)&sin6;
108 0 : slen = sizeof(sin6);
109 : } else {
110 42 : memset(&sin, 0, sizeof(sin));
111 42 : sin.sin_family = AF_INET;
112 42 : memcpy(&sin.sin_addr, &bs->key.peer, sizeof(sin.sin_addr));
113 42 : sin.sin_port =
114 : (port) ? *port
115 42 : : (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
116 : ? htons(BFD_DEF_MHOP_DEST_PORT)
117 42 : : htons(BFD_DEFDESTPORT);
118 :
119 42 : sd = bs->sock;
120 42 : sa = (struct sockaddr *)&sin;
121 42 : slen = sizeof(sin);
122 : }
123 :
124 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
125 : sa->sa_len = slen;
126 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
127 42 : rv = sendto(sd, data, datalen, 0, sa, slen);
128 42 : if (rv <= 0) {
129 0 : if (bglobal.debug_network)
130 0 : zlog_debug("packet-send: send failure: %s",
131 : strerror(errno));
132 0 : return -1;
133 : }
134 42 : if (rv < (ssize_t)datalen) {
135 0 : if (bglobal.debug_network)
136 0 : zlog_debug("packet-send: send partial: %s",
137 : strerror(errno));
138 : }
139 :
140 : return 0;
141 : }
142 :
143 : #ifdef BFD_LINUX
144 : /*
145 : * Compute the UDP checksum.
146 : *
147 : * Checksum is not set in the packet, just computed.
148 : *
149 : * pkt
150 : * Packet, fully filled out except for checksum field.
151 : *
152 : * pktsize
153 : * sizeof(*pkt)
154 : *
155 : * ip
156 : * IP address that pkt will be transmitted from and too.
157 : *
158 : * Returns:
159 : * Checksum in network byte order.
160 : */
161 0 : static uint16_t bfd_pkt_checksum(struct udphdr *pkt, size_t pktsize,
162 : struct in6_addr *ip, sa_family_t family)
163 : {
164 0 : uint16_t chksum;
165 :
166 0 : pkt->check = 0;
167 :
168 0 : if (family == AF_INET6) {
169 0 : struct ipv6_ph ph = {};
170 :
171 0 : memcpy(&ph.src, ip, sizeof(ph.src));
172 0 : memcpy(&ph.dst, ip, sizeof(ph.dst));
173 0 : ph.ulpl = htons(pktsize);
174 0 : ph.next_hdr = IPPROTO_UDP;
175 0 : chksum = in_cksum_with_ph6(&ph, pkt, pktsize);
176 : } else {
177 0 : struct ipv4_ph ph = {};
178 :
179 0 : memcpy(&ph.src, ip, sizeof(ph.src));
180 0 : memcpy(&ph.dst, ip, sizeof(ph.dst));
181 0 : ph.proto = IPPROTO_UDP;
182 0 : ph.len = htons(pktsize);
183 0 : chksum = in_cksum_with_ph4(&ph, pkt, pktsize);
184 : }
185 :
186 0 : return chksum;
187 : }
188 :
189 : /*
190 : * This routine creates the entire ECHO packet so that it will be looped
191 : * in the forwarding plane of the peer router instead of going up the
192 : * stack in BFD to be looped. If we haven't learned the peers MAC yet
193 : * no echo is sent.
194 : *
195 : * echo packet with src/dst IP equal to local IP
196 : * dest MAC as peer's MAC
197 : *
198 : * currently support ipv4
199 : */
200 0 : void ptm_bfd_echo_fp_snd(struct bfd_session *bfd)
201 : {
202 0 : int sd;
203 0 : struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
204 0 : int total_len = 0;
205 0 : struct ethhdr *eth;
206 0 : struct udphdr *uh;
207 0 : struct iphdr *iph;
208 0 : struct bfd_echo_pkt *beph;
209 0 : static char sendbuff[100];
210 0 : struct timeval time_sent;
211 :
212 0 : if (!bvrf)
213 0 : return;
214 0 : if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
215 : return;
216 0 : if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
217 0 : SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
218 :
219 0 : memset(sendbuff, 0, sizeof(sendbuff));
220 :
221 : /* add eth hdr */
222 0 : eth = (struct ethhdr *)(sendbuff);
223 0 : memcpy(eth->h_source, bfd->ifp->hw_addr, sizeof(eth->h_source));
224 0 : memcpy(eth->h_dest, bfd->peer_hw_addr, sizeof(eth->h_dest));
225 :
226 0 : total_len += sizeof(struct ethhdr);
227 :
228 0 : sd = bvrf->bg_echo;
229 0 : eth->h_proto = htons(ETH_P_IP);
230 :
231 : /* add ip hdr */
232 0 : iph = (struct iphdr *)(sendbuff + sizeof(struct ethhdr));
233 :
234 0 : iph->ihl = sizeof(struct ip) >> 2;
235 0 : iph->version = IPVERSION;
236 0 : iph->tos = IPTOS_PREC_INTERNETCONTROL;
237 0 : iph->id = (uint16_t)frr_weak_random();
238 0 : iph->ttl = BFD_TTL_VAL;
239 0 : iph->protocol = IPPROTO_UDP;
240 0 : memcpy(&iph->saddr, &bfd->local_address.sa_sin.sin_addr,
241 : sizeof(bfd->local_address.sa_sin.sin_addr));
242 0 : memcpy(&iph->daddr, &bfd->local_address.sa_sin.sin_addr,
243 : sizeof(bfd->local_address.sa_sin.sin_addr));
244 0 : total_len += sizeof(struct iphdr);
245 :
246 : /* add udp hdr */
247 0 : uh = (struct udphdr *)(sendbuff + sizeof(struct iphdr) +
248 : sizeof(struct ethhdr));
249 0 : uh->source = htons(BFD_DEF_ECHO_PORT);
250 0 : uh->dest = htons(BFD_DEF_ECHO_PORT);
251 :
252 0 : total_len += sizeof(struct udphdr);
253 :
254 : /* add bfd echo */
255 0 : beph = (struct bfd_echo_pkt *)(sendbuff + sizeof(struct udphdr) +
256 : sizeof(struct iphdr) +
257 : sizeof(struct ethhdr));
258 :
259 0 : beph->ver = BFD_ECHO_VERSION;
260 0 : beph->len = BFD_ECHO_PKT_LEN;
261 0 : beph->my_discr = htonl(bfd->discrs.my_discr);
262 :
263 : /* RTT calculation: add starting time in packet */
264 0 : monotime(&time_sent);
265 0 : beph->time_sent_sec = htobe64(time_sent.tv_sec);
266 0 : beph->time_sent_usec = htobe64(time_sent.tv_usec);
267 :
268 0 : total_len += sizeof(struct bfd_echo_pkt);
269 0 : uh->len =
270 0 : htons(total_len - sizeof(struct iphdr) - sizeof(struct ethhdr));
271 0 : uh->check = bfd_pkt_checksum(
272 : uh, (total_len - sizeof(struct iphdr) - sizeof(struct ethhdr)),
273 : (struct in6_addr *)&iph->saddr, AF_INET);
274 :
275 0 : iph->tot_len = htons(total_len - sizeof(struct ethhdr));
276 0 : iph->check = in_cksum((const void *)iph, sizeof(struct iphdr));
277 :
278 0 : if (bp_udp_send_fp(sd, (uint8_t *)&sendbuff, total_len, bfd) == -1)
279 : return;
280 :
281 0 : bfd->stats.tx_echo_pkt++;
282 : }
283 : #endif
284 :
285 0 : void ptm_bfd_echo_snd(struct bfd_session *bfd)
286 : {
287 0 : struct sockaddr *sa;
288 0 : socklen_t salen;
289 0 : int sd;
290 0 : struct bfd_echo_pkt bep;
291 0 : struct sockaddr_in sin;
292 0 : struct sockaddr_in6 sin6;
293 0 : struct bfd_vrf_global *bvrf = bfd_vrf_look_by_session(bfd);
294 :
295 0 : if (!bvrf)
296 0 : return;
297 0 : if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
298 0 : SET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
299 :
300 0 : memset(&bep, 0, sizeof(bep));
301 0 : bep.ver = BFD_ECHO_VERSION;
302 0 : bep.len = BFD_ECHO_PKT_LEN;
303 0 : bep.my_discr = htonl(bfd->discrs.my_discr);
304 :
305 0 : if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) {
306 0 : if (bvrf->bg_echov6 == -1)
307 : return;
308 0 : sd = bvrf->bg_echov6;
309 0 : memset(&sin6, 0, sizeof(sin6));
310 0 : sin6.sin6_family = AF_INET6;
311 0 : memcpy(&sin6.sin6_addr, &bfd->key.peer, sizeof(sin6.sin6_addr));
312 0 : if (bfd->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
313 0 : sin6.sin6_scope_id = bfd->ifp->ifindex;
314 :
315 0 : sin6.sin6_port = htons(BFD_DEF_ECHO_PORT);
316 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
317 : sin6.sin6_len = sizeof(sin6);
318 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
319 :
320 0 : sa = (struct sockaddr *)&sin6;
321 0 : salen = sizeof(sin6);
322 : } else {
323 0 : sd = bvrf->bg_echo;
324 0 : memset(&sin, 0, sizeof(sin));
325 0 : sin.sin_family = AF_INET;
326 0 : memcpy(&sin.sin_addr, &bfd->key.peer, sizeof(sin.sin_addr));
327 0 : sin.sin_port = htons(BFD_DEF_ECHO_PORT);
328 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
329 : sin.sin_len = sizeof(sin);
330 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
331 :
332 0 : sa = (struct sockaddr *)&sin;
333 0 : salen = sizeof(sin);
334 : }
335 0 : if (bp_udp_send(sd, BFD_TTL_VAL, (uint8_t *)&bep, sizeof(bep), sa,
336 : salen)
337 : == -1)
338 : return;
339 :
340 0 : bfd->stats.tx_echo_pkt++;
341 : }
342 :
343 0 : static int ptm_bfd_process_echo_pkt(struct bfd_vrf_global *bvrf, int s)
344 : {
345 0 : struct bfd_session *bfd;
346 0 : uint32_t my_discr = 0;
347 0 : uint64_t my_rtt = 0;
348 0 : uint8_t ttl = 0;
349 :
350 : /* Receive and parse echo packet. */
351 0 : if (bp_bfd_echo_in(bvrf, s, &ttl, &my_discr, &my_rtt) == -1)
352 : return 0;
353 :
354 : /* Your discriminator not zero - use it to find session */
355 0 : bfd = bfd_id_lookup(my_discr);
356 0 : if (bfd == NULL) {
357 0 : if (bglobal.debug_network)
358 0 : zlog_debug("echo-packet: no matching session (id:%u)",
359 : my_discr);
360 0 : return -1;
361 : }
362 :
363 0 : if (!CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) {
364 0 : if (bglobal.debug_network)
365 0 : zlog_debug("echo-packet: echo disabled [%s] (id:%u)",
366 : bs_to_string(bfd), my_discr);
367 0 : return -1;
368 : }
369 :
370 : /* RTT Calculation: add current RTT to samples */
371 0 : if (my_rtt != 0) {
372 0 : bfd->rtt[bfd->rtt_index] = my_rtt;
373 0 : bfd->rtt_index++;
374 0 : if (bfd->rtt_index >= BFD_RTT_SAMPLE)
375 0 : bfd->rtt_index = 0;
376 0 : if (bfd->rtt_valid < BFD_RTT_SAMPLE)
377 0 : bfd->rtt_valid++;
378 : }
379 :
380 0 : bfd->stats.rx_echo_pkt++;
381 :
382 : /* Compute detect time */
383 0 : bfd->echo_detect_TO = bfd->remote_detect_mult * bfd->echo_xmt_TO;
384 :
385 : /* Update echo receive timeout. */
386 0 : if (bfd->echo_detect_TO > 0)
387 0 : bfd_echo_recvtimer_update(bfd);
388 :
389 : return 0;
390 : }
391 :
392 42 : void ptm_bfd_snd(struct bfd_session *bfd, int fbit)
393 : {
394 42 : struct bfd_pkt cp = {};
395 :
396 : /* Set fields according to section 6.5.7 */
397 42 : cp.diag = bfd->local_diag;
398 42 : BFD_SETVER(cp.diag, BFD_VERSION);
399 42 : cp.flags = 0;
400 42 : BFD_SETSTATE(cp.flags, bfd->ses_state);
401 :
402 42 : if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_CBIT))
403 0 : BFD_SETCBIT(cp.flags, BFD_CBIT);
404 :
405 42 : BFD_SETDEMANDBIT(cp.flags, BFD_DEF_DEMAND);
406 :
407 : /*
408 : * Polling and Final can't be set at the same time.
409 : *
410 : * RFC 5880, Section 6.5.
411 : */
412 42 : BFD_SETFBIT(cp.flags, fbit);
413 4 : if (fbit == 0)
414 38 : BFD_SETPBIT(cp.flags, bfd->polling);
415 :
416 42 : cp.detect_mult = bfd->detect_mult;
417 42 : cp.len = BFD_PKT_LEN;
418 42 : cp.discrs.my_discr = htonl(bfd->discrs.my_discr);
419 42 : cp.discrs.remote_discr = htonl(bfd->discrs.remote_discr);
420 42 : if (bfd->polling) {
421 8 : cp.timers.desired_min_tx =
422 8 : htonl(bfd->timers.desired_min_tx);
423 8 : cp.timers.required_min_rx =
424 8 : htonl(bfd->timers.required_min_rx);
425 : } else {
426 : /*
427 : * We can only announce current setting on poll, this
428 : * avoids timing mismatch with our peer and give it
429 : * the oportunity to learn. See `bs_final_handler` for
430 : * more information.
431 : */
432 34 : cp.timers.desired_min_tx =
433 34 : htonl(bfd->cur_timers.desired_min_tx);
434 34 : cp.timers.required_min_rx =
435 34 : htonl(bfd->cur_timers.required_min_rx);
436 : }
437 42 : cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx);
438 :
439 42 : if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0)
440 0 : return;
441 :
442 42 : bfd->stats.tx_ctrl_pkt++;
443 : }
444 :
445 : #ifdef BFD_LINUX
446 : /*
447 : * receive the ipv4 echo packet that was loopback in the peers forwarding plane
448 : */
449 0 : ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen,
450 : uint8_t *ttl, ifindex_t *ifindex,
451 : struct sockaddr_any *local, struct sockaddr_any *peer)
452 : {
453 0 : ssize_t mlen;
454 0 : struct sockaddr_ll msgaddr;
455 0 : struct msghdr msghdr;
456 0 : struct iovec iov[1];
457 0 : uint16_t recv_checksum;
458 0 : uint16_t checksum;
459 0 : struct iphdr *ip;
460 0 : struct udphdr *uh;
461 :
462 : /* Prepare the recvmsg params. */
463 0 : iov[0].iov_base = msgbuf;
464 0 : iov[0].iov_len = msgbuflen;
465 :
466 0 : memset(&msghdr, 0, sizeof(msghdr));
467 0 : msghdr.msg_name = &msgaddr;
468 0 : msghdr.msg_namelen = sizeof(msgaddr);
469 0 : msghdr.msg_iov = iov;
470 0 : msghdr.msg_iovlen = 1;
471 :
472 0 : mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
473 0 : if (mlen == -1) {
474 0 : if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR)
475 0 : zlog_err("%s: recv failed: %s", __func__,
476 : strerror(errno));
477 :
478 0 : return -1;
479 : }
480 :
481 0 : ip = (struct iphdr *)(msgbuf + sizeof(struct ethhdr));
482 :
483 : /* verify ip checksum */
484 0 : recv_checksum = ip->check;
485 0 : ip->check = 0;
486 0 : checksum = in_cksum((const void *)ip, sizeof(struct iphdr));
487 0 : if (recv_checksum != checksum) {
488 0 : if (bglobal.debug_network)
489 0 : zlog_debug(
490 : "%s: invalid iphdr checksum expected 0x%x rcvd 0x%x",
491 : __func__, checksum, recv_checksum);
492 0 : return -1;
493 : }
494 :
495 0 : *ttl = ip->ttl;
496 0 : if (*ttl != 254) {
497 : /* Echo should be looped in peer's forwarding plane, but it also
498 : * comes up to BFD so silently drop it
499 : */
500 0 : if (ip->daddr == ip->saddr)
501 : return -1;
502 :
503 0 : if (bglobal.debug_network)
504 0 : zlog_debug("%s: invalid TTL: %u", __func__, *ttl);
505 0 : return -1;
506 : }
507 :
508 0 : local->sa_sin.sin_family = AF_INET;
509 0 : memcpy(&local->sa_sin.sin_addr, &ip->saddr, sizeof(ip->saddr));
510 0 : peer->sa_sin.sin_family = AF_INET;
511 0 : memcpy(&peer->sa_sin.sin_addr, &ip->daddr, sizeof(ip->daddr));
512 :
513 0 : *ifindex = msgaddr.sll_ifindex;
514 :
515 : /* verify udp checksum */
516 0 : uh = (struct udphdr *)(msgbuf + sizeof(struct iphdr) +
517 : sizeof(struct ethhdr));
518 0 : recv_checksum = uh->check;
519 0 : uh->check = 0;
520 0 : checksum = bfd_pkt_checksum(uh, ntohs(uh->len),
521 0 : (struct in6_addr *)&ip->saddr, AF_INET);
522 0 : if (recv_checksum != checksum) {
523 0 : if (bglobal.debug_network)
524 0 : zlog_debug(
525 : "%s: invalid udphdr checksum expected 0x%x rcvd 0x%x",
526 : __func__, checksum, recv_checksum);
527 0 : return -1;
528 : }
529 : return mlen;
530 : }
531 : #endif
532 :
533 35 : ssize_t bfd_recv_ipv4(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
534 : ifindex_t *ifindex, struct sockaddr_any *local,
535 : struct sockaddr_any *peer)
536 : {
537 35 : struct cmsghdr *cm;
538 35 : ssize_t mlen;
539 35 : struct sockaddr_in msgaddr;
540 35 : struct msghdr msghdr;
541 35 : struct iovec iov[1];
542 35 : uint8_t cmsgbuf[255];
543 :
544 : /* Prepare the recvmsg params. */
545 35 : iov[0].iov_base = msgbuf;
546 35 : iov[0].iov_len = msgbuflen;
547 :
548 35 : memset(&msghdr, 0, sizeof(msghdr));
549 35 : msghdr.msg_name = &msgaddr;
550 35 : msghdr.msg_namelen = sizeof(msgaddr);
551 35 : msghdr.msg_iov = iov;
552 35 : msghdr.msg_iovlen = 1;
553 35 : msghdr.msg_control = cmsgbuf;
554 35 : msghdr.msg_controllen = sizeof(cmsgbuf);
555 :
556 35 : mlen = recvmsg(sd, &msghdr, MSG_DONTWAIT);
557 35 : if (mlen == -1) {
558 0 : if (errno != EAGAIN)
559 0 : zlog_err("ipv4-recv: recv failed: %s", strerror(errno));
560 :
561 0 : return -1;
562 : }
563 :
564 : /* Get source address */
565 35 : peer->sa_sin = *((struct sockaddr_in *)(msghdr.msg_name));
566 :
567 : /* Get and check TTL */
568 35 : for (cm = CMSG_FIRSTHDR(&msghdr); cm != NULL;
569 175 : cm = CMSG_NXTHDR(&msghdr, cm)) {
570 70 : if (cm->cmsg_level != IPPROTO_IP)
571 0 : continue;
572 :
573 70 : switch (cm->cmsg_type) {
574 : #ifdef BFD_LINUX
575 35 : case IP_TTL: {
576 35 : uint32_t ttlval;
577 :
578 35 : memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
579 35 : if (ttlval > 255) {
580 0 : if (bglobal.debug_network)
581 0 : zlog_debug("%s: invalid TTL: %u",
582 : __func__, ttlval);
583 0 : return -1;
584 : }
585 35 : *ttl = ttlval;
586 35 : break;
587 : }
588 :
589 : case IP_PKTINFO: {
590 35 : struct in_pktinfo *pi =
591 : (struct in_pktinfo *)CMSG_DATA(cm);
592 :
593 35 : if (pi == NULL)
594 : break;
595 :
596 35 : local->sa_sin.sin_family = AF_INET;
597 35 : local->sa_sin.sin_addr = pi->ipi_addr;
598 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
599 : local->sa_sin.sin_len = sizeof(local->sa_sin);
600 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
601 :
602 35 : *ifindex = pi->ipi_ifindex;
603 35 : break;
604 : }
605 : #endif /* BFD_LINUX */
606 : #ifdef BFD_BSD
607 : case IP_RECVTTL: {
608 : memcpy(ttl, CMSG_DATA(cm), sizeof(*ttl));
609 : break;
610 : }
611 :
612 : case IP_RECVDSTADDR: {
613 : struct in_addr ia;
614 :
615 : memcpy(&ia, CMSG_DATA(cm), sizeof(ia));
616 : local->sa_sin.sin_family = AF_INET;
617 : local->sa_sin.sin_addr = ia;
618 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
619 : local->sa_sin.sin_len = sizeof(local->sa_sin);
620 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
621 : break;
622 : }
623 : #endif /* BFD_BSD */
624 :
625 : default:
626 : /*
627 : * On *BSDs we expect to land here when skipping
628 : * the IP_RECVIF header. It will be handled by
629 : * getsockopt_ifindex() below.
630 : */
631 : /* NOTHING */
632 : break;
633 : }
634 : }
635 :
636 : /* OS agnostic way of getting interface name. */
637 35 : if (*ifindex == IFINDEX_INTERNAL)
638 0 : *ifindex = getsockopt_ifindex(AF_INET, &msghdr);
639 :
640 : return mlen;
641 : }
642 :
643 0 : ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl,
644 : ifindex_t *ifindex, struct sockaddr_any *local,
645 : struct sockaddr_any *peer)
646 : {
647 0 : struct cmsghdr *cm;
648 0 : struct in6_pktinfo *pi6 = NULL;
649 0 : ssize_t mlen;
650 0 : uint32_t ttlval;
651 0 : struct sockaddr_in6 msgaddr6;
652 0 : struct msghdr msghdr6;
653 0 : struct iovec iov[1];
654 0 : uint8_t cmsgbuf6[255];
655 :
656 : /* Prepare the recvmsg params. */
657 0 : iov[0].iov_base = msgbuf;
658 0 : iov[0].iov_len = msgbuflen;
659 :
660 0 : memset(&msghdr6, 0, sizeof(msghdr6));
661 0 : msghdr6.msg_name = &msgaddr6;
662 0 : msghdr6.msg_namelen = sizeof(msgaddr6);
663 0 : msghdr6.msg_iov = iov;
664 0 : msghdr6.msg_iovlen = 1;
665 0 : msghdr6.msg_control = cmsgbuf6;
666 0 : msghdr6.msg_controllen = sizeof(cmsgbuf6);
667 :
668 0 : mlen = recvmsg(sd, &msghdr6, MSG_DONTWAIT);
669 0 : if (mlen == -1) {
670 0 : if (errno != EAGAIN)
671 0 : zlog_err("ipv6-recv: recv failed: %s", strerror(errno));
672 :
673 0 : return -1;
674 : }
675 :
676 : /* Get source address */
677 0 : peer->sa_sin6 = *((struct sockaddr_in6 *)(msghdr6.msg_name));
678 :
679 : /* Get and check TTL */
680 0 : for (cm = CMSG_FIRSTHDR(&msghdr6); cm != NULL;
681 0 : cm = CMSG_NXTHDR(&msghdr6, cm)) {
682 0 : if (cm->cmsg_level != IPPROTO_IPV6)
683 0 : continue;
684 :
685 0 : if (cm->cmsg_type == IPV6_HOPLIMIT) {
686 0 : memcpy(&ttlval, CMSG_DATA(cm), sizeof(ttlval));
687 0 : if (ttlval > 255) {
688 0 : if (bglobal.debug_network)
689 0 : zlog_debug("%s: invalid TTL: %u",
690 : __func__, ttlval);
691 0 : return -1;
692 : }
693 :
694 0 : *ttl = ttlval;
695 0 : } else if (cm->cmsg_type == IPV6_PKTINFO) {
696 0 : pi6 = (struct in6_pktinfo *)CMSG_DATA(cm);
697 0 : if (pi6) {
698 0 : local->sa_sin6.sin6_family = AF_INET6;
699 0 : local->sa_sin6.sin6_addr = pi6->ipi6_addr;
700 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
701 : local->sa_sin6.sin6_len = sizeof(local->sa_sin6);
702 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
703 :
704 0 : *ifindex = pi6->ipi6_ifindex;
705 :
706 : /* Set scope ID for link local addresses. */
707 0 : if (IN6_IS_ADDR_LINKLOCAL(
708 : &peer->sa_sin6.sin6_addr))
709 0 : peer->sa_sin6.sin6_scope_id = *ifindex;
710 0 : if (IN6_IS_ADDR_LINKLOCAL(
711 : &local->sa_sin6.sin6_addr))
712 0 : local->sa_sin6.sin6_scope_id = *ifindex;
713 : }
714 : }
715 : }
716 :
717 : return mlen;
718 : }
719 :
720 35 : static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd)
721 : {
722 35 : if (sd == bvrf->bg_shop) {
723 35 : THREAD_OFF(bvrf->bg_ev[0]);
724 35 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
725 : &bvrf->bg_ev[0]);
726 0 : } else if (sd == bvrf->bg_mhop) {
727 0 : THREAD_OFF(bvrf->bg_ev[1]);
728 0 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
729 : &bvrf->bg_ev[1]);
730 0 : } else if (sd == bvrf->bg_shop6) {
731 0 : THREAD_OFF(bvrf->bg_ev[2]);
732 0 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
733 : &bvrf->bg_ev[2]);
734 0 : } else if (sd == bvrf->bg_mhop6) {
735 0 : THREAD_OFF(bvrf->bg_ev[3]);
736 0 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
737 : &bvrf->bg_ev[3]);
738 0 : } else if (sd == bvrf->bg_echo) {
739 0 : THREAD_OFF(bvrf->bg_ev[4]);
740 0 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
741 : &bvrf->bg_ev[4]);
742 0 : } else if (sd == bvrf->bg_echov6) {
743 0 : THREAD_OFF(bvrf->bg_ev[5]);
744 0 : thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
745 : &bvrf->bg_ev[5]);
746 : }
747 35 : }
748 :
749 : PRINTFRR(6, 7)
750 10 : static void cp_debug(bool mhop, struct sockaddr_any *peer,
751 : struct sockaddr_any *local, ifindex_t ifindex,
752 : vrf_id_t vrfid, const char *fmt, ...)
753 : {
754 10 : char buf[512], peerstr[128], localstr[128], portstr[64], vrfstr[64];
755 10 : va_list vl;
756 :
757 : /* Don't to any processing if debug is disabled. */
758 10 : if (bglobal.debug_network == false)
759 10 : return;
760 :
761 0 : if (peer->sa_sin.sin_family)
762 0 : snprintf(peerstr, sizeof(peerstr), " peer:%s", satostr(peer));
763 : else
764 0 : peerstr[0] = 0;
765 :
766 0 : if (local->sa_sin.sin_family)
767 0 : snprintf(localstr, sizeof(localstr), " local:%s",
768 : satostr(local));
769 : else
770 0 : localstr[0] = 0;
771 :
772 0 : if (ifindex != IFINDEX_INTERNAL)
773 0 : snprintf(portstr, sizeof(portstr), " port:%u", ifindex);
774 : else
775 0 : portstr[0] = 0;
776 :
777 0 : if (vrfid != VRF_DEFAULT)
778 0 : snprintf(vrfstr, sizeof(vrfstr), " vrf:%u", vrfid);
779 : else
780 0 : vrfstr[0] = 0;
781 :
782 0 : va_start(vl, fmt);
783 0 : vsnprintf(buf, sizeof(buf), fmt, vl);
784 0 : va_end(vl);
785 :
786 0 : zlog_debug("control-packet: %s [mhop:%s%s%s%s%s]", buf,
787 : mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr);
788 : }
789 :
790 35 : void bfd_recv_cb(struct thread *t)
791 : {
792 35 : int sd = THREAD_FD(t);
793 35 : struct bfd_session *bfd;
794 35 : struct bfd_pkt *cp;
795 35 : bool is_mhop;
796 35 : ssize_t mlen = 0;
797 35 : uint8_t ttl = 0;
798 35 : vrf_id_t vrfid;
799 35 : ifindex_t ifindex = IFINDEX_INTERNAL;
800 35 : struct sockaddr_any local, peer;
801 35 : uint8_t msgbuf[1516];
802 35 : struct interface *ifp = NULL;
803 35 : struct bfd_vrf_global *bvrf = THREAD_ARG(t);
804 :
805 : /* Schedule next read. */
806 35 : bfd_sd_reschedule(bvrf, sd);
807 :
808 : /* Handle echo packets. */
809 35 : if (sd == bvrf->bg_echo || sd == bvrf->bg_echov6) {
810 0 : ptm_bfd_process_echo_pkt(bvrf, sd);
811 10 : return;
812 : }
813 :
814 : /* Sanitize input/output. */
815 35 : memset(&local, 0, sizeof(local));
816 35 : memset(&peer, 0, sizeof(peer));
817 :
818 : /* Handle control packets. */
819 35 : is_mhop = false;
820 35 : if (sd == bvrf->bg_shop || sd == bvrf->bg_mhop) {
821 35 : is_mhop = sd == bvrf->bg_mhop;
822 35 : mlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
823 : &local, &peer);
824 0 : } else if (sd == bvrf->bg_shop6 || sd == bvrf->bg_mhop6) {
825 0 : is_mhop = sd == bvrf->bg_mhop6;
826 0 : mlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), &ttl, &ifindex,
827 : &local, &peer);
828 : }
829 :
830 : /*
831 : * With netns backend, we have a separate socket in each VRF. It means
832 : * that bvrf here is correct and we believe the bvrf->vrf->vrf_id.
833 : * With VRF-lite backend, we have a single socket in the default VRF.
834 : * It means that we can't believe the bvrf->vrf->vrf_id. But in
835 : * VRF-lite, the ifindex is globally unique, so we can retrieve the
836 : * correct vrf_id from the interface.
837 : */
838 35 : vrfid = bvrf->vrf->vrf_id;
839 35 : if (ifindex) {
840 35 : ifp = if_lookup_by_index(ifindex, vrfid);
841 35 : if (ifp)
842 35 : vrfid = ifp->vrf->vrf_id;
843 : }
844 :
845 : /* Implement RFC 5880 6.8.6 */
846 35 : if (mlen < BFD_PKT_LEN) {
847 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
848 : "too small (%zd bytes)", mlen);
849 0 : return;
850 : }
851 :
852 : /* Validate single hop packet TTL. */
853 35 : if ((!is_mhop) && (ttl != BFD_TTL_VAL)) {
854 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
855 : "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL);
856 0 : return;
857 : }
858 :
859 : /*
860 : * Parse the control header for inconsistencies:
861 : * - Invalid version;
862 : * - Bad multiplier configuration;
863 : * - Short packets;
864 : * - Invalid discriminator;
865 : */
866 35 : cp = (struct bfd_pkt *)(msgbuf);
867 35 : if (BFD_GETVER(cp->diag) != BFD_VERSION) {
868 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
869 : "bad version %d", BFD_GETVER(cp->diag));
870 0 : return;
871 : }
872 :
873 35 : if (cp->detect_mult == 0) {
874 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
875 : "detect multiplier set to zero");
876 0 : return;
877 : }
878 :
879 35 : if ((cp->len < BFD_PKT_LEN) || (cp->len > mlen)) {
880 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "too small");
881 0 : return;
882 : }
883 :
884 35 : if (cp->discrs.my_discr == 0) {
885 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
886 : "'my discriminator' is zero");
887 0 : return;
888 : }
889 :
890 : /* Find the session that this packet belongs. */
891 35 : bfd = ptm_bfd_sess_find(cp, &peer, &local, ifp, vrfid, is_mhop);
892 35 : if (bfd == NULL) {
893 10 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
894 : "no session found");
895 10 : return;
896 : }
897 : /*
898 : * We may have a situation where received packet is on wrong vrf
899 : */
900 25 : if (bfd && bfd->vrf && bfd->vrf != bvrf->vrf) {
901 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
902 : "wrong vrfid.");
903 0 : return;
904 : }
905 :
906 : /* Ensure that existing good sessions are not overridden. */
907 25 : if (!cp->discrs.remote_discr && bfd->ses_state != PTM_BFD_DOWN &&
908 : bfd->ses_state != PTM_BFD_ADM_DOWN) {
909 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
910 : "'remote discriminator' is zero, not overridden");
911 0 : return;
912 : }
913 :
914 : /*
915 : * Multi hop: validate packet TTL.
916 : * Single hop: set local address that received the packet.
917 : * set peers mac address for echo packets
918 : */
919 25 : if (is_mhop) {
920 0 : if (ttl < bfd->mh_ttl) {
921 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
922 : "exceeded max hop count (expected %d, got %d)",
923 : bfd->mh_ttl, ttl);
924 0 : return;
925 : }
926 : } else {
927 :
928 25 : if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC)
929 8 : bfd->local_address = local;
930 : #ifdef BFD_LINUX
931 25 : if (ifp)
932 25 : bfd_peer_mac_set(sd, bfd, &peer, ifp);
933 : #endif
934 : }
935 :
936 25 : bfd->stats.rx_ctrl_pkt++;
937 :
938 : /*
939 : * If no interface was detected, save the interface where the
940 : * packet came in.
941 : */
942 25 : if (!is_mhop && bfd->ifp == NULL)
943 0 : bfd->ifp = ifp;
944 :
945 : /* Log remote discriminator changes. */
946 25 : if ((bfd->discrs.remote_discr != 0)
947 17 : && (bfd->discrs.remote_discr != ntohl(cp->discrs.my_discr)))
948 0 : cp_debug(is_mhop, &peer, &local, ifindex, vrfid,
949 : "remote discriminator mismatch (expected %u, got %u)",
950 : bfd->discrs.remote_discr, ntohl(cp->discrs.my_discr));
951 :
952 25 : bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr);
953 :
954 : /* Save remote diagnostics before state switch. */
955 25 : bfd->remote_diag = cp->diag & BFD_DIAGMASK;
956 :
957 : /* Update remote timers settings. */
958 25 : bfd->remote_timers.desired_min_tx = ntohl(cp->timers.desired_min_tx);
959 25 : bfd->remote_timers.required_min_rx = ntohl(cp->timers.required_min_rx);
960 25 : bfd->remote_timers.required_min_echo =
961 25 : ntohl(cp->timers.required_min_echo);
962 25 : bfd->remote_detect_mult = cp->detect_mult;
963 :
964 25 : if (BFD_GETCBIT(cp->flags))
965 4 : bfd->remote_cbit = 1;
966 : else
967 21 : bfd->remote_cbit = 0;
968 :
969 : /* State switch from section 6.2. */
970 25 : bs_state_handler(bfd, BFD_GETSTATE(cp->flags));
971 :
972 : /* RFC 5880, Section 6.5: handle POLL/FINAL negotiation sequence. */
973 25 : if (bfd->polling && BFD_GETFBIT(cp->flags)) {
974 : /* Disable polling. */
975 4 : bfd->polling = 0;
976 :
977 : /* Handle poll finalization. */
978 4 : bs_final_handler(bfd);
979 : }
980 :
981 : /*
982 : * Detection timeout calculation:
983 : * The minimum detection timeout is the remote detection
984 : * multipler (number of packets to be missed) times the agreed
985 : * transmission interval.
986 : *
987 : * RFC 5880, Section 6.8.4.
988 : */
989 25 : if (bfd->cur_timers.required_min_rx > bfd->remote_timers.desired_min_tx)
990 5 : bfd->detect_TO = bfd->remote_detect_mult
991 5 : * bfd->cur_timers.required_min_rx;
992 : else
993 20 : bfd->detect_TO = bfd->remote_detect_mult
994 20 : * bfd->remote_timers.desired_min_tx;
995 :
996 : /* Apply new receive timer immediately. */
997 25 : bfd_recvtimer_update(bfd);
998 :
999 : /* Handle echo timers changes. */
1000 25 : bs_echo_timer_handler(bfd);
1001 :
1002 : /*
1003 : * We've received a packet with the POLL bit set, we must send
1004 : * a control packet back with the FINAL bit set.
1005 : *
1006 : * RFC 5880, Section 6.5.
1007 : */
1008 25 : if (BFD_GETPBIT(cp->flags)) {
1009 : /* We are finalizing a poll negotiation. */
1010 4 : bs_final_handler(bfd);
1011 :
1012 : /* Send the control packet with the final bit immediately. */
1013 4 : ptm_bfd_snd(bfd, 1);
1014 : }
1015 : }
1016 :
1017 : /*
1018 : * bp_bfd_echo_in: proccesses an BFD echo packet. On TTL == BFD_TTL_VAL
1019 : * the packet is looped back or returns the my discriminator ID along
1020 : * with the TTL.
1021 : *
1022 : * Returns -1 on error or loopback or 0 on success.
1023 : */
1024 0 : int bp_bfd_echo_in(struct bfd_vrf_global *bvrf, int sd, uint8_t *ttl,
1025 : uint32_t *my_discr, uint64_t *my_rtt)
1026 : {
1027 0 : struct bfd_echo_pkt *bep;
1028 0 : ssize_t rlen;
1029 0 : struct sockaddr_any local, peer;
1030 0 : ifindex_t ifindex = IFINDEX_INTERNAL;
1031 0 : vrf_id_t vrfid = VRF_DEFAULT;
1032 0 : uint8_t msgbuf[1516];
1033 0 : size_t bfd_offset = 0;
1034 :
1035 0 : if (sd == bvrf->bg_echo) {
1036 : #ifdef BFD_LINUX
1037 0 : rlen = bfd_recv_ipv4_fp(sd, msgbuf, sizeof(msgbuf), ttl,
1038 : &ifindex, &local, &peer);
1039 :
1040 : /* silently drop echo packet that is looped in fastpath but
1041 : * still comes up to BFD
1042 : */
1043 0 : if (rlen == -1)
1044 : return -1;
1045 : bfd_offset = sizeof(struct udphdr) + sizeof(struct iphdr) +
1046 : sizeof(struct ethhdr);
1047 : #else
1048 : rlen = bfd_recv_ipv4(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
1049 : &local, &peer);
1050 : bfd_offset = 0;
1051 : #endif
1052 : } else {
1053 0 : rlen = bfd_recv_ipv6(sd, msgbuf, sizeof(msgbuf), ttl, &ifindex,
1054 : &local, &peer);
1055 0 : bfd_offset = 0;
1056 : }
1057 :
1058 : /* Short packet, better not risk reading it. */
1059 0 : if (rlen < (ssize_t)sizeof(*bep)) {
1060 0 : cp_debug(false, &peer, &local, ifindex, vrfid,
1061 : "small echo packet");
1062 0 : return -1;
1063 : }
1064 :
1065 : /* Test for loopback for ipv6, ipv4 is looped in forwarding plane */
1066 0 : if ((*ttl == BFD_TTL_VAL) && (sd == bvrf->bg_echov6)) {
1067 0 : bp_udp_send(sd, *ttl - 1, msgbuf, rlen,
1068 : (struct sockaddr *)&peer,
1069 0 : (sd == bvrf->bg_echo) ? sizeof(peer.sa_sin)
1070 : : sizeof(peer.sa_sin6));
1071 0 : return -1;
1072 : }
1073 :
1074 : /* Read my discriminator from BFD Echo packet. */
1075 0 : bep = (struct bfd_echo_pkt *)(msgbuf + bfd_offset);
1076 0 : *my_discr = ntohl(bep->my_discr);
1077 0 : if (*my_discr == 0) {
1078 0 : cp_debug(false, &peer, &local, ifindex, vrfid,
1079 : "invalid echo packet discriminator (zero)");
1080 0 : return -1;
1081 : }
1082 :
1083 : #ifdef BFD_LINUX
1084 : /* RTT Calculation: determine RTT time of IPv4 echo pkt */
1085 0 : if (sd == bvrf->bg_echo) {
1086 0 : struct timeval time_sent = {0, 0};
1087 :
1088 0 : time_sent.tv_sec = be64toh(bep->time_sent_sec);
1089 0 : time_sent.tv_usec = be64toh(bep->time_sent_usec);
1090 0 : *my_rtt = monotime_since(&time_sent, NULL);
1091 : }
1092 : #endif
1093 :
1094 : return 0;
1095 : }
1096 :
1097 : #ifdef BFD_LINUX
1098 : /*
1099 : * send a bfd packet with src/dst same IP so that the peer will receive
1100 : * the packet and forward it back to sender in the forwarding plane
1101 : */
1102 0 : int bp_udp_send_fp(int sd, uint8_t *data, size_t datalen,
1103 : struct bfd_session *bfd)
1104 : {
1105 0 : ssize_t wlen;
1106 0 : struct msghdr msg = {0};
1107 0 : struct iovec iov[1];
1108 0 : uint8_t msgctl[255];
1109 0 : struct sockaddr_ll sadr_ll = {0};
1110 :
1111 0 : sadr_ll.sll_ifindex = bfd->ifp->ifindex;
1112 0 : sadr_ll.sll_halen = ETH_ALEN;
1113 0 : memcpy(sadr_ll.sll_addr, bfd->peer_hw_addr, sizeof(bfd->peer_hw_addr));
1114 0 : sadr_ll.sll_protocol = htons(ETH_P_IP);
1115 :
1116 : /* Prepare message data. */
1117 0 : iov[0].iov_base = data;
1118 0 : iov[0].iov_len = datalen;
1119 :
1120 0 : memset(msgctl, 0, sizeof(msgctl));
1121 0 : msg.msg_name = &sadr_ll;
1122 0 : msg.msg_namelen = sizeof(sadr_ll);
1123 0 : msg.msg_iov = iov;
1124 0 : msg.msg_iovlen = 1;
1125 :
1126 : /* Send echo to peer */
1127 0 : wlen = sendmsg(sd, &msg, 0);
1128 :
1129 0 : if (wlen <= 0) {
1130 0 : if (bglobal.debug_network)
1131 0 : zlog_debug("%s: loopback failure: (%d) %s", __func__,
1132 : errno, strerror(errno));
1133 0 : return -1;
1134 0 : } else if (wlen < (ssize_t)datalen) {
1135 0 : if (bglobal.debug_network)
1136 0 : zlog_debug("%s: partial send: %zd expected %zu",
1137 : __func__, wlen, datalen);
1138 0 : return -1;
1139 : }
1140 :
1141 : return 0;
1142 : }
1143 : #endif
1144 :
1145 0 : int bp_udp_send(int sd, uint8_t ttl, uint8_t *data, size_t datalen,
1146 : struct sockaddr *to, socklen_t tolen)
1147 : {
1148 0 : struct cmsghdr *cmsg;
1149 0 : ssize_t wlen;
1150 0 : int ttlval = ttl;
1151 0 : bool is_ipv6 = to->sa_family == AF_INET6;
1152 0 : struct msghdr msg;
1153 0 : struct iovec iov[1];
1154 0 : uint8_t msgctl[255];
1155 :
1156 : /* Prepare message data. */
1157 0 : iov[0].iov_base = data;
1158 0 : iov[0].iov_len = datalen;
1159 :
1160 0 : memset(&msg, 0, sizeof(msg));
1161 0 : memset(msgctl, 0, sizeof(msgctl));
1162 0 : msg.msg_name = to;
1163 0 : msg.msg_namelen = tolen;
1164 0 : msg.msg_iov = iov;
1165 0 : msg.msg_iovlen = 1;
1166 :
1167 : /* Prepare the packet TTL information. */
1168 0 : if (ttl > 0) {
1169 : /* Use ancillary data. */
1170 0 : msg.msg_control = msgctl;
1171 0 : msg.msg_controllen = CMSG_LEN(sizeof(ttlval));
1172 :
1173 : /* Configure the ancillary data. */
1174 0 : cmsg = CMSG_FIRSTHDR(&msg);
1175 0 : cmsg->cmsg_len = CMSG_LEN(sizeof(ttlval));
1176 0 : if (is_ipv6) {
1177 0 : cmsg->cmsg_level = IPPROTO_IPV6;
1178 0 : cmsg->cmsg_type = IPV6_HOPLIMIT;
1179 : } else {
1180 : #ifdef BFD_LINUX
1181 0 : cmsg->cmsg_level = IPPROTO_IP;
1182 0 : cmsg->cmsg_type = IP_TTL;
1183 : #else
1184 : /* FreeBSD does not support TTL in ancillary data. */
1185 : msg.msg_control = NULL;
1186 : msg.msg_controllen = 0;
1187 :
1188 : bp_set_ttl(sd, ttl);
1189 : #endif /* BFD_BSD */
1190 : }
1191 0 : memcpy(CMSG_DATA(cmsg), &ttlval, sizeof(ttlval));
1192 : }
1193 :
1194 : /* Send echo back. */
1195 0 : wlen = sendmsg(sd, &msg, 0);
1196 0 : if (wlen <= 0) {
1197 0 : if (bglobal.debug_network)
1198 0 : zlog_debug("%s: loopback failure: (%d) %s", __func__,
1199 : errno, strerror(errno));
1200 0 : return -1;
1201 0 : } else if (wlen < (ssize_t)datalen) {
1202 0 : if (bglobal.debug_network)
1203 0 : zlog_debug("%s: partial send: %zd expected %zu",
1204 : __func__, wlen, datalen);
1205 0 : return -1;
1206 : }
1207 :
1208 : return 0;
1209 : }
1210 :
1211 :
1212 : /*
1213 : * Sockets creation.
1214 : */
1215 :
1216 :
1217 : /*
1218 : * IPv4 sockets
1219 : */
1220 32 : int bp_set_ttl(int sd, uint8_t value)
1221 : {
1222 32 : int ttl = value;
1223 :
1224 32 : if (setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
1225 0 : zlog_warn("%s: setsockopt(IP_TTL, %d): %s", __func__, value,
1226 : strerror(errno));
1227 0 : return -1;
1228 : }
1229 :
1230 : return 0;
1231 : }
1232 :
1233 16 : int bp_set_tos(int sd, uint8_t value)
1234 : {
1235 16 : int tos = value;
1236 :
1237 16 : if (setsockopt(sd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1) {
1238 0 : zlog_warn("%s: setsockopt(IP_TOS, %d): %s", __func__, value,
1239 : strerror(errno));
1240 0 : return -1;
1241 : }
1242 :
1243 : return 0;
1244 : }
1245 :
1246 40 : static bool bp_set_reuse_addr(int sd)
1247 : {
1248 40 : int one = 1;
1249 :
1250 40 : if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
1251 0 : zlog_warn("%s: setsockopt(SO_REUSEADDR, %d): %s", __func__, one,
1252 : strerror(errno));
1253 0 : return false;
1254 : }
1255 : return true;
1256 : }
1257 :
1258 40 : static bool bp_set_reuse_port(int sd)
1259 : {
1260 40 : int one = 1;
1261 :
1262 40 : if (setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) == -1) {
1263 0 : zlog_warn("%s: setsockopt(SO_REUSEPORT, %d): %s", __func__, one,
1264 : strerror(errno));
1265 0 : return false;
1266 : }
1267 : return true;
1268 : }
1269 :
1270 :
1271 16 : static void bp_set_ipopts(int sd)
1272 : {
1273 16 : int rcvttl = BFD_RCV_TTL_VAL;
1274 :
1275 16 : if (!bp_set_reuse_addr(sd))
1276 0 : zlog_fatal("set-reuse-addr: failed");
1277 :
1278 16 : if (!bp_set_reuse_port(sd))
1279 0 : zlog_fatal("set-reuse-port: failed");
1280 :
1281 16 : if (bp_set_ttl(sd, BFD_TTL_VAL) != 0)
1282 0 : zlog_fatal("set-ipopts: TTL configuration failed");
1283 :
1284 16 : if (setsockopt(sd, IPPROTO_IP, IP_RECVTTL, &rcvttl, sizeof(rcvttl))
1285 : == -1)
1286 0 : zlog_fatal("set-ipopts: setsockopt(IP_RECVTTL, %d): %s", rcvttl,
1287 : strerror(errno));
1288 :
1289 : #ifdef BFD_LINUX
1290 16 : int pktinfo = BFD_PKT_INFO_VAL;
1291 :
1292 : /* Figure out address and interface to do the peer matching. */
1293 16 : if (setsockopt(sd, IPPROTO_IP, IP_PKTINFO, &pktinfo, sizeof(pktinfo))
1294 : == -1)
1295 0 : zlog_fatal("set-ipopts: setsockopt(IP_PKTINFO, %d): %s",
1296 : pktinfo, strerror(errno));
1297 : #endif /* BFD_LINUX */
1298 : #ifdef BFD_BSD
1299 : int yes = 1;
1300 :
1301 : /* Find out our address for peer matching. */
1302 : if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) == -1)
1303 : zlog_fatal("set-ipopts: setsockopt(IP_RECVDSTADDR, %d): %s",
1304 : yes, strerror(errno));
1305 :
1306 : /* Find out interface where the packet came in. */
1307 : if (setsockopt_ifindex(AF_INET, sd, yes) == -1)
1308 : zlog_fatal("set-ipopts: setsockopt_ipv4_ifindex(%d): %s", yes,
1309 : strerror(errno));
1310 : #endif /* BFD_BSD */
1311 16 : }
1312 :
1313 16 : static void bp_bind_ip(int sd, uint16_t port)
1314 : {
1315 16 : struct sockaddr_in sin;
1316 :
1317 16 : memset(&sin, 0, sizeof(sin));
1318 16 : sin.sin_family = AF_INET;
1319 16 : sin.sin_addr.s_addr = htonl(INADDR_ANY);
1320 16 : sin.sin_port = htons(port);
1321 16 : if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
1322 0 : zlog_fatal("bind-ip: bind: %s", strerror(errno));
1323 16 : }
1324 :
1325 8 : int bp_udp_shop(const struct vrf *vrf)
1326 : {
1327 8 : int sd;
1328 :
1329 8 : frr_with_privs(&bglobal.bfdd_privs) {
1330 8 : sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1331 8 : vrf->name);
1332 : }
1333 8 : if (sd == -1)
1334 0 : zlog_fatal("udp-shop: socket: %s", strerror(errno));
1335 :
1336 8 : bp_set_ipopts(sd);
1337 8 : bp_bind_ip(sd, BFD_DEFDESTPORT);
1338 8 : return sd;
1339 : }
1340 :
1341 8 : int bp_udp_mhop(const struct vrf *vrf)
1342 : {
1343 8 : int sd;
1344 :
1345 8 : frr_with_privs(&bglobal.bfdd_privs) {
1346 8 : sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1347 8 : vrf->name);
1348 : }
1349 8 : if (sd == -1)
1350 0 : zlog_fatal("udp-mhop: socket: %s", strerror(errno));
1351 :
1352 8 : bp_set_ipopts(sd);
1353 8 : bp_bind_ip(sd, BFD_DEF_MHOP_DEST_PORT);
1354 :
1355 8 : return sd;
1356 : }
1357 :
1358 16 : int bp_peer_socket(const struct bfd_session *bs)
1359 : {
1360 16 : int sd, pcount;
1361 16 : struct sockaddr_in sin;
1362 16 : static int srcPort = BFD_SRCPORTINIT;
1363 16 : const char *device_to_bind = NULL;
1364 :
1365 16 : if (bs->key.ifname[0])
1366 16 : device_to_bind = (const char *)bs->key.ifname;
1367 0 : else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1368 0 : || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1369 0 : && bs->key.vrfname[0])))
1370 0 : device_to_bind = (const char *)bs->key.vrfname;
1371 :
1372 16 : frr_with_privs(&bglobal.bfdd_privs) {
1373 16 : sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,
1374 16 : bs->vrf->vrf_id, device_to_bind);
1375 : }
1376 16 : if (sd == -1) {
1377 0 : zlog_err("ipv4-new: failed to create socket: %s",
1378 : strerror(errno));
1379 0 : return -1;
1380 : }
1381 :
1382 : /* Set TTL to 255 for all transmitted packets */
1383 16 : if (bp_set_ttl(sd, BFD_TTL_VAL) != 0) {
1384 0 : close(sd);
1385 0 : return -1;
1386 : }
1387 :
1388 : /* Set TOS to CS6 for all transmitted packets */
1389 16 : if (bp_set_tos(sd, BFD_TOS_VAL) != 0) {
1390 0 : close(sd);
1391 0 : return -1;
1392 : }
1393 :
1394 : /* Find an available source port in the proper range */
1395 16 : memset(&sin, 0, sizeof(sin));
1396 16 : sin.sin_family = AF_INET;
1397 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1398 : sin.sin_len = sizeof(sin);
1399 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1400 16 : memcpy(&sin.sin_addr, &bs->key.local, sizeof(sin.sin_addr));
1401 :
1402 16 : pcount = 0;
1403 16 : do {
1404 16 : if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1405 : /* Searched all ports, none available */
1406 0 : zlog_err("ipv4-new: failed to bind port: %s",
1407 : strerror(errno));
1408 0 : close(sd);
1409 0 : return -1;
1410 : }
1411 16 : if (srcPort >= BFD_SRCPORTMAX)
1412 0 : srcPort = BFD_SRCPORTINIT;
1413 16 : sin.sin_port = htons(srcPort++);
1414 16 : } while (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0);
1415 :
1416 : return sd;
1417 : }
1418 :
1419 :
1420 : /*
1421 : * IPv6 sockets
1422 : */
1423 :
1424 0 : int bp_peer_socketv6(const struct bfd_session *bs)
1425 : {
1426 0 : int sd, pcount;
1427 0 : struct sockaddr_in6 sin6;
1428 0 : static int srcPort = BFD_SRCPORTINIT;
1429 0 : const char *device_to_bind = NULL;
1430 :
1431 0 : if (bs->key.ifname[0])
1432 0 : device_to_bind = (const char *)bs->key.ifname;
1433 0 : else if ((!vrf_is_backend_netns() && bs->vrf->vrf_id != VRF_DEFAULT)
1434 0 : || ((CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
1435 0 : && bs->key.vrfname[0])))
1436 0 : device_to_bind = (const char *)bs->key.vrfname;
1437 :
1438 0 : frr_with_privs(&bglobal.bfdd_privs) {
1439 0 : sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,
1440 0 : bs->vrf->vrf_id, device_to_bind);
1441 : }
1442 0 : if (sd == -1) {
1443 0 : zlog_err("ipv6-new: failed to create socket: %s",
1444 : strerror(errno));
1445 0 : return -1;
1446 : }
1447 :
1448 : /* Set TTL to 255 for all transmitted packets */
1449 0 : if (bp_set_ttlv6(sd, BFD_TTL_VAL) != 0) {
1450 0 : close(sd);
1451 0 : return -1;
1452 : }
1453 :
1454 : /* Set TOS to CS6 for all transmitted packets */
1455 0 : if (bp_set_tosv6(sd, BFD_TOS_VAL) != 0) {
1456 0 : close(sd);
1457 0 : return -1;
1458 : }
1459 :
1460 : /* Find an available source port in the proper range */
1461 0 : memset(&sin6, 0, sizeof(sin6));
1462 0 : sin6.sin6_family = AF_INET6;
1463 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1464 : sin6.sin6_len = sizeof(sin6);
1465 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1466 0 : memcpy(&sin6.sin6_addr, &bs->key.local, sizeof(sin6.sin6_addr));
1467 0 : if (bs->ifp && IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr))
1468 0 : sin6.sin6_scope_id = bs->ifp->ifindex;
1469 :
1470 : pcount = 0;
1471 0 : do {
1472 0 : if ((++pcount) > (BFD_SRCPORTMAX - BFD_SRCPORTINIT)) {
1473 : /* Searched all ports, none available */
1474 0 : zlog_err("ipv6-new: failed to bind port: %s",
1475 : strerror(errno));
1476 0 : close(sd);
1477 0 : return -1;
1478 : }
1479 0 : if (srcPort >= BFD_SRCPORTMAX)
1480 0 : srcPort = BFD_SRCPORTINIT;
1481 0 : sin6.sin6_port = htons(srcPort++);
1482 0 : } while (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0);
1483 :
1484 : return sd;
1485 : }
1486 :
1487 24 : int bp_set_ttlv6(int sd, uint8_t value)
1488 : {
1489 24 : int ttl = value;
1490 :
1491 24 : if (setsockopt(sd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl))
1492 : == -1) {
1493 0 : zlog_warn("set-ttlv6: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1494 : value, strerror(errno));
1495 0 : return -1;
1496 : }
1497 :
1498 : return 0;
1499 : }
1500 :
1501 0 : int bp_set_tosv6(int sd, uint8_t value)
1502 : {
1503 0 : int tos = value;
1504 :
1505 0 : if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos))
1506 : == -1) {
1507 0 : zlog_warn("set-tosv6: setsockopt(IPV6_TCLASS, %d): %s", value,
1508 : strerror(errno));
1509 0 : return -1;
1510 : }
1511 :
1512 : return 0;
1513 : }
1514 :
1515 24 : static void bp_set_ipv6opts(int sd)
1516 : {
1517 24 : int ipv6_pktinfo = BFD_IPV6_PKT_INFO_VAL;
1518 24 : int ipv6_only = BFD_IPV6_ONLY_VAL;
1519 :
1520 24 : if (!bp_set_reuse_addr(sd))
1521 0 : zlog_fatal("set-reuse-addr: failed");
1522 :
1523 24 : if (!bp_set_reuse_port(sd))
1524 0 : zlog_fatal("set-reuse-port: failed");
1525 :
1526 24 : if (bp_set_ttlv6(sd, BFD_TTL_VAL) == -1)
1527 0 : zlog_fatal(
1528 : "set-ipv6opts: setsockopt(IPV6_UNICAST_HOPS, %d): %s",
1529 : BFD_TTL_VAL, strerror(errno));
1530 :
1531 24 : if (setsockopt_ipv6_hoplimit(sd, BFD_RCV_TTL_VAL) == -1)
1532 0 : zlog_fatal("set-ipv6opts: setsockopt(IPV6_HOPLIMIT, %d): %s",
1533 : BFD_RCV_TTL_VAL, strerror(errno));
1534 :
1535 24 : if (setsockopt_ipv6_pktinfo(sd, ipv6_pktinfo) == -1)
1536 0 : zlog_fatal("set-ipv6opts: setsockopt(IPV6_PKTINFO, %d): %s",
1537 : ipv6_pktinfo, strerror(errno));
1538 :
1539 24 : if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only,
1540 : sizeof(ipv6_only))
1541 : == -1)
1542 0 : zlog_fatal("set-ipv6opts: setsockopt(IPV6_V6ONLY, %d): %s",
1543 : ipv6_only, strerror(errno));
1544 24 : }
1545 :
1546 24 : static void bp_bind_ipv6(int sd, uint16_t port)
1547 : {
1548 24 : struct sockaddr_in6 sin6;
1549 :
1550 24 : memset(&sin6, 0, sizeof(sin6));
1551 24 : sin6.sin6_family = AF_INET6;
1552 24 : sin6.sin6_addr = in6addr_any;
1553 24 : sin6.sin6_port = htons(port);
1554 : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1555 : sin6.sin6_len = sizeof(sin6);
1556 : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
1557 24 : if (bind(sd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1558 0 : zlog_fatal("bind-ipv6: bind: %s", strerror(errno));
1559 24 : }
1560 :
1561 8 : int bp_udp6_shop(const struct vrf *vrf)
1562 : {
1563 8 : int sd;
1564 :
1565 8 : frr_with_privs(&bglobal.bfdd_privs) {
1566 8 : sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1567 8 : vrf->name);
1568 : }
1569 8 : if (sd == -1) {
1570 0 : if (errno != EAFNOSUPPORT)
1571 0 : zlog_fatal("udp6-shop: socket: %s", strerror(errno));
1572 : else
1573 0 : zlog_warn("udp6-shop: V6 is not supported, continuing");
1574 :
1575 0 : return -1;
1576 : }
1577 :
1578 8 : bp_set_ipv6opts(sd);
1579 8 : bp_bind_ipv6(sd, BFD_DEFDESTPORT);
1580 :
1581 8 : return sd;
1582 : }
1583 :
1584 8 : int bp_udp6_mhop(const struct vrf *vrf)
1585 : {
1586 8 : int sd;
1587 :
1588 8 : frr_with_privs(&bglobal.bfdd_privs) {
1589 8 : sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id,
1590 8 : vrf->name);
1591 : }
1592 8 : if (sd == -1) {
1593 0 : if (errno != EAFNOSUPPORT)
1594 0 : zlog_fatal("udp6-mhop: socket: %s", strerror(errno));
1595 : else
1596 0 : zlog_warn("udp6-mhop: V6 is not supported, continuing");
1597 :
1598 0 : return -1;
1599 : }
1600 :
1601 8 : bp_set_ipv6opts(sd);
1602 8 : bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT);
1603 :
1604 8 : return sd;
1605 : }
1606 :
1607 : #ifdef BFD_LINUX
1608 : /* tcpdump -dd udp dst port 3785 */
1609 : struct sock_filter my_filterudp[] = {
1610 : {0x28, 0, 0, 0x0000000c}, {0x15, 0, 8, 0x00000800},
1611 : {0x30, 0, 0, 0x00000017}, {0x15, 0, 6, 0x00000011},
1612 : {0x28, 0, 0, 0x00000014}, {0x45, 4, 0, 0x00001fff},
1613 : {0xb1, 0, 0, 0x0000000e}, {0x48, 0, 0, 0x00000010},
1614 : {0x15, 0, 1, 0x00000ec9}, {0x6, 0, 0, 0x00040000},
1615 : {0x6, 0, 0, 0x00000000},
1616 : };
1617 :
1618 : #define MY_FILTER_LENGTH 11
1619 :
1620 8 : int bp_echo_socket(const struct vrf *vrf)
1621 : {
1622 8 : int s;
1623 :
1624 8 : frr_with_privs (&bglobal.bfdd_privs) {
1625 8 : s = vrf_socket(AF_PACKET, SOCK_RAW, ETH_P_IP, vrf->vrf_id,
1626 8 : vrf->name);
1627 : }
1628 :
1629 8 : if (s == -1)
1630 0 : zlog_fatal("echo-socket: socket: %s", strerror(errno));
1631 :
1632 8 : struct sock_fprog pf;
1633 8 : struct sockaddr_ll sll = {0};
1634 :
1635 : /* adjust filter for socket to only receive ECHO packets */
1636 8 : pf.filter = my_filterudp;
1637 8 : pf.len = MY_FILTER_LENGTH;
1638 8 : if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf)) ==
1639 : -1) {
1640 0 : zlog_warn("%s: setsockopt(SO_ATTACH_FILTER): %s", __func__,
1641 : strerror(errno));
1642 0 : close(s);
1643 0 : return -1;
1644 : }
1645 :
1646 8 : memset(&sll, 0, sizeof(sll));
1647 8 : sll.sll_family = AF_PACKET;
1648 8 : sll.sll_protocol = htons(ETH_P_IP);
1649 8 : sll.sll_ifindex = 0;
1650 8 : if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
1651 0 : zlog_warn("Failed to bind echo socket: %s",
1652 : safe_strerror(errno));
1653 0 : close(s);
1654 0 : return -1;
1655 : }
1656 :
1657 : return s;
1658 : }
1659 : #else
1660 : int bp_echo_socket(const struct vrf *vrf)
1661 : {
1662 : int s;
1663 :
1664 : frr_with_privs(&bglobal.bfdd_privs) {
1665 : s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
1666 : }
1667 : if (s == -1)
1668 : zlog_fatal("echo-socket: socket: %s", strerror(errno));
1669 :
1670 : bp_set_ipopts(s);
1671 : bp_bind_ip(s, BFD_DEF_ECHO_PORT);
1672 :
1673 : return s;
1674 : }
1675 : #endif
1676 :
1677 8 : int bp_echov6_socket(const struct vrf *vrf)
1678 : {
1679 8 : int s;
1680 :
1681 8 : frr_with_privs(&bglobal.bfdd_privs) {
1682 8 : s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name);
1683 : }
1684 8 : if (s == -1) {
1685 0 : if (errno != EAFNOSUPPORT)
1686 0 : zlog_fatal("echov6-socket: socket: %s",
1687 : strerror(errno));
1688 : else
1689 0 : zlog_warn("echov6-socket: V6 is not supported, continuing");
1690 :
1691 0 : return -1;
1692 : }
1693 :
1694 8 : bp_set_ipv6opts(s);
1695 8 : bp_bind_ipv6(s, BFD_DEF_ECHO_PORT);
1696 :
1697 8 : return s;
1698 : }
1699 :
1700 : #ifdef BFD_LINUX
1701 : /* get peer's mac address to be used with Echo packets when they are looped in
1702 : * peers forwarding plane
1703 : */
1704 25 : void bfd_peer_mac_set(int sd, struct bfd_session *bfd,
1705 : struct sockaddr_any *peer, struct interface *ifp)
1706 : {
1707 25 : struct arpreq arpreq_;
1708 :
1709 25 : if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET))
1710 17 : return;
1711 8 : if (ifp->flags & IFF_NOARP)
1712 : return;
1713 :
1714 8 : if (peer->sa_sin.sin_family == AF_INET) {
1715 : /* IPV4 */
1716 8 : struct sockaddr_in *addr =
1717 : (struct sockaddr_in *)&arpreq_.arp_pa;
1718 :
1719 8 : memset(&arpreq_, 0, sizeof(struct arpreq));
1720 8 : addr->sin_family = AF_INET;
1721 8 : memcpy(&addr->sin_addr.s_addr, &peer->sa_sin.sin_addr,
1722 : sizeof(addr->sin_addr));
1723 8 : strlcpy(arpreq_.arp_dev, ifp->name, sizeof(arpreq_.arp_dev));
1724 :
1725 8 : if (ioctl(sd, SIOCGARP, &arpreq_) < 0) {
1726 0 : zlog_warn(
1727 : "BFD: getting peer's mac on %s failed error %s",
1728 : ifp->name, strerror(errno));
1729 0 : UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1730 0 : memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));
1731 :
1732 : } else {
1733 8 : memcpy(bfd->peer_hw_addr, arpreq_.arp_ha.sa_data,
1734 : sizeof(bfd->peer_hw_addr));
1735 8 : SET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
1736 : }
1737 : }
1738 : }
1739 : #endif
|