Line data Source code
1 : // Copyright (c) 2012-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/libaddr
4 : //
5 : // Permission is hereby granted, free of charge, to any person obtaining a
6 : // copy of this software and associated documentation files (the
7 : // "Software"), to deal in the Software without restriction, including
8 : // without limitation the rights to use, copy, modify, merge, publish,
9 : // distribute, sublicense, and/or sell copies of the Software, and to
10 : // permit persons to whom the Software is furnished to do so, subject to
11 : // the following conditions:
12 : //
13 : // The above copyright notice and this permission notice shall be included
14 : // in all copies or substantial portions of the Software.
15 : //
16 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 :
24 :
25 : /** \file
26 : * \brief The implementation of the addr class.
27 : *
28 : * This file includes the implementation of the addr class. The one that
29 : * deals with low level classes.
30 : */
31 :
32 :
33 : // addr library
34 : //
35 : #include "libaddr/iface.h"
36 : #include "libaddr/route.h"
37 :
38 :
39 : // C++ library
40 : //
41 : #include <algorithm>
42 : #include <iostream>
43 :
44 :
45 : // C library
46 : //
47 : #include <ifaddrs.h>
48 : #include <net/if.h>
49 :
50 :
51 : // last include
52 : //
53 : #include <snapdev/poison.h>
54 :
55 :
56 :
57 : namespace addr
58 : {
59 :
60 :
61 : /** \brief Details used by the addr class implementation.
62 : *
63 : * We have a function to check whether an address is part of
64 : * the interfaces of your computer. This check requires the
65 : * use of a `struct ifaddrs` and as such it requires to
66 : * delete that structure. We define a deleter for that
67 : * strucure here.
68 : */
69 : namespace
70 : {
71 :
72 : /** \brief Delete an ifaddrs structure.
73 : *
74 : * This deleter is used to make sure all the ifaddrs get released when
75 : * an exception occurs or the function using such exists.
76 : *
77 : * \param[in] ia The ifaddrs structure to free.
78 : */
79 15 : void ifaddrs_deleter(struct ifaddrs * ia)
80 : {
81 15 : freeifaddrs(ia);
82 15 : }
83 :
84 :
85 : }
86 : // no name namespace
87 :
88 :
89 :
90 :
91 :
92 : /** \brief Initializes an interface name/index pair.
93 : *
94 : * This function creates an interface name/index object.
95 : *
96 : * In some circumstances (NETLINK) you need to specify the index of an
97 : * interface. The kernel keeps a list of interface by index starting at 1
98 : * and each have a name such as "eth0". This function initializes
99 : * one of those pairs.
100 : *
101 : * \param[in] index The index of the interface.
102 : * \param[in] name The name of the interface.
103 : */
104 0 : iface_index_name::iface_index_name(int index, std::string const & name)
105 : : f_index(index)
106 0 : , f_name(name)
107 : {
108 0 : }
109 :
110 :
111 : /** \brief Get the index of this interface.
112 : *
113 : * This function is used to retrieve the index of a name/index pair.
114 : *
115 : * \return The index of the name/index pair.
116 : */
117 0 : int iface_index_name::get_index() const
118 : {
119 0 : return f_index;
120 : }
121 :
122 :
123 : /** \brief Get the name of this interface.
124 : *
125 : * This function is used to retrieve the name of a name/index pair.
126 : *
127 : * \return The name of the name/index pair.
128 : */
129 0 : std::string const & iface_index_name::get_name() const
130 : {
131 0 : return f_name;
132 : }
133 :
134 :
135 :
136 : /** \brief Get the list of existing interfaces.
137 : *
138 : * This function gathers the complete list of interfaces by index and
139 : * name pairs. The result is a vector of iface_index_name objects.
140 : *
141 : * Note that over time the index of an interface can change since interfaces
142 : * can be added and removed from your network configuration. It is a good
143 : * idea to not cache that information.
144 : *
145 : * \return A vector of index/name pair objects.
146 : */
147 0 : iface_index_name::vector_t get_interface_name_index()
148 : {
149 0 : iface_index_name::vector_t result;
150 :
151 : // the index starts at 1
152 : //
153 0 : for(int index(1);; ++index)
154 : {
155 : // get the next interface name by index
156 : //
157 0 : char name[IF_NAMESIZE + 1];
158 0 : if(if_indextoname(index, name) == nullptr)
159 : {
160 0 : return result;
161 : }
162 :
163 : // make sure the name is null terminated
164 : //
165 0 : name[IF_NAMESIZE] = '\0';
166 :
167 0 : iface_index_name const in(index, name);
168 0 : result.push_back(in);
169 0 : }
170 : // NOT REACHED, the loop is broken by a "return"
171 : }
172 :
173 :
174 : /** \brief Get the index of an interface from its name.
175 : *
176 : * If you are given the name of an interface, you can retrieve its index
177 : * by calling this function. The resulting value is the index from 1 to n.
178 : *
179 : * If the named interface is not found, then the function returns 0.
180 : *
181 : * \return The interface index or 0 on error.
182 : */
183 0 : unsigned int get_interface_index_by_name(std::string const & name)
184 : {
185 0 : return if_nametoindex(name.c_str());
186 : }
187 :
188 :
189 :
190 :
191 :
192 :
193 : /** \brief Return a list of local addresses on this machine.
194 : *
195 : * Peruse the list of available interfaces, and return any detected
196 : * ip addresses in a vector.
197 : *
198 : * These addresses include:
199 : *
200 : * \li A mask whenever available (very likely if the interface is up).
201 : * \li A name you can retrieve with get_iface_name()
202 : * \li A set of flags defining the current status of the network interface
203 : * (i.e. IFF_UP, IFF_BROADCAST, IFF_NOARP, etc.)
204 : *
205 : * \return A vector of all the local interface IP addresses.
206 : */
207 15 : iface::vector_t iface::get_local_addresses()
208 : {
209 : // get the list of interface addresses
210 : //
211 15 : struct ifaddrs * ifa_start(nullptr);
212 15 : if(getifaddrs(&ifa_start) != 0)
213 : {
214 : // TODO: Should this throw, or just return an empty list quietly?
215 : //
216 : return iface::vector_t(); // LCOV_EXCL_LINE
217 : }
218 :
219 30 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
220 :
221 15 : uint8_t mask[16];
222 30 : iface::vector_t iface_list;
223 300 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
224 : {
225 : // the documentation says there may be no address at all
226 : // skip such entries at the moment
227 : //
228 285 : if(ifa->ifa_addr == nullptr)
229 : {
230 0 : continue;
231 : }
232 :
233 : // initialize an interface
234 510 : iface the_interface;
235 :
236 : // copy the name and flags as is
237 : //
238 : // TBD: can the ifa_name even be a null pointer?
239 : //
240 285 : the_interface.f_name = ifa->ifa_name;
241 285 : the_interface.f_flags = ifa->ifa_flags; // IFF_... flags (see `man 7 netdevice` search for SIOCGIFFLAGS)
242 :
243 : // get the family to know how to treat the address
244 : //
245 : // when an interface has an IPv4 and an IPv6, there are two entries in
246 : // the list, both with the same name
247 : //
248 285 : uint16_t const family(ifa->ifa_addr->sa_family);
249 :
250 345 : switch(family)
251 : {
252 150 : case AF_INET:
253 150 : the_interface.f_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)));
254 :
255 150 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
256 135 : && ifa->ifa_broadaddr != nullptr)
257 : {
258 135 : the_interface.f_broadcast_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_broadaddr)));
259 : }
260 150 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
261 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
262 : {
263 : the_interface.f_destination_address.set_ipv4(*(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
264 : }
265 :
266 : // if present, add the mask as well
267 : //
268 150 : if(ifa->ifa_netmask != nullptr)
269 : {
270 : // for the IPv4 mask, we have to break it down in such a
271 : // way as to make it IPv6 compatible
272 : //
273 150 : memset(mask, 255, 12);
274 150 : mask[12] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 0;
275 150 : mask[13] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 8;
276 150 : mask[14] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 16;
277 150 : mask[15] = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_netmask)->sin_addr.s_addr >> 24;
278 150 : the_interface.f_address.set_mask(mask);
279 : }
280 150 : break;
281 :
282 75 : case AF_INET6:
283 75 : the_interface.f_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)));
284 :
285 75 : if((ifa->ifa_flags & IFF_BROADCAST) != 0
286 60 : && ifa->ifa_broadaddr != nullptr)
287 : {
288 : the_interface.f_broadcast_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_broadaddr))); // LCOV_EXCL_LINE
289 : }
290 75 : if((ifa->ifa_flags & IFF_POINTOPOINT) != 0
291 : && ifa->ifa_dstaddr != nullptr) // LCOV_EXCL_LINE
292 : {
293 : the_interface.f_destination_address.set_ipv6(*(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_dstaddr))); // LCOV_EXCL_LINE
294 : }
295 :
296 : // if present, add the mask as well
297 : //
298 75 : if(ifa->ifa_netmask != nullptr)
299 : {
300 75 : the_interface.f_address.set_mask(reinterpret_cast<uint8_t *>(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_netmask)->sin6_addr));
301 : }
302 75 : break;
303 :
304 60 : default:
305 : // TODO: can we just ignore unexpected address families?
306 : //throw addr_invalid_structure("Unknown address family!");
307 60 : continue;
308 :
309 : }
310 :
311 225 : iface_list.push_back(the_interface);
312 : }
313 :
314 15 : return iface_list;
315 : }
316 :
317 :
318 : /** \brief Get the interface name.
319 : *
320 : * This function returns the name of the interface such as 'eth0' or 'p4p1'.
321 : *
322 : * The name is used in a few places such as the ioctl() function with the
323 : * SIOCGIFMTU command. Otherwise, it's mainly for display and easing use
324 : * (i.e. to let users select which interface to connect to.)
325 : *
326 : * \return The interface name.
327 : */
328 17 : std::string iface::get_name() const
329 : {
330 17 : return f_name;
331 : }
332 :
333 :
334 : /** \brief Get the interface setup flags.
335 : *
336 : * This function returns a set of flags defined on that interface. The flags
337 : * are defined in the `man 7 netdevice` as the IFF_... flags. The flags are
338 : * defined under the SIOCGIFFLAGS and SIOCSIFFLAGS entries.
339 : *
340 : * One flag of interest is the IFF_UP flag. This means the interface is
341 : * active (even if not actually in use.)
342 : *
343 : * \return The interface flags.
344 : */
345 45 : unsigned int iface::get_flags() const
346 : {
347 45 : return f_flags;
348 : }
349 :
350 :
351 : /** \brief Get this interface address.
352 : *
353 : * This function returns the address of the interface. This address is very
354 : * likely to have a mask (i.e. 192.168.0.0/255.255.0.0).
355 : *
356 : * The address may be an IPv4 or an IPv6 address.
357 : *
358 : * \return The address of the interface.
359 : */
360 207 : addr const & iface::get_address() const
361 : {
362 207 : return f_address;
363 : }
364 :
365 :
366 : /** \brief Get the broadcast address.
367 : *
368 : * This function returns a constant reference to the broadcast address
369 : * of this interface. The address is always available in this class. It
370 : * will be set to the ANY address if it was not defined. Note, however,
371 : * that even though the ANY address is not a valid broadcast address,
372 : * you should call the has_broadcast_address() function to know whether
373 : * this address is indeed defined.
374 : *
375 : * \return The broadcast address of this interface.
376 : */
377 15 : addr const & iface::get_broadcast_address() const
378 : {
379 15 : return f_broadcast_address;
380 : }
381 :
382 :
383 : /** \brief Get the destination address.
384 : *
385 : * This function returns a constant reference to the destination address
386 : * of this interface. The address is always available in this class. It
387 : * will be set to the ANY address if it was not defined. Note, however,
388 : * that the ANY address is a valid destination address (i.e. default
389 : * route).
390 : *
391 : * To know whether the destination address is defined in that interface,
392 : * make sure to call the has_destination_address() function first.
393 : *
394 : * \return The destination address of this interface.
395 : */
396 15 : addr const & iface::get_destination_address() const
397 : {
398 15 : return f_destination_address;
399 : }
400 :
401 :
402 : /** \brief Check whether a broadcast address.
403 : *
404 : * The broadcast address is not present on all interfaces. When it is, this
405 : * function returns true.
406 : *
407 : * Note that you can always call the get_broadcast_address(), but if
408 : * undefined it will appear as a default address (NETWORK_TYPE_ANY)
409 : * which you can't distinguish from a valid address although a
410 : * the NETWORK_TYPE_ANY is not a valid address for a broacast
411 : * address.
412 : *
413 : * \note
414 : * When a broadcast address is defined on an interface, then there can't
415 : * be a destination address.
416 : *
417 : * \return true if a broadcast address is defined.
418 : */
419 30 : bool iface::has_broadcast_address() const
420 : {
421 30 : return (f_flags & IFF_BROADCAST) != 0;
422 : }
423 :
424 :
425 : /** \brief Check whether this interface defines a destination address.
426 : *
427 : * This function returns true if this interface defined a destination
428 : * address. Either way you can call the get_destination_address()
429 : * function, however, the address will be of type NETWORK_TYPE_ANY
430 : * which does not tell you whether it was defined or not.
431 : *
432 : * Ethernet and the local interfaces all define a destination address.
433 : *
434 : * \note
435 : * The destination address is not assigned any specific mask (all
436 : * are ff or 255).
437 : *
438 : * \note
439 : * When there is a destination address defined on an interface, then
440 : * there can't be a broadcast address.
441 : *
442 : * \return true when that interface defined a destination address.
443 : */
444 30 : bool iface::has_destination_address() const
445 : {
446 30 : return (f_flags & IFF_POINTOPOINT) != 0;
447 : }
448 :
449 :
450 : /** \brief Search for the interface corresponding to this address.
451 : *
452 : * Peruse the list of available interfaces and return the one that matches
453 : * this address if any, otherwise return a null pointer.
454 : *
455 : * Say you create an addr object with the IP address "127.0.0.1" and then
456 : * call this function. You will get a pointer to the "lo" interface and
457 : * can check the validity of the flags (i.e. is the interface UP, can it
458 : * BROADCAST or MULTICAST your UDP packets, etc.)
459 : *
460 : * If the address is a remote address, then this function returns a null
461 : * pointer.
462 : *
463 : * \note
464 : * This function replaces the addr::is_computer_interface_address() function.
465 : * If this function returns a non-null pointer when allow_default_destination
466 : * set to false, then you've got the same result plus you have access to all
467 : * the available information from that interface.
468 : *
469 : * \warning
470 : * If you allow for the default destination, this function calls the
471 : * route::get_ipv4_routes() function which can be costly. Try to avoid
472 : * doing that in a loop.
473 : *
474 : * \param[in] a The address used to search for an interface.
475 : * \param[in] allow_default_destination If true and \p a doesn't match
476 : * any of the interfaces, use the one interface with its
477 : * destination set to 0.0.0.0 or equivalent.
478 : *
479 : * \return A pointer to an interface IP address.
480 : */
481 14 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
482 : {
483 28 : iface::vector_t interfaces(iface::get_local_addresses());
484 :
485 204 : for(auto i : interfaces)
486 : {
487 192 : if(i.get_address().match(a))
488 : {
489 2 : return iface::pointer_t(new iface(i));
490 : }
491 : }
492 :
493 : // if there is a default, keep a copy in case we do not find a
494 : // local address while looking (and only if the user requested
495 : // such, which is the default)
496 : //
497 12 : if(!allow_default_destination)
498 : {
499 11 : return iface::pointer_t();
500 : }
501 :
502 : // to determine the default interface, we need the list of routes
503 : // so we first gather that information and then search for the
504 : // interface that has that name
505 : //
506 2 : route::vector_t routes(route::get_ipv4_routes());
507 2 : route::pointer_t default_route(find_default_route(routes));
508 1 : if(default_route == nullptr)
509 : {
510 : return iface::pointer_t(); // LCOV_EXCL_LINE
511 : }
512 :
513 1 : std::string const & default_iface(default_route->get_interface_name());
514 1 : auto it(std::find_if(
515 : interfaces.cbegin()
516 : , interfaces.cend()
517 11 : , [default_iface](auto & i)
518 : {
519 6 : return i.get_name() == default_iface;
520 7 : }));
521 1 : if(it == interfaces.cend())
522 : {
523 : return iface::pointer_t(); // LCOV_EXCL_LINE
524 : }
525 :
526 1 : return iface::pointer_t(new iface(*it));
527 : }
528 :
529 :
530 : #if 0
531 : /** \brief Check whether this address represents this computer.
532 : *
533 : * This function reads the addresses as given to us by the getifaddrs()
534 : * function. This is a system function that returns a complete list of
535 : * all the addresses this computer is managing / represents. In other
536 : * words, a list of address that other computers can use to connect
537 : * to this computer (assuming proper firewall, of course.)
538 : *
539 : * \warning
540 : * The list of addresses from getifaddrs() is not being cached. So you
541 : * probably do not want to call this function in a loop. That being
542 : * said, I still would imagine that retrieving that list is fast.
543 : *
544 : * \todo
545 : * We need to apply the mask to make this work properly. This is why
546 : * the current implementation fails big time (used by snapcommunicator.cpp).
547 : *
548 : * \return a computer_interface_address_t enumeration: error, true, or
549 : * false at this time; on error errno should be set to represent
550 : * what the error was.
551 : */
552 :
553 : // replaced by with a pointer_t == nullptr if there was no match,
554 : // although make sure to set allow_default_destination to false
555 : //
556 : iface::pointer_t find_addr_interface(addr const & a, bool allow_default_destination)
557 :
558 : bool is_computer_interface_address(addr const & a)
559 : {
560 : iface::vector_t interfaces(iface::get_local_addresses());
561 :
562 : for(auto i : interfaces)
563 : {
564 : }
565 :
566 :
567 : // TBD: maybe we could cache the ifaddrs for a small amount of time
568 : // (i.e. 1 minute) so additional calls within that time
569 : // can go even faster?
570 : //
571 :
572 : // get the list of interface addresses
573 : //
574 : struct ifaddrs * ifa_start(nullptr);
575 : if(getifaddrs(&ifa_start) != 0)
576 : {
577 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
578 : }
579 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
580 :
581 : bool const ipv4(a.is_ipv4());
582 : uint16_t const family(ipv4 ? AF_INET : AF_INET6);
583 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
584 : {
585 : if(ifa->ifa_addr != nullptr
586 : && ifa->ifa_addr->sa_family == family)
587 : {
588 : if(ipv4)
589 : {
590 : // the interface address structure is a 'struct sockaddr_in'
591 : //
592 : if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
593 : f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
594 : sizeof(struct in_addr)) == 0)
595 : {
596 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
597 : }
598 : }
599 : else
600 : {
601 : // the interface address structure is a 'struct sockaddr_in6'
602 : //
603 : if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr,
604 : &f_address.sin6_addr,
605 : sizeof(f_address.sin6_addr)) == 0)
606 : {
607 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
608 : }
609 : }
610 : }
611 : }
612 :
613 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
614 : }
615 : #endif
616 :
617 :
618 6 : }
619 : // namespace addr
620 : // vim: ts=4 sw=4 et
|