aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gilbert <floppym@gentoo.org>2019-08-30 14:24:47 -0400
committerZac Medico <zmedico@gentoo.org>2019-08-30 20:10:03 -0700
commit70ec13029e5cc8a1decfab7134d3addea8612bd7 (patch)
treecd42a485765109eb89a6d4fef9f507eabf3c020b
parentrsync: proxychains compatibility (bug 693026) (diff)
downloadportage-70ec13029e5cc8a1decfab7134d3addea8612bd7.tar.gz
portage-70ec13029e5cc8a1decfab7134d3addea8612bd7.tar.bz2
portage-70ec13029e5cc8a1decfab7134d3addea8612bd7.zip
Use RTNETLINK to configure the loopback interface
This eliminates the dependency on iproute2 on Linux. Bug: https://bugs.gentoo.org/690758 Signed-off-by: Mike Gilbert <floppym@gentoo.org> Signed-off-by: Zac Medico <zmedico@gentoo.org>
-rw-r--r--lib/portage/process.py26
-rw-r--r--lib/portage/util/netlink.py98
2 files changed, 108 insertions, 16 deletions
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)