xref: /aosp_15_r20/external/libnl/lib/idiag/idiag_msg_obj.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
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