]> granicus.if.org Git - strace/blob - netlink_inet_diag.c
netlink: add a basic rtnetlink parser of neigh messages
[strace] / netlink_inet_diag.c
1 /*
2  * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
3  * Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com>
4  * Copyright (c) 2017 The strace developers.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "defs.h"
31 #include "netlink.h"
32 #include "netlink_sock_diag.h"
33 #include "nlattr.h"
34 #include "print_fields.h"
35
36 #include <arpa/inet.h>
37
38 #include <linux/sock_diag.h>
39 #include <linux/inet_diag.h>
40
41 #include "xlat/inet_diag_attrs.h"
42 #include "xlat/inet_diag_bytecodes.h"
43 #include "xlat/inet_diag_extended_flags.h"
44 #include "xlat/inet_diag_req_attrs.h"
45
46 #include "xlat/tcp_states.h"
47 #include "xlat/tcp_state_flags.h"
48
49 void
50 print_inet_diag_sockid(const struct inet_diag_sockid *id, const uint8_t family)
51 {
52         PRINT_FIELD_NET_PORT("{", *id, idiag_sport);
53         PRINT_FIELD_NET_PORT(", ", *id, idiag_dport);
54         PRINT_FIELD_INET_ADDR(", ", *id, idiag_src, family);
55         PRINT_FIELD_INET_ADDR(", ", *id, idiag_dst, family);
56         PRINT_FIELD_IFINDEX(", ", *id, idiag_if);
57         PRINT_FIELD_COOKIE(", ", *id, idiag_cookie);
58         tprints("}");
59 }
60
61 static void
62 decode_inet_addr(struct tcb *const tcp,
63                  const kernel_ulong_t addr,
64                  const unsigned int len,
65                  const int family)
66 {
67         union {
68                 struct in_addr  a4;
69                 struct in6_addr a6;
70         } addrbuf;
71         size_t size = 0;
72
73         switch (family) {
74         case AF_INET:
75                 size = sizeof(addrbuf.a4);
76                 break;
77         case AF_INET6:
78                 size = sizeof(addrbuf.a6);
79                 break;
80         }
81
82         if (!size || len < size) {
83                 tprints("addr=");
84                 printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
85                 return;
86         }
87
88         if (umoven(tcp, addr, size, &addrbuf) < 0) {
89                 tprints("addr=");
90                 printaddr(addr);
91                 return;
92         }
93
94         print_inet_addr(family, &addrbuf, size, "addr");
95 }
96
97 static void
98 decode_inet_diag_hostcond(struct tcb *const tcp,
99                           const kernel_ulong_t addr,
100                           const unsigned int len)
101 {
102         struct inet_diag_hostcond cond;
103
104         if (len < sizeof(cond)) {
105                 printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
106                 return;
107         }
108         if (umove_or_printaddr(tcp, addr, &cond))
109                 return;
110
111         PRINT_FIELD_XVAL("{", cond, family, addrfams, "AF_???");
112         PRINT_FIELD_U(", ", cond, prefix_len);
113         PRINT_FIELD_U(", ", cond, port);
114
115         if (len > sizeof(cond)) {
116                 tprints(", ");
117                 decode_inet_addr(tcp, addr + sizeof(cond),
118                                  len - sizeof(cond), cond.family);
119         }
120         tprints("}");
121 }
122
123 static void
124 print_inet_diag_bc_op(const struct inet_diag_bc_op *const op)
125 {
126         PRINT_FIELD_XVAL("{", *op, code, inet_diag_bytecodes,
127                          "INET_DIAG_BC_???");
128         PRINT_FIELD_U(", ", *op, yes);
129         PRINT_FIELD_U(", ", *op, no);
130         tprints("}");
131 }
132
133 static void
134 decode_inet_diag_markcond(struct tcb *const tcp,
135                           const kernel_ulong_t addr,
136                           const unsigned int len)
137 {
138         struct inet_diag_markcond markcond;
139
140         if (len < sizeof(markcond))
141                 printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
142         else if (!umove_or_printaddr(tcp, addr, &markcond)) {
143                 PRINT_FIELD_U("{", markcond, mark);
144                 PRINT_FIELD_U(", ", markcond, mask);
145                 tprints("}");
146         }
147 }
148
149 static void
150 decode_bytecode_data(struct tcb *const tcp,
151                      const kernel_ulong_t addr,
152                      const unsigned int len,
153                      const unsigned char code)
154 {
155         switch (code) {
156         case INET_DIAG_BC_S_COND:
157         case INET_DIAG_BC_D_COND:
158                 decode_inet_diag_hostcond(tcp, addr, len);
159                 break;
160         case INET_DIAG_BC_DEV_COND: {
161                 uint32_t ifindex;
162
163                 if (len < sizeof(ifindex))
164                         printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
165                 else if (!umove_or_printaddr(tcp, addr, &ifindex))
166                         print_ifindex(ifindex);
167                 break;
168         }
169         case INET_DIAG_BC_S_GE:
170         case INET_DIAG_BC_S_LE:
171         case INET_DIAG_BC_D_GE:
172         case INET_DIAG_BC_D_LE: {
173                 struct inet_diag_bc_op op;
174
175                 if (len < sizeof(op))
176                         printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
177                 else if (!umove_or_printaddr(tcp, addr, &op))
178                         print_inet_diag_bc_op(&op);
179                 break;
180         }
181         case INET_DIAG_BC_MARK_COND:
182                 decode_inet_diag_markcond(tcp, addr, len);
183                 break;
184         case INET_DIAG_BC_AUTO:
185         case INET_DIAG_BC_JMP:
186         case INET_DIAG_BC_NOP:
187         default:
188                 printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
189                 break;
190         }
191 }
192
193 static bool
194 decode_inet_diag_bc_op(struct tcb *const tcp,
195                        const kernel_ulong_t addr,
196                        const unsigned int len,
197                        const void *const opaque_data)
198 {
199         struct inet_diag_bc_op op;
200
201         if (len < sizeof(op))
202                 return false;
203         if (umove_or_printaddr(tcp, addr, &op))
204                 return true;
205
206         if (len > sizeof(op))
207                 tprints("{");
208
209         print_inet_diag_bc_op(&op);
210
211         if (len > sizeof(op)) {
212                 tprints(", ");
213                 decode_bytecode_data(tcp, addr + sizeof(op),
214                                      len - sizeof(op), op.code);
215                 tprints("}");
216         }
217
218         return true;
219 }
220
221 static const nla_decoder_t inet_diag_req_nla_decoders[] = {
222         [INET_DIAG_REQ_BYTECODE] = decode_inet_diag_bc_op
223 };
224
225 static void
226 decode_inet_diag_req_compat(struct tcb *const tcp,
227                             const struct nlmsghdr *const nlmsghdr,
228                             const uint8_t family,
229                             const kernel_ulong_t addr,
230                             const unsigned int len)
231 {
232         struct inet_diag_req req = { .idiag_family = family };
233         size_t offset = sizeof(req.idiag_family);
234         bool decode_nla = false;
235
236         PRINT_FIELD_XVAL("{", req, idiag_family, addrfams, "AF_???");
237         tprints(", ");
238         if (len >= sizeof(req)) {
239                 if (!umoven_or_printaddr(tcp, addr + offset,
240                                          sizeof(req) - offset,
241                                          (void *) &req + offset)) {
242                         PRINT_FIELD_U("", req, idiag_src_len);
243                         PRINT_FIELD_U(", ", req, idiag_dst_len);
244                         PRINT_FIELD_FLAGS(", ", req, idiag_ext,
245                                           inet_diag_extended_flags,
246                                           "1<<INET_DIAG_\?\?\?-1");
247                         PRINT_FIELD_INET_DIAG_SOCKID(", ", req, id,
248                                                      req.idiag_family);
249                         PRINT_FIELD_FLAGS(", ", req, idiag_states,
250                                           tcp_state_flags, "1<<TCP_???");
251                         PRINT_FIELD_U(", ", req, idiag_dbs);
252                         decode_nla = true;
253                 }
254         } else
255                 tprints("...");
256         tprints("}");
257
258         offset = NLMSG_ALIGN(sizeof(req));
259         if (decode_nla && len > offset) {
260                 tprints(", ");
261                 decode_nlattr(tcp, addr + offset, len - offset,
262                               inet_diag_req_attrs, "INET_DIAG_REQ_???",
263                               inet_diag_req_nla_decoders,
264                               ARRAY_SIZE(inet_diag_req_nla_decoders), NULL);
265         }
266 }
267
268 static void
269 decode_inet_diag_req_v2(struct tcb *const tcp,
270                         const struct nlmsghdr *const nlmsghdr,
271                         const uint8_t family,
272                         const kernel_ulong_t addr,
273                         const unsigned int len)
274 {
275         struct inet_diag_req_v2 req = { .sdiag_family = family };
276         size_t offset = sizeof(req.sdiag_family);
277         bool decode_nla = false;
278
279         PRINT_FIELD_XVAL("{", req, sdiag_family, addrfams, "AF_???");
280         tprints(", ");
281         if (len >= sizeof(req)) {
282                 if (!umoven_or_printaddr(tcp, addr + offset,
283                                          sizeof(req) - offset,
284                                          (void *) &req + offset)) {
285                         PRINT_FIELD_XVAL("", req, sdiag_protocol,
286                                          inet_protocols, "IPPROTO_???");
287                         PRINT_FIELD_FLAGS(", ", req, idiag_ext,
288                                           inet_diag_extended_flags,
289                                           "1<<INET_DIAG_\?\?\?-1");
290                         PRINT_FIELD_FLAGS(", ", req, idiag_states,
291                                           tcp_state_flags, "1<<TCP_???");
292                         PRINT_FIELD_INET_DIAG_SOCKID(", ", req, id,
293                                                      req.sdiag_family);
294                         decode_nla = true;
295                 }
296         } else
297                 tprints("...");
298         tprints("}");
299
300         offset = NLMSG_ALIGN(sizeof(req));
301         if (decode_nla && len > offset) {
302                 tprints(", ");
303                 decode_nlattr(tcp, addr + offset, len - offset,
304                               inet_diag_req_attrs, "INET_DIAG_REQ_???",
305                               inet_diag_req_nla_decoders,
306                               ARRAY_SIZE(inet_diag_req_nla_decoders), NULL);
307         }
308 }
309
310 DECL_NETLINK_DIAG_DECODER(decode_inet_diag_req)
311 {
312         if (nlmsghdr->nlmsg_type == TCPDIAG_GETSOCK
313             || nlmsghdr->nlmsg_type == DCCPDIAG_GETSOCK)
314                 return decode_inet_diag_req_compat(tcp, nlmsghdr,
315                                                    family, addr, len);
316         else
317                 return decode_inet_diag_req_v2(tcp, nlmsghdr,
318                                                family, addr, len);
319 }
320
321 static bool
322 decode_inet_diag_meminfo(struct tcb *const tcp,
323                          const kernel_ulong_t addr,
324                          const unsigned int len,
325                          const void *const opaque_data)
326 {
327         struct inet_diag_meminfo minfo;
328
329         if (len < sizeof(minfo))
330                 return false;
331         if (umove_or_printaddr(tcp, addr, &minfo))
332                 return true;
333
334         PRINT_FIELD_U("{", minfo, idiag_rmem);
335         PRINT_FIELD_U(", ", minfo, idiag_wmem);
336         PRINT_FIELD_U(", ", minfo, idiag_fmem);
337         PRINT_FIELD_U(", ", minfo, idiag_tmem);
338         tprints("}");
339
340         return true;
341 }
342
343 static bool
344 decode_tcpvegas_info(struct tcb *const tcp,
345                      const kernel_ulong_t addr,
346                      const unsigned int len,
347                      const void *const opaque_data)
348 {
349         struct tcpvegas_info vegas;
350
351         if (len < sizeof(vegas))
352                 return false;
353         if (umove_or_printaddr(tcp, addr, &vegas))
354                 return true;
355
356         PRINT_FIELD_U("{", vegas, tcpv_enabled);
357         PRINT_FIELD_U(", ", vegas, tcpv_rttcnt);
358         PRINT_FIELD_U(", ", vegas, tcpv_rtt);
359         PRINT_FIELD_U(", ", vegas, tcpv_minrtt);
360         tprints("}");
361
362         return true;
363 }
364
365 static bool
366 decode_tcp_dctcp_info(struct tcb *const tcp,
367                       const kernel_ulong_t addr,
368                       const unsigned int len,
369                       const void *const opaque_data)
370 {
371         struct tcp_dctcp_info dctcp;
372
373         if (len < sizeof(dctcp))
374                 return false;
375         if (umove_or_printaddr(tcp, addr, &dctcp))
376                 return true;
377
378         PRINT_FIELD_U("{", dctcp, dctcp_enabled);
379         PRINT_FIELD_U(", ", dctcp, dctcp_ce_state);
380         PRINT_FIELD_U(", ", dctcp, dctcp_alpha);
381         PRINT_FIELD_U(", ", dctcp, dctcp_ab_ecn);
382         PRINT_FIELD_U(", ", dctcp, dctcp_ab_tot);
383         tprints("}");
384
385         return true;
386 }
387
388 static bool
389 decode_tcp_bbr_info(struct tcb *const tcp,
390                     const kernel_ulong_t addr,
391                     const unsigned int len,
392                     const void *const opaque_data)
393 {
394         struct tcp_bbr_info bbr;
395
396         if (len < sizeof(bbr))
397                 return false;
398         if (umove_or_printaddr(tcp, addr, &bbr))
399                 return true;
400
401         PRINT_FIELD_X("{", bbr, bbr_bw_lo);
402         PRINT_FIELD_X(", ", bbr, bbr_bw_hi);
403         PRINT_FIELD_U(", ", bbr, bbr_min_rtt);
404         PRINT_FIELD_U(", ", bbr, bbr_pacing_gain);
405         PRINT_FIELD_U(", ", bbr, bbr_cwnd_gain);
406         tprints("}");
407
408         return true;
409 }
410
411 static const nla_decoder_t inet_diag_msg_nla_decoders[] = {
412         [INET_DIAG_MEMINFO]     = decode_inet_diag_meminfo,
413         [INET_DIAG_INFO]        = NULL,                 /* unimplemented */
414         [INET_DIAG_VEGASINFO]   = decode_tcpvegas_info,
415         [INET_DIAG_CONG]        = decode_nla_str,
416         [INET_DIAG_TOS]         = decode_nla_u8,
417         [INET_DIAG_TCLASS]      = decode_nla_u8,
418         [INET_DIAG_SKMEMINFO]   = decode_nla_meminfo,
419         [INET_DIAG_SHUTDOWN]    = decode_nla_u8,
420         [INET_DIAG_DCTCPINFO]   = decode_tcp_dctcp_info,
421         [INET_DIAG_PROTOCOL]    = decode_nla_u8,
422         [INET_DIAG_SKV6ONLY]    = decode_nla_u8,
423         [INET_DIAG_LOCALS]      = NULL,                 /* unimplemented */
424         [INET_DIAG_PEERS]       = NULL,                 /* unimplemented */
425         [INET_DIAG_PAD]         = NULL,
426         [INET_DIAG_MARK]        = decode_nla_u32,
427         [INET_DIAG_BBRINFO]     = decode_tcp_bbr_info
428 };
429
430 DECL_NETLINK_DIAG_DECODER(decode_inet_diag_msg)
431 {
432         struct inet_diag_msg msg = { .idiag_family = family };
433         size_t offset = sizeof(msg.idiag_family);
434         bool decode_nla = false;
435
436         PRINT_FIELD_XVAL("{", msg, idiag_family, addrfams, "AF_???");
437         tprints(", ");
438         if (len >= sizeof(msg)) {
439                 if (!umoven_or_printaddr(tcp, addr + offset,
440                                          sizeof(msg) - offset,
441                                          (void *) &msg + offset)) {
442                         PRINT_FIELD_XVAL("", msg, idiag_state,
443                                          tcp_states, "TCP_???");
444                         PRINT_FIELD_U(", ", msg, idiag_timer);
445                         PRINT_FIELD_U(", ", msg, idiag_retrans);
446                         PRINT_FIELD_INET_DIAG_SOCKID(", ", msg, id,
447                                                      msg.idiag_family);
448                         PRINT_FIELD_U(", ", msg, idiag_expires);
449                         PRINT_FIELD_U(", ", msg, idiag_rqueue);
450                         PRINT_FIELD_U(", ", msg, idiag_wqueue);
451                         PRINT_FIELD_U(", ", msg, idiag_uid);
452                         PRINT_FIELD_U(", ", msg, idiag_inode);
453                         decode_nla = true;
454                 }
455         } else
456                 tprints("...");
457         tprints("}");
458
459         offset = NLMSG_ALIGN(sizeof(msg));
460         if (decode_nla && len > offset) {
461                 tprints(", ");
462                 decode_nlattr(tcp, addr + offset, len - offset,
463                               inet_diag_attrs, "INET_DIAG_???",
464                               inet_diag_msg_nla_decoders,
465                               ARRAY_SIZE(inet_diag_msg_nla_decoders), NULL);
466         }
467 }