Gentoo Archives: gentoo-portage-dev

From: Mike Gilbert <floppym@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH 1/2] Use RTNETLINK to configure the loopback interface
Date: Fri, 30 Aug 2019 18:24:55
Message-Id: 20190830182448.1203327-1-floppym@gentoo.org
1 This eliminates the dependency on iproute2 on Linux.
2
3 Signed-off-by: Mike Gilbert <floppym@g.o>
4 ---
5 lib/portage/process.py | 26 ++++------
6 lib/portage/util/netlink.py | 98 +++++++++++++++++++++++++++++++++++++
7 2 files changed, 108 insertions(+), 16 deletions(-)
8 create mode 100644 lib/portage/util/netlink.py
9
10 diff --git a/lib/portage/process.py b/lib/portage/process.py
11 index 2a2cbd972..df63ae72f 100644
12 --- a/lib/portage/process.py
13 +++ b/lib/portage/process.py
14 @@ -10,7 +10,6 @@ import multiprocessing
15 import platform
16 import signal
17 import socket
18 -import struct
19 import subprocess
20 import sys
21 import traceback
22 @@ -489,17 +488,6 @@ def _configure_loopback_interface():
23 Configure the loopback interface.
24 """
25
26 - IFF_UP = 0x1
27 - ifreq = struct.pack('16sh', b'lo', IFF_UP)
28 - SIOCSIFFLAGS = 0x8914
29 -
30 - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
31 - try:
32 - fcntl.ioctl(sock, SIOCSIFFLAGS, ifreq)
33 - except IOError as e:
34 - writemsg("Unable to enable loopback interface: %s\n" % e.strerror, noiselevel=-1)
35 - sock.close()
36 -
37 # We add some additional addresses to work around odd behavior in glibc's
38 # getaddrinfo() implementation when the AI_ADDRCONFIG flag is set.
39 #
40 @@ -514,12 +502,18 @@ def _configure_loopback_interface():
41 # Bug: https://bugs.gentoo.org/690758
42 # Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=12377#c13
43
44 + # Avoid importing this module on systems that may not support netlink sockets.
45 + from portage.util.netlink import RtNetlink
46 +
47 try:
48 - subprocess.call(['ip', 'address', 'add', '10.0.0.1/8', 'dev', 'lo'])
49 - if _has_ipv6():
50 - subprocess.call(['ip', 'address', 'add', 'fd00::1/8', 'dev', 'lo'])
51 + with RtNetlink() as rtnl:
52 + ifindex = rtnl.get_link_ifindex(b'lo')
53 + rtnl.set_link_up(ifindex)
54 + rtnl.add_address(ifindex, socket.AF_INET, '10.0.0.1', 8)
55 + if _has_ipv6():
56 + rtnl.add_address(ifindex, socket.AF_INET6, 'fd::1', 8)
57 except EnvironmentError as e:
58 - writemsg("Error calling 'ip': %s\n" % e.strerror, noiselevel=-1)
59 + writemsg("Unable to configure loopback interface: %s\n" % e.strerror, noiselevel=-1)
60
61 def _exec(binary, mycommand, opt_name, fd_pipes,
62 env, gid, groups, uid, umask, cwd,
63 diff --git a/lib/portage/util/netlink.py b/lib/portage/util/netlink.py
64 new file mode 100644
65 index 000000000..950ed8f69
66 --- /dev/null
67 +++ b/lib/portage/util/netlink.py
68 @@ -0,0 +1,98 @@
69 +# Copyright 2019 Gentoo Authors
70 +# Distributed under the terms of the GNU General Public License v2
71 +
72 +from io import BytesIO
73 +from os import strerror
74 +from struct import Struct
75 +
76 +import socket
77 +from socket import (
78 + AF_NETLINK, AF_UNSPEC,
79 + MSG_PEEK, MSG_TRUNC,
80 + NETLINK_ROUTE,
81 + SOCK_DGRAM,
82 + inet_pton,
83 +)
84 +
85 +IFA_LOCAL = 2
86 +IFF_UP = 0x1
87 +IFLA_IFNAME = 3
88 +NLMSG_ERROR = 2
89 +RTM_NEWLINK = 16
90 +RTM_GETLINK = 18
91 +RTM_NEWADDR = 20
92 +NLM_F_REQUEST = 0x1
93 +NLM_F_ACK = 0x4
94 +NLM_F_EXCL = 0x200
95 +NLM_F_CREATE = 0x400
96 +
97 +nlmsghdr = Struct('=IHHII')
98 +nlmsgerr = Struct('i')
99 +rtattr = Struct('HH')
100 +ifinfomsg = Struct('BHiII')
101 +ifaddrmsg = Struct('BBBBi')
102 +
103 +def create_nlmsg(nlmsg_type, nlmsg_flags, nlmsg_seq, nlmsg_pid, data):
104 + nlmsg_len = nlmsghdr.size + len(data)
105 + return nlmsghdr.pack(nlmsg_len, nlmsg_type, nlmsg_flags, nlmsg_seq, nlmsg_pid) + data
106 +
107 +def create_rtattr(rta_type, data):
108 + rta_len = rtattr.size + len(data)
109 + return rtattr.pack(rta_len, rta_type) + data
110 +
111 +def parse_message(msg):
112 + buf = BytesIO(msg)
113 + hdr = nlmsghdr.unpack(buf.read(nlmsghdr.size))
114 + if hdr[1] == NLMSG_ERROR:
115 + err = nlmsgerr.unpack(buf.read(nlmsgerr.size))
116 + error = -err[0]
117 + if error != 0:
118 + raise OSError(error, strerror(error))
119 + elif hdr[1] == RTM_NEWLINK:
120 + # kernel responds to RTM_GETLINK with RTM_NEWLINK.
121 + # We only care about the ifindex for get_link_ifindex.
122 + return ifinfomsg.unpack(buf.read(ifinfomsg.size))
123 +
124 +class RtNetlink:
125 + def __init__(self):
126 + self.sock = socket.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)
127 + self.addr = (0, 0)
128 + try:
129 + self.sock.bind(self.addr)
130 + except socket.error:
131 + self.sock.close()
132 + raise
133 +
134 + def __enter__(self):
135 + return self
136 +
137 + def __exit__(self, exc_type, exc_value, traceback):
138 + self.sock.close()
139 +
140 + def send_message(self, msg):
141 + self.sock.sendto(msg, self.addr)
142 + # Messages are variable length, but 128 is enough for the the ones we care about.
143 + resp = self.sock.recv(128)
144 + return parse_message(resp)
145 +
146 + def get_link_ifindex(self, ifname):
147 + body = ifinfomsg.pack(AF_UNSPEC, 0, 0, 0, 0)
148 + body += create_rtattr(IFLA_IFNAME, ifname)
149 + flags = NLM_F_REQUEST
150 + msg = create_nlmsg(RTM_GETLINK, flags, 1, 0, body)
151 + resp = self.send_message(msg)
152 + return resp[2]
153 +
154 + def set_link_up(self, ifindex):
155 + body = ifinfomsg.pack(AF_UNSPEC, 0, ifindex, IFF_UP, IFF_UP)
156 + flags = NLM_F_REQUEST|NLM_F_ACK
157 + msg = create_nlmsg(RTM_NEWLINK, flags, 1, 0, body)
158 + self.send_message(msg)
159 +
160 + def add_address(self, ifindex, family, address, prefixlen):
161 + body = ifaddrmsg.pack(family, prefixlen, 0, 0, ifindex)
162 + addr = inet_pton(family, address)
163 + body += create_rtattr(IFA_LOCAL, addr)
164 + flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE
165 + msg = create_nlmsg(RTM_NEWADDR, flags, 1, 0, body)
166 + self.send_message(msg)
167 --
168 2.23.0

Replies