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