name constraints - support IP addresses with netmask
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index d78c60f..c2a32b2 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -141,11 +141,30 @@
         oid = _obj2txt(backend, gn.d.registeredID)
         return x509.RegisteredID(x509.ObjectIdentifier(oid))
     elif gn.type == backend._lib.GEN_IPADD:
-        return x509.IPAddress(
-            ipaddress.ip_address(
-                _asn1_string_to_bytes(backend, gn.d.iPAddress)
-            )
-        )
+        data = backend._ffi.buffer(
+            gn.d.iPAddress.data, gn.d.iPAddress.length
+        )[:]
+        data_len = len(data)
+        if data_len == 8 or data_len == 32:
+            # This is an IPv4 or IPv6 Network and not a single IP. This
+            # type of data appears in Name Constraints. Unfortunately,
+            # ipaddress doesn't support packed bytes + netmask. Additionally,
+            # IPv6Network can only handle CIDR rather than the full 16 byte
+            # netmask. To handle this we convert the netmask to integer, then
+            # find the first 0 bit, which will be the prefix. If another 1
+            # bit is present after that the netmask is invalid.
+            base = ipaddress.ip_address(data[:data_len // 2])
+            netmask = utils.int_from_bytes(data[data_len // 2:], 'big')
+            bits = bin(netmask)[2:]
+            prefix = bits.find('0')
+            if bits[prefix:].find('1') != -1:
+                raise ValueError("Invalid netmask")
+
+            ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
+        else:
+            ip = ipaddress.ip_address(data)
+
+        return x509.IPAddress(ip)
     elif gn.type == backend._lib.GEN_DIRNAME:
         return x509.DirectoryName(
             _decode_x509_name(backend, gn.d.directoryName)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 993802b..7a7e79e 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2184,6 +2184,41 @@
             ]
         )
 
+    def test_permitted_excluded_with_ips(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_permitted_excluded.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        nc = cert.extensions.get_extension_for_oid(
+            x509.OID_NAME_CONSTRAINTS
+        ).value
+        assert nc == x509.NameConstraints(
+            permitted_subtrees=[
+                x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.0/24")),
+                x509.IPAddress(ipaddress.IPv6Network(u"FF:0:0:0:0:0:0:0/96")),
+            ],
+            excluded_subtrees=[
+                x509.DNSName(u".domain.com"),
+                x509.UniformResourceIdentifier(u"http://test.local"),
+            ]
+        )
+
+    def test_invalid_netmask(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "nc_invalid_ip_netmask.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        with pytest.raises(ValueError):
+            cert.extensions.get_extension_for_oid(
+                x509.OID_NAME_CONSTRAINTS
+            )
+
 
 class TestDistributionPoint(object):
     def test_distribution_point_full_name_not_general_names(self):