public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface
@ 2019-08-30 18:24 Mike Gilbert
  2019-08-30 18:24 ` [gentoo-portage-dev] [PATCH 2/2] Add test case for unshare_net code in portage.process Mike Gilbert
  2019-08-31  6:39 ` [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Zac Medico
  0 siblings, 2 replies; 3+ messages in thread
From: Mike Gilbert @ 2019-08-30 18:24 UTC (permalink / raw
  To: gentoo-portage-dev

This eliminates the dependency on iproute2 on Linux.

Signed-off-by: Mike Gilbert <floppym@gentoo.org>
---
 lib/portage/process.py      | 26 ++++------
 lib/portage/util/netlink.py | 98 +++++++++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+), 16 deletions(-)
 create mode 100644 lib/portage/util/netlink.py

diff --git a/lib/portage/process.py b/lib/portage/process.py
index 2a2cbd972..df63ae72f 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -10,7 +10,6 @@ import multiprocessing
 import platform
 import signal
 import socket
-import struct
 import subprocess
 import sys
 import traceback
@@ -489,17 +488,6 @@ def _configure_loopback_interface():
 	Configure the loopback interface.
 	"""
 
-	IFF_UP = 0x1
-	ifreq = struct.pack('16sh', b'lo', IFF_UP)
-	SIOCSIFFLAGS = 0x8914
-
-	sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
-	try:
-		fcntl.ioctl(sock, SIOCSIFFLAGS, ifreq)
-	except IOError as e:
-		writemsg("Unable to enable loopback interface: %s\n" % e.strerror, noiselevel=-1)
-	sock.close()
-
 	# We add some additional addresses to work around odd behavior in glibc's
 	# getaddrinfo() implementation when the AI_ADDRCONFIG flag is set.
 	#
@@ -514,12 +502,18 @@ def _configure_loopback_interface():
 	# Bug: https://bugs.gentoo.org/690758
 	# Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=12377#c13
 
+	# Avoid importing this module on systems that may not support netlink sockets.
+	from portage.util.netlink import RtNetlink
+
 	try:
-		subprocess.call(['ip', 'address', 'add', '10.0.0.1/8', 'dev', 'lo'])
-		if _has_ipv6():
-			subprocess.call(['ip', 'address', 'add', 'fd00::1/8', 'dev', 'lo'])
+		with RtNetlink() as rtnl:
+			ifindex = rtnl.get_link_ifindex(b'lo')
+			rtnl.set_link_up(ifindex)
+			rtnl.add_address(ifindex, socket.AF_INET, '10.0.0.1', 8)
+			if _has_ipv6():
+				rtnl.add_address(ifindex, socket.AF_INET6, 'fd::1', 8)
 	except EnvironmentError as e:
-		writemsg("Error calling 'ip': %s\n" % e.strerror, noiselevel=-1)
+		writemsg("Unable to configure loopback interface: %s\n" % e.strerror, noiselevel=-1)
 
 def _exec(binary, mycommand, opt_name, fd_pipes,
 	env, gid, groups, uid, umask, cwd,
diff --git a/lib/portage/util/netlink.py b/lib/portage/util/netlink.py
new file mode 100644
index 000000000..950ed8f69
--- /dev/null
+++ b/lib/portage/util/netlink.py
@@ -0,0 +1,98 @@
+# Copyright 2019 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+from io import BytesIO
+from os import strerror
+from struct import Struct
+
+import socket
+from socket import (
+	AF_NETLINK, AF_UNSPEC,
+	MSG_PEEK, MSG_TRUNC,
+	NETLINK_ROUTE,
+	SOCK_DGRAM,
+	inet_pton,
+)
+
+IFA_LOCAL = 2
+IFF_UP = 0x1
+IFLA_IFNAME = 3
+NLMSG_ERROR = 2
+RTM_NEWLINK = 16
+RTM_GETLINK = 18
+RTM_NEWADDR = 20
+NLM_F_REQUEST = 0x1
+NLM_F_ACK = 0x4
+NLM_F_EXCL = 0x200
+NLM_F_CREATE = 0x400
+
+nlmsghdr = Struct('=IHHII')
+nlmsgerr = Struct('i')
+rtattr = Struct('HH')
+ifinfomsg = Struct('BHiII')
+ifaddrmsg = Struct('BBBBi')
+
+def create_nlmsg(nlmsg_type, nlmsg_flags, nlmsg_seq, nlmsg_pid, data):
+	nlmsg_len = nlmsghdr.size + len(data)
+	return nlmsghdr.pack(nlmsg_len, nlmsg_type, nlmsg_flags, nlmsg_seq, nlmsg_pid) + data
+
+def create_rtattr(rta_type, data):
+	rta_len = rtattr.size + len(data)
+	return rtattr.pack(rta_len, rta_type) + data
+
+def parse_message(msg):
+	buf = BytesIO(msg)
+	hdr = nlmsghdr.unpack(buf.read(nlmsghdr.size))
+	if hdr[1] == NLMSG_ERROR:
+		err = nlmsgerr.unpack(buf.read(nlmsgerr.size))
+		error = -err[0]
+		if error != 0:
+			raise OSError(error, strerror(error))
+	elif hdr[1] == RTM_NEWLINK:
+		# kernel responds to RTM_GETLINK with RTM_NEWLINK.
+		# We only care about the ifindex for get_link_ifindex.
+		return ifinfomsg.unpack(buf.read(ifinfomsg.size))
+
+class RtNetlink:
+	def __init__(self):
+		self.sock = socket.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)
+		self.addr = (0, 0)
+		try:
+			self.sock.bind(self.addr)
+		except socket.error:
+			self.sock.close()
+			raise
+
+	def __enter__(self):
+		return self
+
+	def __exit__(self, exc_type, exc_value, traceback):
+		self.sock.close()
+
+	def send_message(self, msg):
+		self.sock.sendto(msg, self.addr)
+		# Messages are variable length, but 128 is enough for the the ones we care about.
+		resp = self.sock.recv(128)
+		return parse_message(resp)
+
+	def get_link_ifindex(self, ifname):
+		body = ifinfomsg.pack(AF_UNSPEC, 0, 0, 0, 0)
+		body += create_rtattr(IFLA_IFNAME, ifname)
+		flags = NLM_F_REQUEST
+		msg = create_nlmsg(RTM_GETLINK, flags, 1, 0, body)
+		resp = self.send_message(msg)
+		return resp[2]
+
+	def set_link_up(self, ifindex):
+		body = ifinfomsg.pack(AF_UNSPEC, 0, ifindex, IFF_UP, IFF_UP)
+		flags = NLM_F_REQUEST|NLM_F_ACK
+		msg = create_nlmsg(RTM_NEWLINK, flags, 1, 0, body)
+		self.send_message(msg)
+
+	def add_address(self, ifindex, family, address, prefixlen):
+		body = ifaddrmsg.pack(family, prefixlen, 0, 0, ifindex)
+		addr = inet_pton(family, address)
+		body += create_rtattr(IFA_LOCAL, addr)
+		flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE
+		msg = create_nlmsg(RTM_NEWADDR, flags, 1, 0, body)
+		self.send_message(msg)
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-portage-dev] [PATCH 2/2] Add test case for unshare_net code in portage.process
  2019-08-30 18:24 [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Mike Gilbert
@ 2019-08-30 18:24 ` Mike Gilbert
  2019-08-31  6:39 ` [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Zac Medico
  1 sibling, 0 replies; 3+ messages in thread
From: Mike Gilbert @ 2019-08-30 18:24 UTC (permalink / raw
  To: gentoo-portage-dev

Code by Zac Medico, with some small tweaks.
---
 lib/portage/tests/process/test_unshare_net.py | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 lib/portage/tests/process/test_unshare_net.py

diff --git a/lib/portage/tests/process/test_unshare_net.py b/lib/portage/tests/process/test_unshare_net.py
new file mode 100644
index 000000000..b339cf532
--- /dev/null
+++ b/lib/portage/tests/process/test_unshare_net.py
@@ -0,0 +1,32 @@
+# Copyright 2019 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import os
+import platform
+
+import portage.process
+from portage.const import BASH_BINARY
+from portage.tests import TestCase
+
+UNSHARE_NET_TEST_SCRIPT = """
+ping -c 1 -W 1 127.0.0.1 || exit 1
+ping -c 1 -W 1 10.0.0.1 || exit 1
+[[ -n ${IPV6} ]] || exit 0
+ping -c 1 -W 1 ::1 || exit 1
+ping -c 1 -W 1 fd::1 || exit 1
+"""
+
+class UnshareNetTestCase(TestCase):
+
+	def testUnshareNet(self):
+
+		if os.geteuid() != 0:
+			self.skipTest('not root')
+		if platform.system() != 'Linux':
+			self.skipTest('not Linux')
+		if portage.process.find_binary('ping') is None:
+			self.skipTest('ping not found')
+
+		env = os.environ.copy()
+		env['IPV6'] = '1' if portage.process._has_ipv6() else ''
+		self.assertEqual(portage.process.spawn([BASH_BINARY, '-c', UNSHARE_NET_TEST_SCRIPT], unshare_net=True, env=env), 0)
-- 
2.23.0



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface
  2019-08-30 18:24 [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Mike Gilbert
  2019-08-30 18:24 ` [gentoo-portage-dev] [PATCH 2/2] Add test case for unshare_net code in portage.process Mike Gilbert
@ 2019-08-31  6:39 ` Zac Medico
  1 sibling, 0 replies; 3+ messages in thread
From: Zac Medico @ 2019-08-31  6:39 UTC (permalink / raw
  To: gentoo-portage-dev, Mike Gilbert


[-- Attachment #1.1: Type: text/plain, Size: 529 bytes --]

On 8/30/19 11:24 AM, Mike Gilbert wrote:
> This eliminates the dependency on iproute2 on Linux.
> 
> Signed-off-by: Mike Gilbert <floppym@gentoo.org>
> ---
>  lib/portage/process.py      | 26 ++++------
>  lib/portage/util/netlink.py | 98 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 108 insertions(+), 16 deletions(-)
>  create mode 100644 lib/portage/util/netlink.py

Thanks, merged:

https://gitweb.gentoo.org/proj/portage.git/commit/?id=70ec13029e5cc8a1decfab7134d3addea8612bd7
-- 
Thanks,
Zac


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 981 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-08-31  6:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-08-30 18:24 [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Mike Gilbert
2019-08-30 18:24 ` [gentoo-portage-dev] [PATCH 2/2] Add test case for unshare_net code in portage.process Mike Gilbert
2019-08-31  6:39 ` [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface Zac Medico

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox