libnl  3.5.0
idiag_msg_obj.c
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  */
56 struct idiagnl_msg *idiagnl_msg_alloc(void)
57 {
58  return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops);
59 }
60 
61 void idiagnl_msg_get(struct idiagnl_msg *msg)
62 {
63  nl_object_get((struct nl_object *) msg);
64 }
65 
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 
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 
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 
120 static void __init idiagnl_init(void)
121 {
122  nl_cache_mngt_register(&idiagnl_msg_ops);
123 }
124 
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  */
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 
174 uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg)
175 {
176  return msg->idiag_family;
177 }
178 
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 
185 uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg)
186 {
187  return msg->idiag_state;
188 }
189 
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 
196 uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg)
197 {
198  return msg->idiag_timer;
199 }
200 
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 
207 uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg)
208 {
209  return msg->idiag_retrans;
210 }
211 
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 
218 uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg)
219 {
220  return msg->idiag_sport;
221 }
222 
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 
229 uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg)
230 {
231  return msg->idiag_dport;
232 }
233 
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 
240 struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg)
241 {
242  return msg->idiag_src;
243 }
244 
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 
257 struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg)
258 {
259  return msg->idiag_dst;
260 }
261 
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 
274 uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg)
275 {
276  return msg->idiag_ifindex;
277 }
278 
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 
285 uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg)
286 {
287  return msg->idiag_expires;
288 }
289 
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 
296 uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg)
297 {
298  return msg->idiag_rqueue;
299 }
300 
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 
307 uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg)
308 {
309  return msg->idiag_wqueue;
310 }
311 
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 
318 uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg)
319 {
320  return msg->idiag_uid;
321 }
322 
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 
329 uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg)
330 {
331  return msg->idiag_inode;
332 }
333 
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 
340 uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg)
341 {
342  return msg->idiag_tos;
343 }
344 
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 
351 uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg)
352 {
353  return msg->idiag_tclass;
354 }
355 
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 
362 uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg)
363 {
364  return msg->idiag_shutdown;
365 }
366 
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 
373 char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg)
374 {
375  return msg->idiag_cong;
376 }
377 
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 
385 struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg)
386 {
387  return msg->idiag_meminfo;
388 }
389 
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 
400 struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg)
401 {
402  return msg->idiag_vegasinfo;
403 }
404 
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 
415 struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg)
416 {
417  return msg->idiag_tcpinfo;
418 }
419 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 /** @} */
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition: addr.c:494
Dump object briefly on one line.
Definition: types.h:22
8 bit integer
Definition: attr.h:41
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy)
parse attributes of a netlink message
Definition: msg.c:215
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition: addr.c:586
void * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition: msg.c:107
struct nl_object * nl_object_alloc(struct nl_object_ops *ops)
Allocate a new object of kind specified by the operations handle.
Definition: object.c:55
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition: cache_mngt.c:288
Attribute validation policy.
Definition: attr.h:69
uint8_t nla_get_u8(const struct nlattr *nla)
Return value of 8 bit integer attribute.
Definition: attr.c:607
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition: object.c:205
struct nl_addr * nl_addr_build(int family, const void *buf, size_t size)
Allocate abstract address based on a binary address.
Definition: addr.c:218
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
Convert milliseconds to a character string.
Definition: utils.c:595
struct nl_addr * nl_addr_get(struct nl_addr *addr)
Increase the reference counter of an abstract address.
Definition: addr.c:524
NUL terminated character string.
Definition: attr.h:45
Dump all attributes but no statistics.
Definition: types.h:23
int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states, struct nl_cache **result)
Build an inetdiag cache to hold socket state information.
char * nl_size2str(const size_t size, char *buf, const size_t len)
Convert a size toa character string.
Definition: utils.c:358
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition: cache_mngt.c:253
int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family, uint16_t states, uint16_t ext)
Send trivial idiag netlink message.
Definition: idiag.c:65
char * idiagnl_shutdown2str(uint8_t shutdown, char *buf, size_t len)
Convert shutdown state to string.
Definition: idiag.c:262
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition: attr.c:354
char * idiagnl_tcpopts2str(uint8_t attrs, char *buf, size_t len)
Convert TCP option attributes to string.
Definition: idiag.c:248
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition: attr.c:121
uint16_t minlen
Minimal length of payload required.
Definition: attr.h:74
char * idiagnl_tcpstate2str(uint8_t state, char *buf, size_t len)
Convert inetdiag tcp states to strings.
Definition: idiag.c:226
int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:1041
void nl_object_put(struct nl_object *obj)
Release a reference from an object.
Definition: object.c:216
char * idiagnl_timer2str(int timer, char *buf, size_t len)
Convert inet diag timer types to strings.
Definition: idiag.c:145
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition: addr.c:540
Dumping parameters.
Definition: types.h:33
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:962
struct nl_object * nl_object_clone(struct nl_object *obj)
Allocate a new object and copy all data from an existing object.
Definition: object.c:111
char * idiagnl_state2str(int state, char *buf, size_t len)
Convert inet diag socket states to strings.
Definition: idiag.c:110
unsigned int nl_addr_get_len(const struct nl_addr *addr)
Get length of binary address of abstract address object.
Definition: addr.c:954
Dump all attributes including statistics.
Definition: types.h:24
void * nl_addr_get_binary_addr(const struct nl_addr *addr)
Get binary address of abstract address object.
Definition: addr.c:942
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate new cache.
Definition: cache.c:184
uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
Compute 32-bit bitmask representing difference in attribute values.
Definition: object.c:386
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition: addr.c:1000