[pypy-commit] pypy default: Finish the support for AF_PACKET.

arigo noreply at buildbot.pypy.org
Sat Mar 14 19:44:21 CET 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r76374:936a4272826a
Date: 2015-03-14 18:49 +0100
http://bitbucket.org/pypy/pypy/changeset/936a4272826a/

Log:	Finish the support for AF_PACKET.

diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -30,7 +30,7 @@
                                space.wrap(addr.get_protocol()),
                                space.wrap(addr.get_pkttype()),
                                space.wrap(addr.get_hatype()),
-                               space.wrap(addr.get_addr())])
+                               space.wrap(addr.get_haddr())])
     elif rsocket.HAS_AF_UNIX and isinstance(addr, rsocket.UNIXAddress):
         return space.wrap(addr.get_path())
     elif rsocket.HAS_AF_NETLINK and isinstance(addr, rsocket.NETLINKAddress):
@@ -79,7 +79,7 @@
         raise NotImplementedError
 
 # XXX Hack to seperate rpython and pypy
-def addr_from_object(family, space, w_address):
+def addr_from_object(family, fd, space, w_address):
     if family == rsocket.AF_INET:
         w_host, w_port = space.unpackiterable(w_address, 2)
         host = space.str_w(w_host)
@@ -89,8 +89,9 @@
     if family == rsocket.AF_INET6:
         pieces_w = space.unpackiterable(w_address)
         if not (2 <= len(pieces_w) <= 4):
-            raise TypeError("AF_INET6 address must be a tuple of length 2 "
-                               "to 4, not %d" % len(pieces_w))
+            raise oefmt(space.w_TypeError,
+                        "AF_INET6 address must be a tuple of length 2 "
+                        "to 4, not %d", len(pieces_w))
         host = space.str_w(pieces_w[0])
         port = space.int_w(pieces_w[1])
         port = make_ushort_port(space, port)
@@ -105,6 +106,28 @@
     if rsocket.HAS_AF_NETLINK and family == rsocket.AF_NETLINK:
         w_pid, w_groups = space.unpackiterable(w_address, 2)
         return rsocket.NETLINKAddress(space.uint_w(w_pid), space.uint_w(w_groups))
+    if rsocket.HAS_AF_PACKET and family == rsocket.AF_PACKET:
+        pieces_w = space.unpackiterable(w_address)
+        if not (2 <= len(pieces_w) <= 5):
+            raise oefmt(space.w_TypeError,
+                        "AF_PACKET address must be a tuple of length 2 "
+                        "to 5, not %d", len(pieces_w))
+        ifname = space.str_w(pieces_w[0])
+        ifindex = rsocket.PacketAddress.get_ifindex_from_ifname(fd, ifname)
+        protocol = space.int_w(pieces_w[1])
+        if len(pieces_w) > 2: pkttype = space.int_w(pieces_w[2])
+        else:                 pkttype = 0
+        if len(pieces_w) > 3: hatype = space.int_w(pieces_w[3])
+        else:                 hatype = 0
+        if len(pieces_w) > 4: haddr = space.str_w(pieces_w[4])
+        else:                 haddr = ""
+        if len(haddr) > 8:
+            raise OperationError(space.w_ValueError, space.wrap(
+                "Hardware address must be 8 bytes or less"))
+        if protocol < 0 or protocol > 0xfffff:
+            raise OperationError(space.w_OverflowError, space.wrap(
+                "protoNumber must be 0-65535."))
+        return rsocket.PacketAddress(ifindex, protocol, pkttype, hatype, haddr)
     raise RSocketError("unknown address family")
 
 # XXX Hack to seperate rpython and pypy
@@ -172,7 +195,8 @@
     # convert an app-level object into an Address
     # based on the current socket's family
     def addr_from_object(self, space, w_address):
