4 * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
6 * FRR is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * FRR is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with FRR; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 #include <net/if_types.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
38 static const char *sockaddr_to_string(const void *sv
, char *buf
, size_t buflen
);
43 static const char *sockaddr_to_string(const void *sv
, char *buf
, size_t buflen
)
45 const struct sockaddr
*sa
= sv
;
46 const struct sockaddr_in
*sin
= sv
;
47 const struct sockaddr_in6
*sin6
= sv
;
50 switch (sa
->sa_family
) {
52 if (inet_ntop(AF_INET
, &sin
->sin_addr
, buf
, buflen
) != NULL
)
57 if (inet_ntop(AF_INET6
, &sin6
->sin6_addr
, buf
, buflen
) != NULL
)
64 snprintf(buf
, buflen
, "unknown (af=%d)", sa
->sa_family
);
68 int ptm_bfd_fetch_ifindex(const char *ifname
)
70 return if_nametoindex(ifname
);
73 void ptm_bfd_fetch_local_mac(const char *ifname
, uint8_t *mac
)
75 struct ifaddrs
*ifap
, *ifa
;
77 struct sockaddr_dl
*sdl
;
80 /* Always clean the target, zeroed macs mean failure. */
81 memset(mac
, 0, ETHERNET_ADDRESS_LENGTH
);
83 if (getifaddrs(&ifap
) != 0)
86 for (ifa
= ifap
; ifa
!= NULL
; ifa
= ifa
->ifa_next
) {
87 /* Find interface with that name. */
88 if (strcmp(ifa
->ifa_name
, ifname
) != 0)
90 /* Skip non link addresses. We want the MAC address. */
91 if (ifa
->ifa_addr
->sa_family
!= AF_LINK
)
94 sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
95 ifi
= (struct if_data
*)ifa
->ifa_data
;
96 /* Skip non ethernet related data. */
97 if (ifi
->ifi_type
!= IFT_ETHER
)
100 if (sdl
->sdl_alen
!= ETHERNET_ADDRESS_LENGTH
)
101 log_warning("%s:%d mac address length %d (expected %d)",
102 __func__
, __LINE__
, sdl
->sdl_alen
,
103 ETHERNET_ADDRESS_LENGTH
);
105 maclen
= (sdl
->sdl_alen
> ETHERNET_ADDRESS_LENGTH
)
106 ? ETHERNET_ADDRESS_LENGTH
108 memcpy(mac
, LLADDR(sdl
), maclen
);
116 /* Was _fetch_portname_from_ifindex() */
117 void fetch_portname_from_ifindex(int ifindex
, char *ifname
, size_t ifnamelen
)
119 char ifname_tmp
[IF_NAMESIZE
];
121 /* Set ifname to empty to signalize failures. */
122 memset(ifname
, 0, ifnamelen
);
124 if (if_indextoname(ifindex
, ifname_tmp
) == NULL
)
127 if (strlcpy(ifname
, ifname_tmp
, ifnamelen
) > ifnamelen
)
128 log_warning("%s:%d interface name truncated", __func__
,
132 int ptm_bfd_echo_sock_init(void)
135 struct sockaddr_in sin
;
137 s
= socket(AF_INET
, SOCK_DGRAM
, PF_UNSPEC
);
139 log_error("echo-socket: creation failed: %s", strerror(errno
));
143 memset(&sin
, 0, sizeof(sin
));
144 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
145 /* OmniOS doesn't have this field, but uses this code. */
146 sin
.sin_len
= sizeof(sin
);
147 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
148 sin
.sin_family
= AF_INET
;
149 sin
.sin_port
= htons(3785);
150 if (bind(s
, (struct sockaddr
*)&sin
, sizeof(sin
)) == -1) {
151 log_error("echo-socket: bind failure: %s", strerror(errno
));
156 if (setsockopt(s
, IPPROTO_IP
, IP_RECVTTL
, &yes
, sizeof(yes
)) == -1) {
157 log_error("echo-socket: setsockopt(IP_RECVTTL): %s",
164 if (setsockopt(s
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
)) == -1) {
165 log_error("echo-socket: setsockopt(IP_TTL): %s",
174 ssize_t
bsd_echo_sock_read(int sd
, uint8_t *buf
, ssize_t
*buflen
,
175 struct sockaddr_storage
*ss
, socklen_t
*sslen
,
176 uint8_t *ttl
, uint32_t *id
)
178 struct cmsghdr
*cmsg
;
179 struct bfd_echo_pkt
*bep
;
186 /* Prepare socket read. */
187 memset(ss
, 0, sizeof(*ss
));
188 memset(&msg
, 0, sizeof(msg
));
190 iov
.iov_len
= *buflen
;
193 msg
.msg_control
= msgctl
;
194 msg
.msg_controllen
= sizeof(msgctl
);
196 msg
.msg_namelen
= *sslen
;
198 /* Read the socket and treat errors. */
199 readlen
= recvmsg(sd
, &msg
, 0);
201 log_error("%s: recvmsg: socket closed", __func__
);
205 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
)
208 log_error("%s: recvmsg: (%d) %s", __func__
, errno
,
212 /* Short packet, better not risk reading it. */
213 if (readlen
< (ssize_t
)sizeof(*bep
)) {
214 log_warning("%s: short packet (%ld of %d) from %s", __func__
,
215 readlen
, sizeof(*bep
),
216 sockaddr_to_string(ss
, errbuf
, sizeof(errbuf
)));
221 /* Read TTL information. */
223 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
!= NULL
;
224 cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
225 if (cmsg
->cmsg_level
!= IPPROTO_IP
)
227 if (cmsg
->cmsg_type
!= IP_RECVTTL
)
230 *ttl
= *(uint8_t *)CMSG_DATA(cmsg
);
234 log_debug("%s: failed to read TTL", __func__
);
238 /* Read my discriminator from BFD Echo packet. */
239 bep
= (struct bfd_echo_pkt
*)buf
;
242 log_debug("%s: invalid packet discriminator from: %s", __func__
,
243 sockaddr_to_string(ss
, errbuf
, sizeof(errbuf
)));
247 /* Set the returned sockaddr new length. */
248 *sslen
= msg
.msg_namelen
;
253 int ptm_bfd_vxlan_sock_init(void)
255 /* TODO: not supported yet. */
259 int bp_bind_dev(int sd
, const char *dev
)
262 * *BSDs don't support `SO_BINDTODEVICE`, instead you must
263 * manually specify the main address of the interface or use
264 * BPF on the socket descriptor.
269 uint16_t udp4_checksum(struct ip
*ip
, uint8_t *buf
, int len
)
272 struct udp_psuedo_header pudp_hdr
;
275 pudp_hdr
.saddr
= ip
->ip_src
.s_addr
;
276 pudp_hdr
.daddr
= ip
->ip_dst
.s_addr
;
277 pudp_hdr
.reserved
= 0;
278 pudp_hdr
.protocol
= ip
->ip_p
;
279 pudp_hdr
.len
= htons(len
);
281 ptr
= XMALLOC(MTYPE_BFDD_TMP
, UDP_PSUEDO_HDR_LEN
+ len
);
282 memcpy(ptr
, &pudp_hdr
, UDP_PSUEDO_HDR_LEN
);
283 memcpy(ptr
+ UDP_PSUEDO_HDR_LEN
, buf
, len
);
285 csum
= checksum((uint16_t *)ptr
, UDP_PSUEDO_HDR_LEN
+ len
);
286 XFREE(MTYPE_BFDD_TMP
, ptr
);