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