Repository: giampaolo/psutil

Issue:
(Windows) net_if_addrs() should return broadcast address
Netmask and broadcast addresses are currently not returned on Windows. https://pypi.python.org/pypi/netifaces/ lib support both but the code is kinda twisted and not so easy to port. CC @mrjefftang in case you want to give this a try.

Edit: netmask added as of #874

Discussion:
@giampaolo: ...also, now we have `ptp` (point to point) addresses as well (see https://github.com/giampaolo/psutil/pull/668) but I'm not sure whether Windows support them.
@mrjefftang: http://stackoverflow.com/questions/14119348/what-is-the-iphelper-api-to-retrieve-netmask-information
@richard-kinder: Any update on this request? I'm happy to help get this implemented.
@giampaolo: Feel free to make a pull request. Personally I don't think I will do it any
time soon.
Il 23/mar/2016 05:25, "richard-kinder" notifications@github.com ha
scritto:

> Any update on this request? I'm happy to help get this implemented.
> 
> —
> You are receiving this because you authored the thread.
> Reply to this email directly or view it on GitHub
> https://github.com/giampaolo/psutil/issues/669#issuecomment-200060700
@giampaolo: Here's a PR for netmask: https://github.com/giampaolo/psutil/pull/874
@Ragzouken: I don't know psutil well enough to attempt a PR integrating it, but with the address and netmask you can compute the broadcast address like so: 
```
import ipaddress
broadcast = ipaddress.IPv4Network(f"{address}/{netmask}", strict=False).broadcast_address
```@giampaolo: Thanks a lot for the hint. Implemented in https://github.com/giampaolo/psutil/pull/2501.

Solution patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 5b32811e..cd7ad7e8 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -7,6 +7,8 @@ XXXX-XX-XX
 
 **Enhancements**
 
+- 669_, [Windows]: `net_if_addrs()`_ also returns the ``broadcast`` address
+  instead of ``None``.
 - 2480_: Python 2.7 is no longer supported. Latest version supporting Python
   2.7 is psutil 6.1.X. Install it with: ``pip2 install psutil==6.1.*``.
 - 2490_: removed long deprecated ``Process.memory_info_ex()`` method. It was
@@ -15,7 +17,7 @@ XXXX-XX-XX
 
 **Bug fixes**
 
-- 2496_, [Linux]: Avoid segfault (a cPython bug) on ``Process.memory_maps()`` 
+- 2496_, [Linux]: Avoid segfault (a cPython bug) on ``Process.memory_maps()``
   for processes that use hundreds of GBs of memory.
 
 **Compatibility notes**
diff --git a/docs/index.rst b/docs/index.rst
index bde76d0b..3fd122cc 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -734,6 +734,9 @@ Network
   .. versionchanged:: 4.4.0 added support for *netmask* field on Windows which
     is no longer ``None``.
 
+  .. versionchanged:: 7.0.0 added support for *broadcast* field on Windows
+    which is no longer ``None``.
+
 .. function:: net_if_stats()
 
   Return information about each NIC (network interface card) installed on the
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 605138e2..cf4a5805 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -2228,6 +2228,7 @@ def net_if_addrs():
                 # We re-set the family here so that repr(family)
                 # will show AF_LINK rather than AF_PACKET
                 fam = _psplatform.AF_LINK
+
         if fam == _psplatform.AF_LINK:
             # The underlying C function may return an incomplete MAC
             # address in which case we fill it with null bytes, see:
@@ -2235,7 +2236,22 @@ def net_if_addrs():
             separator = ":" if POSIX else "-"
             while addr.count(separator) < 5:
                 addr += f"{separator}00"
-        ret[name].append(_common.snicaddr(fam, addr, mask, broadcast, ptp))
+
+        nt = _common.snicaddr(fam, addr, mask, broadcast, ptp)
+
+        # On Windows broadcast is None, so we determine it via
+        # ipaddress module.
+        if WINDOWS and fam in {socket.AF_INET, socket.AF_INET6}:
+            try:
+                broadcast = _common.broadcast_addr(nt)
+            except Exception as err:  # noqa: BLE001
+                debug(err)
+            else:
+                if broadcast is not None:
+                    nt._replace(broadcast=broadcast)
+
+        ret[name].append(nt)
+
     return dict(ret)
 
 
diff --git a/psutil/_common.py b/psutil/_common.py
index 0c385fa6..c8bcc760 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -2,11 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Common objects shared by __init__.py and _ps*.py modules."""
-
-# Note: this module is imported by setup.py so it should not import
-# psutil or third-party modules.
+"""Common objects shared by __init__.py and _ps*.py modules.
 
+Note: this module is imported by setup.py, so it should not import
+psutil or third-party modules.
+"""
 
 import collections
 import enum
@@ -603,6 +603,28 @@ def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
         return sconn(fd, fam, type_, laddr, raddr, status, pid)
 
 
+def broadcast_addr(addr):
+    """Given the address ntuple returned by ``net_if_addrs()``
+    calculates the broadcast address.
+    """
+    import ipaddress
+
+    if not addr.address or not addr.netmask:
+        return None
+    if addr.family == socket.AF_INET:
+        return str(
+            ipaddress.IPv4Network(
+                f"{addr.address}/{addr.netmask}", strict=False
+            ).broadcast_address
+        )
+    if addr.family == socket.AF_INET6:
+        return str(
+            ipaddress.IPv6Network(
+                f"{addr.address}/{addr.netmask}", strict=False
+            ).broadcast_address
+        )
+
+
 def deprecated_method(replacement):
     """A decorator which can be used to mark a method as deprecated
     'replcement' is the method name which will be called instead.


Test patch:
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index e2efca14..e28e04b4 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -30,6 +30,7 @@ from psutil import OPENBSD
 from psutil import POSIX
 from psutil import SUNOS
 from psutil import WINDOWS
+from psutil._common import broadcast_addr
 from psutil.tests import ASCII_FS
 from psutil.tests import CI_TESTING
 from psutil.tests import GITHUB_ACTIONS
@@ -858,6 +859,14 @@ class TestNetAPIs(PsutilTestCase):
                 elif addr.ptp:
                     assert addr.broadcast is None
 
+                # check broadcast address
+                if (
+                    addr.broadcast
+                    and addr.netmask
+                    and addr.family in {socket.AF_INET, socket.AF_INET6}
+                ):
+                    assert addr.broadcast == broadcast_addr(addr)
+
         if BSD or MACOS or SUNOS:
             if hasattr(socket, "AF_LINK"):
                 assert psutil.AF_LINK == socket.AF_LINK