1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * lib/idiag/idiagnl_msg_obj.c Inet Diag Message Object
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * Copyright (c) 2013 Sassano Systems LLC <joe@sassanosystems.com>
11 */
12
13 #include <netlink-private/netlink.h>
14 #include <netlink/hashtable.h>
15 #include <netlink/idiag/msg.h>
16 #include <netlink/idiag/meminfo.h>
17 #include <netlink/idiag/vegasinfo.h>
18 #include <linux/inet_diag.h>
19
20
21 /** @cond SKIP */
22 #define IDIAGNL_ATTR_FAMILY (0x1 << 1)
23 #define IDIAGNL_ATTR_STATE (0x1 << 2)
24 #define IDIAGNL_ATTR_TIMER (0x1 << 3)
25 #define IDIAGNL_ATTR_RETRANS (0x1 << 4)
26 #define IDIAGNL_ATTR_SPORT (0x1 << 5)
27 #define IDIAGNL_ATTR_DPORT (0x1 << 6)
28 #define IDIAGNL_ATTR_SRC (0x1 << 7)
29 #define IDIAGNL_ATTR_DST (0x1 << 8)
30 #define IDIAGNL_ATTR_IFINDEX (0x1 << 9)
31 #define IDIAGNL_ATTR_EXPIRES (0x1 << 10)
32 #define IDIAGNL_ATTR_RQUEUE (0x1 << 11)
33 #define IDIAGNL_ATTR_WQUEUE (0x1 << 12)
34 #define IDIAGNL_ATTR_UID (0x1 << 13)
35 #define IDIAGNL_ATTR_INODE (0x1 << 14)
36 #define IDIAGNL_ATTR_TOS (0x1 << 15)
37 #define IDIAGNL_ATTR_TCLASS (0x1 << 16)
38 #define IDIAGNL_ATTR_SHUTDOWN (0x1 << 17)
39 #define IDIAGNL_ATTR_CONG (0x1 << 18)
40 #define IDIAGNL_ATTR_MEMINFO (0x1 << 19)
41 #define IDIAGNL_ATTR_VEGASINFO (0x1 << 20)
42 #define IDIAGNL_ATTR_TCPINFO (0x1 << 21)
43 #define IDIAGNL_ATTR_SKMEMINFO (0x1 << 22)
44
45 #define _INET_DIAG_ALL ((1<<(INET_DIAG_MAX+1))-1)
46 /** @endcond */
47
48 /**
49 * @ingroup idiag
50 * @defgroup idiagnl_msg Inet Diag Messages
51 *
52 * @details
53 * @idiagnl_doc{idiagnl_msg, Inet Diag Message Documentation}
54 * @{
55 */
idiagnl_msg_alloc(void)56 struct idiagnl_msg *idiagnl_msg_alloc(void)
57 {
58 return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops);
59 }
60
idiagnl_msg_get(struct idiagnl_msg * msg)61 void idiagnl_msg_get(struct idiagnl_msg *msg)
62 {
63 nl_object_get((struct nl_object *) msg);
64 }
65
idiagnl_msg_put(struct idiagnl_msg * msg)66 void idiagnl_msg_put(struct idiagnl_msg *msg)
67 {
68 nl_object_put((struct nl_object *) msg);
69 }
70
71 static struct nl_cache_ops idiagnl_msg_ops;
72
idiagnl_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)73 static int idiagnl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
74 struct nlmsghdr *nlh, struct nl_parser_param *pp)
75 {
76 struct idiagnl_msg *msg = NULL;
77 int err = 0;
78
79 if ((err = idiagnl_msg_parse(nlh, &msg)) < 0)
80 return err;
81
82 err = pp->pp_cb((struct nl_object *) msg, pp);
83 idiagnl_msg_put(msg);
84
85 return err;
86 }
87
idiagnl_request_update(struct nl_cache * cache,struct nl_sock * sk)88 static int idiagnl_request_update(struct nl_cache *cache, struct nl_sock *sk)
89 {
90 int family = cache->c_iarg1;
91 int states = cache->c_iarg2;
92
93 /* idiagnl_send_simple()'s "ext" argument is u16, which is too small for _INET_DIAG_ALL,
94 * which is more than 16 bits on recent kernels.
95 *
96 * Actually, internally idiagnl_send_simple() sets "struct inet_diag_req"'s "idiag_ext"
97 * field, which is only 8 bits. So, it's even worse.
98 *
99 * FIXME: this probably should be fixed (by adding idiagnl_send_simple2() function), but for
100 * the moment it means we cannot request more than 0xFF.
101 */
102
103 return idiagnl_send_simple(sk, 0, family, states, (uint16_t) _INET_DIAG_ALL);
104 }
105
106 static struct nl_cache_ops idiagnl_msg_ops = {
107 .co_name = "idiag/idiag",
108 .co_hdrsize = sizeof(struct inet_diag_msg),
109 .co_msgtypes = {
110 { TCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
111 { DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
112 END_OF_MSGTYPES_LIST,
113 },
114 .co_protocol = NETLINK_INET_DIAG,
115 .co_request_update = idiagnl_request_update,
116 .co_msg_parser = idiagnl_msg_parser,
117 .co_obj_ops = &idiagnl_msg_obj_ops,
118 };
119
idiagnl_init(void)120 static void __init idiagnl_init(void)
121 {
122 nl_cache_mngt_register(&idiagnl_msg_ops);
123 }
124
idiagnl_exit(void)125 static void __exit idiagnl_exit(void)
126 {
127 nl_cache_mngt_unregister(&idiagnl_msg_ops);
128 }
129
130 /**
131 * @name Cache Management
132 * @{
133 */
134
135 /**
136 * Build an inetdiag cache to hold socket state information.
137 * @arg sk Netlink socket
138 * @arg family The address family to query
139 * @arg states Socket states to query
140 * @arg result Result pointer
141 *
142 * @note The caller is responsible for destroying and free the cache after using
143 * it.
144 * @return 0 on success of a negative error code.
145 */
idiagnl_msg_alloc_cache(struct nl_sock * sk,int family,int states,struct nl_cache ** result)146 int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states,
147 struct nl_cache **result)
148 {
149 struct nl_cache *cache = NULL;
150 int err;
151
152 if (!(cache = nl_cache_alloc(&idiagnl_msg_ops)))
153 return -NLE_NOMEM;
154
155 cache->c_iarg1 = family;
156 cache->c_iarg2 = states;
157
158 if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
159 free(cache);
160 return err;
161 }
162
163 *result = cache;
164 return 0;
165 }
166
167 /** @} */
168
169 /**
170 * @name Attributes
171 * @{
172 */
173
idiagnl_msg_get_family(const struct idiagnl_msg * msg)174 uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg)
175 {
176 return msg->idiag_family;
177 }
178
idiagnl_msg_set_family(struct idiagnl_msg * msg,uint8_t family)179 void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family)
180 {
181 msg->idiag_family = family;
182 msg->ce_mask |= IDIAGNL_ATTR_FAMILY;
183 }
184
idiagnl_msg_get_state(const struct idiagnl_msg * msg)185 uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg)
186 {
187 return msg->idiag_state;
188 }
189
idiagnl_msg_set_state(struct idiagnl_msg * msg,uint8_t state)190 void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state)
191 {
192 msg->idiag_state = state;
193 msg->ce_mask |= IDIAGNL_ATTR_STATE;
194 }
195
idiagnl_msg_get_timer(const struct idiagnl_msg * msg)196 uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg)
197 {
198 return msg->idiag_timer;
199 }
200
idiagnl_msg_set_timer(struct idiagnl_msg * msg,uint8_t timer)201 void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer)
202 {
203 msg->idiag_timer = timer;
204 msg->ce_mask |= IDIAGNL_ATTR_TIMER;
205 }
206
idiagnl_msg_get_retrans(const struct idiagnl_msg * msg)207 uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg)
208 {
209 return msg->idiag_retrans;
210 }
211
idiagnl_msg_set_retrans(struct idiagnl_msg * msg,uint8_t retrans)212 void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans)
213 {
214 msg->idiag_retrans = retrans;
215 msg->ce_mask |= IDIAGNL_ATTR_RETRANS;
216 }
217
idiagnl_msg_get_sport(struct idiagnl_msg * msg)218 uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg)
219 {
220 return msg->idiag_sport;
221 }
222
idiagnl_msg_set_sport(struct idiagnl_msg * msg,uint16_t port)223 void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port)
224 {
225 msg->idiag_sport = port;
226 msg->ce_mask |= IDIAGNL_ATTR_SPORT;
227 }
228
idiagnl_msg_get_dport(struct idiagnl_msg * msg)229 uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg)
230 {
231 return msg->idiag_dport;
232 }
233
idiagnl_msg_set_dport(struct idiagnl_msg * msg,uint16_t port)234 void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port)
235 {
236 msg->idiag_dport = port;
237 msg->ce_mask |= IDIAGNL_ATTR_DPORT;
238 }
239
idiagnl_msg_get_src(const struct idiagnl_msg * msg)240 struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg)
241 {
242 return msg->idiag_src;
243 }
244
idiagnl_msg_set_src(struct idiagnl_msg * msg,struct nl_addr * addr)245 int idiagnl_msg_set_src(struct idiagnl_msg *msg, struct nl_addr *addr)
246 {
247 if (msg->idiag_src)
248 nl_addr_put(msg->idiag_src);
249
250 nl_addr_get(addr);
251 msg->idiag_src = addr;
252 msg->ce_mask |= IDIAGNL_ATTR_SRC;
253
254 return 0;
255 }
256
idiagnl_msg_get_dst(const struct idiagnl_msg * msg)257 struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg)
258 {
259 return msg->idiag_dst;
260 }
261
idiagnl_msg_set_dst(struct idiagnl_msg * msg,struct nl_addr * addr)262 int idiagnl_msg_set_dst(struct idiagnl_msg *msg, struct nl_addr *addr)
263 {
264 if (msg->idiag_dst)
265 nl_addr_put(msg->idiag_dst);
266
267 nl_addr_get(addr);
268 msg->idiag_dst = addr;
269 msg->ce_mask |= IDIAGNL_ATTR_DST;
270
271 return 0;
272 }
273
idiagnl_msg_get_ifindex(const struct idiagnl_msg * msg)274 uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg)
275 {
276 return msg->idiag_ifindex;
277 }
278
idiagnl_msg_set_ifindex(struct idiagnl_msg * msg,uint32_t ifindex)279 void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex)
280 {
281 msg->idiag_ifindex = ifindex;
282 msg->ce_mask |= IDIAGNL_ATTR_IFINDEX;
283 }
284
idiagnl_msg_get_expires(const struct idiagnl_msg * msg)285 uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg)
286 {
287 return msg->idiag_expires;
288 }
289
idiagnl_msg_set_expires(struct idiagnl_msg * msg,uint32_t expires)290 void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires)
291 {
292 msg->idiag_expires = expires;
293 msg->ce_mask |= IDIAGNL_ATTR_EXPIRES;
294 }
295
idiagnl_msg_get_rqueue(const struct idiagnl_msg * msg)296 uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg)
297 {
298 return msg->idiag_rqueue;
299 }
300
idiagnl_msg_set_rqueue(struct idiagnl_msg * msg,uint32_t rqueue)301 void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue)
302 {
303 msg->idiag_rqueue = rqueue;
304 msg->ce_mask |= IDIAGNL_ATTR_RQUEUE;
305 }
306
idiagnl_msg_get_wqueue(const struct idiagnl_msg * msg)307 uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg)
308 {
309 return msg->idiag_wqueue;
310 }
311
idiagnl_msg_set_wqueue(struct idiagnl_msg * msg,uint32_t wqueue)312 void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue)
313 {
314 msg->idiag_wqueue = wqueue;
315 msg->ce_mask |= IDIAGNL_ATTR_WQUEUE;
316 }
317
idiagnl_msg_get_uid(const struct idiagnl_msg * msg)318 uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg)
319 {
320 return msg->idiag_uid;
321 }
322
idiagnl_msg_set_uid(struct idiagnl_msg * msg,uint32_t uid)323 void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid)
324 {
325 msg->idiag_uid = uid;
326 msg->ce_mask |= IDIAGNL_ATTR_UID;
327 }
328
idiagnl_msg_get_inode(const struct idiagnl_msg * msg)329 uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg)
330 {
331 return msg->idiag_inode;
332 }
333
idiagnl_msg_set_inode(struct idiagnl_msg * msg,uint32_t inode)334 void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode)
335 {
336 msg->idiag_inode = inode;
337 msg->ce_mask |= IDIAGNL_ATTR_INODE;
338 }
339
idiagnl_msg_get_tos(const struct idiagnl_msg * msg)340 uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg)
341 {
342 return msg->idiag_tos;
343 }
344
idiagnl_msg_set_tos(struct idiagnl_msg * msg,uint8_t tos)345 void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos)
346 {
347 msg->idiag_tos = tos;
348 msg->ce_mask |= IDIAGNL_ATTR_TOS;
349 }
350
idiagnl_msg_get_tclass(const struct idiagnl_msg * msg)351 uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg)
352 {
353 return msg->idiag_tclass;
354 }
355
idiagnl_msg_set_tclass(struct idiagnl_msg * msg,uint8_t tclass)356 void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass)
357 {
358 msg->idiag_tclass = tclass;
359 msg->ce_mask |= IDIAGNL_ATTR_TCLASS;
360 }
361
idiagnl_msg_get_shutdown(const struct idiagnl_msg * msg)362 uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg)
363 {
364 return msg->idiag_shutdown;
365 }
366
idiagnl_msg_set_shutdown(struct idiagnl_msg * msg,uint8_t shutdown)367 void idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown)
368 {
369 msg->idiag_shutdown = shutdown;
370 msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN;
371 }
372
idiagnl_msg_get_cong(const struct idiagnl_msg * msg)373 char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg)
374 {
375 return msg->idiag_cong;
376 }
377
idiagnl_msg_set_cong(struct idiagnl_msg * msg,char * cong)378 void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong)
379 {
380 free (msg->idiag_cong);
381 msg->idiag_cong = strdup(cong);
382 msg->ce_mask |= IDIAGNL_ATTR_CONG;
383 }
384
idiagnl_msg_get_meminfo(const struct idiagnl_msg * msg)385 struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg)
386 {
387 return msg->idiag_meminfo;
388 }
389
idiagnl_msg_set_meminfo(struct idiagnl_msg * msg,struct idiagnl_meminfo * minfo)390 void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo *minfo)
391 {
392 if (msg->idiag_meminfo)
393 idiagnl_meminfo_put(msg->idiag_meminfo);
394
395 idiagnl_meminfo_get(minfo);
396 msg->idiag_meminfo = minfo;
397 msg->ce_mask |= IDIAGNL_ATTR_MEMINFO;
398 }
399
idiagnl_msg_get_vegasinfo(const struct idiagnl_msg * msg)400 struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg)
401 {
402 return msg->idiag_vegasinfo;
403 }
404
idiagnl_msg_set_vegasinfo(struct idiagnl_msg * msg,struct idiagnl_vegasinfo * vinfo)405 void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo *vinfo)
406 {
407 if (msg->idiag_vegasinfo)
408 idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
409
410 idiagnl_vegasinfo_get(vinfo);
411 msg->idiag_vegasinfo = vinfo;
412 msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
413 }
414
idiagnl_msg_get_tcpinfo(const struct idiagnl_msg * msg)415 struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg)
416 {
417 return msg->idiag_tcpinfo;
418 }
419
idiagnl_msg_set_tcpinfo(struct idiagnl_msg * msg,struct tcp_info * tinfo)420 void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo)
421 {
422 memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info));
423 msg->ce_mask |= IDIAGNL_ATTR_TCPINFO;
424 }
425
426 /** @} */
427
idiag_msg_dump_line(struct nl_object * a,struct nl_dump_params * p)428 static void idiag_msg_dump_line(struct nl_object *a, struct nl_dump_params *p)
429 {
430 struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
431 char buf[64] = { 0 };
432
433 nl_dump_line(p, "family: %s ", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
434 nl_dump(p, "src: %s:%d ", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
435 ntohs(msg->idiag_sport));
436 nl_dump(p, "dst: %s:%d ", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
437 ntohs(msg->idiag_dport));
438 nl_dump(p, "iif: %d ", msg->idiag_ifindex);
439 nl_dump(p, "\n");
440 }
441
idiag_msg_dump_details(struct nl_object * a,struct nl_dump_params * p)442 static void idiag_msg_dump_details(struct nl_object *a, struct nl_dump_params *p)
443 {
444 struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
445 char buf[64], buf2[64];
446
447 nl_dump(p, "\nfamily: %s\n", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
448 nl_dump(p, "state: %s\n",
449 idiagnl_state2str(msg->idiag_state, buf, sizeof(buf)));
450 nl_dump(p, "timer (%s, %s, retransmits: %d)\n",
451 idiagnl_timer2str(msg->idiag_timer, buf, sizeof(buf)),
452 nl_msec2str(msg->idiag_expires, buf2, sizeof(buf2)),
453 msg->idiag_retrans);
454
455 nl_dump(p, "source: %s:%d\n", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
456 ntohs(msg->idiag_sport));
457 nl_dump(p, "destination: %s:%d\n", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
458 ntohs(msg->idiag_dport));
459
460 nl_dump(p, "ifindex: %d\n", msg->idiag_ifindex);
461 nl_dump(p, "rqueue: %-6d wqueue: %-6d\n", msg->idiag_rqueue, msg->idiag_wqueue);
462 nl_dump(p, "uid %d\n", msg->idiag_uid);
463 nl_dump(p, "inode %d\n", msg->idiag_inode);
464 if (msg->idiag_shutdown) {
465 nl_dump(p, "socket shutdown: %s\n",
466 idiagnl_shutdown2str(msg->idiag_shutdown,
467 buf, sizeof(buf)));
468 }
469
470 nl_dump(p, "tos: 0x%x\n", msg->idiag_tos);
471 nl_dump(p, "traffic class: %d\n", msg->idiag_tclass);
472 nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong ? msg->idiag_cong : "");
473 }
474
idiag_msg_dump_stats(struct nl_object * obj,struct nl_dump_params * p)475 static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
476 {
477 struct idiagnl_msg *msg = (struct idiagnl_msg *) obj;
478 char buf[64];
479
480 idiag_msg_dump_details(obj, p);
481
482 nl_dump(p, "tcp info: [\n");
483 nl_dump(p, "\tsocket state: %s\n",
484 idiagnl_state2str(msg->idiag_tcpinfo.tcpi_state,
485 buf, sizeof(buf)));
486 nl_dump(p, "\ttcp state: %s\n",
487 idiagnl_tcpstate2str(msg->idiag_tcpinfo.tcpi_ca_state,
488 buf, sizeof(buf)));
489 nl_dump(p, "\tretransmits: %d\n",
490 msg->idiag_tcpinfo.tcpi_retransmits);
491 nl_dump(p, "\tprobes: %d\n",
492 msg->idiag_tcpinfo.tcpi_probes);
493 nl_dump(p, "\tbackoff: %d\n",
494 msg->idiag_tcpinfo.tcpi_backoff);
495 nl_dump(p, "\toptions: %s\n",
496 idiagnl_tcpopts2str(msg->idiag_tcpinfo.tcpi_options,
497 buf, sizeof(buf)));
498 nl_dump(p, "\tsnd_wscale: %d\n", msg->idiag_tcpinfo.tcpi_snd_wscale);
499 nl_dump(p, "\trcv_wscale: %d\n", msg->idiag_tcpinfo.tcpi_rcv_wscale);
500 nl_dump(p, "\trto: %d\n", msg->idiag_tcpinfo.tcpi_rto);
501 nl_dump(p, "\tato: %d\n", msg->idiag_tcpinfo.tcpi_ato);
502 nl_dump(p, "\tsnd_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_snd_mss,
503 buf, sizeof(buf)));
504 nl_dump(p, "\trcv_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_mss,
505 buf, sizeof(buf)));
506 nl_dump(p, "\tunacked: %d\n", msg->idiag_tcpinfo.tcpi_unacked);
507 nl_dump(p, "\tsacked: %d\n", msg->idiag_tcpinfo.tcpi_sacked);
508
509 nl_dump(p, "\tlost: %d\n", msg->idiag_tcpinfo.tcpi_lost);
510 nl_dump(p, "\tretransmit segments: %d\n",
511 msg->idiag_tcpinfo.tcpi_retrans);
512 nl_dump(p, "\tfackets: %d\n",
513 msg->idiag_tcpinfo.tcpi_fackets);
514 nl_dump(p, "\tlast data sent: %s\n",
515 nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_sent, buf,
516 sizeof(buf)));
517 nl_dump(p, "\tlast ack sent: %s\n",
518 nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_sent, buf, sizeof(buf)));
519 nl_dump(p, "\tlast data recv: %s\n",
520 nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_recv, buf,
521 sizeof(buf)));
522 nl_dump(p, "\tlast ack recv: %s\n",
523 nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_recv, buf,
524 sizeof(buf)));
525 nl_dump(p, "\tpath mtu: %s\n",
526 nl_size2str(msg->idiag_tcpinfo.tcpi_pmtu, buf,
527 sizeof(buf)));
528 nl_dump(p, "\trcv ss threshold: %d\n",
529 msg->idiag_tcpinfo.tcpi_rcv_ssthresh);
530 nl_dump(p, "\tsmoothed round trip time: %d\n",
531 msg->idiag_tcpinfo.tcpi_rtt);
532 nl_dump(p, "\tround trip time variation: %d\n",
533 msg->idiag_tcpinfo.tcpi_rttvar);
534 nl_dump(p, "\tsnd ss threshold: %s\n",
535 nl_size2str(msg->idiag_tcpinfo.tcpi_snd_ssthresh, buf,
536 sizeof(buf)));
537 nl_dump(p, "\tsend congestion window: %d\n",
538 msg->idiag_tcpinfo.tcpi_snd_cwnd);
539 nl_dump(p, "\tadvertised mss: %s\n",
540 nl_size2str(msg->idiag_tcpinfo.tcpi_advmss, buf,
541 sizeof(buf)));
542 nl_dump(p, "\treordering: %d\n",
543 msg->idiag_tcpinfo.tcpi_reordering);
544 nl_dump(p, "\trcv rround trip time: %d\n",
545 msg->idiag_tcpinfo.tcpi_rcv_rtt);
546 nl_dump(p, "\treceive queue space: %s\n",
547 nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_space, buf,
548 sizeof(buf)));
549 nl_dump(p, "\ttotal retransmits: %d\n",
550 msg->idiag_tcpinfo.tcpi_total_retrans);
551 nl_dump(p, "]\n");
552
553 if (msg->idiag_meminfo) {
554 nl_dump(p, "meminfo: [\n");
555 nl_dump(p, "\trmem: %s\n",
556 nl_size2str(msg->idiag_meminfo->idiag_rmem,
557 buf,
558 sizeof(buf)));
559 nl_dump(p, "\twmem: %s\n",
560 nl_size2str(msg->idiag_meminfo->idiag_wmem,
561 buf,
562 sizeof(buf)));
563 nl_dump(p, "\tfmem: %s\n",
564 nl_size2str(msg->idiag_meminfo->idiag_fmem,
565 buf,
566 sizeof(buf)));
567 nl_dump(p, "\ttmem: %s\n",
568 nl_size2str(msg->idiag_meminfo->idiag_tmem,
569 buf,
570 sizeof(buf)));
571 nl_dump(p, "]\n");
572 }
573
574 if (msg->idiag_vegasinfo) {
575 nl_dump(p, "vegasinfo: [\n");
576 nl_dump(p, "\tvegas enabled: %d\n",
577 msg->idiag_vegasinfo->tcpv_enabled);
578 if (msg->idiag_vegasinfo->tcpv_enabled) {
579 nl_dump(p, "\trtt cnt: %d",
580 msg->idiag_vegasinfo->tcpv_rttcnt);
581 nl_dump(p, "\trtt (propagation delay): %d",
582 msg->idiag_vegasinfo->tcpv_rtt);
583 nl_dump(p, "\tmin rtt: %d",
584 msg->idiag_vegasinfo->tcpv_minrtt);
585 }
586 nl_dump(p, "]\n");
587 }
588
589 if (msg->ce_mask & IDIAGNL_ATTR_MEMINFO) {
590 nl_dump(p, "skmeminfo: [\n");
591 nl_dump(p, "\trmem alloc: %d\n",
592 msg->idiag_skmeminfo[SK_MEMINFO_RMEM_ALLOC]);
593 nl_dump(p, "\trcv buf: %s\n",
594 nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_RCVBUF],
595 buf, sizeof(buf)));
596 nl_dump(p, "\twmem alloc: %d\n",
597 msg->idiag_skmeminfo[SK_MEMINFO_WMEM_ALLOC]);
598 nl_dump(p, "\tsnd buf: %s\n",
599 nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_SNDBUF],
600 buf, sizeof(buf)));
601 nl_dump(p, "\tfwd alloc: %d\n",
602 msg->idiag_skmeminfo[SK_MEMINFO_FWD_ALLOC]);
603 nl_dump(p, "\twmem queued: %s\n",
604 nl_size2str(msg->idiag_skmeminfo[SK_MEMINFO_WMEM_QUEUED],
605 buf, sizeof(buf)));
606 nl_dump(p, "\topt mem: %d\n",
607 msg->idiag_skmeminfo[SK_MEMINFO_OPTMEM]);
608 nl_dump(p, "\tbacklog: %d\n",
609 msg->idiag_skmeminfo[SK_MEMINFO_BACKLOG]);
610 nl_dump(p, "]\n\n");
611 }
612 }
613
idiagnl_msg_free(struct nl_object * a)614 static void idiagnl_msg_free(struct nl_object *a)
615 {
616 struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
617 if (a == NULL)
618 return;
619
620 free(msg->idiag_cong);
621 nl_addr_put(msg->idiag_src);
622 nl_addr_put(msg->idiag_dst);
623 idiagnl_meminfo_put(msg->idiag_meminfo);
624 idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
625 }
626
idiagnl_msg_clone(struct nl_object * _dst,struct nl_object * _src)627 static int idiagnl_msg_clone(struct nl_object *_dst, struct nl_object *_src)
628 {
629 struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst;
630 struct idiagnl_msg *src = (struct idiagnl_msg *) _src;
631
632 dst->idiag_cong = NULL;
633 dst->idiag_src = NULL;
634 dst->idiag_dst = NULL;
635 dst->idiag_meminfo = NULL;
636 dst->idiag_vegasinfo = NULL;
637 dst->ce_mask &= ~(IDIAGNL_ATTR_CONG |
638 IDIAGNL_ATTR_SRC |
639 IDIAGNL_ATTR_DST |
640 IDIAGNL_ATTR_MEMINFO |
641 IDIAGNL_ATTR_VEGASINFO);
642
643 if (src->idiag_cong) {
644 if (!(dst->idiag_cong = strdup(src->idiag_cong)))
645 return -NLE_NOMEM;
646 dst->ce_mask |= IDIAGNL_ATTR_CONG;
647 }
648
649 if (src->idiag_src) {
650 if (!(dst->idiag_src = nl_addr_clone(src->idiag_src)))
651 return -NLE_NOMEM;
652 dst->ce_mask |= IDIAGNL_ATTR_SRC;
653 }
654
655 if (src->idiag_dst) {
656 if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst)))
657 return -NLE_NOMEM;
658 dst->ce_mask |= IDIAGNL_ATTR_DST;
659 }
660
661 if (src->idiag_meminfo) {
662 if (!(dst->idiag_meminfo = (struct idiagnl_meminfo *) nl_object_clone((struct nl_object *) src->idiag_meminfo)))
663 return -NLE_NOMEM;
664 dst->ce_mask |= IDIAGNL_ATTR_MEMINFO;
665 }
666
667 if (src->idiag_vegasinfo) {
668 if (!(dst->idiag_vegasinfo = (struct idiagnl_vegasinfo *) nl_object_clone((struct nl_object *) src->idiag_vegasinfo)))
669 return -NLE_NOMEM;
670 dst->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
671 }
672
673 return 0;
674 }
675
676 static struct nla_policy ext_policy[INET_DIAG_MAX+1] = {
677 [INET_DIAG_MEMINFO] = { .minlen = sizeof(struct inet_diag_meminfo) },
678 [INET_DIAG_INFO] = { .minlen = sizeof(struct tcp_info) },
679 [INET_DIAG_VEGASINFO] = { .minlen = sizeof(struct tcpvegas_info) },
680 [INET_DIAG_CONG] = { .type = NLA_STRING },
681 [INET_DIAG_TOS] = { .type = NLA_U8 },
682 [INET_DIAG_TCLASS] = { .type = NLA_U8 },
683 /* Older kernel doesn't have SK_MEMINFO_BACKLOG */
684 [INET_DIAG_SKMEMINFO] = { .minlen = (sizeof(uint32_t) * (SK_MEMINFO_OPTMEM + 1)) },
685 [INET_DIAG_SHUTDOWN] = { .type = NLA_U8 },
686 };
687
idiagnl_msg_parse(struct nlmsghdr * nlh,struct idiagnl_msg ** result)688 int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result)
689 {
690 struct idiagnl_msg *msg = NULL;
691 struct inet_diag_msg *raw_msg = NULL;
692 struct nl_addr *src = NULL, *dst = NULL;
693 struct nlattr *tb[INET_DIAG_MAX+1];
694 int err = 0;
695
696 msg = idiagnl_msg_alloc();
697 if (!msg)
698 goto errout_nomem;
699
700 err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, INET_DIAG_MAX,
701 ext_policy);
702 if (err < 0)
703 goto errout;
704
705 raw_msg = nlmsg_data(nlh);
706 msg->idiag_family = raw_msg->idiag_family;
707 msg->idiag_state = raw_msg->idiag_state;
708 msg->idiag_timer = raw_msg->idiag_timer;
709 msg->idiag_retrans = raw_msg->idiag_retrans;
710 msg->idiag_expires = raw_msg->idiag_expires;
711 msg->idiag_rqueue = raw_msg->idiag_rqueue;
712 msg->idiag_wqueue = raw_msg->idiag_wqueue;
713 msg->idiag_uid = raw_msg->idiag_uid;
714 msg->idiag_inode = raw_msg->idiag_inode;
715 msg->idiag_sport = raw_msg->id.idiag_sport;
716 msg->idiag_dport = raw_msg->id.idiag_dport;
717 msg->idiag_ifindex = raw_msg->id.idiag_if;
718
719 msg->ce_mask = (IDIAGNL_ATTR_FAMILY |
720 IDIAGNL_ATTR_STATE |
721 IDIAGNL_ATTR_TIMER |
722 IDIAGNL_ATTR_RETRANS |
723 IDIAGNL_ATTR_EXPIRES |
724 IDIAGNL_ATTR_RQUEUE |
725 IDIAGNL_ATTR_WQUEUE |
726 IDIAGNL_ATTR_UID |
727 IDIAGNL_ATTR_INODE |
728 IDIAGNL_ATTR_SPORT |
729 IDIAGNL_ATTR_DPORT |
730 IDIAGNL_ATTR_IFINDEX);
731
732 dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst,
733 sizeof(raw_msg->id.idiag_dst));
734 if (!dst)
735 goto errout_nomem;
736
737 err = idiagnl_msg_set_dst(msg, dst);
738 if (err < 0)
739 goto errout;
740
741 nl_addr_put(dst);
742
743 src = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_src,
744 sizeof(raw_msg->id.idiag_src));
745 if (!src)
746 goto errout_nomem;
747
748 err = idiagnl_msg_set_src(msg, src);
749 if (err < 0)
750 goto errout;
751
752 nl_addr_put(src);
753
754 if (tb[INET_DIAG_TOS]) {
755 msg->idiag_tos = nla_get_u8(tb[INET_DIAG_TOS]);
756 msg->ce_mask |= IDIAGNL_ATTR_TOS;
757 }
758
759 if (tb[INET_DIAG_TCLASS]) {
760 msg->idiag_tclass = nla_get_u8(tb[INET_DIAG_TCLASS]);
761 msg->ce_mask |= IDIAGNL_ATTR_TCLASS;
762 }
763
764 if (tb[INET_DIAG_SHUTDOWN]) {
765 msg->idiag_shutdown = nla_get_u8(tb[INET_DIAG_SHUTDOWN]);
766 msg->ce_mask |= IDIAGNL_ATTR_SHUTDOWN;
767 }
768
769 if (tb[INET_DIAG_CONG]) {
770 msg->idiag_cong = nla_strdup(tb[INET_DIAG_CONG]);
771 msg->ce_mask |= IDIAGNL_ATTR_CONG;
772 }
773
774 if (tb[INET_DIAG_INFO]) {
775 nla_memcpy(&msg->idiag_tcpinfo, tb[INET_DIAG_INFO],
776 sizeof(msg->idiag_tcpinfo));
777 msg->ce_mask |= IDIAGNL_ATTR_TCPINFO;
778 }
779
780 if (tb[INET_DIAG_MEMINFO]) {
781 struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc();
782 struct inet_diag_meminfo *raw_minfo = NULL;
783
784 if (!minfo)
785 goto errout_nomem;
786
787 raw_minfo = (struct inet_diag_meminfo *)
788 nla_data(tb[INET_DIAG_MEMINFO]);
789
790 idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem);
791 idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem);
792 idiagnl_meminfo_set_fmem(minfo, raw_minfo->idiag_fmem);
793 idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem);
794
795 msg->idiag_meminfo = minfo;
796 msg->ce_mask |= IDIAGNL_ATTR_MEMINFO;
797 }
798
799 if (tb[INET_DIAG_VEGASINFO]) {
800 struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc();
801 struct tcpvegas_info *raw_vinfo = NULL;
802
803 if (!vinfo)
804 goto errout_nomem;
805
806 raw_vinfo = (struct tcpvegas_info *)
807 nla_data(tb[INET_DIAG_VEGASINFO]);
808
809 idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled);
810 idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt);
811 idiagnl_vegasinfo_set_rtt(vinfo, raw_vinfo->tcpv_rtt);
812 idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt);
813
814 msg->idiag_vegasinfo = vinfo;
815 msg->ce_mask |= IDIAGNL_ATTR_VEGASINFO;
816 }
817
818 if (tb[INET_DIAG_SKMEMINFO]) {
819 nla_memcpy(&msg->idiag_skmeminfo, tb[INET_DIAG_SKMEMINFO],
820 sizeof(msg->idiag_skmeminfo));
821 msg->ce_mask |= IDIAGNL_ATTR_SKMEMINFO;
822 }
823
824 *result = msg;
825 return 0;
826
827 errout:
828 idiagnl_msg_put(msg);
829 return err;
830
831 errout_nomem:
832 err = -NLE_NOMEM;
833 goto errout;
834 }
835
836 static const struct trans_tbl idiagnl_attrs[] = {
837 __ADD(IDIAGNL_ATTR_FAMILY, family),
838 __ADD(IDIAGNL_ATTR_STATE, state),
839 __ADD(IDIAGNL_ATTR_TIMER, timer),
840 __ADD(IDIAGNL_ATTR_RETRANS, retrans),
841 __ADD(IDIAGNL_ATTR_SPORT, sport),
842 __ADD(IDIAGNL_ATTR_DPORT, dport),
843 __ADD(IDIAGNL_ATTR_SRC, src),
844 __ADD(IDIAGNL_ATTR_DST, dst),
845 __ADD(IDIAGNL_ATTR_IFINDEX, ifindex),
846 __ADD(IDIAGNL_ATTR_EXPIRES, expires),
847 __ADD(IDIAGNL_ATTR_RQUEUE, rqueue),
848 __ADD(IDIAGNL_ATTR_WQUEUE, wqueue),
849 __ADD(IDIAGNL_ATTR_UID, uid),
850 __ADD(IDIAGNL_ATTR_INODE, inode),
851 __ADD(IDIAGNL_ATTR_TOS, tos),
852 __ADD(IDIAGNL_ATTR_TCLASS, tclass),
853 __ADD(IDIAGNL_ATTR_SHUTDOWN, shutdown),
854 __ADD(IDIAGNL_ATTR_CONG, cong),
855 __ADD(IDIAGNL_ATTR_MEMINFO, meminfo),
856 __ADD(IDIAGNL_ATTR_VEGASINFO, vegasinfo),
857 __ADD(IDIAGNL_ATTR_TCPINFO, tcpinfo),
858 __ADD(IDIAGNL_ATTR_SKMEMINFO, skmeminfo),
859 };
860
_idiagnl_attrs2str(int attrs,char * buf,size_t len)861 static char *_idiagnl_attrs2str(int attrs, char *buf, size_t len)
862 {
863 return __flags2str(attrs, buf, len, idiagnl_attrs,
864 ARRAY_SIZE(idiagnl_attrs));
865 }
866
idiagnl_compare(struct nl_object * _a,struct nl_object * _b,uint64_t attrs,int flags)867 static uint64_t idiagnl_compare(struct nl_object *_a, struct nl_object *_b,
868 uint64_t attrs, int flags)
869 {
870 struct idiagnl_msg *a = (struct idiagnl_msg *) _a;
871 struct idiagnl_msg *b = (struct idiagnl_msg *) _b;
872 uint64_t diff = 0;
873
874 #define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, IDIAGNL_ATTR_##ATTR, a, b, EXPR)
875 diff |= _DIFF(FAMILY, a->idiag_family != b->idiag_family);
876 diff |= _DIFF(STATE, a->idiag_state != b->idiag_state);
877 diff |= _DIFF(TIMER, a->idiag_timer != b->idiag_timer);
878 diff |= _DIFF(RETRANS, a->idiag_retrans != b->idiag_retrans);
879 diff |= _DIFF(SPORT, a->idiag_sport != b->idiag_sport);
880 diff |= _DIFF(DPORT, a->idiag_dport != b->idiag_dport);
881 diff |= _DIFF(SRC, nl_addr_cmp (a->idiag_src, b->idiag_src));
882 diff |= _DIFF(DST, nl_addr_cmp (a->idiag_dst, b->idiag_dst));
883 diff |= _DIFF(IFINDEX, a->idiag_ifindex != b->idiag_ifindex);
884 diff |= _DIFF(EXPIRES, a->idiag_expires != b->idiag_expires);
885 diff |= _DIFF(RQUEUE, a->idiag_rqueue != b->idiag_rqueue);
886 diff |= _DIFF(WQUEUE, a->idiag_wqueue != b->idiag_wqueue);
887 diff |= _DIFF(UID, a->idiag_uid != b->idiag_uid);
888 diff |= _DIFF(INODE, a->idiag_inode != b->idiag_inode);
889 diff |= _DIFF(TOS, a->idiag_tos != b->idiag_tos);
890 diff |= _DIFF(TCLASS, a->idiag_tclass != b->idiag_tclass);
891 diff |= _DIFF(SHUTDOWN, a->idiag_shutdown != b->idiag_shutdown);
892 diff |= _DIFF(CONG, strcmp(a->idiag_cong, b->idiag_cong));
893 diff |= _DIFF(MEMINFO, nl_object_diff((struct nl_object *) a->idiag_meminfo, (struct nl_object *) b->idiag_meminfo));
894 diff |= _DIFF(VEGASINFO, nl_object_diff((struct nl_object *) a->idiag_vegasinfo, (struct nl_object *) b->idiag_vegasinfo));
895 diff |= _DIFF(TCPINFO, memcmp(&a->idiag_tcpinfo, &b->idiag_tcpinfo, sizeof(a->idiag_tcpinfo)));
896 diff |= _DIFF(SKMEMINFO, memcmp(a->idiag_skmeminfo, b->idiag_skmeminfo, sizeof(a->idiag_skmeminfo)));
897 #undef _DIFF
898 return diff;
899 }
900
idiagnl_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t table_sz)901 static void idiagnl_keygen(struct nl_object *obj, uint32_t *hashkey,
902 uint32_t table_sz)
903 {
904 struct idiagnl_msg *msg = (struct idiagnl_msg *)obj;
905 unsigned int key_sz;
906 struct idiagnl_hash_key {
907 uint8_t family;
908 uint32_t src_hash;
909 uint32_t dst_hash;
910 uint16_t sport;
911 uint16_t dport;
912 } __attribute__((packed)) key;
913
914 key_sz = sizeof(key);
915 key.family = msg->idiag_family;
916 key.src_hash = 0;
917 key.dst_hash = 0;
918 key.sport = msg->idiag_sport;
919 key.dport = msg->idiag_dport;
920
921 if (msg->idiag_src) {
922 key.src_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_src),
923 nl_addr_get_len(msg->idiag_src), 0);
924 }
925 if (msg->idiag_dst) {
926 key.dst_hash = nl_hash (nl_addr_get_binary_addr(msg->idiag_dst),
927 nl_addr_get_len(msg->idiag_dst), 0);
928 }
929
930 *hashkey = nl_hash(&key, key_sz, 0) % table_sz;
931
932 NL_DBG(5, "idiagnl %p key (fam %d src_hash %d dst_hash %d sport %d dport %d) keysz %d, hash 0x%x\n",
933 msg, key.family, key.src_hash, key.dst_hash, key.sport, key.dport, key_sz, *hashkey);
934
935 return;
936 }
937
938 /** @cond SKIP */
939 struct nl_object_ops idiagnl_msg_obj_ops = {
940 .oo_name = "idiag/idiag_msg",
941 .oo_size = sizeof(struct idiagnl_msg),
942 .oo_free_data = idiagnl_msg_free,
943 .oo_clone = idiagnl_msg_clone,
944 .oo_dump = {
945 [NL_DUMP_LINE] = idiag_msg_dump_line,
946 [NL_DUMP_DETAILS] = idiag_msg_dump_details,
947 [NL_DUMP_STATS] = idiag_msg_dump_stats,
948 },
949 .oo_compare = idiagnl_compare,
950 .oo_keygen = idiagnl_keygen,
951 .oo_attrs2str = _idiagnl_attrs2str,
952 .oo_id_attrs = (IDIAGNL_ATTR_FAMILY |
953 IDIAGNL_ATTR_SRC |
954 IDIAGNL_ATTR_DST |
955 IDIAGNL_ATTR_SPORT |
956 IDIAGNL_ATTR_DPORT),
957 };
958 /** @endcond */
959
960 /** @} */
961