Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Interface.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_Interface_h_
5#define _Stroika_Foundation_IO_Network_Interface_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10
12#include "Stroika/Foundation/Common/Common.h"
14#include "Stroika/Foundation/Common/GUID.h"
15#include "Stroika/Foundation/Containers/Collection.h"
16#include "Stroika/Foundation/Containers/KeyedCollection.h"
17#include "Stroika/Foundation/Containers/Set.h"
20#include "Stroika/Foundation/IO/Network/SocketAddress.h"
21
22/**
23 * \file
24 *
25 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
26 *
27 * TODO:
28 * @todo Use http://stackoverflow.com/questions/14264371/how-to-get-nic-details-from-a-c-program to grab ethernet speed
29 * for inteface (bandwidth of network interface)
30 *
31 * @todo Handle case of multiple address bindings to a single interface (groupby).
32 *
33 * @todo Incomplete POSIX IMPL
34 * IPV6 compat for POSIX (already done for windows)
35 *
36 * @todo Fix use of assert - SB exceptions mostly...
37 */
38
40
41 using Characters::String;
42
43 /**
44 * Capture details describing a network interface.
45 */
46 struct Interface {
47 /**
48 * The OS's own identity for the interface:
49 * POSIX: its name - e.g. eth0, en0 (on Linux the device's name - not that of an IPv4 address's label, such as
50 * eth0:1, whose address belongs to eth0)
51 * Windows: IP_ADAPTER_ADDRESSES::AdapterName - its GUID (not very printable; @see fFriendlyName)
52 *
53 * Unique among the interfaces there at any one moment - the OS ensures that (Linux: within the process's network
54 * namespace) - so it identifies an interface within one listing (@see SystemInterfacesMgr::GetAll).
55 *
56 * \note But it is not a lasting identity. On POSIX an interface can be renamed, and once one goes away its name may be
57 * given to another (e.g. a VPN's utun3, or a replugged USB adapter's en5). On Windows the GUID lasts for a real
58 * adapter, but a re-created virtual one (a VPN's, a Hyper-V switch) gets a new one.
59 *
60 * So key a collection of Interfaces by it only when they were all there at one moment (one listing). One kept
61 * across time - a history of the networks seen, say - can hold two different interfaces with the same ID, and
62 * the same interface under two IDs, so it needs some other notion of identity (its hardware address, perhaps,
63 * where it has one).
64 */
66
67 /**
68 * @see SystemIDType
69 */
71
72#if qStroika_Platform_POSIX
73 /**
74 * On unix, its the interface name, e.g. eth0, eth1, etc.
75 * On Windows, this is concept doesn't really exist.
76 */
77 nonvirtual String GetInterfaceName () const;
78#endif
79
80 /**
81 * Never a real interface's index - the OS numbers them from 1, and uses 0 for 'none'. Passed where an interface index
82 * is wanted, it means any interface - the OS picks one (as V4::kAddrAny and V6::kAddrAny do for an address).
83 */
84 static constexpr unsigned int kAnyIndex = 0;
85
86 /**
87 * The operating system's number for the interface - how IPv6 names an interface (in its multicast APIs, and in a
88 * link-local address's scope). Called its index, but NOT its position in the list SystemInterfacesMgr::GetAll ()
89 * returns. POSIX: if_nametoindex (); Windows: IP_ADAPTER_ADDRESSES::IfIndex (Ipv6IfIndex when the interface has no
90 * IPv4). Never kAnyIndex.
91 *
92 * \note RACE: an index looks like a lasting identity, but is not one - and unlike an address or a name changing, which
93 * is expected (other programs add and remove interfaces), this is easy to miss. An index names an interface only
94 * while that interface exists: no OS promises not to reuse it, so once an interface goes away, its index may be
95 * given to a DIFFERENT interface, and code still holding it silently acts on the wrong one. And an interface that
96 * goes away and comes back (an adapter disabled and re-enabled, a VPN reconnecting, a USB adapter replugged) may
97 * come back with a different index; none survives a reboot.
98 *
99 * So rather than keep an index, pass the Interface itself where an API takes one (as ConnectionlessSocket's
100 * multicast calls do), which looks its index up - by fInterfaceID - at the call; else re-read the interface
101 * (SystemInterfacesMgr::GetById) just before using its index. In particular, to undo what was done by index
102 * (leave a multicast group, say), look the index up again rather than reuse the number it was done with. And redo
103 * whatever was done by index when the interfaces change (LinkMonitor) - a socket keeps the multicast interface it
104 * was given, say. (A multicast membership, though, is tied to the interface itself, so goes away with it.) For a
105 * longer-lasting name, use fInterfaceID.
106 */
107 optional<unsigned int> fIndex;
108
109 /**
110 * This is a generally good display name to describe a network interface.
111 *
112 * WINDOWS:
113 * IP_ADAPTER_ADDRESSES::FriendlyName
114 *
115 * A user-friendly name for the adapter. For example: "Local Area Connection 1." This name appears
116 * in contexts such as the ipconfig command line program and the Connection folder.
117 *
118 * Note this name is often user-editable
119 */
121
122 /**
123 * This description of the adpapter is typically a short string describing the hardware, such as
124 * "Intel(R) Dual Band Wireless-AC 7260"
125 */
126 optional<String> fDescription;
127
128 /**
129 * Network GUID
130 * @see https://docs.microsoft.com/en-us/windows/desktop/api/iptypes/ns-iptypes-_ip_adapter_addresses_lh "NetworkGuid"
131 *
132 * I can find no documentation on what this means, and all the network interfaces appear to have the same value (e.g. fake networks and real ones).
133 * VERGING on deprecating this. It doesn't appear to have any use. But leave for now... -- LGP 2018-12-16
134 */
135 optional<Common::GUID> fNetworkGUID;
136
137 /**
138 *
139 * \note Common::DefaultNames<> supported
140 *
141 * Most of these types are obvious, but eDeviceVirtualInternalNetwork is for special internal networks, like for virtualbox, Docker, or other purposes.
142 * They will generally (always) have private IP Addresses distinct from the main real external IP address of the machine in question. It may not always
143 * be possible to identify such networks.
144 */
145 enum class Type {
146 eLoopback,
147 eWiredEthernet,
148 eWIFI,
149 eTunnel,
150
151 /**
152 * Virtual adapter, like virtualbox, etc
153 */
155
156 eOther,
157
158 Stroika_Define_Enum_Bounds (eLoopback, eOther)
159 };
160
161 /**
162 */
163 optional<Type> fType;
164
165 /**
166 * This - if present - is typically an ethernet macaddr (6 bytes in hex separated by :)
167 */
168 optional<String> fHardwareAddress;
169
170 /**
171 * bits per second
172 */
173 optional<uint64_t> fTransmitSpeedBaud;
174
175 /**
176 * bits per second
177 */
178 optional<uint64_t> fReceiveLinkSpeedBaud;
179
180 /**
181 * \note intentionally omitted fields 'Description' because included in parent object, connectionInfo state (cuz same as wireless adapater info state),
182 * and....
183 */
185 optional<String> fSSID;
186
187 /**
188 *
189 * \note Common::DefaultNames<> supported
190 */
191 enum class State {
192 eNotReady, // WLAN_INTERFACE_STATE::wlan_interface_state_not_ready,
193 eConnected, // WLAN_INTERFACE_STATE::wlan_interface_state_connected,
194 eAdHocNetworkFormed, // WLAN_INTERFACE_STATE::wlan_interface_state_ad_hoc_network_formed - First node in a ad hoc network
195 eDisconnecting, // WLAN_INTERFACE_STATE::wlan_interface_state_disconnecting,
196 eDisconnected, // WLAN_INTERFACE_STATE::wlan_interface_state_disconnected,
197 eAssociating, // WLAN_INTERFACE_STATE::wlan_interface_state_associating - Attempting to associate with a network
198 eDiscovering, // WLAN_INTERFACE_STATE::wlan_interface_state_discovering - Auto configuration is discovering settings for the network
199 eAuthenticating, // WLAN_INTERFACE_STATE::wlan_interface_state_authenticating
200 eUnknown,
201
202 Stroika_Define_Enum_Bounds (eNotReady, eUnknown)
203 };
204 optional<State> fState;
205
206 /**
207 *
208 * \note Common::DefaultNames<> supported
209 */
210 enum class ConnectionMode {
211 eProfile, // _WLAN_CONNECTION_MODE::wlan_connection_mode_profile- A profile is used to make the connection
212 eTemporaryProfile, // _WLAN_CONNECTION_MODE::wlan_connection_mode_temporary_profile - A temporary profile is used to make the connection
213 eDiscoverSecrure, // _WLAN_CONNECTION_MODE::wlan_connection_mode_discovery_secure - Secure discovery is used to make the connection
214 eDiscoverInsecure, // _WLAN_CONNECTION_MODE::wlan_connection_mode_discovery_unsecure,
215 eAuto, // _WLAN_CONNECTION_MODE::wlan_connection_mode_auto - connection initiated by wireless service automatically using a persistent profile
216 eInvalid, // _WLAN_CONNECTION_MODE::wlan_connection_mode_invalid
217 eUnknown,
218
219 Stroika_Define_Enum_Bounds (eProfile, eUnknown)
220 };
221 optional<ConnectionMode> fConnectionMode;
222
223 optional<String> fProfileName;
224
225 /**
226 *
227 * \note Common::DefaultNames<> supported
228 */
229 enum class BSSType {
230 eInfrastructure, // DOT11_BSS_TYPE::dot11_BSS_type_infrastructure
231 eIndependent, // DOT11_BSS_TYPE::dot11_BSS_type_independent
232 eAny, // DOT11_BSS_TYPE::dot11_BSS_type_any
233 eUnknown,
234
235 Stroika_Define_Enum_Bounds (eInfrastructure, eUnknown)
236 };
237 optional<BSSType> fBSSType;
238
239 optional<String> fMACAddress;
240
241 /**
242 *
243 * \note Common::DefaultNames<> supported
244 */
246 eFHSS, // DOT11_PHY_TYPE::dot11_phy_type_fhss = 1, Frequency-hopping spread-spectrum
247 eDSSS, // DOT11_PHY_TYPE:: dot11_phy_type_dsss = 2, Direct sequence spread spectrum
248 eIRBaseBand, // DOT11_PHY_TYPE::dot11_phy_type_irbaseband = 3,Infrared (IR) baseband
249 e80211a, // DOT11_PHY_TYPE::dot11_phy_type_ofdm = 4, // 11a
250 e80211b, // DOT11_PHY_TYPE::dot11_phy_type_hrdsss = 5, // 11b
251 e80211g, // DOT11_PHY_TYPE::dot11_phy_type_erp = 6, // 11g
252 e80211n, // DOT11_PHY_TYPE:: dot11_phy_type_ht = 7, // 11n
253 e80211ac, // DOT11_PHY_TYPE::dot11_phy_type_vht = 8, // 11ac
254 e80211ad, // DOT11_PHY_TYPE::dot11_phy_type_dmg = 9, // 11ad
255 e80211ax, // DOT11_PHY_TYPE::dot11_phy_type_he = 10, // 11ax
256 eUnknown, // DOT11_PHY_TYPE::dot11_phy_type_unknown = 0,
257
258 Stroika_Define_Enum_Bounds (eFHSS, eUnknown)
259 };
260 optional<PhysicalConnectionType> fPhysicalConnectionType;
261
262 /**
263 * A percentage value that represents the signal quality of the network.
264 * This member contains a value between 0 and 100. A value of 0 implies an actual RSSI signal strength of -100 dbm.
265 * A value of 100 implies an actual RSSI signal strength of -50 dbm. You can calculate the RSSI signal
266 * strength value for wlanSignalQuality values between 1 and 99 using linear interpolation.
267 *
268 * https://stackoverflow.com/questions/15797920/how-to-convert-wifi-signal-strength-from-quality-percent-to-rssi-dbm
269 */
270 optional<double> fSignalQuality;
271
272 optional<bool> fSecurityEnabled; // any wireless security enabled (versus open) // NOT SURE HOW DIFFERS FROM _DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_80211_OPEN
273 optional<bool> f8021XEnabled; // 802.1X
274
275 /**
276 *
277 * \note Common::DefaultNames<> supported
278 */
279 enum class AuthAlgorithm {
280 eOpen, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_80211_OPEN = 1,
281 ePresharedKey, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_80211_SHARED_KEY = 2,
282 eWPA, // DOT11_AUTH_ALGORITHM:: DOT11_AUTH_ALGO_WPA = 3,
283 eWPA_PSK, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_WPA_PSK = 4,
284 eWPA_NONE, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_WPA_NONE = 5, // used in NatSTA only
285 eRSNA, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_RSNA = 6,
286 eRSNA_PSK, // DOT11_AUTH_ALGORITHM::DOT11_AUTH_ALGO_RSNA_PSK = 7,
287 eUnknown,
288
289 Stroika_Define_Enum_Bounds (eOpen, eUnknown)
290 };
291 optional<AuthAlgorithm> fAuthAlgorithm;
292
293 optional<String> fCipher;
294
295 /**
296 * @see Characters::ToString ();
297 */
298 nonvirtual String ToString () const;
299 };
300
301 optional<WirelessInfo> fWirelessInfo; // has_value () if-and-only-if type fType == eWIFI
302
303 /**
304 */
305 struct Bindings {
306 /**
307 * until Stroika 2.1b9, this was called Network::fBindings
308 * These are the ranges of addresses (bindings?) associated with this network interface.
309 * Except for special cases (e.g. promiscuous mode) these are the only addreses expected to be one hop away on this
310 * network interface
311 */
312 Containers::Collection<CIDR> fAddressRanges; // can be IPv4 or IPv6
313
314 /**
315 * Very similar to fAddressRanges, but these addresses are the ones we listen to, as targetted for this
316 * machine.
317 */
318 Containers::Collection<InternetAddress> fAddresses; // can be IPv4 or IPv6
319
320 /**
321 * @see Characters::ToString ();
322 */
323 nonvirtual String ToString () const;
324 };
325
326 /**
327 * until Stroika 2.1b9, this was Containers::Collection<CIDR>: now fBindings.fAddressRanges
328 */
329 Bindings fBindings;
330
331 /**
332 * Typically there will be zero or one, and if one, this is the default gateway. This maybe missing if it couldn't be retrieved from the operating system.
333 */
334 optional<Containers::Sequence<InternetAddress>> fGateways;
335
336 /**
337 * The default set of (per adapter) dns servers. This maybe missing if it couldn't be retrieved from the operating system.
338 */
339 optional<Containers::Sequence<InternetAddress>> fDNSServers;
340
341 /**
342 * \note Common::DefaultNames<> supported
343 */
344 enum class Status {
345
346 /**
347 * This tells if the low level network interface is powered on, and physically connected (e.g. cable plugged in).
348 *
349 * This is always present if eRunning, but may also be present in the set of states if it can be determed that the interface is plugged in but disabled
350 * for reasons of software state (e.g. disabled interface, or testing mode).
351 */
353
354 /**
355 * This tells if the network interface is ready to send and recieve packets.
356 *
357 * \note see https://tools.ietf.org/html/rfc2020 and https://tools.ietf.org/html/rfc2863 - IfAdminStatus and IfOperStatus
358 * Corresponds to IfOperStatus==IfOperStatusUp
359 */
360 eRunning,
361
363 };
364
365 /**
366 * The state of the interface (@see Status) if known.
367 *
368 * \note not has_value () if IfOperStatusUnknown
369 */
370 optional<Containers::Set<Status>> fStatus;
371
372 /**
373 * @see Characters::ToString ();
374 */
375 nonvirtual String ToString () const;
376
377 /**
378 * An Interface's fInterfaceID - the key of InterfacesByID. (A struct, not a lambda: a lambda's type in a header
379 * would be an ODR violation.)
380 */
381 struct IDExtractor {
382 SystemIDType operator() (const Interface& i) const noexcept
383 {
384 return i.fInterfaceID;
385 }
386 };
387 };
388
389 /**
390 * \brief Interfaces seen at one moment (e.g. one SystemInterfacesMgr::GetAll ()), keyed by fInterfaceID.
391 *
392 * \note fInterfaceID is unique only among the interfaces there at one moment (@see Interface::SystemIDType) - so
393 * interfaces kept across time (a history of the networks seen, say) do not belong in one of these.
394 */
397
398 /**
399 * \todo @todo https://github.com/SophistSolutions/Stroika/issues/844 (STK-710)
400 * add some level of caching to this object, possible with arguments to CTOR saying
401 * allowed latency for fetch from OS
402 *
403 * IF I can do with netlink or something similar, probably at least optionally do this statically so that
404 * all instances can easily leverage the cache.
405 */
407 public:
408 /**
409 * Collect all the interfaces (and their status) from the operating system - keyed by their fInterfaceID.
410 *
411 * \note An Interface's position in this list means nothing - its index is Interface::fIndex (@see its RACE note).
412 */
413 nonvirtual InterfacesByID GetAll ();
414
415 public:
416 /**
417 * Find the interface object with the given ID.
418 *
419 * @see Interface::fInterfaceID
420 */
421 nonvirtual optional<Interface> GetById (const Interface::SystemIDType& interfaceID);
422
423 public:
424 /**
425 * A given address should be found in at most one interface.
426 * Note this doesnt check fBoundAddresses, but fBoundAddressRanges, so what addresses are listened for, not
427 * actually associated with the interface
428 */
429 nonvirtual optional<Interface> GetContainingAddress (const InternetAddress& ia);
430 };
431
432 /**
433 * \brief This machine's address for reaching peer - the local address a datagram sent to peer would carry, so the address
434 * peer can reach this machine at, on the network between them.
435 *
436 * Asks the routing table (via a UDP socket connected to peer - which sends nothing). peer's port does not matter. A
437 * link-local IPv6 peer must carry its scope id - as the sender's address from ConnectionlessSocket::Ptr::ReceiveFrom does.
438 *
439 * Returns nullopt if there is no route to peer.
440 *
441 * \note peer is a SocketAddress, not an InternetAddress, only because an InternetAddress cannot hold the scope id a
442 * link-local IPv6 peer needs. Once it can, this should take an InternetAddress -
443 * https://github.com/SophistSolutions/Stroika/issues/1201
444 */
445 optional<InternetAddress> GetLocalAddressToReach (const SocketAddress& peer);
446
447}
448
449/*
450 ********************************************************************************
451 ***************************** Implementation Details ***************************
452 ********************************************************************************
453 */
454#include "Interface.inl"
455
456#endif /*_Stroika_Foundation_IO_Network_Interface_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
nonvirtual optional< Interface > GetById(const Interface::SystemIDType &interfaceID)
nonvirtual optional< Interface > GetContainingAddress(const InternetAddress &ia)
optional< InternetAddress > GetLocalAddressToReach(const SocketAddress &peer)
This machine's address for reaching peer - the local address a datagram sent to peer would carry,...
optional< Containers::Set< Status > > fStatus
Definition Interface.h:370
optional< Containers::Sequence< InternetAddress > > fDNSServers
Definition Interface.h:339
static constexpr unsigned int kAnyIndex
Definition Interface.h:84
optional< Containers::Sequence< InternetAddress > > fGateways
Definition Interface.h:334
optional< Common::GUID > fNetworkGUID
Definition Interface.h:135