Home | History | Annotate | Download | only in idiag
      1 /*
      2  * lib/idiag/idiagnl_msg_obj.c Inet Diag Message Object
      3  *
      4  *	This library is free software; you can redistribute it and/or
      5  *	modify it under the terms of the GNU Lesser General Public
      6  *	License as published by the Free Software Foundation version 2.1
      7  *	of the License.
      8  *
      9  * Copyright (c) 2013 Sassano Systems LLC <joe (at) sassanosystems.com>
     10  */
     11 
     12 #include <netlink-private/netlink.h>
     13 #include <netlink/idiag/msg.h>
     14 #include <netlink/idiag/meminfo.h>
     15 #include <netlink/idiag/vegasinfo.h>
     16 #include <linux/inet_diag.h>
     17 
     18 /**
     19  * @ingroup idiag
     20  * @defgroup idiagnl_msg Inet Diag Messages
     21  *
     22  * @details
     23  * @idiagnl_doc{idiagnl_msg, Inet Diag Message Documentation}
     24  * @{
     25  */
     26 struct idiagnl_msg *idiagnl_msg_alloc(void)
     27 {
     28 	return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops);
     29 }
     30 
     31 void idiagnl_msg_get(struct idiagnl_msg *msg)
     32 {
     33 	nl_object_get((struct nl_object *) msg);
     34 }
     35 
     36 void idiagnl_msg_put(struct idiagnl_msg *msg)
     37 {
     38 	nl_object_put((struct nl_object *) msg);
     39 }
     40 
     41 static struct nl_cache_ops idiagnl_msg_ops;
     42 
     43 static int idiagnl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
     44 		struct nlmsghdr *nlh, struct nl_parser_param *pp)
     45 {
     46 	struct idiagnl_msg *msg = NULL;
     47 	int err = 0;
     48 
     49 	if ((err = idiagnl_msg_parse(nlh, &msg)) < 0)
     50 		return err;
     51 
     52 	err = pp->pp_cb((struct nl_object *) msg, pp);
     53 	idiagnl_msg_put(msg);
     54 
     55 	return err;
     56 }
     57 
     58 static int idiagnl_request_update(struct nl_cache *cache, struct nl_sock *sk)
     59 {
     60 	int family = cache->c_iarg1;
     61 	int states = cache->c_iarg2;
     62 
     63 	return idiagnl_send_simple(sk, 0, family, states, IDIAG_ATTR_ALL);
     64 }
     65 
     66 static struct nl_cache_ops idiagnl_msg_ops = {
     67 	.co_name		= "idiag/idiag",
     68 	.co_hdrsize		= sizeof(struct inet_diag_msg),
     69 	.co_msgtypes		= {
     70 		{ IDIAG_TCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
     71 		{ IDIAG_DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
     72 		END_OF_MSGTYPES_LIST,
     73 	},
     74 	.co_protocol		= NETLINK_INET_DIAG,
     75 	.co_request_update	= idiagnl_request_update,
     76 	.co_msg_parser		= idiagnl_msg_parser,
     77 	.co_obj_ops		= &idiagnl_msg_obj_ops,
     78 };
     79 
     80 static void __init idiagnl_init(void)
     81 {
     82 	nl_cache_mngt_register(&idiagnl_msg_ops);
     83 }
     84 
     85 static void __exit idiagnl_exit(void)
     86 {
     87 	nl_cache_mngt_unregister(&idiagnl_msg_ops);
     88 }
     89 
     90 /**
     91  * @name Cache Management
     92  * @{
     93  */
     94 
     95 /**
     96  * Build an inetdiag cache to hold socket state information.
     97  * @arg	sk      Netlink socket
     98  * @arg family  The address family to query
     99  * @arg states  Socket states to query
    100  * @arg result  Result pointer
    101  *
    102  * @note The caller is responsible for destroying and free the cache after using
    103  *  it.
    104  * @return 0 on success of a negative error code.
    105  */
    106 int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states,
    107 		struct nl_cache **result)
    108 {
    109 	struct nl_cache *cache = NULL;
    110 	int err;
    111 
    112 	if (!(cache = nl_cache_alloc(&idiagnl_msg_ops)))
    113 		return -NLE_NOMEM;
    114 
    115 	cache->c_iarg1 = family;
    116 	cache->c_iarg2 = states;
    117 
    118 	if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
    119 		free(cache);
    120 		return err;
    121 	}
    122 
    123 	*result = cache;
    124 	return 0;
    125 }
    126 
    127 /** @} */
    128 
    129 /**
    130  * @name Attributes
    131  * @{
    132  */
    133 
    134 uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg)
    135 {
    136 	return msg->idiag_family;
    137 }
    138 
    139 void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family)
    140 {
    141 	msg->idiag_family = family;
    142 }
    143 
    144 uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg)
    145 {
    146 	return msg->idiag_state;
    147 }
    148 
    149 void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state)
    150 {
    151 	msg->idiag_state = state;
    152 }
    153 
    154 uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg)
    155 {
    156 	return msg->idiag_timer;
    157 }
    158 
    159 void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer)
    160 {
    161 	msg->idiag_timer = timer;
    162 }
    163 
    164 uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg)
    165 {
    166 	return msg->idiag_retrans;
    167 }
    168 
    169 void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans)
    170 {
    171 	msg->idiag_retrans = retrans;
    172 }
    173 
    174 uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg)
    175 {
    176 	return msg->idiag_sport;
    177 }
    178 
    179 void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port)
    180 {
    181 	msg->idiag_sport = port;
    182 }
    183 
    184 uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg)
    185 {
    186 	return msg->idiag_dport;
    187 }
    188 
    189 void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port)
    190 {
    191 	msg->idiag_dport = port;
    192 }
    193 
    194 struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg)
    195 {
    196 	return msg->idiag_src;
    197 }
    198 
    199 int idiagnl_msg_set_src(struct idiagnl_msg *msg, struct nl_addr *addr)
    200 {
    201 	if (msg->idiag_src)
    202 		nl_addr_put(msg->idiag_src);
    203 
    204 	nl_addr_get(addr);
    205 	msg->idiag_src = addr;
    206 
    207 	return 0;
    208 }
    209 
    210 struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg)
    211 {
    212 	return msg->idiag_dst;
    213 }
    214 
    215 int idiagnl_msg_set_dst(struct idiagnl_msg *msg, struct nl_addr *addr)
    216 {
    217 	if (msg->idiag_dst)
    218 		nl_addr_put(msg->idiag_dst);
    219 
    220 	nl_addr_get(addr);
    221 	msg->idiag_dst = addr;
    222 
    223 	return 0;
    224 }
    225 
    226 uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg)
    227 {
    228 	return msg->idiag_ifindex;
    229 }
    230 
    231 void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex)
    232 {
    233 	msg->idiag_ifindex = ifindex;
    234 }
    235 
    236 uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg)
    237 {
    238 	return msg->idiag_expires;
    239 }
    240 
    241 void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires)
    242 {
    243 	msg->idiag_expires = expires;
    244 }
    245 
    246 uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg)
    247 {
    248 	return msg->idiag_rqueue;
    249 }
    250 
    251 void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue)
    252 {
    253 	msg->idiag_rqueue = rqueue;
    254 }
    255 
    256 uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg)
    257 {
    258 	return msg->idiag_wqueue;
    259 }
    260 
    261 void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue)
    262 {
    263 	msg->idiag_wqueue = wqueue;
    264 }
    265 
    266 uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg)
    267 {
    268 	return msg->idiag_uid;
    269 }
    270 
    271 void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid)
    272 {
    273 	msg->idiag_uid = uid;
    274 }
    275 
    276 uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg)
    277 {
    278 	return msg->idiag_inode;
    279 }
    280 
    281 void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode)
    282 {
    283 	msg->idiag_inode = inode;
    284 }
    285 
    286 uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg)
    287 {
    288 	return msg->idiag_tos;
    289 }
    290 
    291 void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos)
    292 {
    293 	msg->idiag_tos = tos;
    294 }
    295 
    296 uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg)
    297 {
    298 	return msg->idiag_tclass;
    299 }
    300 
    301 void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass)
    302 {
    303 	msg->idiag_tclass = tclass;
    304 }
    305 
    306 uint8_t	idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg)
    307 {
    308 	return msg->idiag_shutdown;
    309 }
    310 
    311 void  idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown)
    312 {
    313 	msg->idiag_shutdown = shutdown;
    314 }
    315 
    316 char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg)
    317 {
    318 	return msg->idiag_cong;
    319 }
    320 
    321 void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong)
    322 {
    323 	msg->idiag_cong = strdup(cong);
    324 }
    325 
    326 struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg)
    327 {
    328 	return msg->idiag_meminfo;
    329 }
    330 
    331 void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo
    332 		*minfo)
    333 {
    334 	if (msg->idiag_meminfo)
    335 		idiagnl_meminfo_put(msg->idiag_meminfo);
    336 
    337 	idiagnl_meminfo_get(minfo);
    338 	msg->idiag_meminfo = minfo;
    339 }
    340 
    341 struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg)
    342 {
    343 	return msg->idiag_vegasinfo;
    344 }
    345 
    346 void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo
    347 		*vinfo)
    348 {
    349 	if (msg->idiag_vegasinfo)
    350 		idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
    351 
    352 	idiagnl_vegasinfo_get(vinfo);
    353 	msg->idiag_vegasinfo = vinfo;
    354 }
    355 
    356 struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg)
    357 {
    358 	return msg->idiag_tcpinfo;
    359 }
    360 
    361 void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo)
    362 {
    363 	memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info));
    364 }
    365 
    366 /** @} */
    367 
    368 static void idiag_msg_dump_line(struct nl_object *a, struct nl_dump_params *p)
    369 {
    370 	struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
    371 	char buf[64] = { 0 };
    372 
    373 	nl_dump_line(p, "family: %s ", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
    374 	nl_dump(p, "src: %s:%d ", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
    375 			ntohs(msg->idiag_sport));
    376 	nl_dump(p, "dst: %s:%d ", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
    377 			ntohs(msg->idiag_dport));
    378 	nl_dump(p, "iif: %d ", msg->idiag_ifindex);
    379 	nl_dump(p, "\n");
    380 }
    381 
    382 static void idiag_msg_dump_details(struct nl_object *a, struct nl_dump_params *p)
    383 {
    384 	struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
    385 	char buf[64], buf2[64];
    386 
    387 	nl_dump(p, "\nfamily: %s\n", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
    388 	nl_dump(p, "state: %s\n",
    389 			idiagnl_state2str(msg->idiag_state, buf, sizeof(buf)));
    390 	nl_dump(p, "timer (%s, %s, retransmits: %d)\n",
    391 			idiagnl_timer2str(msg->idiag_timer, buf, sizeof(buf)),
    392 			nl_msec2str(msg->idiag_expires, buf2, sizeof(buf2)),
    393 			msg->idiag_retrans);
    394 
    395 	nl_dump(p, "source: %s:%d\n", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
    396 			ntohs(msg->idiag_sport));
    397 	nl_dump(p, "destination: %s:%d\n", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
    398 			ntohs(msg->idiag_dport));
    399 
    400 	nl_dump(p, "ifindex: %d\n", msg->idiag_ifindex);
    401 	nl_dump(p, "rqueue: %-6d wqueue: %-6d\n", msg->idiag_rqueue, msg->idiag_wqueue);
    402 	nl_dump(p, "uid %d\n", msg->idiag_uid);
    403 	nl_dump(p, "inode %d\n", msg->idiag_inode);
    404 	if (msg->idiag_shutdown) {
    405 		nl_dump(p, "socket shutdown: %s\n",
    406 				idiagnl_shutdown2str(msg->idiag_shutdown,
    407 					buf, sizeof(buf)));
    408 	}
    409 
    410 	nl_dump(p, "tos: 0x%x\n", msg->idiag_tos);
    411 	nl_dump(p, "traffic class: %d\n", msg->idiag_tclass);
    412 	nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong);
    413 }
    414 
    415 static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
    416 {
    417 	struct idiagnl_msg *msg = (struct idiagnl_msg *) obj;
    418 	char buf[64];
    419 
    420 	idiag_msg_dump_details(obj, p);
    421 
    422 	nl_dump(p, "tcp info:  [\n");
    423 	nl_dump(p, "\tsocket state: %s\n",
    424 			idiagnl_state2str(msg->idiag_tcpinfo.tcpi_state,
    425 				buf, sizeof(buf)));
    426 	nl_dump(p, "\ttcp state: %s\n",
    427 			idiagnl_tcpstate2str(msg->idiag_tcpinfo.tcpi_ca_state,
    428 				buf, sizeof(buf)));
    429 	nl_dump(p, "\tretransmits: %d\n",
    430 			msg->idiag_tcpinfo.tcpi_retransmits);
    431 	nl_dump(p, "\tprobes: %d\n",
    432 			msg->idiag_tcpinfo.tcpi_probes);
    433 	nl_dump(p, "\tbackoff: %d\n",
    434 			msg->idiag_tcpinfo.tcpi_backoff);
    435 	nl_dump(p, "\toptions: %s\n",
    436 			idiagnl_tcpopts2str(msg->idiag_tcpinfo.tcpi_options,
    437 				buf, sizeof(buf)));
    438 	nl_dump(p, "\tsnd_wscale: %d\n", msg->idiag_tcpinfo.tcpi_snd_wscale);
    439 	nl_dump(p, "\trcv_wscale: %d\n", msg->idiag_tcpinfo.tcpi_rcv_wscale);
    440 	nl_dump(p, "\trto: %d\n", msg->idiag_tcpinfo.tcpi_rto);
    441 	nl_dump(p, "\tato: %d\n", msg->idiag_tcpinfo.tcpi_ato);
    442 	nl_dump(p, "\tsnd_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_snd_mss,
    443 				buf, sizeof(buf)));
    444 	nl_dump(p, "\trcv_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_mss,
    445 				buf, sizeof(buf)));
    446 	nl_dump(p, "\tunacked: %d\n", msg->idiag_tcpinfo.tcpi_unacked);
    447 	nl_dump(p, "\tsacked: %d\n", msg->idiag_tcpinfo.tcpi_sacked);
    448 
    449 	nl_dump(p, "\tlost: %d\n", msg->idiag_tcpinfo.tcpi_lost);
    450 	nl_dump(p, "\tretransmit segments: %d\n",
    451 			msg->idiag_tcpinfo.tcpi_retrans);
    452 	nl_dump(p, "\tfackets: %d\n",
    453 			msg->idiag_tcpinfo.tcpi_fackets);
    454 	nl_dump(p, "\tlast data sent: %s\n",
    455 			nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_sent, buf,
    456 				sizeof(buf)));
    457 	nl_dump(p, "\tlast ack sent: %s\n",
    458 			nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_sent, buf, sizeof(buf)));
    459 	nl_dump(p, "\tlast data recv: %s\n",
    460 			nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_recv, buf,
    461 				sizeof(buf)));
    462 	nl_dump(p, "\tlast ack recv: %s\n",
    463 			nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_recv, buf,
    464 				sizeof(buf)));
    465 	nl_dump(p, "\tpath mtu: %s\n",
    466 			nl_size2str(msg->idiag_tcpinfo.tcpi_pmtu, buf,
    467 				sizeof(buf)));
    468 	nl_dump(p, "\trcv ss threshold: %d\n",
    469 			msg->idiag_tcpinfo.tcpi_rcv_ssthresh);
    470 	nl_dump(p, "\tsmoothed round trip time: %d\n",
    471 			msg->idiag_tcpinfo.tcpi_rtt);
    472 	nl_dump(p, "\tround trip time variation: %d\n",
    473 			msg->idiag_tcpinfo.tcpi_rttvar);
    474 	nl_dump(p, "\tsnd ss threshold: %s\n",
    475 			nl_size2str(msg->idiag_tcpinfo.tcpi_snd_ssthresh, buf,
    476 				sizeof(buf)));
    477 	nl_dump(p, "\tsend congestion window: %d\n",
    478 			msg->idiag_tcpinfo.tcpi_snd_cwnd);
    479 	nl_dump(p, "\tadvertised mss: %s\n",
    480 			nl_size2str(msg->idiag_tcpinfo.tcpi_advmss, buf,
    481 				sizeof(buf)));
    482 	nl_dump(p, "\treordering: %d\n",
    483 			msg->idiag_tcpinfo.tcpi_reordering);
    484 	nl_dump(p, "\trcv rround trip time: %d\n",
    485 			msg->idiag_tcpinfo.tcpi_rcv_rtt);
    486 	nl_dump(p, "\treceive queue space: %s\n",
    487 			nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_space, buf,
    488 				sizeof(buf)));
    489 	nl_dump(p, "\ttotal retransmits: %d\n",
    490 			msg->idiag_tcpinfo.tcpi_total_retrans);
    491 	nl_dump(p, "]\n");
    492 
    493 	if (msg->idiag_meminfo) {
    494 		nl_dump(p, "meminfo:  [\n");
    495 		nl_dump(p, "\trmem: %s\n",
    496 				nl_size2str(msg->idiag_meminfo->idiag_rmem,
    497 					    buf,
    498 					    sizeof(buf)));
    499 		nl_dump(p, "\twmem: %s\n",
    500 				nl_size2str(msg->idiag_meminfo->idiag_wmem,
    501 					    buf,
    502 					    sizeof(buf)));
    503 		nl_dump(p, "\tfmem: %s\n",
    504 				nl_size2str(msg->idiag_meminfo->idiag_fmem,
    505 					    buf,
    506 					    sizeof(buf)));
    507 		nl_dump(p, "\ttmem: %s\n",
    508 				nl_size2str(msg->idiag_meminfo->idiag_tmem,
    509 					    buf,
    510 					    sizeof(buf)));
    511 		nl_dump(p, "]\n");
    512 	}
    513 
    514 	if (msg->idiag_vegasinfo) {
    515 		nl_dump(p, "vegasinfo:  [\n");
    516 		nl_dump(p, "\tvegas enabled: %d\n",
    517 				msg->idiag_vegasinfo->tcpv_enabled);
    518 		if (msg->idiag_vegasinfo->tcpv_enabled) {
    519 			nl_dump(p, "\trtt cnt: %d",
    520 					msg->idiag_vegasinfo->tcpv_rttcnt);
    521 			nl_dump(p, "\trtt (propagation delay): %d",
    522 					msg->idiag_vegasinfo->tcpv_rtt);
    523 			nl_dump(p, "\tmin rtt: %d",
    524 					msg->idiag_vegasinfo->tcpv_minrtt);
    525 		}
    526 		nl_dump(p, "]\n");
    527 	}
    528 
    529 	nl_dump(p, "skmeminfo:  [\n");
    530 	nl_dump(p, "\trmem alloc: %d\n",
    531 			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RMEM_ALLOC]);
    532 	nl_dump(p, "\trcv buf: %s\n",
    533 			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RCVBUF],
    534 				buf, sizeof(buf)));
    535 	nl_dump(p, "\twmem alloc: %d\n",
    536 			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_ALLOC]);
    537 	nl_dump(p, "\tsnd buf: %s\n",
    538 			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_SNDBUF],
    539 				buf, sizeof(buf)));
    540 	nl_dump(p, "\tfwd alloc: %d\n",
    541 			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_FWD_ALLOC]);
    542 	nl_dump(p, "\twmem queued: %s\n",
    543 			nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_QUEUED],
    544 				buf, sizeof(buf)));
    545 	nl_dump(p, "\topt mem: %d\n",
    546 			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_OPTMEM]);
    547 	nl_dump(p, "\tbacklog: %d\n",
    548 			msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_BACKLOG]);
    549 	nl_dump(p, "]\n\n");
    550 }
    551 
    552 static void idiagnl_msg_free(struct nl_object *a)
    553 {
    554 	struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
    555 	if (a == NULL)
    556 		return;
    557 
    558 	free(msg->idiag_cong);
    559 	nl_addr_put(msg->idiag_src);
    560 	nl_addr_put(msg->idiag_dst);
    561 	idiagnl_meminfo_put(msg->idiag_meminfo);
    562 	idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
    563 }
    564 
    565 static int idiagnl_msg_clone(struct nl_object *_dst, struct nl_object *_src)
    566 {
    567 	struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst;
    568 	struct idiagnl_msg *src = (struct idiagnl_msg *) _src;
    569 
    570 	if (src->idiag_src)
    571 		if (!(dst->idiag_src = nl_addr_clone(src->idiag_src)))
    572 			return -NLE_NOMEM;
    573 
    574 	if (src->idiag_dst)
    575 		if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst)))
    576 			return -NLE_NOMEM;
    577 
    578 	return 0;
    579 }
    580 
    581 static struct nla_policy ext_policy[IDIAG_ATTR_MAX] = {
    582 	[IDIAG_ATTR_MEMINFO]    = { .minlen = sizeof(struct inet_diag_meminfo) },
    583 	[IDIAG_ATTR_INFO]       = { .minlen = sizeof(struct tcp_info)	},
    584 	[IDIAG_ATTR_VEGASINFO]  = { .minlen = sizeof(struct tcpvegas_info) },
    585 	[IDIAG_ATTR_CONG]       = { .type = NLA_STRING },
    586 	[IDIAG_ATTR_TOS]        = { .type = NLA_U8 },
    587 	[IDIAG_ATTR_TCLASS]     = { .type = NLA_U8 },
    588 	[IDIAG_ATTR_SKMEMINFO]  = { .minlen = (sizeof(uint32_t) * IDIAG_SK_MEMINFO_VARS)  },
    589 	[IDIAG_ATTR_SHUTDOWN]   = { .type = NLA_U8 },
    590 };
    591 
    592 int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result)
    593 {
    594 	struct idiagnl_msg *msg = NULL;
    595 	struct inet_diag_msg *raw_msg = NULL;
    596 	struct nl_addr *src = NULL, *dst = NULL;
    597 	struct nlattr *tb[IDIAG_ATTR_MAX];
    598 	int err = 0;
    599 
    600 	msg = idiagnl_msg_alloc();
    601 	if (!msg)
    602 		goto errout_nomem;
    603 
    604 	err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, IDIAG_ATTR_MAX,
    605 			ext_policy);
    606 	if (err < 0)
    607 		goto errout;
    608 
    609 	raw_msg = nlmsg_data(nlh);
    610 	msg->idiag_family = raw_msg->idiag_family;
    611 	msg->idiag_state = raw_msg->idiag_state;
    612 	msg->idiag_timer = raw_msg->idiag_timer;
    613 	msg->idiag_retrans = raw_msg->idiag_retrans;
    614 	msg->idiag_expires = raw_msg->idiag_expires;
    615 	msg->idiag_rqueue = raw_msg->idiag_rqueue;
    616 	msg->idiag_wqueue = raw_msg->idiag_wqueue;
    617 	msg->idiag_uid = raw_msg->idiag_uid;
    618 	msg->idiag_inode = raw_msg->idiag_inode;
    619 	msg->idiag_sport = raw_msg->id.idiag_sport;
    620 	msg->idiag_dport = raw_msg->id.idiag_dport;
    621 	msg->idiag_ifindex = raw_msg->id.idiag_if;
    622 
    623 	dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst,
    624 			sizeof(raw_msg->id.idiag_dst));
    625 	if (!dst)
    626 		goto errout_nomem;
    627 
    628 	err = idiagnl_msg_set_dst(msg, dst);
    629 	if (err < 0)
    630 		goto errout;
    631 
    632 	nl_addr_put(dst);
    633 
    634 	src = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_src,
    635 			sizeof(raw_msg->id.idiag_src));
    636 	if (!src)
    637 		goto errout_nomem;
    638 
    639 	err = idiagnl_msg_set_src(msg, src);
    640 	if (err < 0)
    641 		goto errout;
    642 
    643 	nl_addr_put(src);
    644 
    645 	if (tb[IDIAG_ATTR_TOS])
    646 		msg->idiag_tos = nla_get_u8(tb[IDIAG_ATTR_TOS]);
    647 
    648 	if (tb[IDIAG_ATTR_TCLASS])
    649 		msg->idiag_tclass = nla_get_u8(tb[IDIAG_ATTR_TCLASS]);
    650 
    651 	if (tb[IDIAG_ATTR_SHUTDOWN])
    652 		msg->idiag_shutdown = nla_get_u8(tb[IDIAG_ATTR_SHUTDOWN]);
    653 
    654 	if (tb[IDIAG_ATTR_CONG])
    655 		msg->idiag_cong = nla_strdup(tb[IDIAG_ATTR_CONG]);
    656 
    657 	if (tb[IDIAG_ATTR_INFO])
    658 		nla_memcpy(&msg->idiag_tcpinfo, tb[IDIAG_ATTR_INFO],
    659 				sizeof(msg->idiag_tcpinfo));
    660 
    661 	if (tb[IDIAG_ATTR_MEMINFO]) {
    662 		struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc();
    663 		struct inet_diag_meminfo *raw_minfo = NULL;
    664 
    665 		if (!minfo)
    666 			goto errout_nomem;
    667 
    668 		raw_minfo = (struct inet_diag_meminfo *)
    669 			nla_data(tb[IDIAG_ATTR_MEMINFO]);
    670 
    671 		idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem);
    672 		idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem);
    673 		idiagnl_meminfo_set_fmem(minfo, raw_minfo->idiag_fmem);
    674 		idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem);
    675 
    676 		msg->idiag_meminfo = minfo;
    677 	}
    678 
    679 	if (tb[IDIAG_ATTR_VEGASINFO]) {
    680 		struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc();
    681 		struct tcpvegas_info *raw_vinfo = NULL;
    682 
    683 		if (!vinfo)
    684 			goto errout_nomem;
    685 
    686 		raw_vinfo = (struct tcpvegas_info *)
    687 			nla_data(tb[IDIAG_ATTR_VEGASINFO]);
    688 
    689 		idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled);
    690 		idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt);
    691 		idiagnl_vegasinfo_set_rtt(vinfo, raw_vinfo->tcpv_rtt);
    692 		idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt);
    693 
    694 		msg->idiag_vegasinfo = vinfo;
    695 	}
    696 
    697 	if (tb[IDIAG_ATTR_SKMEMINFO])
    698 		nla_memcpy(&msg->idiag_skmeminfo, tb[IDIAG_ATTR_SKMEMINFO],
    699 				sizeof(msg->idiag_skmeminfo));
    700 
    701 	*result = msg;
    702 	return 0;
    703 
    704 errout:
    705 	idiagnl_msg_put(msg);
    706 	return err;
    707 
    708 errout_nomem:
    709 	err = -NLE_NOMEM;
    710 	goto errout;
    711 }
    712 
    713 /** @cond SKIP */
    714 struct nl_object_ops idiagnl_msg_obj_ops = {
    715 	.oo_name			 = "idiag/idiag_msg",
    716 	.oo_size			 = sizeof(struct idiagnl_msg),
    717 	.oo_free_data			 = idiagnl_msg_free,
    718 	.oo_clone			 = idiagnl_msg_clone,
    719 	.oo_dump			 = {
    720 		[NL_DUMP_LINE]		 = idiag_msg_dump_line,
    721 		[NL_DUMP_DETAILS]	 = idiag_msg_dump_details,
    722 		[NL_DUMP_STATS]		 = idiag_msg_dump_stats,
    723 	},
    724 	.oo_attrs2str			= idiagnl_attrs2str,
    725 	.oo_id_attrs			= (IDIAG_ATTR_INFO)
    726 };
    727 /** @endcond */
    728 
    729 /** @} */
    730