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