]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2012 Inktank | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include "common/pick_address.h" | |
9f95a23c | 16 | |
39ae355f | 17 | #include <bitset> |
9f95a23c | 18 | #include <netdb.h> |
522d829b | 19 | #include <netinet/in.h> |
9f95a23c TL |
20 | #include <string> |
21 | #include <string.h> | |
22 | #include <vector> | |
23 | ||
522d829b | 24 | #include <boost/algorithm/string/predicate.hpp> |
9f95a23c TL |
25 | #include <fmt/format.h> |
26 | ||
7c673cae FG |
27 | #include "include/ipaddr.h" |
28 | #include "include/str_list.h" | |
11fdf7f2 TL |
29 | #include "common/ceph_context.h" |
30 | #ifndef WITH_SEASTAR | |
31 | #include "common/config.h" | |
32 | #include "common/config_obs.h" | |
33 | #endif | |
7c673cae FG |
34 | #include "common/debug.h" |
35 | #include "common/errno.h" | |
11fdf7f2 | 36 | #include "common/numa.h" |
7c673cae | 37 | |
522d829b TL |
38 | #ifndef HAVE_IN_ADDR_T |
39 | typedef uint32_t in_addr_t; | |
40 | #endif | |
41 | ||
42 | #ifndef IN_LOOPBACKNET | |
43 | #define IN_LOOPBACKNET 127 | |
44 | #endif | |
45 | ||
7c673cae FG |
46 | #define dout_subsys ceph_subsys_ |
47 | ||
f67539c2 TL |
48 | using std::string; |
49 | using std::vector; | |
50 | ||
522d829b TL |
51 | namespace { |
52 | ||
53 | bool matches_with_name(const ifaddrs& ifa, const std::string& if_name) | |
54 | { | |
55 | return if_name.compare(ifa.ifa_name) == 0; | |
56 | } | |
57 | ||
58 | static int is_loopback_addr(sockaddr* addr) | |
59 | { | |
60 | if (addr->sa_family == AF_INET) { | |
61 | const sockaddr_in* sin = (struct sockaddr_in *)(addr); | |
62 | const in_addr_t net = ntohl(sin->sin_addr.s_addr) >> IN_CLASSA_NSHIFT; | |
63 | return net == IN_LOOPBACKNET ? 1 : 0; | |
64 | } else if (addr->sa_family == AF_INET6) { | |
65 | sockaddr_in6* sin6 = (struct sockaddr_in6 *)(addr); | |
66 | return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ? 1 : 0; | |
67 | } else { | |
68 | return -1; | |
69 | } | |
70 | } | |
71 | ||
72 | static int grade_addr(const ifaddrs& ifa) | |
73 | { | |
74 | if (ifa.ifa_addr == nullptr) { | |
75 | return -1; | |
76 | } | |
77 | int score = 0; | |
78 | if (ifa.ifa_flags & IFF_UP) { | |
79 | score += 4; | |
80 | } | |
81 | switch (is_loopback_addr(ifa.ifa_addr)) { | |
82 | case 0: | |
83 | // prefer non-loopback addresses | |
84 | score += 2; | |
85 | break; | |
86 | case 1: | |
87 | score += 0; | |
88 | break; | |
89 | default: | |
90 | score = -1; | |
91 | break; | |
92 | } | |
93 | return score; | |
94 | } | |
95 | ||
96 | bool matches_with_net(const ifaddrs& ifa, | |
97 | const sockaddr* net, | |
98 | unsigned int prefix_len, | |
99 | unsigned ipv) | |
100 | { | |
101 | switch (net->sa_family) { | |
102 | case AF_INET: | |
103 | if (ipv & CEPH_PICK_ADDRESS_IPV4) { | |
104 | return matches_ipv4_in_subnet(ifa, (struct sockaddr_in*)net, prefix_len); | |
105 | } | |
106 | break; | |
107 | case AF_INET6: | |
108 | if (ipv & CEPH_PICK_ADDRESS_IPV6) { | |
109 | return matches_ipv6_in_subnet(ifa, (struct sockaddr_in6*)net, prefix_len); | |
110 | } | |
111 | break; | |
112 | } | |
113 | return false; | |
114 | } | |
115 | ||
116 | bool matches_with_net(CephContext *cct, | |
117 | const ifaddrs& ifa, | |
118 | const std::string& s, | |
119 | unsigned ipv) | |
120 | { | |
121 | struct sockaddr_storage net; | |
122 | unsigned int prefix_len; | |
123 | if (!parse_network(s.c_str(), &net, &prefix_len)) { | |
124 | lderr(cct) << "unable to parse network: " << s << dendl; | |
125 | exit(1); | |
126 | } | |
127 | return matches_with_net(ifa, (sockaddr*)&net, prefix_len, ipv); | |
128 | } | |
129 | ||
130 | int grade_with_numa_node(const ifaddrs& ifa, int numa_node) | |
131 | { | |
132 | #if defined(WITH_SEASTAR) || defined(_WIN32) | |
133 | return 0; | |
134 | #else | |
135 | if (numa_node < 0) { | |
136 | return 0; | |
137 | } | |
138 | int if_node = -1; | |
139 | int r = get_iface_numa_node(ifa.ifa_name, &if_node); | |
140 | if (r < 0) { | |
141 | return 0; | |
142 | } | |
143 | return if_node == numa_node ? 1 : 0; | |
144 | #endif | |
145 | } | |
146 | } | |
147 | ||
3efd9988 FG |
148 | const struct sockaddr *find_ip_in_subnet_list( |
149 | CephContext *cct, | |
150 | const struct ifaddrs *ifa, | |
11fdf7f2 | 151 | unsigned ipv, |
3efd9988 | 152 | const std::string &networks, |
11fdf7f2 TL |
153 | const std::string &interfaces, |
154 | int numa_node) | |
7c673cae | 155 | { |
522d829b TL |
156 | const auto ifs = get_str_list(interfaces); |
157 | const auto nets = get_str_list(networks); | |
158 | if (!ifs.empty() && nets.empty()) { | |
3efd9988 FG |
159 | lderr(cct) << "interface names specified but not network names" << dendl; |
160 | exit(1); | |
3efd9988 | 161 | } |
7c673cae | 162 | |
522d829b TL |
163 | int best_score = 0; |
164 | const sockaddr* best_addr = nullptr; | |
165 | for (const auto* addr = ifa; addr != nullptr; addr = addr->ifa_next) { | |
166 | if (!ifs.empty() && | |
167 | std::none_of(std::begin(ifs), std::end(ifs), | |
168 | [&](const auto& if_name) { | |
169 | return matches_with_name(*addr, if_name); | |
170 | })) { | |
171 | continue; | |
3efd9988 | 172 | } |
522d829b TL |
173 | if (!nets.empty() && |
174 | std::none_of(std::begin(nets), std::end(nets), | |
175 | [&](const auto& net) { | |
176 | return matches_with_net(cct, *addr, net, ipv); | |
177 | })) { | |
178 | continue; | |
11fdf7f2 | 179 | } |
522d829b TL |
180 | int score = grade_addr(*addr); |
181 | if (score < 0) { | |
182 | continue; | |
3efd9988 | 183 | } |
522d829b TL |
184 | score += grade_with_numa_node(*addr, numa_node); |
185 | if (score > best_score) { | |
186 | best_score = score; | |
187 | best_addr = addr->ifa_addr; | |
7c673cae | 188 | } |
3efd9988 | 189 | } |
522d829b | 190 | return best_addr; |
7c673cae FG |
191 | } |
192 | ||
11fdf7f2 | 193 | #ifndef WITH_SEASTAR |
7c673cae FG |
194 | // observe this change |
195 | struct Observer : public md_config_obs_t { | |
196 | const char *keys[2]; | |
197 | explicit Observer(const char *c) { | |
198 | keys[0] = c; | |
199 | keys[1] = NULL; | |
200 | } | |
201 | ||
202 | const char** get_tracked_conf_keys() const override { | |
203 | return (const char **)keys; | |
204 | } | |
11fdf7f2 | 205 | void handle_conf_change(const ConfigProxy& conf, |
7c673cae FG |
206 | const std::set <std::string> &changed) override { |
207 | // do nothing. | |
208 | } | |
209 | }; | |
210 | ||
211 | static void fill_in_one_address(CephContext *cct, | |
212 | const struct ifaddrs *ifa, | |
522d829b TL |
213 | const string &networks, |
214 | const string &interfaces, | |
11fdf7f2 TL |
215 | const char *conf_var, |
216 | int numa_node = -1) | |
7c673cae | 217 | { |
11fdf7f2 TL |
218 | const struct sockaddr *found = find_ip_in_subnet_list( |
219 | cct, | |
220 | ifa, | |
221 | CEPH_PICK_ADDRESS_IPV4|CEPH_PICK_ADDRESS_IPV6, | |
222 | networks, | |
223 | interfaces, | |
224 | numa_node); | |
7c673cae | 225 | if (!found) { |
3efd9988 FG |
226 | lderr(cct) << "unable to find any IP address in networks '" << networks |
227 | << "' interfaces '" << interfaces << "'" << dendl; | |
7c673cae FG |
228 | exit(1); |
229 | } | |
230 | ||
231 | char buf[INET6_ADDRSTRLEN]; | |
232 | int err; | |
233 | ||
234 | err = getnameinfo(found, | |
235 | (found->sa_family == AF_INET) | |
236 | ? sizeof(struct sockaddr_in) | |
237 | : sizeof(struct sockaddr_in6), | |
238 | ||
239 | buf, sizeof(buf), | |
11fdf7f2 | 240 | nullptr, 0, |
7c673cae FG |
241 | NI_NUMERICHOST); |
242 | if (err != 0) { | |
243 | lderr(cct) << "unable to convert chosen address to string: " << gai_strerror(err) << dendl; | |
244 | exit(1); | |
245 | } | |
246 | ||
247 | Observer obs(conf_var); | |
248 | ||
11fdf7f2 | 249 | cct->_conf.add_observer(&obs); |
7c673cae | 250 | |
11fdf7f2 TL |
251 | cct->_conf.set_val_or_die(conf_var, buf); |
252 | cct->_conf.apply_changes(nullptr); | |
7c673cae | 253 | |
11fdf7f2 | 254 | cct->_conf.remove_observer(&obs); |
7c673cae FG |
255 | } |
256 | ||
257 | void pick_addresses(CephContext *cct, int needs) | |
258 | { | |
11fdf7f2 TL |
259 | auto public_addr = cct->_conf.get_val<entity_addr_t>("public_addr"); |
260 | auto public_network = cct->_conf.get_val<std::string>("public_network"); | |
261 | auto public_network_interface = | |
262 | cct->_conf.get_val<std::string>("public_network_interface"); | |
263 | auto cluster_addr = cct->_conf.get_val<entity_addr_t>("cluster_addr"); | |
264 | auto cluster_network = cct->_conf.get_val<std::string>("cluster_network"); | |
265 | auto cluster_network_interface = | |
266 | cct->_conf.get_val<std::string>("cluster_network_interface"); | |
267 | ||
522d829b TL |
268 | struct ifaddrs *ifa; |
269 | int r = getifaddrs(&ifa); | |
11fdf7f2 | 270 | if (r < 0) { |
7c673cae FG |
271 | string err = cpp_strerror(errno); |
272 | lderr(cct) << "unable to fetch interfaces and addresses: " << err << dendl; | |
273 | exit(1); | |
274 | } | |
522d829b | 275 | auto free_ifa = make_scope_guard([ifa] { freeifaddrs(ifa); }); |
11fdf7f2 TL |
276 | if ((needs & CEPH_PICK_ADDRESS_PUBLIC) && |
277 | public_addr.is_blank_ip() && !public_network.empty()) { | |
278 | fill_in_one_address(cct, ifa, public_network, public_network_interface, | |
522d829b | 279 | "public_addr"); |
7c673cae FG |
280 | } |
281 | ||
11fdf7f2 TL |
282 | if ((needs & CEPH_PICK_ADDRESS_CLUSTER) && cluster_addr.is_blank_ip()) { |
283 | if (!cluster_network.empty()) { | |
284 | fill_in_one_address(cct, ifa, cluster_network, cluster_network_interface, | |
522d829b | 285 | "cluster_addr"); |
7c673cae | 286 | } else { |
11fdf7f2 | 287 | if (!public_network.empty()) { |
7c673cae FG |
288 | lderr(cct) << "Public network was set, but cluster network was not set " << dendl; |
289 | lderr(cct) << " Using public network also for cluster network" << dendl; | |
11fdf7f2 | 290 | fill_in_one_address(cct, ifa, public_network, public_network_interface, |
522d829b | 291 | "cluster_addr"); |
7c673cae FG |
292 | } |
293 | } | |
294 | } | |
7c673cae | 295 | } |
11fdf7f2 TL |
296 | #endif // !WITH_SEASTAR |
297 | ||
20effc67 | 298 | static std::optional<entity_addr_t> get_one_address( |
11fdf7f2 TL |
299 | CephContext *cct, |
300 | const struct ifaddrs *ifa, | |
301 | unsigned ipv, | |
522d829b TL |
302 | const string &networks, |
303 | const string &interfaces, | |
11fdf7f2 TL |
304 | int numa_node = -1) |
305 | { | |
522d829b TL |
306 | const struct sockaddr *found = find_ip_in_subnet_list(cct, ifa, ipv, |
307 | networks, | |
308 | interfaces, | |
309 | numa_node); | |
11fdf7f2 | 310 | if (!found) { |
20effc67 | 311 | std::string_view ip_type; |
11fdf7f2 TL |
312 | if ((ipv & CEPH_PICK_ADDRESS_IPV4) && (ipv & CEPH_PICK_ADDRESS_IPV6)) { |
313 | ip_type = "IPv4 or IPv6"; | |
314 | } else if (ipv & CEPH_PICK_ADDRESS_IPV4) { | |
315 | ip_type = "IPv4"; | |
316 | } else { | |
317 | ip_type = "IPv6"; | |
318 | } | |
319 | lderr(cct) << "unable to find any " << ip_type << " address in networks '" | |
320 | << networks << "' interfaces '" << interfaces << "'" << dendl; | |
20effc67 | 321 | return {}; |
11fdf7f2 TL |
322 | } |
323 | ||
324 | char buf[INET6_ADDRSTRLEN]; | |
325 | int err; | |
7c673cae | 326 | |
11fdf7f2 TL |
327 | err = getnameinfo(found, |
328 | (found->sa_family == AF_INET) | |
329 | ? sizeof(struct sockaddr_in) | |
330 | : sizeof(struct sockaddr_in6), | |
331 | ||
332 | buf, sizeof(buf), | |
333 | nullptr, 0, | |
334 | NI_NUMERICHOST); | |
335 | if (err != 0) { | |
336 | lderr(cct) << "unable to convert chosen address to string: " << gai_strerror(err) << dendl; | |
20effc67 | 337 | return {}; |
11fdf7f2 TL |
338 | } |
339 | ||
340 | entity_addr_t addr; | |
20effc67 TL |
341 | if (addr.parse(buf)) { |
342 | return addr; | |
343 | } else { | |
344 | return {}; | |
11fdf7f2 | 345 | } |
11fdf7f2 TL |
346 | } |
347 | ||
348 | int pick_addresses( | |
349 | CephContext *cct, | |
350 | unsigned flags, | |
351 | struct ifaddrs *ifa, | |
352 | entity_addrvec_t *addrs, | |
353 | int preferred_numa_node) | |
354 | { | |
355 | addrs->v.clear(); | |
356 | ||
357 | unsigned addrt = (flags & (CEPH_PICK_ADDRESS_PUBLIC | | |
39ae355f | 358 | CEPH_PICK_ADDRESS_PUBLIC_BIND | |
11fdf7f2 | 359 | CEPH_PICK_ADDRESS_CLUSTER)); |
39ae355f TL |
360 | // TODO: move to std::popcount when it's available for all release lines |
361 | // we are interested in (quincy was a blocker at the time of writing) | |
362 | if (std::bitset<sizeof(addrt)*CHAR_BIT>(addrt).count() != 1) { | |
363 | // these flags are mutually exclusive and one of them must be | |
364 | // always set (in other words: it's mode selection). | |
11fdf7f2 TL |
365 | return -EINVAL; |
366 | } | |
367 | unsigned msgrv = flags & (CEPH_PICK_ADDRESS_MSGR1 | | |
368 | CEPH_PICK_ADDRESS_MSGR2); | |
369 | if (msgrv == 0) { | |
370 | if (cct->_conf.get_val<bool>("ms_bind_msgr1")) { | |
371 | msgrv |= CEPH_PICK_ADDRESS_MSGR1; | |
372 | } | |
373 | if (cct->_conf.get_val<bool>("ms_bind_msgr2")) { | |
374 | msgrv |= CEPH_PICK_ADDRESS_MSGR2; | |
375 | } | |
376 | if (msgrv == 0) { | |
377 | return -EINVAL; | |
378 | } | |
379 | } | |
380 | unsigned ipv = flags & (CEPH_PICK_ADDRESS_IPV4 | | |
381 | CEPH_PICK_ADDRESS_IPV6); | |
382 | if (ipv == 0) { | |
383 | if (cct->_conf.get_val<bool>("ms_bind_ipv4")) { | |
384 | ipv |= CEPH_PICK_ADDRESS_IPV4; | |
385 | } | |
386 | if (cct->_conf.get_val<bool>("ms_bind_ipv6")) { | |
387 | ipv |= CEPH_PICK_ADDRESS_IPV6; | |
388 | } | |
389 | if (ipv == 0) { | |
390 | return -EINVAL; | |
391 | } | |
392 | if (cct->_conf.get_val<bool>("ms_bind_prefer_ipv4")) { | |
393 | flags |= CEPH_PICK_ADDRESS_PREFER_IPV4; | |
394 | } else { | |
395 | flags &= ~CEPH_PICK_ADDRESS_PREFER_IPV4; | |
396 | } | |
397 | } | |
398 | ||
399 | entity_addr_t addr; | |
400 | string networks; | |
401 | string interfaces; | |
402 | if (addrt & CEPH_PICK_ADDRESS_PUBLIC) { | |
403 | addr = cct->_conf.get_val<entity_addr_t>("public_addr"); | |
404 | networks = cct->_conf.get_val<std::string>("public_network"); | |
405 | interfaces = | |
406 | cct->_conf.get_val<std::string>("public_network_interface"); | |
39ae355f TL |
407 | } else if (addrt & CEPH_PICK_ADDRESS_PUBLIC_BIND) { |
408 | addr = cct->_conf.get_val<entity_addr_t>("public_bind_addr"); | |
409 | // XXX: we don't support _network nor _network_interface for | |
410 | // the public_bind addrs yet. | |
411 | if (addr.is_blank_ip()) { | |
412 | return -ENOENT; | |
413 | } | |
11fdf7f2 TL |
414 | } else { |
415 | addr = cct->_conf.get_val<entity_addr_t>("cluster_addr"); | |
416 | networks = cct->_conf.get_val<std::string>("cluster_network"); | |
417 | interfaces = | |
418 | cct->_conf.get_val<std::string>("cluster_network_interface"); | |
419 | if (networks.empty()) { | |
420 | lderr(cct) << "Falling back to public interface" << dendl; | |
421 | // fall back to public_ network and interface if cluster is not set | |
422 | networks = cct->_conf.get_val<std::string>("public_network"); | |
423 | interfaces = | |
424 | cct->_conf.get_val<std::string>("public_network_interface"); | |
425 | } | |
426 | } | |
427 | if (addr.is_blank_ip() && | |
428 | !networks.empty()) { | |
522d829b | 429 | // note: pass in ipv to filter the matching addresses |
20effc67 TL |
430 | for (auto pick_mask : {CEPH_PICK_ADDRESS_IPV4, CEPH_PICK_ADDRESS_IPV6}) { |
431 | if (ipv & pick_mask) { | |
432 | auto ip_addr = get_one_address(cct, ifa, pick_mask, | |
433 | networks, interfaces, | |
434 | preferred_numa_node); | |
435 | if (ip_addr) { | |
436 | addrs->v.push_back(*ip_addr); | |
437 | } else { | |
438 | // picked but not found | |
439 | return -1; | |
440 | } | |
441 | } | |
11fdf7f2 TL |
442 | } |
443 | } | |
444 | ||
445 | // note: we may have a blank addr here | |
446 | ||
447 | // ipv4 and/or ipv6? | |
448 | if (addrs->v.empty()) { | |
11fdf7f2 | 449 | addr.set_type(entity_addr_t::TYPE_MSGR2); |
20effc67 TL |
450 | for (auto pick_mask : {CEPH_PICK_ADDRESS_IPV4, CEPH_PICK_ADDRESS_IPV6}) { |
451 | if (ipv & pick_mask) { | |
452 | addr.set_family(pick_mask == CEPH_PICK_ADDRESS_IPV4 ? AF_INET : AF_INET6); | |
453 | addrs->v.push_back(addr); | |
454 | } | |
11fdf7f2 TL |
455 | } |
456 | } | |
457 | ||
20effc67 TL |
458 | std::sort(addrs->v.begin(), addrs->v.end(), |
459 | [flags] (entity_addr_t& lhs, entity_addr_t& rhs) { | |
460 | if (flags & CEPH_PICK_ADDRESS_PREFER_IPV4) { | |
461 | return lhs.is_ipv4() && rhs.is_ipv6(); | |
462 | } else { | |
463 | return lhs.is_ipv6() && rhs.is_ipv4(); | |
464 | } | |
465 | }); | |
466 | ||
11fdf7f2 TL |
467 | // msgr2 or legacy or both? |
468 | if (msgrv == (CEPH_PICK_ADDRESS_MSGR1 | CEPH_PICK_ADDRESS_MSGR2)) { | |
469 | vector<entity_addr_t> v; | |
470 | v.swap(addrs->v); | |
471 | for (auto a : v) { | |
472 | a.set_type(entity_addr_t::TYPE_MSGR2); | |
473 | if (flags & CEPH_PICK_ADDRESS_DEFAULT_MON_PORTS) { | |
474 | a.set_port(CEPH_MON_PORT_IANA); | |
475 | } | |
476 | addrs->v.push_back(a); | |
477 | a.set_type(entity_addr_t::TYPE_LEGACY); | |
478 | if (flags & CEPH_PICK_ADDRESS_DEFAULT_MON_PORTS) { | |
479 | a.set_port(CEPH_MON_PORT_LEGACY); | |
480 | } | |
481 | addrs->v.push_back(a); | |
482 | } | |
483 | } else if (msgrv == CEPH_PICK_ADDRESS_MSGR1) { | |
484 | for (auto& a : addrs->v) { | |
485 | a.set_type(entity_addr_t::TYPE_LEGACY); | |
486 | } | |
487 | } else { | |
488 | for (auto& a : addrs->v) { | |
489 | a.set_type(entity_addr_t::TYPE_MSGR2); | |
490 | } | |
491 | } | |
492 | ||
493 | return 0; | |
494 | } | |
495 | ||
496 | int pick_addresses( | |
497 | CephContext *cct, | |
498 | unsigned flags, | |
499 | entity_addrvec_t *addrs, | |
500 | int preferred_numa_node) | |
501 | { | |
502 | struct ifaddrs *ifa; | |
503 | int r = getifaddrs(&ifa); | |
504 | if (r < 0) { | |
505 | r = -errno; | |
506 | string err = cpp_strerror(r); | |
507 | lderr(cct) << "unable to fetch interfaces and addresses: " | |
508 | << cpp_strerror(r) << dendl; | |
509 | return r; | |
510 | } | |
511 | r = pick_addresses(cct, flags, ifa, addrs, preferred_numa_node); | |
512 | freeifaddrs(ifa); | |
513 | return r; | |
514 | } | |
b5b8bbf5 FG |
515 | |
516 | std::string pick_iface(CephContext *cct, const struct sockaddr_storage &network) | |
517 | { | |
518 | struct ifaddrs *ifa; | |
519 | int r = getifaddrs(&ifa); | |
520 | if (r < 0) { | |
521 | string err = cpp_strerror(errno); | |
522 | lderr(cct) << "unable to fetch interfaces and addresses: " << err << dendl; | |
523 | return {}; | |
524 | } | |
522d829b | 525 | auto free_ifa = make_scope_guard([ifa] { freeifaddrs(ifa); }); |
f67539c2 | 526 | const unsigned int prefix_len = std::max(sizeof(in_addr::s_addr), sizeof(in6_addr::s6_addr)) * CHAR_BIT; |
522d829b TL |
527 | for (auto addr = ifa; addr != nullptr; addr = addr->ifa_next) { |
528 | if (matches_with_net(*ifa, (const struct sockaddr *) &network, prefix_len, | |
529 | CEPH_PICK_ADDRESS_IPV4 | CEPH_PICK_ADDRESS_IPV6)) { | |
530 | return addr->ifa_name; | |
531 | } | |
b5b8bbf5 | 532 | } |
522d829b | 533 | return {}; |
b5b8bbf5 FG |
534 | } |
535 | ||
536 | ||
f67539c2 | 537 | bool have_local_addr(CephContext *cct, const std::list<entity_addr_t>& ls, entity_addr_t *match) |
7c673cae FG |
538 | { |
539 | struct ifaddrs *ifa; | |
540 | int r = getifaddrs(&ifa); | |
541 | if (r < 0) { | |
542 | lderr(cct) << "unable to fetch interfaces and addresses: " << cpp_strerror(errno) << dendl; | |
543 | exit(1); | |
544 | } | |
522d829b | 545 | auto free_ifa = make_scope_guard([ifa] { freeifaddrs(ifa); }); |
7c673cae | 546 | |
11fdf7f2 | 547 | for (struct ifaddrs *addrs = ifa; addrs != nullptr; addrs = addrs->ifa_next) { |
7c673cae FG |
548 | if (addrs->ifa_addr) { |
549 | entity_addr_t a; | |
550 | a.set_sockaddr(addrs->ifa_addr); | |
11fdf7f2 TL |
551 | for (auto& p : ls) { |
552 | if (a.is_same_host(p)) { | |
553 | *match = p; | |
522d829b | 554 | return true; |
7c673cae FG |
555 | } |
556 | } | |
557 | } | |
558 | } | |
522d829b | 559 | return false; |
7c673cae | 560 | } |
11fdf7f2 TL |
561 | |
562 | int get_iface_numa_node( | |
563 | const std::string& iface, | |
564 | int *node) | |
565 | { | |
9f95a23c TL |
566 | enum class iface_t { |
567 | PHY_PORT, | |
568 | BOND_PORT | |
569 | } ifatype = iface_t::PHY_PORT; | |
f67539c2 | 570 | std::string_view ifa{iface}; |
9f95a23c TL |
571 | if (auto pos = ifa.find(":"); pos != ifa.npos) { |
572 | ifa.remove_suffix(ifa.size() - pos); | |
92f5a8d4 | 573 | } |
9f95a23c | 574 | string fn = fmt::format("/sys/class/net/{}/device/numa_node", ifa); |
92f5a8d4 TL |
575 | int fd = ::open(fn.c_str(), O_RDONLY); |
576 | if (fd < 0) { | |
9f95a23c | 577 | fn = fmt::format("/sys/class/net/{}/bonding/slaves", ifa); |
92f5a8d4 TL |
578 | fd = ::open(fn.c_str(), O_RDONLY); |
579 | if (fd < 0) { | |
580 | return -errno; | |
581 | } | |
9f95a23c | 582 | ifatype = iface_t::BOND_PORT; |
92f5a8d4 | 583 | } |
11fdf7f2 TL |
584 | |
585 | int r = 0; | |
586 | char buf[1024]; | |
587 | char *endptr = 0; | |
11fdf7f2 TL |
588 | r = safe_read(fd, &buf, sizeof(buf)); |
589 | if (r < 0) { | |
590 | goto out; | |
591 | } | |
592 | buf[r] = 0; | |
593 | while (r > 0 && ::isspace(buf[--r])) { | |
594 | buf[r] = 0; | |
595 | } | |
92f5a8d4 TL |
596 | |
597 | switch (ifatype) { | |
9f95a23c | 598 | case iface_t::PHY_PORT: |
92f5a8d4 TL |
599 | *node = strtoll(buf, &endptr, 10); |
600 | if (endptr != buf + strlen(buf)) { | |
601 | r = -EINVAL; | |
602 | goto out; | |
603 | } | |
604 | r = 0; | |
605 | break; | |
9f95a23c TL |
606 | case iface_t::BOND_PORT: |
607 | int bond_node = -1; | |
92f5a8d4 | 608 | std::vector<std::string> sv; |
9f95a23c TL |
609 | std::string ifacestr = buf; |
610 | get_str_vec(ifacestr, " ", sv); | |
92f5a8d4 TL |
611 | for (auto& iter : sv) { |
612 | int bn = -1; | |
613 | r = get_iface_numa_node(iter, &bn); | |
614 | if (r >= 0) { | |
615 | if (bond_node == -1 || bn == bond_node) { | |
616 | bond_node = bn; | |
617 | } else { | |
618 | *node = -2; | |
619 | goto out; | |
620 | } | |
621 | } else { | |
622 | goto out; | |
623 | } | |
624 | } | |
625 | *node = bond_node; | |
626 | break; | |
11fdf7f2 | 627 | } |
92f5a8d4 TL |
628 | |
629 | out: | |
11fdf7f2 TL |
630 | ::close(fd); |
631 | return r; | |
632 | } | |
92f5a8d4 | 633 |