-        return addr_from_object(self.sock.family, space, w_address)
+        fd = intmask(self.sock.fd)
+        return addr_from_object(self.sock.family, fd, space, w_address)
 
     def bind_w(self, space, w_addr):
         """bind(address)
diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py
--- a/pypy/module/_socket/test/test_sock_app.py
+++ b/pypy/module/_socket/test/test_sock_app.py
@@ -1,4 +1,4 @@
-import sys
+import sys, os
 import py
 from pypy.tool.pytest.objspace import gettestobjspace
 from rpython.tool.udir import udir
@@ -615,6 +615,28 @@
             os.chdir(oldcwd)
 
 
+class AppTestPacket:
+    def setup_class(cls):
+        if not hasattr(os, 'getuid') or os.getuid() != 0:
+            py.test.skip("AF_PACKET needs to be root for testing")
+        w_ok = space.appexec([], "(): import _socket; " +
+                                 "return hasattr(_socket, 'AF_PACKET')")
+        if not space.is_true(w_ok):
+            py.test.skip("no AF_PACKET on this platform")
+        cls.space = space
+
+    def test_convert_between_tuple_and_sockaddr_ll(self):
+        import _socket
+        s = _socket.socket(_socket.AF_PACKET, _socket.SOCK_RAW)
+        assert s.getsockname() == ('', 0, 0, 0, '')
+        s.bind(('lo', 123))
+        a, b, c, d, e = s.getsockname()
+        assert (a, b, c) == ('lo', 123, 0)
+        assert isinstance(d, int)
+        assert isinstance(e, str)
+        assert 0 <= len(e) <= 8
+
+
 class AppTestSocketTCP:
     HOST = 'localhost'
 
diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py
--- a/rpython/rlib/_rsocket_rffi.py
+++ b/rpython/rlib/_rsocket_rffi.py
@@ -199,7 +199,7 @@
 WSA_INVALID_PARAMETER WSA_NOT_ENOUGH_MEMORY WSA_OPERATION_ABORTED
 SIO_RCVALL SIO_KEEPALIVE_VALS
 
-SIOCGIFNAME
+SIOCGIFNAME SIOCGIFINDEX
 '''.split()
 
 for name in constant_names:
@@ -328,7 +328,8 @@
 
     if _HAS_AF_PACKET:
         CConfig.sockaddr_ll = platform.Struct('struct sockaddr_ll',
-                              [('sll_ifindex', rffi.INT),
+                              [('sll_family', rffi.INT),
+                               ('sll_ifindex', rffi.INT),
                                ('sll_protocol', rffi.INT),
                                ('sll_pkttype', rffi.INT),
                                ('sll_hatype', rffi.INT),
diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py
--- a/rpython/rlib/rsocket.py
+++ b/rpython/rlib/rsocket.py
@@ -200,23 +200,49 @@
         family = AF_PACKET
         struct = _c.sockaddr_ll
         maxlen = minlen = sizeof(struct)
+        ifr_name_size = _c.ifreq.c_ifr_name.length
+        sll_addr_size = _c.sockaddr_ll.c_sll_addr.length
+
+        def __init__(self, ifindex, protocol, pkttype=0, hatype=0, haddr=""):
+            addr = lltype.malloc(_c.sockaddr_ll, flavor='raw', zero=True,
+                                 track_allocation=False)
+            self.setdata(addr, PacketAddress.maxlen)
+            rffi.setintfield(addr, 'c_sll_family', AF_PACKET)
+            rffi.setintfield(addr, 'c_sll_protocol', htons(protocol))
+            rffi.setintfield(addr, 'c_sll_ifindex', ifindex)
+            rffi.setintfield(addr, 'c_sll_pkttype', pkttype)
+            rffi.setintfield(addr, 'c_sll_hatype', hatype)
+            halen = rffi.str2chararray(haddr,
+                                       rffi.cast(rffi.CCHARP, addr.c_sll_addr),
+                                       PacketAddress.sll_addr_size)
+            rffi.setintfield(addr, 'c_sll_halen', halen)
+
+        @staticmethod
+        def get_ifindex_from_ifname(fd, ifname):
+            p = lltype.malloc(_c.ifreq, flavor='raw')
+            iflen = rffi.str2chararray(ifname,
+                                       rffi.cast(rffi.CCHARP, p.c_ifr_name),
+                                       PacketAddress.ifr_name_size - 1)
+            p.c_ifr_name[iflen] = '\0'
+            err = _c.ioctl(fd, _c.SIOCGIFINDEX, p)
+            ifindex = p.c_ifr_ifindex
+            lltype.free(p, flavor='raw')
+            if err != 0:
+                raise RSocketError("invalid interface name")
+            return ifindex
 
         def get_ifname(self, fd):
+            ifname = ""
             a = self.lock(_c.sockaddr_ll)
-            p = lltype.malloc(_c.ifreq, flavor='raw')
-            rffi.setintfield(p, 'c_ifr_ifindex',
-                             rffi.getintfield(a, 'c_sll_ifindex'))
-            if (_c.ioctl(fd, _c.SIOCGIFNAME, p) == 0):
-                # eh, the iface name is a constant length array
-                i = 0
-                d = []
-                while p.c_ifr_name[i] != '\x00' and i < len(p.c_ifr_name):
-                    d.append(p.c_ifr_name[i])
-                    i += 1
-                ifname = ''.join(d)
-            else:
-                ifname = ""
-            lltype.free(p, flavor='raw')
+            ifindex = rffi.getintfield(a, 'c_sll_ifindex')
+            if ifindex:
+                p = lltype.malloc(_c.ifreq, flavor='raw')
+                rffi.setintfield(p, 'c_ifr_ifindex', ifindex)
+                if (_c.ioctl(fd, _c.SIOCGIFNAME, p) == 0):
+                    ifname = rffi.charp2strn(
+                        rffi.cast(rffi.CCHARP, p.c_ifr_name),
+                        PacketAddress.ifr_name_size)
+                lltype.free(p, flavor='raw')
             self.unlock()
             return ifname
 
@@ -235,11 +261,11 @@
 
         def get_hatype(self):
             a = self.lock(_c.sockaddr_ll)
-            res = bool(rffi.getintfield(a, 'c_sll_hatype'))
+            res = rffi.getintfield(a, 'c_sll_hatype')
             self.unlock()
             return res
 
-        def get_addr(self):
+        def get_haddr(self):
             a = self.lock(_c.sockaddr_ll)
             lgt = rffi.getintfield(a, 'c_sll_halen')
             d = []
diff --git a/rpython/rtyper/lltypesystem/rffi.py b/rpython/rtyper/lltypesystem/rffi.py
--- a/rpython/rtyper/lltypesystem/rffi.py
+++ b/rpython/rtyper/lltypesystem/rffi.py
@@ -796,8 +796,10 @@
 
     # str -> already-existing char[maxsize]
     def str2chararray(s, array, maxsize):
+        length = min(len(s), maxsize)
         ll_s = llstrtype(s)
-        copy_string_to_raw(ll_s, array, 0, min(len(s), maxsize))
+        copy_string_to_raw(ll_s, array, 0, length)
+        return length
     str2chararray._annenforceargs_ = [strtype, None, int]
 
     # char* -> str
diff --git a/rpython/rtyper/lltypesystem/test/test_rffi.py b/rpython/rtyper/lltypesystem/test/test_rffi.py
--- a/rpython/rtyper/lltypesystem/test/test_rffi.py
+++ b/rpython/rtyper/lltypesystem/test/test_rffi.py
@@ -682,16 +682,16 @@
                             compilation_info=eci)
         def f():
             raw = str2charp("XxxZy")
-            str2chararray("abcdef", raw, 4)
+            n = str2chararray("abcdef", raw, 4)
             assert raw[0] == 'a'
             assert raw[1] == 'b'
             assert raw[2] == 'c'
             assert raw[3] == 'd'
             assert raw[4] == 'y'
             lltype.free(raw, flavor='raw')
-            return 0
+            return n
 
-        assert interpret(f, []) == 0
+        assert interpret(f, []) == 4
 
     def test_around_extcall(self):
         if sys.platform == "win32":


More information about the pypy-commit mailing list