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