Skip to content

Commit

Permalink
dev/netifaces2
Browse files Browse the repository at this point in the history
  • Loading branch information
relativityspace-jsmith committed Feb 21, 2024
1 parent 5b9f3d1 commit 40753e2
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 29 deletions.
1 change: 1 addition & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/multicast_expert.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Development Setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ python -m poetry shell # This activates a virtual environment containing the dep

### Running Tests
```
python -m mypy . # Checks types
mypy . # Checks types
# Linux only, allows the loopback tests to pass
sudo ip route add 239.2.2.0/24 dev lo
sudo ip -6 route add table local ff11::/16 dev lo
python -m pytest . # Checks actual code
pytest . # Checks actual code
```

### Uploading to PyPi
Expand Down
58 changes: 38 additions & 20 deletions multicast_expert/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import socket
from typing import List, Dict, Optional, Union, Tuple
import ipaddress
Expand All @@ -9,6 +11,12 @@
is_mac = platform.system() == "Darwin"


# The netifaces2 rewritten version allows multicast_expert to work using a maintained backend (yay!).
# However, it uses the same import name as the original netifaces (aww), so we have to get creative to
# detect it. It adds a new default_gateway() function which is much simpler to use.
using_netifaces_2 = hasattr(netifaces, "default_gateway")


# Type which can represent an IPv4 or an IPv6 address.
# For IPv4, address is a tuple of IP address (str) and port number.
# For IPv6, address is a tuple of IP address (str), port number, flow info (int), and scope ID (int).
Expand All @@ -35,17 +43,17 @@ def get_interface_ips(include_ipv4: bool = True, include_ipv6: bool = True) -> L
ip_list = []
for interface in netifaces.interfaces():

all_addresses: List[Dict[str, str]] = []
all_addresses: List[Dict[netifaces.defs.AddressType, str]] = []
addresses_at_each_level = netifaces.ifaddresses(interface)

if include_ipv4:
# Note: Check needed because some interfaces do not have an ipv4 or ipv6 address
if netifaces.AF_INET in addresses_at_each_level:
all_addresses.extend(addresses_at_each_level[netifaces.AF_INET])
all_addresses.extend(addresses_at_each_level[netifaces.AF_INET]) # type: ignore[index]

if include_ipv6:
if netifaces.AF_INET6 in addresses_at_each_level:
all_addresses.extend(addresses_at_each_level[netifaces.AF_INET6])
all_addresses.extend(addresses_at_each_level[netifaces.AF_INET6]) # type: ignore[index]

for address_dict in all_addresses:
ip_list.append(address_dict["addr"])
Expand All @@ -58,41 +66,51 @@ def get_default_gateway_iface_ip_v6() -> Optional[str]:
Get the IP address of the interface that connects to the default IPv6 gateway, if it
can be determined. If it cannot be determined, None is returned.
"""
return get_default_gateway_iface_ip(netifaces.AF_INET6)
return get_default_gateway_iface_ip(netifaces.AF_INET6) # type: ignore[arg-type]


def get_default_gateway_iface_ip_v4() -> Optional[str]:
"""
Get the IP address of the interface that connects to the default IPv4 gateway, if it
can be determined. If it cannot be determined, None is returned.
"""
return get_default_gateway_iface_ip(netifaces.AF_INET)
return get_default_gateway_iface_ip(netifaces.AF_INET) # type: ignore[arg-type]


def get_default_gateway_iface_ip(addr_family: int) -> Optional[str]:
def get_default_gateway_iface_ip(addr_family: netifaces.InterfaceType) -> Optional[str]:
"""
Get the IP address of the interface that connects to the default gateway of the given addr_family, if it
can be determined. If it cannot be determined, None is returned.
"""

# Enumerate all gateways using netifaces
try:
gateways = netifaces.gateways()
except OSError:
return None
if using_netifaces_2:

# If it can, it will identify one of those as the default gateway for traffic.
# If not, return none.
if not "default" in gateways:
return None
if not addr_family in gateways["default"]:
return None
# In netifaces 2, we get the
default_gateways = netifaces.default_gateway()
if addr_family in default_gateways:
default_gateway_iface = default_gateways[addr_family][1]
else:
return None

else: # netifaces classic (TM)
# Enumerate all gateways using netifaces
try:
gateways = netifaces.gateways()
except OSError:
return None

# If it can, it will identify one of those as the default gateway for traffic.
# If not, return none.
if not "default" in gateways:
return None
if not addr_family in gateways["default"]:
return None

default_gateway = gateways["default"][addr_family]
default_gateway_iface = default_gateway[1] # element 1 is the iface name, per the docs
default_gateway = gateways["default"][addr_family]
default_gateway_iface = default_gateway[1] # element 1 is the iface name, per the docs

# Now, use the interface name to get the IP address of that interface
interface_addresses: Dict[int, List[Dict[str, str]]] = netifaces.ifaddresses(default_gateway_iface)
interface_addresses = netifaces.ifaddresses(default_gateway_iface)
if addr_family not in interface_addresses:
return None
return interface_addresses[addr_family][0]["addr"]
Expand Down
4 changes: 0 additions & 4 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
[mypy]
strict = True

# Don't enforce types for netifaces which has no type information
[mypy-netifaces]
ignore_missing_imports = True
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository = "https://github.com/multiplemonomials/multicast_expert"

[tool.poetry.dependencies]
python = "^3.8" # Need Python 3.8 for socket.IPPROTO_IPV6
netifaces = "0.*"
netifaces2 = "0.*"

[tool.poetry.dev-dependencies]
pytest = "^6.2"
Expand Down

0 comments on commit 40753e2

Please sign in to comment.