|
20 | 20 |
|
21 | 21 | import errno
|
22 | 22 | from socket import * # pylint: disable=wildcard-import
|
| 23 | +import struct |
23 | 24 |
|
24 | 25 | import cstruct
|
25 | 26 | import net_test
|
|
37 | 38 | # Message types.
|
38 | 39 | TCPDIAG_GETSOCK = 18
|
39 | 40 |
|
| 41 | +# Request attributes. |
| 42 | +INET_DIAG_REQ_BYTECODE = 1 |
| 43 | + |
40 | 44 | # Extensions.
|
41 | 45 | INET_DIAG_NONE = 0
|
42 | 46 | INET_DIAG_MEMINFO = 1
|
|
49 | 53 | INET_DIAG_SHUTDOWN = 8
|
50 | 54 | INET_DIAG_DCTCPINFO = 9
|
51 | 55 |
|
| 56 | +# Bytecode operations. |
| 57 | +INET_DIAG_BC_NOP = 0 |
| 58 | +INET_DIAG_BC_JMP = 1 |
| 59 | +INET_DIAG_BC_S_GE = 2 |
| 60 | +INET_DIAG_BC_S_LE = 3 |
| 61 | +INET_DIAG_BC_D_GE = 4 |
| 62 | +INET_DIAG_BC_D_LE = 5 |
| 63 | +INET_DIAG_BC_AUTO = 6 |
| 64 | +INET_DIAG_BC_S_COND = 7 |
| 65 | +INET_DIAG_BC_D_COND = 8 |
| 66 | + |
52 | 67 | # Data structure formats.
|
53 | 68 | # These aren't constants, they're classes. So, pylint: disable=invalid-name
|
54 | 69 | InetDiagSockId = cstruct.Struct(
|
|
62 | 77 | [InetDiagSockId])
|
63 | 78 | InetDiagMeminfo = cstruct.Struct(
|
64 | 79 | "InetDiagMeminfo", "=IIII", "rmem wmem fmem tmem")
|
| 80 | +InetDiagBcOp = cstruct.Struct("InetDiagBcOp", "BBH", "code yes no") |
| 81 | +InetDiagHostcond = cstruct.Struct("InetDiagHostcond", "=BBxxi", |
| 82 | + "family prefix_len port") |
65 | 83 |
|
66 | 84 | SkMeminfo = cstruct.Struct(
|
67 | 85 | "SkMeminfo", "=IIIIIIII",
|
@@ -133,22 +151,108 @@ def MaybeDebugCommand(self, command, data):
|
133 | 151 | def _EmptyInetDiagSockId():
|
134 | 152 | return InetDiagSockId(("\x00" * len(InetDiagSockId)))
|
135 | 153 |
|
136 |
| - def Dump(self, diag_req): |
137 |
| - out = self._Dump(SOCK_DIAG_BY_FAMILY, diag_req, InetDiagMsg, "") |
| 154 | + def PackBytecode(self, instructions): |
| 155 | + """Compiles instructions to inet_diag bytecode. |
| 156 | +
|
| 157 | + The input is a list of (INET_DIAG_BC_xxx, yes, no, arg) tuples, where yes |
| 158 | + and no are relative jump offsets measured in instructions. The yes branch |
| 159 | + is taken if the instruction matches. |
| 160 | +
|
| 161 | + To accept, jump 1 past the last instruction. To reject, jump 2 past the |
| 162 | + last instruction. |
| 163 | +
|
| 164 | + The target of a no jump is only valid if it is reachable by following |
| 165 | + only yes jumps from the first instruction - see inet_diag_bc_audit and |
| 166 | + valid_cc. This means that if cond1 and cond2 are two mutually exclusive |
| 167 | + filter terms, it is not possible to implement cond1 OR cond2 using: |
| 168 | +
|
| 169 | + ... |
| 170 | + cond1 2 1 arg |
| 171 | + cond2 1 2 arg |
| 172 | + accept |
| 173 | + reject |
| 174 | +
|
| 175 | + but only using: |
| 176 | +
|
| 177 | + ... |
| 178 | + cond1 1 2 arg |
| 179 | + jmp 1 2 |
| 180 | + cond2 1 2 arg |
| 181 | + accept |
| 182 | + reject |
| 183 | +
|
| 184 | + The jmp instruction ignores yes and always jumps to no, but yes must be 1 |
| 185 | + or the bytecode won't validate. It doesn't have to be jmp - any instruction |
| 186 | + that is guaranteed not to match on real data will do. |
| 187 | +
|
| 188 | + Args: |
| 189 | + instructions: list of instruction tuples |
| 190 | +
|
| 191 | + Returns: |
| 192 | + A string, the raw bytecode. |
| 193 | + """ |
| 194 | + args = [] |
| 195 | + positions = [0] |
| 196 | + |
| 197 | + for op, yes, no, arg in instructions: |
| 198 | + |
| 199 | + if yes <= 0 or no <= 0: |
| 200 | + raise ValueError("Jumps must be > 0") |
| 201 | + |
| 202 | + if op in [INET_DIAG_BC_NOP, INET_DIAG_BC_JMP, INET_DIAG_BC_AUTO]: |
| 203 | + arg = "" |
| 204 | + elif op in [INET_DIAG_BC_S_GE, INET_DIAG_BC_S_LE, |
| 205 | + INET_DIAG_BC_D_GE, INET_DIAG_BC_D_LE]: |
| 206 | + arg = "\x00\x00" + struct.pack("=H", arg) |
| 207 | + elif op in [INET_DIAG_BC_S_COND, INET_DIAG_BC_D_COND]: |
| 208 | + addr, prefixlen, port = arg |
| 209 | + family = AF_INET6 if ":" in addr else AF_INET |
| 210 | + addr = inet_pton(family, addr) |
| 211 | + arg = InetDiagHostcond((family, prefixlen, port)).Pack() + addr |
| 212 | + else: |
| 213 | + raise ValueError("Unsupported opcode %d" % op) |
| 214 | + |
| 215 | + args.append(arg) |
| 216 | + length = len(InetDiagBcOp) + len(arg) |
| 217 | + positions.append(positions[-1] + length) |
| 218 | + |
| 219 | + # Reject label. |
| 220 | + positions.append(positions[-1] + 4) # Why 4? Because the kernel uses 4. |
| 221 | + assert len(args) == len(instructions) == len(positions) - 2 |
| 222 | + |
| 223 | + # print positions |
| 224 | + |
| 225 | + packed = "" |
| 226 | + for i, (op, yes, no, arg) in enumerate(instructions): |
| 227 | + yes = positions[i + yes] - positions[i] |
| 228 | + no = positions[i + no] - positions[i] |
| 229 | + instruction = InetDiagBcOp((op, yes, no)).Pack() + args[i] |
| 230 | + #print "%3d: %d %3d %3d %s %s" % (positions[i], op, yes, no, |
| 231 | + # arg, instruction.encode("hex")) |
| 232 | + packed += instruction |
| 233 | + #print |
| 234 | + |
| 235 | + return packed |
| 236 | + |
| 237 | + def Dump(self, diag_req, bytecode=""): |
| 238 | + out = self._Dump(SOCK_DIAG_BY_FAMILY, diag_req, InetDiagMsg, bytecode) |
138 | 239 | return out
|
139 | 240 |
|
140 |
| - def DumpAllInetSockets(self, protocol, sock_id=None, ext=0, |
| 241 | + def DumpAllInetSockets(self, protocol, bytecode, sock_id=None, ext=0, |
141 | 242 | states=ALL_NON_TIME_WAIT):
|
142 | 243 | """Dumps IPv4 or IPv6 sockets matching the specified parameters."""
|
143 | 244 | # DumpSockets(AF_UNSPEC) does not result in dumping all inet sockets, it
|
144 | 245 | # results in ENOENT.
|
145 | 246 | if sock_id is None:
|
146 | 247 | sock_id = self._EmptyInetDiagSockId()
|
147 | 248 |
|
| 249 | + if bytecode: |
| 250 | + bytecode = self._NlAttr(INET_DIAG_REQ_BYTECODE, bytecode) |
| 251 | + |
148 | 252 | sockets = []
|
149 | 253 | for family in [AF_INET, AF_INET6]:
|
150 | 254 | diag_req = InetDiagReqV2((family, protocol, ext, states, sock_id))
|
151 |
| - sockets += self.Dump(diag_req) |
| 255 | + sockets += self.Dump(diag_req, bytecode) |
152 | 256 |
|
153 | 257 | return sockets
|
154 | 258 |
|
@@ -255,6 +359,6 @@ def CloseSocketFromFd(self, s):
|
255 | 359 | sock_id.dport = 443
|
256 | 360 | ext = 1 << (INET_DIAG_TOS - 1) | 1 << (INET_DIAG_TCLASS - 1)
|
257 | 361 | states = 0xffffffff
|
258 |
| - diag_msgs = n.DumpAllInetSockets(IPPROTO_TCP, |
| 362 | + diag_msgs = n.DumpAllInetSockets(IPPROTO_TCP, "", |
259 | 363 | sock_id=sock_id, ext=ext, states=states)
|
260 | 364 | print diag_msgs
|
0 commit comments