]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/bsd.c
Merge branch 'master' into bfd-final
[mirror_frr.git] / bfdd / bsd.c
1 /*
2 * *BSD specific code
3 *
4 * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
5 *
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
9 * later version.
10 *
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.
15 *
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
19 * 02111-1307, USA.
20 */
21
22 #include <zebra.h>
23
24 #ifdef BFD_BSD
25
26 #include <net/if.h>
27 #include <net/if_types.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #include <ifaddrs.h>
32
33 #include "bfd.h"
34
35 /*
36 * Prototypes
37 */
38 static const char *sockaddr_to_string(const void *sv, char *buf, size_t buflen);
39
40 /*
41 * Definitions.
42 */
43 static const char *sockaddr_to_string(const void *sv, char *buf, size_t buflen)
44 {
45 const struct sockaddr *sa = sv;
46 const struct sockaddr_in *sin = sv;
47 const struct sockaddr_in6 *sin6 = sv;
48 int unknown = 1;
49
50 switch (sa->sa_family) {
51 case AF_INET:
52 if (inet_ntop(AF_INET, &sin->sin_addr, buf, buflen) != NULL)
53 unknown = 0;
54 break;
55
56 case AF_INET6:
57 if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, buflen) != NULL)
58 unknown = 0;
59 break;
60 }
61 if (unknown == 0)
62 return buf;
63
64 snprintf(buf, buflen, "unknown (af=%d)", sa->sa_family);
65 return buf;
66 }
67
68 int ptm_bfd_fetch_ifindex(const char *ifname)
69 {
70 return if_nametoindex(ifname);
71 }
72
73 void ptm_bfd_fetch_local_mac(const char *ifname, uint8_t *mac)
74 {
75 struct ifaddrs *ifap, *ifa;
76 struct if_data *ifi;
77 struct sockaddr_dl *sdl;
78 size_t maclen;
79
80 /* Always clean the target, zeroed macs mean failure. */
81 memset(mac, 0, ETHERNET_ADDRESS_LENGTH);
82
83 if (getifaddrs(&ifap) != 0)
84 return;
85
86 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
87 /* Find interface with that name. */
88 if (strcmp(ifa->ifa_name, ifname) != 0)
89 continue;
90 /* Skip non link addresses. We want the MAC address. */
91 if (ifa->ifa_addr->sa_family != AF_LINK)
92 continue;
93
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)
98 continue;
99
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);
104
105 maclen = (sdl->sdl_alen > ETHERNET_ADDRESS_LENGTH)
106 ? ETHERNET_ADDRESS_LENGTH
107 : sdl->sdl_alen;
108 memcpy(mac, LLADDR(sdl), maclen);
109 break;
110 }
111
112 freeifaddrs(ifap);
113 }
114
115
116 /* Was _fetch_portname_from_ifindex() */
117 void fetch_portname_from_ifindex(int ifindex, char *ifname, size_t ifnamelen)
118 {
119 char ifname_tmp[IF_NAMESIZE];
120
121 /* Set ifname to empty to signalize failures. */
122 memset(ifname, 0, ifnamelen);
123
124 if (if_indextoname(ifindex, ifname_tmp) == NULL)
125 return;
126
127 if (strlcpy(ifname, ifname_tmp, ifnamelen) > ifnamelen)
128 log_warning("%s:%d interface name truncated", __func__,
129 __LINE__);
130 }
131
132 int ptm_bfd_echo_sock_init(void)
133 {
134 int s, ttl, yes = 1;
135 struct sockaddr_in sin;
136
137 s = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
138 if (s == -1) {
139 log_error("echo-socket: creation failed: %s", strerror(errno));
140 return -1;
141 }
142
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));
152 close(s);
153 return -1;
154 }
155
156 if (setsockopt(s, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) == -1) {
157 log_error("echo-socket: setsockopt(IP_RECVTTL): %s",
158 strerror(errno));
159 close(s);
160 return -1;
161 }
162
163 ttl = BFD_TTL_VAL;
164 if (setsockopt(s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) == -1) {
165 log_error("echo-socket: setsockopt(IP_TTL): %s",
166 strerror(errno));
167 close(s);
168 return -1;
169 }
170
171 return s;
172 }
173
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)
177 {
178 struct cmsghdr *cmsg;
179 struct bfd_echo_pkt *bep;
180 ssize_t readlen;
181 struct iovec iov;
182 struct msghdr msg;
183 uint8_t msgctl[255];
184 char errbuf[255];
185
186 /* Prepare socket read. */
187 memset(ss, 0, sizeof(*ss));
188 memset(&msg, 0, sizeof(msg));
189 iov.iov_base = buf;
190 iov.iov_len = *buflen;
191 msg.msg_iov = &iov;
192 msg.msg_iovlen = 1;
193 msg.msg_control = msgctl;
194 msg.msg_controllen = sizeof(msgctl);
195 msg.msg_name = ss;
196 msg.msg_namelen = *sslen;
197
198 /* Read the socket and treat errors. */
199 readlen = recvmsg(sd, &msg, 0);
200 if (readlen == 0) {
201 log_error("%s: recvmsg: socket closed", __func__);
202 return -1;
203 }
204 if (readlen == -1) {
205 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
206 return -1;
207
208 log_error("%s: recvmsg: (%d) %s", __func__, errno,
209 strerror(errno));
210 return -1;
211 }
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)));
217 return -1;
218 }
219 *buflen = readlen;
220
221 /* Read TTL information. */
222 *ttl = 0;
223 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
224 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
225 if (cmsg->cmsg_level != IPPROTO_IP)
226 continue;
227 if (cmsg->cmsg_type != IP_RECVTTL)
228 continue;
229
230 *ttl = *(uint8_t *)CMSG_DATA(cmsg);
231 break;
232 }
233 if (*ttl == 0) {
234 log_debug("%s: failed to read TTL", __func__);
235 return -1;
236 }
237
238 /* Read my discriminator from BFD Echo packet. */
239 bep = (struct bfd_echo_pkt *)buf;
240 *id = bep->my_discr;
241 if (*id == 0) {
242 log_debug("%s: invalid packet discriminator from: %s", __func__,
243 sockaddr_to_string(ss, errbuf, sizeof(errbuf)));
244 return -1;
245 }
246
247 /* Set the returned sockaddr new length. */
248 *sslen = msg.msg_namelen;
249
250 return 0;
251 }
252
253 int ptm_bfd_vxlan_sock_init(void)
254 {
255 /* TODO: not supported yet. */
256 return -1;
257 }
258
259 int bp_bind_dev(int sd, const char *dev)
260 {
261 /*
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.
265 */
266 return 0;
267 }
268
269 uint16_t udp4_checksum(struct ip *ip, uint8_t *buf, int len)
270 {
271 char *ptr;
272 struct udp_psuedo_header pudp_hdr;
273 uint16_t csum;
274
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);
280
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);
284
285 csum = checksum((uint16_t *)ptr, UDP_PSUEDO_HDR_LEN + len);
286 XFREE(MTYPE_BFDD_TMP, ptr);
287 return csum;
288 }
289
290 #endif /* BFD_BSD */