xref: /freebsd/tests/atf_python/sys/net/rtsock.py (revision 8eb2bee6c0f4957c6c1cea826e59cda4d18a2a64)
1*8eb2bee6SAlexander V. Chernikov#!/usr/local/bin/python3
2*8eb2bee6SAlexander V. Chernikovimport os
3*8eb2bee6SAlexander V. Chernikovimport socket
4*8eb2bee6SAlexander V. Chernikovimport struct
5*8eb2bee6SAlexander V. Chernikovimport sys
6*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_byte
7*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_char
8*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_int
9*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_long
10*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_uint32
11*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_ulong
12*8eb2bee6SAlexander V. Chernikovfrom ctypes import c_ushort
13*8eb2bee6SAlexander V. Chernikovfrom ctypes import sizeof
14*8eb2bee6SAlexander V. Chernikovfrom ctypes import Structure
15*8eb2bee6SAlexander V. Chernikovfrom typing import Dict
16*8eb2bee6SAlexander V. Chernikovfrom typing import List
17*8eb2bee6SAlexander V. Chernikovfrom typing import Optional
18*8eb2bee6SAlexander V. Chernikovfrom typing import Union
19*8eb2bee6SAlexander V. Chernikov
20*8eb2bee6SAlexander V. Chernikov
21*8eb2bee6SAlexander V. Chernikovdef roundup2(val: int, num: int) -> int:
22*8eb2bee6SAlexander V. Chernikov    if val % num:
23*8eb2bee6SAlexander V. Chernikov        return (val | (num - 1)) + 1
24*8eb2bee6SAlexander V. Chernikov    else:
25*8eb2bee6SAlexander V. Chernikov        return val
26*8eb2bee6SAlexander V. Chernikov
27*8eb2bee6SAlexander V. Chernikov
28*8eb2bee6SAlexander V. Chernikovclass RtSockException(OSError):
29*8eb2bee6SAlexander V. Chernikov    pass
30*8eb2bee6SAlexander V. Chernikov
31*8eb2bee6SAlexander V. Chernikov
32*8eb2bee6SAlexander V. Chernikovclass RtConst:
33*8eb2bee6SAlexander V. Chernikov    RTM_VERSION = 5
34*8eb2bee6SAlexander V. Chernikov    ALIGN = sizeof(c_long)
35*8eb2bee6SAlexander V. Chernikov
36*8eb2bee6SAlexander V. Chernikov    AF_INET = socket.AF_INET
37*8eb2bee6SAlexander V. Chernikov    AF_INET6 = socket.AF_INET6
38*8eb2bee6SAlexander V. Chernikov    AF_LINK = socket.AF_LINK
39*8eb2bee6SAlexander V. Chernikov
40*8eb2bee6SAlexander V. Chernikov    RTA_DST = 0x1
41*8eb2bee6SAlexander V. Chernikov    RTA_GATEWAY = 0x2
42*8eb2bee6SAlexander V. Chernikov    RTA_NETMASK = 0x4
43*8eb2bee6SAlexander V. Chernikov    RTA_GENMASK = 0x8
44*8eb2bee6SAlexander V. Chernikov    RTA_IFP = 0x10
45*8eb2bee6SAlexander V. Chernikov    RTA_IFA = 0x20
46*8eb2bee6SAlexander V. Chernikov    RTA_AUTHOR = 0x40
47*8eb2bee6SAlexander V. Chernikov    RTA_BRD = 0x80
48*8eb2bee6SAlexander V. Chernikov
49*8eb2bee6SAlexander V. Chernikov    RTM_ADD = 1
50*8eb2bee6SAlexander V. Chernikov    RTM_DELETE = 2
51*8eb2bee6SAlexander V. Chernikov    RTM_CHANGE = 3
52*8eb2bee6SAlexander V. Chernikov    RTM_GET = 4
53*8eb2bee6SAlexander V. Chernikov
54*8eb2bee6SAlexander V. Chernikov    RTF_UP = 0x1
55*8eb2bee6SAlexander V. Chernikov    RTF_GATEWAY = 0x2
56*8eb2bee6SAlexander V. Chernikov    RTF_HOST = 0x4
57*8eb2bee6SAlexander V. Chernikov    RTF_REJECT = 0x8
58*8eb2bee6SAlexander V. Chernikov    RTF_DYNAMIC = 0x10
59*8eb2bee6SAlexander V. Chernikov    RTF_MODIFIED = 0x20
60*8eb2bee6SAlexander V. Chernikov    RTF_DONE = 0x40
61*8eb2bee6SAlexander V. Chernikov    RTF_XRESOLVE = 0x200
62*8eb2bee6SAlexander V. Chernikov    RTF_LLINFO = 0x400
63*8eb2bee6SAlexander V. Chernikov    RTF_LLDATA = 0x400
64*8eb2bee6SAlexander V. Chernikov    RTF_STATIC = 0x800
65*8eb2bee6SAlexander V. Chernikov    RTF_BLACKHOLE = 0x1000
66*8eb2bee6SAlexander V. Chernikov    RTF_PROTO2 = 0x4000
67*8eb2bee6SAlexander V. Chernikov    RTF_PROTO1 = 0x8000
68*8eb2bee6SAlexander V. Chernikov    RTF_PROTO3 = 0x40000
69*8eb2bee6SAlexander V. Chernikov    RTF_FIXEDMTU = 0x80000
70*8eb2bee6SAlexander V. Chernikov    RTF_PINNED = 0x100000
71*8eb2bee6SAlexander V. Chernikov    RTF_LOCAL = 0x200000
72*8eb2bee6SAlexander V. Chernikov    RTF_BROADCAST = 0x400000
73*8eb2bee6SAlexander V. Chernikov    RTF_MULTICAST = 0x800000
74*8eb2bee6SAlexander V. Chernikov    RTF_STICKY = 0x10000000
75*8eb2bee6SAlexander V. Chernikov    RTF_RNH_LOCKED = 0x40000000
76*8eb2bee6SAlexander V. Chernikov    RTF_GWFLAG_COMPAT = 0x80000000
77*8eb2bee6SAlexander V. Chernikov
78*8eb2bee6SAlexander V. Chernikov    RTV_MTU = 0x1
79*8eb2bee6SAlexander V. Chernikov    RTV_HOPCOUNT = 0x2
80*8eb2bee6SAlexander V. Chernikov    RTV_EXPIRE = 0x4
81*8eb2bee6SAlexander V. Chernikov    RTV_RPIPE = 0x8
82*8eb2bee6SAlexander V. Chernikov    RTV_SPIPE = 0x10
83*8eb2bee6SAlexander V. Chernikov    RTV_SSTHRESH = 0x20
84*8eb2bee6SAlexander V. Chernikov    RTV_RTT = 0x40
85*8eb2bee6SAlexander V. Chernikov    RTV_RTTVAR = 0x80
86*8eb2bee6SAlexander V. Chernikov    RTV_WEIGHT = 0x100
87*8eb2bee6SAlexander V. Chernikov
88*8eb2bee6SAlexander V. Chernikov    @staticmethod
89*8eb2bee6SAlexander V. Chernikov    def get_props(prefix: str) -> List[str]:
90*8eb2bee6SAlexander V. Chernikov        return [n for n in dir(RtConst) if n.startswith(prefix)]
91*8eb2bee6SAlexander V. Chernikov
92*8eb2bee6SAlexander V. Chernikov    @staticmethod
93*8eb2bee6SAlexander V. Chernikov    def get_name(prefix: str, value: int) -> str:
94*8eb2bee6SAlexander V. Chernikov        props = RtConst.get_props(prefix)
95*8eb2bee6SAlexander V. Chernikov        for prop in props:
96*8eb2bee6SAlexander V. Chernikov            if getattr(RtConst, prop) == value:
97*8eb2bee6SAlexander V. Chernikov                return prop
98*8eb2bee6SAlexander V. Chernikov        return "U:{}:{}".format(prefix, value)
99*8eb2bee6SAlexander V. Chernikov
100*8eb2bee6SAlexander V. Chernikov    @staticmethod
101*8eb2bee6SAlexander V. Chernikov    def get_bitmask_map(prefix: str, value: int) -> Dict[int, str]:
102*8eb2bee6SAlexander V. Chernikov        props = RtConst.get_props(prefix)
103*8eb2bee6SAlexander V. Chernikov        propmap = {getattr(RtConst, prop): prop for prop in props}
104*8eb2bee6SAlexander V. Chernikov        v = 1
105*8eb2bee6SAlexander V. Chernikov        ret = {}
106*8eb2bee6SAlexander V. Chernikov        while value:
107*8eb2bee6SAlexander V. Chernikov            if v & value:
108*8eb2bee6SAlexander V. Chernikov                if v in propmap:
109*8eb2bee6SAlexander V. Chernikov                    ret[v] = propmap[v]
110*8eb2bee6SAlexander V. Chernikov                else:
111*8eb2bee6SAlexander V. Chernikov                    ret[v] = hex(v)
112*8eb2bee6SAlexander V. Chernikov                value -= v
113*8eb2bee6SAlexander V. Chernikov            v *= 2
114*8eb2bee6SAlexander V. Chernikov        return ret
115*8eb2bee6SAlexander V. Chernikov
116*8eb2bee6SAlexander V. Chernikov    @staticmethod
117*8eb2bee6SAlexander V. Chernikov    def get_bitmask_str(prefix: str, value: int) -> str:
118*8eb2bee6SAlexander V. Chernikov        bmap = RtConst.get_bitmask_map(prefix, value)
119*8eb2bee6SAlexander V. Chernikov        return ",".join([v for k, v in bmap.items()])
120*8eb2bee6SAlexander V. Chernikov
121*8eb2bee6SAlexander V. Chernikov
122*8eb2bee6SAlexander V. Chernikovclass RtMetrics(Structure):
123*8eb2bee6SAlexander V. Chernikov    _fields_ = [
124*8eb2bee6SAlexander V. Chernikov        ("rmx_locks", c_ulong),
125*8eb2bee6SAlexander V. Chernikov        ("rmx_mtu", c_ulong),
126*8eb2bee6SAlexander V. Chernikov        ("rmx_hopcount", c_ulong),
127*8eb2bee6SAlexander V. Chernikov        ("rmx_expire", c_ulong),
128*8eb2bee6SAlexander V. Chernikov        ("rmx_recvpipe", c_ulong),
129*8eb2bee6SAlexander V. Chernikov        ("rmx_sendpipe", c_ulong),
130*8eb2bee6SAlexander V. Chernikov        ("rmx_ssthresh", c_ulong),
131*8eb2bee6SAlexander V. Chernikov        ("rmx_rtt", c_ulong),
132*8eb2bee6SAlexander V. Chernikov        ("rmx_rttvar", c_ulong),
133*8eb2bee6SAlexander V. Chernikov        ("rmx_pksent", c_ulong),
134*8eb2bee6SAlexander V. Chernikov        ("rmx_weight", c_ulong),
135*8eb2bee6SAlexander V. Chernikov        ("rmx_nhidx", c_ulong),
136*8eb2bee6SAlexander V. Chernikov        ("rmx_filler", c_ulong * 2),
137*8eb2bee6SAlexander V. Chernikov    ]
138*8eb2bee6SAlexander V. Chernikov
139*8eb2bee6SAlexander V. Chernikov
140*8eb2bee6SAlexander V. Chernikovclass RtMsgHdr(Structure):
141*8eb2bee6SAlexander V. Chernikov    _fields_ = [
142*8eb2bee6SAlexander V. Chernikov        ("rtm_msglen", c_ushort),
143*8eb2bee6SAlexander V. Chernikov        ("rtm_version", c_byte),
144*8eb2bee6SAlexander V. Chernikov        ("rtm_type", c_byte),
145*8eb2bee6SAlexander V. Chernikov        ("rtm_index", c_ushort),
146*8eb2bee6SAlexander V. Chernikov        ("_rtm_spare1", c_ushort),
147*8eb2bee6SAlexander V. Chernikov        ("rtm_flags", c_int),
148*8eb2bee6SAlexander V. Chernikov        ("rtm_addrs", c_int),
149*8eb2bee6SAlexander V. Chernikov        ("rtm_pid", c_int),
150*8eb2bee6SAlexander V. Chernikov        ("rtm_seq", c_int),
151*8eb2bee6SAlexander V. Chernikov        ("rtm_errno", c_int),
152*8eb2bee6SAlexander V. Chernikov        ("rtm_fmask", c_int),
153*8eb2bee6SAlexander V. Chernikov        ("rtm_inits", c_ulong),
154*8eb2bee6SAlexander V. Chernikov        ("rtm_rmx", RtMetrics),
155*8eb2bee6SAlexander V. Chernikov    ]
156*8eb2bee6SAlexander V. Chernikov
157*8eb2bee6SAlexander V. Chernikov
158*8eb2bee6SAlexander V. Chernikovclass SockaddrIn(Structure):
159*8eb2bee6SAlexander V. Chernikov    _fields_ = [
160*8eb2bee6SAlexander V. Chernikov        ("sin_len", c_byte),
161*8eb2bee6SAlexander V. Chernikov        ("sin_family", c_byte),
162*8eb2bee6SAlexander V. Chernikov        ("sin_port", c_ushort),
163*8eb2bee6SAlexander V. Chernikov        ("sin_addr", c_uint32),
164*8eb2bee6SAlexander V. Chernikov        ("sin_zero", c_char * 8),
165*8eb2bee6SAlexander V. Chernikov    ]
166*8eb2bee6SAlexander V. Chernikov
167*8eb2bee6SAlexander V. Chernikov
168*8eb2bee6SAlexander V. Chernikovclass SockaddrIn6(Structure):
169*8eb2bee6SAlexander V. Chernikov    _fields_ = [
170*8eb2bee6SAlexander V. Chernikov        ("sin6_len", c_byte),
171*8eb2bee6SAlexander V. Chernikov        ("sin6_family", c_byte),
172*8eb2bee6SAlexander V. Chernikov        ("sin6_port", c_ushort),
173*8eb2bee6SAlexander V. Chernikov        ("sin6_flowinfo", c_uint32),
174*8eb2bee6SAlexander V. Chernikov        ("sin6_addr", c_byte * 16),
175*8eb2bee6SAlexander V. Chernikov        ("sin6_scope_id", c_uint32),
176*8eb2bee6SAlexander V. Chernikov    ]
177*8eb2bee6SAlexander V. Chernikov
178*8eb2bee6SAlexander V. Chernikov
179*8eb2bee6SAlexander V. Chernikovclass SockaddrDl(Structure):
180*8eb2bee6SAlexander V. Chernikov    _fields_ = [
181*8eb2bee6SAlexander V. Chernikov        ("sdl_len", c_byte),
182*8eb2bee6SAlexander V. Chernikov        ("sdl_family", c_byte),
183*8eb2bee6SAlexander V. Chernikov        ("sdl_index", c_ushort),
184*8eb2bee6SAlexander V. Chernikov        ("sdl_type", c_byte),
185*8eb2bee6SAlexander V. Chernikov        ("sdl_nlen", c_byte),
186*8eb2bee6SAlexander V. Chernikov        ("sdl_alen", c_byte),
187*8eb2bee6SAlexander V. Chernikov        ("sdl_slen", c_byte),
188*8eb2bee6SAlexander V. Chernikov        ("sdl_data", c_byte * 8),
189*8eb2bee6SAlexander V. Chernikov    ]
190*8eb2bee6SAlexander V. Chernikov
191*8eb2bee6SAlexander V. Chernikov
192*8eb2bee6SAlexander V. Chernikovclass SaHelper(object):
193*8eb2bee6SAlexander V. Chernikov    @staticmethod
194*8eb2bee6SAlexander V. Chernikov    def is_ipv6(ip: str) -> bool:
195*8eb2bee6SAlexander V. Chernikov        return ":" in ip
196*8eb2bee6SAlexander V. Chernikov
197*8eb2bee6SAlexander V. Chernikov    @staticmethod
198*8eb2bee6SAlexander V. Chernikov    def ip_sa(ip: str, scopeid: int = 0) -> bytes:
199*8eb2bee6SAlexander V. Chernikov        if SaHelper.is_ipv6(ip):
200*8eb2bee6SAlexander V. Chernikov            return SaHelper.ip6_sa(ip, scopeid)
201*8eb2bee6SAlexander V. Chernikov        else:
202*8eb2bee6SAlexander V. Chernikov            return SaHelper.ip4_sa(ip)
203*8eb2bee6SAlexander V. Chernikov
204*8eb2bee6SAlexander V. Chernikov    @staticmethod
205*8eb2bee6SAlexander V. Chernikov    def ip4_sa(ip: str) -> bytes:
206*8eb2bee6SAlexander V. Chernikov        addr_int = int.from_bytes(socket.inet_pton(2, ip), sys.byteorder)
207*8eb2bee6SAlexander V. Chernikov        sin = SockaddrIn(sizeof(SockaddrIn), socket.AF_INET, 0, addr_int)
208*8eb2bee6SAlexander V. Chernikov        return bytes(sin)
209*8eb2bee6SAlexander V. Chernikov
210*8eb2bee6SAlexander V. Chernikov    @staticmethod
211*8eb2bee6SAlexander V. Chernikov    def ip6_sa(ip6: str, scopeid: int) -> bytes:
212*8eb2bee6SAlexander V. Chernikov        addr_bytes = (c_byte * 16)()
213*8eb2bee6SAlexander V. Chernikov        for i, b in enumerate(socket.inet_pton(socket.AF_INET6, ip6)):
214*8eb2bee6SAlexander V. Chernikov            addr_bytes[i] = b
215*8eb2bee6SAlexander V. Chernikov        sin6 = SockaddrIn6(
216*8eb2bee6SAlexander V. Chernikov            sizeof(SockaddrIn6), socket.AF_INET6, 0, 0, addr_bytes, scopeid
217*8eb2bee6SAlexander V. Chernikov        )
218*8eb2bee6SAlexander V. Chernikov        return bytes(sin6)
219*8eb2bee6SAlexander V. Chernikov
220*8eb2bee6SAlexander V. Chernikov    @staticmethod
221*8eb2bee6SAlexander V. Chernikov    def link_sa(ifindex: int = 0, iftype: int = 0) -> bytes:
222*8eb2bee6SAlexander V. Chernikov        sa = SockaddrDl(sizeof(SockaddrDl), socket.AF_LINK, c_ushort(ifindex), iftype)
223*8eb2bee6SAlexander V. Chernikov        return bytes(sa)
224*8eb2bee6SAlexander V. Chernikov
225*8eb2bee6SAlexander V. Chernikov    @staticmethod
226*8eb2bee6SAlexander V. Chernikov    def pxlen4_sa(pxlen: int) -> bytes:
227*8eb2bee6SAlexander V. Chernikov        return SaHelper.ip_sa(SaHelper.pxlen_to_ip4(pxlen))
228*8eb2bee6SAlexander V. Chernikov
229*8eb2bee6SAlexander V. Chernikov    @staticmethod
230*8eb2bee6SAlexander V. Chernikov    def pxlen_to_ip4(pxlen: int) -> str:
231*8eb2bee6SAlexander V. Chernikov        if pxlen == 32:
232*8eb2bee6SAlexander V. Chernikov            return "255.255.255.255"
233*8eb2bee6SAlexander V. Chernikov        else:
234*8eb2bee6SAlexander V. Chernikov            addr = 0xFFFFFFFF - ((1 << (32 - pxlen)) - 1)
235*8eb2bee6SAlexander V. Chernikov            addr_bytes = struct.pack("!I", addr)
236*8eb2bee6SAlexander V. Chernikov            return socket.inet_ntop(socket.AF_INET, addr_bytes)
237*8eb2bee6SAlexander V. Chernikov
238*8eb2bee6SAlexander V. Chernikov    @staticmethod
239*8eb2bee6SAlexander V. Chernikov    def pxlen6_sa(pxlen: int) -> bytes:
240*8eb2bee6SAlexander V. Chernikov        return SaHelper.ip_sa(SaHelper.pxlen_to_ip6(pxlen))
241*8eb2bee6SAlexander V. Chernikov
242*8eb2bee6SAlexander V. Chernikov    @staticmethod
243*8eb2bee6SAlexander V. Chernikov    def pxlen_to_ip6(pxlen: int) -> str:
244*8eb2bee6SAlexander V. Chernikov        ip6_b = [0] * 16
245*8eb2bee6SAlexander V. Chernikov        start = 0
246*8eb2bee6SAlexander V. Chernikov        while pxlen > 8:
247*8eb2bee6SAlexander V. Chernikov            ip6_b[start] = 0xFF
248*8eb2bee6SAlexander V. Chernikov            pxlen -= 8
249*8eb2bee6SAlexander V. Chernikov            start += 1
250*8eb2bee6SAlexander V. Chernikov        ip6_b[start] = 0xFF - ((1 << (8 - pxlen)) - 1)
251*8eb2bee6SAlexander V. Chernikov        return socket.inet_ntop(socket.AF_INET6, bytes(ip6_b))
252*8eb2bee6SAlexander V. Chernikov
253*8eb2bee6SAlexander V. Chernikov    @staticmethod
254*8eb2bee6SAlexander V. Chernikov    def print_sa_inet(sa: bytes):
255*8eb2bee6SAlexander V. Chernikov        if len(sa) < 8:
256*8eb2bee6SAlexander V. Chernikov            raise RtSockException("IPv4 sa size too small: {}".format(len(sa)))
257*8eb2bee6SAlexander V. Chernikov        addr = socket.inet_ntop(socket.AF_INET, sa[4:8])
258*8eb2bee6SAlexander V. Chernikov        return "{}".format(addr)
259*8eb2bee6SAlexander V. Chernikov
260*8eb2bee6SAlexander V. Chernikov    @staticmethod
261*8eb2bee6SAlexander V. Chernikov    def print_sa_inet6(sa: bytes):
262*8eb2bee6SAlexander V. Chernikov        if len(sa) < sizeof(SockaddrIn6):
263*8eb2bee6SAlexander V. Chernikov            raise RtSockException("IPv6 sa size too small: {}".format(len(sa)))
264*8eb2bee6SAlexander V. Chernikov        addr = socket.inet_ntop(socket.AF_INET6, sa[8:24])
265*8eb2bee6SAlexander V. Chernikov        scopeid = struct.unpack(">I", sa[24:28])[0]
266*8eb2bee6SAlexander V. Chernikov        return "{} scopeid {}".format(addr, scopeid)
267*8eb2bee6SAlexander V. Chernikov
268*8eb2bee6SAlexander V. Chernikov    @staticmethod
269*8eb2bee6SAlexander V. Chernikov    def print_sa_link(sa: bytes, hd: Optional[bool] = True):
270*8eb2bee6SAlexander V. Chernikov        if len(sa) < sizeof(SockaddrDl):
271*8eb2bee6SAlexander V. Chernikov            raise RtSockException("LINK sa size too small: {}".format(len(sa)))
272*8eb2bee6SAlexander V. Chernikov        sdl = SockaddrDl.from_buffer_copy(sa)
273*8eb2bee6SAlexander V. Chernikov        if sdl.sdl_index:
274*8eb2bee6SAlexander V. Chernikov            ifindex = "link#{} ".format(sdl.sdl_index)
275*8eb2bee6SAlexander V. Chernikov        else:
276*8eb2bee6SAlexander V. Chernikov            ifindex = ""
277*8eb2bee6SAlexander V. Chernikov        if sdl.sdl_nlen:
278*8eb2bee6SAlexander V. Chernikov            iface_offset = 8
279*8eb2bee6SAlexander V. Chernikov            if sdl.sdl_nlen + iface_offset > len(sa):
280*8eb2bee6SAlexander V. Chernikov                raise RtSockException(
281*8eb2bee6SAlexander V. Chernikov                    "LINK sa sdl_nlen {} > total len {}".format(sdl.sdl_nlen, len(sa))
282*8eb2bee6SAlexander V. Chernikov                )
283*8eb2bee6SAlexander V. Chernikov            ifname = "ifname:{} ".format(
284*8eb2bee6SAlexander V. Chernikov                bytes.decode(sa[iface_offset : iface_offset + sdl.sdl_nlen])
285*8eb2bee6SAlexander V. Chernikov            )
286*8eb2bee6SAlexander V. Chernikov        else:
287*8eb2bee6SAlexander V. Chernikov            ifname = ""
288*8eb2bee6SAlexander V. Chernikov        return "{}{}".format(ifindex, ifname)
289*8eb2bee6SAlexander V. Chernikov
290*8eb2bee6SAlexander V. Chernikov    @staticmethod
291*8eb2bee6SAlexander V. Chernikov    def print_sa_unknown(sa: bytes):
292*8eb2bee6SAlexander V. Chernikov        return "unknown_type:{}".format(sa[1])
293*8eb2bee6SAlexander V. Chernikov
294*8eb2bee6SAlexander V. Chernikov    @classmethod
295*8eb2bee6SAlexander V. Chernikov    def print_sa(cls, sa: bytes, hd: Optional[bool] = False):
296*8eb2bee6SAlexander V. Chernikov        if sa[0] != len(sa):
297*8eb2bee6SAlexander V. Chernikov            raise Exception("sa size {} != buffer size {}".format(sa[0], len(sa)))
298*8eb2bee6SAlexander V. Chernikov
299*8eb2bee6SAlexander V. Chernikov        if len(sa) < 2:
300*8eb2bee6SAlexander V. Chernikov            raise Exception(
301*8eb2bee6SAlexander V. Chernikov                "sa type {} too short: {}".format(
302*8eb2bee6SAlexander V. Chernikov                    RtConst.get_name("AF_", sa[1]), len(sa)
303*8eb2bee6SAlexander V. Chernikov                )
304*8eb2bee6SAlexander V. Chernikov            )
305*8eb2bee6SAlexander V. Chernikov
306*8eb2bee6SAlexander V. Chernikov        if sa[1] == socket.AF_INET:
307*8eb2bee6SAlexander V. Chernikov            text = cls.print_sa_inet(sa)
308*8eb2bee6SAlexander V. Chernikov        elif sa[1] == socket.AF_INET6:
309*8eb2bee6SAlexander V. Chernikov            text = cls.print_sa_inet6(sa)
310*8eb2bee6SAlexander V. Chernikov        elif sa[1] == socket.AF_LINK:
311*8eb2bee6SAlexander V. Chernikov            text = cls.print_sa_link(sa)
312*8eb2bee6SAlexander V. Chernikov        else:
313*8eb2bee6SAlexander V. Chernikov            text = cls.print_sa_unknown(sa)
314*8eb2bee6SAlexander V. Chernikov        if hd:
315*8eb2bee6SAlexander V. Chernikov            dump = " [{!r}]".format(sa)
316*8eb2bee6SAlexander V. Chernikov        else:
317*8eb2bee6SAlexander V. Chernikov            dump = ""
318*8eb2bee6SAlexander V. Chernikov        return "{}{}".format(text, dump)
319*8eb2bee6SAlexander V. Chernikov
320*8eb2bee6SAlexander V. Chernikov
321*8eb2bee6SAlexander V. Chernikovclass BaseRtsockMessage(object):
322*8eb2bee6SAlexander V. Chernikov    def __init__(self, rtm_type):
323*8eb2bee6SAlexander V. Chernikov        self.rtm_type = rtm_type
324*8eb2bee6SAlexander V. Chernikov        self.sa = SaHelper()
325*8eb2bee6SAlexander V. Chernikov
326*8eb2bee6SAlexander V. Chernikov    @staticmethod
327*8eb2bee6SAlexander V. Chernikov    def print_rtm_type(rtm_type):
328*8eb2bee6SAlexander V. Chernikov        return RtConst.get_name("RTM_", rtm_type)
329*8eb2bee6SAlexander V. Chernikov
330*8eb2bee6SAlexander V. Chernikov    @property
331*8eb2bee6SAlexander V. Chernikov    def rtm_type_str(self):
332*8eb2bee6SAlexander V. Chernikov        return self.print_rtm_type(self.rtm_type)
333*8eb2bee6SAlexander V. Chernikov
334*8eb2bee6SAlexander V. Chernikov
335*8eb2bee6SAlexander V. Chernikovclass RtsockRtMessage(BaseRtsockMessage):
336*8eb2bee6SAlexander V. Chernikov    messages = [
337*8eb2bee6SAlexander V. Chernikov        RtConst.RTM_ADD,
338*8eb2bee6SAlexander V. Chernikov        RtConst.RTM_DELETE,
339*8eb2bee6SAlexander V. Chernikov        RtConst.RTM_CHANGE,
340*8eb2bee6SAlexander V. Chernikov        RtConst.RTM_GET,
341*8eb2bee6SAlexander V. Chernikov    ]
342*8eb2bee6SAlexander V. Chernikov
343*8eb2bee6SAlexander V. Chernikov    def __init__(self, rtm_type, rtm_seq=1, dst_sa=None, mask_sa=None):
344*8eb2bee6SAlexander V. Chernikov        super().__init__(rtm_type)
345*8eb2bee6SAlexander V. Chernikov        self.rtm_flags = 0
346*8eb2bee6SAlexander V. Chernikov        self.rtm_seq = rtm_seq
347*8eb2bee6SAlexander V. Chernikov        self._attrs = {}
348*8eb2bee6SAlexander V. Chernikov        self.rtm_errno = 0
349*8eb2bee6SAlexander V. Chernikov        self.rtm_pid = 0
350*8eb2bee6SAlexander V. Chernikov        self.rtm_inits = 0
351*8eb2bee6SAlexander V. Chernikov        self.rtm_rmx = RtMetrics()
352*8eb2bee6SAlexander V. Chernikov        self._orig_data = None
353*8eb2bee6SAlexander V. Chernikov        if dst_sa:
354*8eb2bee6SAlexander V. Chernikov            self.add_sa_attr(RtConst.RTA_DST, dst_sa)
355*8eb2bee6SAlexander V. Chernikov        if mask_sa:
356*8eb2bee6SAlexander V. Chernikov            self.add_sa_attr(RtConst.RTA_NETMASK, mask_sa)
357*8eb2bee6SAlexander V. Chernikov
358*8eb2bee6SAlexander V. Chernikov    def add_sa_attr(self, attr_type, attr_bytes: bytes):
359*8eb2bee6SAlexander V. Chernikov        self._attrs[attr_type] = attr_bytes
360*8eb2bee6SAlexander V. Chernikov
361*8eb2bee6SAlexander V. Chernikov    def add_ip_attr(self, attr_type, ip_addr: str, scopeid: int = 0):
362*8eb2bee6SAlexander V. Chernikov        if ":" in ip_addr:
363*8eb2bee6SAlexander V. Chernikov            self.add_ip6_attr(attr_type, ip_addr, scopeid)
364*8eb2bee6SAlexander V. Chernikov        else:
365*8eb2bee6SAlexander V. Chernikov            self.add_ip4_attr(attr_type, ip_addr)
366*8eb2bee6SAlexander V. Chernikov
367*8eb2bee6SAlexander V. Chernikov    def add_ip4_attr(self, attr_type, ip: str):
368*8eb2bee6SAlexander V. Chernikov        self.add_sa_attr(attr_type, self.sa.ip_sa(ip))
369*8eb2bee6SAlexander V. Chernikov
370*8eb2bee6SAlexander V. Chernikov    def add_ip6_attr(self, attr_type, ip6: str, scopeid: int):
371*8eb2bee6SAlexander V. Chernikov        self.add_sa_attr(attr_type, self.sa.ip6_sa(ip6, scopeid))
372*8eb2bee6SAlexander V. Chernikov
373*8eb2bee6SAlexander V. Chernikov    def add_link_attr(self, attr_type, ifindex: Optional[int] = 0):
374*8eb2bee6SAlexander V. Chernikov        self.add_sa_attr(attr_type, self.sa.link_sa(ifindex))
375*8eb2bee6SAlexander V. Chernikov
376*8eb2bee6SAlexander V. Chernikov    def get_sa(self, attr_type) -> bytes:
377*8eb2bee6SAlexander V. Chernikov        return self._attrs.get(attr_type)
378*8eb2bee6SAlexander V. Chernikov
379*8eb2bee6SAlexander V. Chernikov    def print_message(self):
380*8eb2bee6SAlexander V. Chernikov        # RTM_GET: Report Metrics: len 272, pid: 87839, seq 1, errno 0, flags:<UP,GATEWAY,DONE,STATIC>
381*8eb2bee6SAlexander V. Chernikov        if self._orig_data:
382*8eb2bee6SAlexander V. Chernikov            rtm_len = len(self._orig_data)
383*8eb2bee6SAlexander V. Chernikov        else:
384*8eb2bee6SAlexander V. Chernikov            rtm_len = len(bytes(self))
385*8eb2bee6SAlexander V. Chernikov        print(
386*8eb2bee6SAlexander V. Chernikov            "{}: len {}, pid: {}, seq {}, errno {}, flags: <{}>".format(
387*8eb2bee6SAlexander V. Chernikov                self.rtm_type_str,
388*8eb2bee6SAlexander V. Chernikov                rtm_len,
389*8eb2bee6SAlexander V. Chernikov                self.rtm_pid,
390*8eb2bee6SAlexander V. Chernikov                self.rtm_seq,
391*8eb2bee6SAlexander V. Chernikov                self.rtm_errno,
392*8eb2bee6SAlexander V. Chernikov                RtConst.get_bitmask_str("RTF_", self.rtm_flags),
393*8eb2bee6SAlexander V. Chernikov            )
394*8eb2bee6SAlexander V. Chernikov        )
395*8eb2bee6SAlexander V. Chernikov        rtm_addrs = sum(list(self._attrs.keys()))
396*8eb2bee6SAlexander V. Chernikov        print("Addrs: <{}>".format(RtConst.get_bitmask_str("RTA_", rtm_addrs)))
397*8eb2bee6SAlexander V. Chernikov        for attr in sorted(self._attrs.keys()):
398*8eb2bee6SAlexander V. Chernikov            sa_data = SaHelper.print_sa(self._attrs[attr])
399*8eb2bee6SAlexander V. Chernikov            print(" {}: {}".format(RtConst.get_name("RTA_", attr), sa_data))
400*8eb2bee6SAlexander V. Chernikov
401*8eb2bee6SAlexander V. Chernikov    def print_in_message(self):
402*8eb2bee6SAlexander V. Chernikov        print("vvvvvvvv  IN vvvvvvvv")
403*8eb2bee6SAlexander V. Chernikov        self.print_message()
404*8eb2bee6SAlexander V. Chernikov        print()
405*8eb2bee6SAlexander V. Chernikov
406*8eb2bee6SAlexander V. Chernikov    def verify_sa_inet(self, sa_data):
407*8eb2bee6SAlexander V. Chernikov        if len(sa_data) < 8:
408*8eb2bee6SAlexander V. Chernikov            raise Exception("IPv4 sa size too small: {}".format(sa_data))
409*8eb2bee6SAlexander V. Chernikov        if sa_data[0] > len(sa_data):
410*8eb2bee6SAlexander V. Chernikov            raise Exception(
411*8eb2bee6SAlexander V. Chernikov                "IPv4 sin_len too big: {} vs sa size {}: {}".format(
412*8eb2bee6SAlexander V. Chernikov                    sa_data[0], len(sa_data), sa_data
413*8eb2bee6SAlexander V. Chernikov                )
414*8eb2bee6SAlexander V. Chernikov            )
415*8eb2bee6SAlexander V. Chernikov        sin = SockaddrIn.from_buffer_copy(sa_data)
416*8eb2bee6SAlexander V. Chernikov        assert sin.sin_port == 0
417*8eb2bee6SAlexander V. Chernikov        assert sin.sin_zero == [0] * 8
418*8eb2bee6SAlexander V. Chernikov
419*8eb2bee6SAlexander V. Chernikov    def compare_sa(self, sa_type, sa_data):
420*8eb2bee6SAlexander V. Chernikov        if len(sa_data) < 4:
421*8eb2bee6SAlexander V. Chernikov            sa_type_name = RtConst.get_name("RTA_", sa_type)
422*8eb2bee6SAlexander V. Chernikov            raise Exception(
423*8eb2bee6SAlexander V. Chernikov                "sa_len for type {} too short: {}".format(sa_type_name, len(sa_data))
424*8eb2bee6SAlexander V. Chernikov            )
425*8eb2bee6SAlexander V. Chernikov        our_sa = self._attrs[sa_type]
426*8eb2bee6SAlexander V. Chernikov        assert SaHelper.print_sa(sa_data) == SaHelper.print_sa(our_sa)
427*8eb2bee6SAlexander V. Chernikov        assert len(sa_data) == len(our_sa)
428*8eb2bee6SAlexander V. Chernikov        assert sa_data == our_sa
429*8eb2bee6SAlexander V. Chernikov
430*8eb2bee6SAlexander V. Chernikov    def verify(self, rtm_type: int, rtm_sa):
431*8eb2bee6SAlexander V. Chernikov        assert self.rtm_type_str == self.print_rtm_type(rtm_type)
432*8eb2bee6SAlexander V. Chernikov        assert self.rtm_errno == 0
433*8eb2bee6SAlexander V. Chernikov        hdr = RtMsgHdr.from_buffer_copy(self._orig_data)
434*8eb2bee6SAlexander V. Chernikov        assert hdr._rtm_spare1 == 0
435*8eb2bee6SAlexander V. Chernikov        for sa_type, sa_data in rtm_sa.items():
436*8eb2bee6SAlexander V. Chernikov            if sa_type not in self._attrs:
437*8eb2bee6SAlexander V. Chernikov                sa_type_name = RtConst.get_name("RTA_", sa_type)
438*8eb2bee6SAlexander V. Chernikov                raise Exception("SA type {} not present".format(sa_type_name))
439*8eb2bee6SAlexander V. Chernikov            self.compare_sa(sa_type, sa_data)
440*8eb2bee6SAlexander V. Chernikov
441*8eb2bee6SAlexander V. Chernikov    @classmethod
442*8eb2bee6SAlexander V. Chernikov    def from_bytes(cls, data: bytes):
443*8eb2bee6SAlexander V. Chernikov        if len(data) < sizeof(RtMsgHdr):
444*8eb2bee6SAlexander V. Chernikov            raise Exception(
445*8eb2bee6SAlexander V. Chernikov                "messages size {} is less than expected {}".format(
446*8eb2bee6SAlexander V. Chernikov                    len(data), sizeof(RtMsgHdr)
447*8eb2bee6SAlexander V. Chernikov                )
448*8eb2bee6SAlexander V. Chernikov            )
449*8eb2bee6SAlexander V. Chernikov        hdr = RtMsgHdr.from_buffer_copy(data)
450*8eb2bee6SAlexander V. Chernikov
451*8eb2bee6SAlexander V. Chernikov        self = cls(hdr.rtm_type)
452*8eb2bee6SAlexander V. Chernikov        self.rtm_flags = hdr.rtm_flags
453*8eb2bee6SAlexander V. Chernikov        self.rtm_seq = hdr.rtm_seq
454*8eb2bee6SAlexander V. Chernikov        self.rtm_errno = hdr.rtm_errno
455*8eb2bee6SAlexander V. Chernikov        self.rtm_pid = hdr.rtm_pid
456*8eb2bee6SAlexander V. Chernikov        self.rtm_inits = hdr.rtm_inits
457*8eb2bee6SAlexander V. Chernikov        self.rtm_rmx = hdr.rtm_rmx
458*8eb2bee6SAlexander V. Chernikov        self._orig_data = data
459*8eb2bee6SAlexander V. Chernikov
460*8eb2bee6SAlexander V. Chernikov        off = sizeof(RtMsgHdr)
461*8eb2bee6SAlexander V. Chernikov        v = 1
462*8eb2bee6SAlexander V. Chernikov        addrs_mask = hdr.rtm_addrs
463*8eb2bee6SAlexander V. Chernikov        while addrs_mask:
464*8eb2bee6SAlexander V. Chernikov            if addrs_mask & v:
465*8eb2bee6SAlexander V. Chernikov                addrs_mask -= v
466*8eb2bee6SAlexander V. Chernikov
467*8eb2bee6SAlexander V. Chernikov                if off + data[off] > len(data):
468*8eb2bee6SAlexander V. Chernikov                    raise Exception(
469*8eb2bee6SAlexander V. Chernikov                        "SA sizeof for {} > total message length: {}+{} > {}".format(
470*8eb2bee6SAlexander V. Chernikov                            RtConst.get_name("RTA_", v), off, data[off], len(data)
471*8eb2bee6SAlexander V. Chernikov                        )
472*8eb2bee6SAlexander V. Chernikov                    )
473*8eb2bee6SAlexander V. Chernikov                self._attrs[v] = data[off : off + data[off]]
474*8eb2bee6SAlexander V. Chernikov                off += roundup2(data[off], RtConst.ALIGN)
475*8eb2bee6SAlexander V. Chernikov            v *= 2
476*8eb2bee6SAlexander V. Chernikov        return self
477*8eb2bee6SAlexander V. Chernikov
478*8eb2bee6SAlexander V. Chernikov    def __bytes__(self):
479*8eb2bee6SAlexander V. Chernikov        sz = sizeof(RtMsgHdr)
480*8eb2bee6SAlexander V. Chernikov        addrs_mask = 0
481*8eb2bee6SAlexander V. Chernikov        for k, v in self._attrs.items():
482*8eb2bee6SAlexander V. Chernikov            sz += roundup2(len(v), RtConst.ALIGN)
483*8eb2bee6SAlexander V. Chernikov            addrs_mask += k
484*8eb2bee6SAlexander V. Chernikov        hdr = RtMsgHdr(
485*8eb2bee6SAlexander V. Chernikov            rtm_msglen=sz,
486*8eb2bee6SAlexander V. Chernikov            rtm_version=RtConst.RTM_VERSION,
487*8eb2bee6SAlexander V. Chernikov            rtm_type=self.rtm_type,
488*8eb2bee6SAlexander V. Chernikov            rtm_flags=self.rtm_flags,
489*8eb2bee6SAlexander V. Chernikov            rtm_seq=self.rtm_seq,
490*8eb2bee6SAlexander V. Chernikov            rtm_addrs=addrs_mask,
491*8eb2bee6SAlexander V. Chernikov            rtm_inits=self.rtm_inits,
492*8eb2bee6SAlexander V. Chernikov            rtm_rmx=self.rtm_rmx,
493*8eb2bee6SAlexander V. Chernikov        )
494*8eb2bee6SAlexander V. Chernikov        buf = bytearray(sz)
495*8eb2bee6SAlexander V. Chernikov        buf[0 : sizeof(RtMsgHdr)] = hdr
496*8eb2bee6SAlexander V. Chernikov        off = sizeof(RtMsgHdr)
497*8eb2bee6SAlexander V. Chernikov        for attr in sorted(self._attrs.keys()):
498*8eb2bee6SAlexander V. Chernikov            v = self._attrs[attr]
499*8eb2bee6SAlexander V. Chernikov            sa_len = len(v)
500*8eb2bee6SAlexander V. Chernikov            buf[off : off + sa_len] = v
501*8eb2bee6SAlexander V. Chernikov            off += roundup2(len(v), RtConst.ALIGN)
502*8eb2bee6SAlexander V. Chernikov        return bytes(buf)
503*8eb2bee6SAlexander V. Chernikov
504*8eb2bee6SAlexander V. Chernikov
505*8eb2bee6SAlexander V. Chernikovclass Rtsock:
506*8eb2bee6SAlexander V. Chernikov    def __init__(self):
507*8eb2bee6SAlexander V. Chernikov        self.socket = self._setup_rtsock()
508*8eb2bee6SAlexander V. Chernikov        self.rtm_seq = 1
509*8eb2bee6SAlexander V. Chernikov        self.msgmap = self.build_msgmap()
510*8eb2bee6SAlexander V. Chernikov
511*8eb2bee6SAlexander V. Chernikov    def build_msgmap(self):
512*8eb2bee6SAlexander V. Chernikov        classes = [RtsockRtMessage]
513*8eb2bee6SAlexander V. Chernikov        xmap = {}
514*8eb2bee6SAlexander V. Chernikov        for cls in classes:
515*8eb2bee6SAlexander V. Chernikov            for message in cls.messages:
516*8eb2bee6SAlexander V. Chernikov                xmap[message] = cls
517*8eb2bee6SAlexander V. Chernikov        return xmap
518*8eb2bee6SAlexander V. Chernikov
519*8eb2bee6SAlexander V. Chernikov    def get_seq(self):
520*8eb2bee6SAlexander V. Chernikov        ret = self.rtm_seq
521*8eb2bee6SAlexander V. Chernikov        self.rtm_seq += 1
522*8eb2bee6SAlexander V. Chernikov        return ret
523*8eb2bee6SAlexander V. Chernikov
524*8eb2bee6SAlexander V. Chernikov    def get_weight(self, weight) -> int:
525*8eb2bee6SAlexander V. Chernikov        if weight:
526*8eb2bee6SAlexander V. Chernikov            return weight
527*8eb2bee6SAlexander V. Chernikov        else:
528*8eb2bee6SAlexander V. Chernikov            return 1  # RT_DEFAULT_WEIGHT
529*8eb2bee6SAlexander V. Chernikov
530*8eb2bee6SAlexander V. Chernikov    def new_rtm_any(self, msg_type, prefix: str, gw: Union[str, bytes]):
531*8eb2bee6SAlexander V. Chernikov        px = prefix.split("/")
532*8eb2bee6SAlexander V. Chernikov        addr_sa = SaHelper.ip_sa(px[0])
533*8eb2bee6SAlexander V. Chernikov        if len(px) > 1:
534*8eb2bee6SAlexander V. Chernikov            pxlen = int(px[1])
535*8eb2bee6SAlexander V. Chernikov            if SaHelper.is_ipv6(px[0]):
536*8eb2bee6SAlexander V. Chernikov                mask_sa = SaHelper.pxlen6_sa(pxlen)
537*8eb2bee6SAlexander V. Chernikov            else:
538*8eb2bee6SAlexander V. Chernikov                mask_sa = SaHelper.pxlen4_sa(pxlen)
539*8eb2bee6SAlexander V. Chernikov        else:
540*8eb2bee6SAlexander V. Chernikov            mask_sa = None
541*8eb2bee6SAlexander V. Chernikov        msg = RtsockRtMessage(msg_type, self.get_seq(), addr_sa, mask_sa)
542*8eb2bee6SAlexander V. Chernikov        if isinstance(gw, bytes):
543*8eb2bee6SAlexander V. Chernikov            msg.add_sa_attr(RtConst.RTA_GATEWAY, gw)
544*8eb2bee6SAlexander V. Chernikov        else:
545*8eb2bee6SAlexander V. Chernikov            # String
546*8eb2bee6SAlexander V. Chernikov            msg.add_ip_attr(RtConst.RTA_GATEWAY, gw)
547*8eb2bee6SAlexander V. Chernikov        return msg
548*8eb2bee6SAlexander V. Chernikov
549*8eb2bee6SAlexander V. Chernikov    def new_rtm_add(self, prefix: str, gw: Union[str, bytes]):
550*8eb2bee6SAlexander V. Chernikov        return self.new_rtm_any(RtConst.RTM_ADD, prefix, gw)
551*8eb2bee6SAlexander V. Chernikov
552*8eb2bee6SAlexander V. Chernikov    def new_rtm_del(self, prefix: str, gw: Union[str, bytes]):
553*8eb2bee6SAlexander V. Chernikov        return self.new_rtm_any(RtConst.RTM_DELETE, prefix, gw)
554*8eb2bee6SAlexander V. Chernikov
555*8eb2bee6SAlexander V. Chernikov    def new_rtm_change(self, prefix: str, gw: Union[str, bytes]):
556*8eb2bee6SAlexander V. Chernikov        return self.new_rtm_any(RtConst.RTM_CHANGE, prefix, gw)
557*8eb2bee6SAlexander V. Chernikov
558*8eb2bee6SAlexander V. Chernikov    def _setup_rtsock(self) -> socket.socket:
559*8eb2bee6SAlexander V. Chernikov        s = socket.socket(socket.AF_ROUTE, socket.SOCK_RAW, socket.AF_UNSPEC)
560*8eb2bee6SAlexander V. Chernikov        s.setsockopt(socket.SOL_SOCKET, socket.SO_USELOOPBACK, 1)
561*8eb2bee6SAlexander V. Chernikov        return s
562*8eb2bee6SAlexander V. Chernikov
563*8eb2bee6SAlexander V. Chernikov    def print_hd(self, data: bytes):
564*8eb2bee6SAlexander V. Chernikov        width = 16
565*8eb2bee6SAlexander V. Chernikov        print("==========================================")
566*8eb2bee6SAlexander V. Chernikov        for chunk in [data[i : i + width] for i in range(0, len(data), width)]:
567*8eb2bee6SAlexander V. Chernikov            for b in chunk:
568*8eb2bee6SAlexander V. Chernikov                print("0x{:02X} ".format(b), end="")
569*8eb2bee6SAlexander V. Chernikov            print()
570*8eb2bee6SAlexander V. Chernikov        print()
571*8eb2bee6SAlexander V. Chernikov
572*8eb2bee6SAlexander V. Chernikov    def write_message(self, msg):
573*8eb2bee6SAlexander V. Chernikov        print("vvvvvvvv OUT vvvvvvvv")
574*8eb2bee6SAlexander V. Chernikov        msg.print_message()
575*8eb2bee6SAlexander V. Chernikov        print()
576*8eb2bee6SAlexander V. Chernikov        msg_bytes = bytes(msg)
577*8eb2bee6SAlexander V. Chernikov        ret = os.write(self.socket.fileno(), msg_bytes)
578*8eb2bee6SAlexander V. Chernikov        if ret != -1:
579*8eb2bee6SAlexander V. Chernikov            assert ret == len(msg_bytes)
580*8eb2bee6SAlexander V. Chernikov
581*8eb2bee6SAlexander V. Chernikov    def parse_message(self, data: bytes):
582*8eb2bee6SAlexander V. Chernikov        if len(data) < 4:
583*8eb2bee6SAlexander V. Chernikov            raise OSError("Short read from rtsock: {} bytes".format(len(data)))
584*8eb2bee6SAlexander V. Chernikov        rtm_type = data[4]
585*8eb2bee6SAlexander V. Chernikov        if rtm_type not in self.msgmap:
586*8eb2bee6SAlexander V. Chernikov            return None
587*8eb2bee6SAlexander V. Chernikov
588*8eb2bee6SAlexander V. Chernikov    def write_data(self, data: bytes):
589*8eb2bee6SAlexander V. Chernikov        self.socket.send(data)
590*8eb2bee6SAlexander V. Chernikov
591*8eb2bee6SAlexander V. Chernikov    def read_data(self, seq: Optional[int] = None) -> bytes:
592*8eb2bee6SAlexander V. Chernikov        while True:
593*8eb2bee6SAlexander V. Chernikov            data = self.socket.recv(4096)
594*8eb2bee6SAlexander V. Chernikov            if seq is None:
595*8eb2bee6SAlexander V. Chernikov                break
596*8eb2bee6SAlexander V. Chernikov            if len(data) > sizeof(RtMsgHdr):
597*8eb2bee6SAlexander V. Chernikov                hdr = RtMsgHdr.from_buffer_copy(data)
598*8eb2bee6SAlexander V. Chernikov                if hdr.rtm_seq == seq:
599*8eb2bee6SAlexander V. Chernikov                    break
600*8eb2bee6SAlexander V. Chernikov        return data
601*8eb2bee6SAlexander V. Chernikov
602*8eb2bee6SAlexander V. Chernikov    def read_message(self) -> bytes:
603*8eb2bee6SAlexander V. Chernikov        data = self.read_data()
604*8eb2bee6SAlexander V. Chernikov        return self.parse_message(data)
605