00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 #include <netlink-local.h>
00162 #include <netlink/netlink.h>
00163 #include <netlink/utils.h>
00164 #include <netlink/cache.h>
00165 #include <netlink/attr.h>
00166 #include <linux/socket.h>
00167
00168 static size_t default_msg_size;
00169
00170 static void __init init_msg_size(void)
00171 {
00172 default_msg_size = getpagesize();
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 int nlmsg_msg_size(int payload)
00185 {
00186 return NLMSG_HDRLEN + payload;
00187 }
00188
00189
00190
00191
00192
00193 int nlmsg_total_size(int payload)
00194 {
00195 return NLMSG_ALIGN(nlmsg_msg_size(payload));
00196 }
00197
00198
00199
00200
00201
00202 int nlmsg_padlen(int payload)
00203 {
00204 return nlmsg_total_size(payload) - nlmsg_msg_size(payload);
00205 }
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 void *nlmsg_data(const struct nlmsghdr *nlh)
00219 {
00220 return (unsigned char *) nlh + NLMSG_HDRLEN;
00221 }
00222
00223 void *nlmsg_tail(const struct nlmsghdr *nlh)
00224 {
00225 return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
00226 }
00227
00228
00229
00230
00231
00232 int nlmsg_len(const struct nlmsghdr *nlh)
00233 {
00234 return nlh->nlmsg_len - NLMSG_HDRLEN;
00235 }
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249 struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
00250 {
00251 unsigned char *data = nlmsg_data(nlh);
00252 return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
00253 }
00254
00255
00256
00257
00258
00259
00260 int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
00261 {
00262 return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
00263 }
00264
00265
00266
00267
00268
00269
00270
00271
00272 int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
00273 {
00274 if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
00275 return 0;
00276
00277 return 1;
00278 }
00279
00280
00281
00282
00283
00284
00285 int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
00286 {
00287 return (remaining >= (int)sizeof(struct nlmsghdr) &&
00288 nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
00289 nlh->nlmsg_len <= remaining);
00290 }
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300 struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining)
00301 {
00302 int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
00303
00304 *remaining -= totlen;
00305
00306 return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
00307 }
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
00320 int maxtype, struct nla_policy *policy)
00321 {
00322 if (!nlmsg_valid_hdr(nlh, hdrlen))
00323 return -NLE_MSG_TOOSHORT;
00324
00325 return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
00326 nlmsg_attrlen(nlh, hdrlen), policy);
00327 }
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337 struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype)
00338 {
00339 return nla_find(nlmsg_attrdata(nlh, hdrlen),
00340 nlmsg_attrlen(nlh, hdrlen), attrtype);
00341 }
00342
00343
00344
00345
00346
00347
00348
00349
00350 int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
00351 struct nla_policy *policy)
00352 {
00353 if (!nlmsg_valid_hdr(nlh, hdrlen))
00354 return -NLE_MSG_TOOSHORT;
00355
00356 return nla_validate(nlmsg_attrdata(nlh, hdrlen),
00357 nlmsg_attrlen(nlh, hdrlen), maxtype, policy);
00358 }
00359
00360
00361
00362
00363
00364
00365
00366
00367 static struct nl_msg *__nlmsg_alloc(size_t len)
00368 {
00369 struct nl_msg *nm;
00370
00371 nm = calloc(1, sizeof(*nm));
00372 if (!nm)
00373 goto errout;
00374
00375 nm->nm_refcnt = 1;
00376
00377 nm->nm_nlh = malloc(len);
00378 if (!nm->nm_nlh)
00379 goto errout;
00380
00381 memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr));
00382
00383 nm->nm_protocol = -1;
00384 nm->nm_size = len;
00385 nm->nm_nlh->nlmsg_len = nlmsg_total_size(0);
00386
00387 NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len);
00388
00389 return nm;
00390 errout:
00391 free(nm);
00392 return NULL;
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404 struct nl_msg *nlmsg_alloc(void)
00405 {
00406 return __nlmsg_alloc(default_msg_size);
00407 }
00408
00409
00410
00411
00412 struct nl_msg *nlmsg_alloc_size(size_t max)
00413 {
00414 return __nlmsg_alloc(max);
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr)
00428 {
00429 struct nl_msg *nm;
00430
00431 nm = nlmsg_alloc();
00432 if (nm && hdr) {
00433 struct nlmsghdr *new = nm->nm_nlh;
00434
00435 new->nlmsg_type = hdr->nlmsg_type;
00436 new->nlmsg_flags = hdr->nlmsg_flags;
00437 new->nlmsg_seq = hdr->nlmsg_seq;
00438 new->nlmsg_pid = hdr->nlmsg_pid;
00439 }
00440
00441 return nm;
00442 }
00443
00444
00445
00446
00447
00448
00449
00450
00451 struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags)
00452 {
00453 struct nl_msg *msg;
00454 struct nlmsghdr nlh = {
00455 .nlmsg_type = nlmsgtype,
00456 .nlmsg_flags = flags,
00457 };
00458
00459 msg = nlmsg_inherit(&nlh);
00460 if (msg)
00461 NL_DBG(2, "msg %p: Allocated new simple message\n", msg);
00462
00463 return msg;
00464 }
00465
00466
00467
00468
00469
00470 void nlmsg_set_default_size(size_t max)
00471 {
00472 if (max < nlmsg_total_size(0))
00473 max = nlmsg_total_size(0);
00474
00475 default_msg_size = max;
00476 }
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487 struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr)
00488 {
00489 struct nl_msg *nm;
00490
00491 nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len));
00492 if (!nm)
00493 goto errout;
00494
00495 memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len);
00496
00497 return nm;
00498 errout:
00499 nlmsg_free(nm);
00500 return NULL;
00501 }
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
00516 {
00517 void *buf = n->nm_nlh;
00518 size_t nlmsg_len = n->nm_nlh->nlmsg_len;
00519 size_t tlen;
00520
00521 tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
00522
00523 if ((tlen + nlmsg_len) > n->nm_size)
00524 return NULL;
00525
00526 buf += nlmsg_len;
00527 n->nm_nlh->nlmsg_len += tlen;
00528
00529 if (tlen > len)
00530 memset(buf + len, 0, tlen - len);
00531
00532 NL_DBG(2, "msg %p: Reserved %zu bytes, pad=%d, nlmsg_len=%d\n",
00533 n, len, pad, n->nm_nlh->nlmsg_len);
00534
00535 return buf;
00536 }
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550 int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
00551 {
00552 void *tmp;
00553
00554 tmp = nlmsg_reserve(n, len, pad);
00555 if (tmp == NULL)
00556 return -NLE_NOMEM;
00557
00558 memcpy(tmp, data, len);
00559 NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad);
00560
00561 return 0;
00562 }
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 int nlmsg_expand(struct nl_msg *n, size_t newlen)
00579 {
00580 void *tmp;
00581
00582 if (newlen <= n->nm_size)
00583 return -NLE_INVAL;
00584
00585 tmp = realloc(n->nm_nlh, newlen);
00586 if (tmp == NULL)
00587 return -NLE_NOMEM;
00588
00589 n->nm_nlh = tmp;
00590 n->nm_size = newlen;
00591
00592 return 0;
00593 }
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611 struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq,
00612 int type, int payload, int flags)
00613 {
00614 struct nlmsghdr *nlh;
00615
00616 if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN)
00617 BUG();
00618
00619 nlh = (struct nlmsghdr *) n->nm_nlh;
00620 nlh->nlmsg_type = type;
00621 nlh->nlmsg_flags = flags;
00622 nlh->nlmsg_pid = pid;
00623 nlh->nlmsg_seq = seq;
00624
00625 NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, "
00626 "seq=%d\n", n, type, flags, pid, seq);
00627
00628 if (payload > 0 &&
00629 nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL)
00630 return NULL;
00631
00632 return nlh;
00633 }
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644 struct nlmsghdr *nlmsg_hdr(struct nl_msg *n)
00645 {
00646 return n->nm_nlh;
00647 }
00648
00649
00650
00651
00652
00653 void nlmsg_get(struct nl_msg *msg)
00654 {
00655 msg->nm_refcnt++;
00656 NL_DBG(4, "New reference to message %p, total %d\n",
00657 msg, msg->nm_refcnt);
00658 }
00659
00660
00661
00662
00663
00664
00665
00666 void nlmsg_free(struct nl_msg *msg)
00667 {
00668 if (!msg)
00669 return;
00670
00671 msg->nm_refcnt--;
00672 NL_DBG(4, "Returned message reference %p, %d remaining\n",
00673 msg, msg->nm_refcnt);
00674
00675 if (msg->nm_refcnt < 0)
00676 BUG();
00677
00678 if (msg->nm_refcnt <= 0) {
00679 free(msg->nm_nlh);
00680 free(msg);
00681 NL_DBG(2, "msg %p: Freed\n", msg);
00682 }
00683 }
00684
00685
00686
00687
00688
00689
00690
00691
00692 void nlmsg_set_proto(struct nl_msg *msg, int protocol)
00693 {
00694 msg->nm_protocol = protocol;
00695 }
00696
00697 int nlmsg_get_proto(struct nl_msg *msg)
00698 {
00699 return msg->nm_protocol;
00700 }
00701
00702 size_t nlmsg_get_max_size(struct nl_msg *msg)
00703 {
00704 return msg->nm_size;
00705 }
00706
00707 void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr)
00708 {
00709 memcpy(&msg->nm_src, addr, sizeof(*addr));
00710 }
00711
00712 struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg)
00713 {
00714 return &msg->nm_src;
00715 }
00716
00717 void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr)
00718 {
00719 memcpy(&msg->nm_dst, addr, sizeof(*addr));
00720 }
00721
00722 struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg)
00723 {
00724 return &msg->nm_dst;
00725 }
00726
00727 void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds)
00728 {
00729 memcpy(&msg->nm_creds, creds, sizeof(*creds));
00730 msg->nm_flags |= NL_MSG_CRED_PRESENT;
00731 }
00732
00733 struct ucred *nlmsg_get_creds(struct nl_msg *msg)
00734 {
00735 if (msg->nm_flags & NL_MSG_CRED_PRESENT)
00736 return &msg->nm_creds;
00737 return NULL;
00738 }
00739
00740
00741
00742
00743
00744
00745
00746
00747 static struct trans_tbl nl_msgtypes[] = {
00748 __ADD(NLMSG_NOOP,NOOP)
00749 __ADD(NLMSG_ERROR,ERROR)
00750 __ADD(NLMSG_DONE,DONE)
00751 __ADD(NLMSG_OVERRUN,OVERRUN)
00752 };
00753
00754 char *nl_nlmsgtype2str(int type, char *buf, size_t size)
00755 {
00756 return __type2str(type, buf, size, nl_msgtypes,
00757 ARRAY_SIZE(nl_msgtypes));
00758 }
00759
00760 int nl_str2nlmsgtype(const char *name)
00761 {
00762 return __str2type(name, nl_msgtypes, ARRAY_SIZE(nl_msgtypes));
00763 }
00764
00765
00766
00767
00768
00769
00770
00771
00772 char *nl_nlmsg_flags2str(int flags, char *buf, size_t len)
00773 {
00774 memset(buf, 0, len);
00775
00776 #define PRINT_FLAG(f) \
00777 if (flags & NLM_F_##f) { \
00778 flags &= ~NLM_F_##f; \
00779 strncat(buf, #f, len - strlen(buf) - 1); \
00780 if (flags) \
00781 strncat(buf, ",", len - strlen(buf) - 1); \
00782 }
00783
00784 PRINT_FLAG(REQUEST);
00785 PRINT_FLAG(MULTI);
00786 PRINT_FLAG(ACK);
00787 PRINT_FLAG(ECHO);
00788 PRINT_FLAG(ROOT);
00789 PRINT_FLAG(MATCH);
00790 PRINT_FLAG(ATOMIC);
00791 PRINT_FLAG(REPLACE);
00792 PRINT_FLAG(EXCL);
00793 PRINT_FLAG(CREATE);
00794 PRINT_FLAG(APPEND);
00795
00796 if (flags) {
00797 char s[32];
00798 snprintf(s, sizeof(s), "0x%x", flags);
00799 strncat(buf, s, len - strlen(buf) - 1);
00800 }
00801 #undef PRINT_FLAG
00802
00803 return buf;
00804 }
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814 struct dp_xdata {
00815 void (*cb)(struct nl_object *, void *);
00816 void *arg;
00817 };
00818
00819
00820 static int parse_cb(struct nl_object *obj, struct nl_parser_param *p)
00821 {
00822 struct dp_xdata *x = p->pp_arg;
00823
00824 x->cb(obj, x->arg);
00825 return 0;
00826 }
00827
00828 int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *),
00829 void *arg)
00830 {
00831 struct nl_cache_ops *ops;
00832 struct nl_parser_param p = {
00833 .pp_cb = parse_cb
00834 };
00835 struct dp_xdata x = {
00836 .cb = cb,
00837 .arg = arg,
00838 };
00839
00840 ops = nl_cache_ops_associate(nlmsg_get_proto(msg),
00841 nlmsg_hdr(msg)->nlmsg_type);
00842 if (ops == NULL)
00843 return -NLE_MSGTYPE_NOSUPPORT;
00844 p.pp_arg = &x;
00845
00846 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
00847 }
00848
00849
00850
00851
00852
00853
00854
00855
00856 static void prefix_line(FILE *ofd, int prefix)
00857 {
00858 int i;
00859
00860 for (i = 0; i < prefix; i++)
00861 fprintf(ofd, " ");
00862 }
00863
00864 static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
00865 {
00866 int i, a, c, limit;
00867 char ascii[21] = {0};
00868
00869 limit = 18 - (prefix * 2);
00870 prefix_line(ofd, prefix);
00871 fprintf(ofd, " ");
00872
00873 for (i = 0, a = 0, c = 0; i < len; i++) {
00874 int v = *(uint8_t *) (start + i);
00875
00876 fprintf(ofd, "%02x ", v);
00877 ascii[a++] = isprint(v) ? v : '.';
00878
00879 if (c == limit-1) {
00880 fprintf(ofd, "%s\n", ascii);
00881 if (i < (len - 1)) {
00882 prefix_line(ofd, prefix);
00883 fprintf(ofd, " ");
00884 }
00885 a = c = 0;
00886 memset(ascii, 0, sizeof(ascii));
00887 } else
00888 c++;
00889 }
00890
00891 if (c != 0) {
00892 for (i = 0; i < (limit - c); i++)
00893 fprintf(ofd, " ");
00894 fprintf(ofd, "%s\n", ascii);
00895 }
00896 }
00897
00898 static void print_hdr(FILE *ofd, struct nl_msg *msg)
00899 {
00900 struct nlmsghdr *nlh = nlmsg_hdr(msg);
00901 struct nl_cache_ops *ops;
00902 struct nl_msgtype *mt;
00903 char buf[128];
00904
00905 fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len);
00906
00907 ops = nl_cache_ops_associate(nlmsg_get_proto(msg), nlh->nlmsg_type);
00908 if (ops) {
00909 mt = nl_msgtype_lookup(ops, nlh->nlmsg_type);
00910 if (!mt)
00911 BUG();
00912
00913 snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name);
00914 } else
00915 nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf));
00916
00917 fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, buf);
00918 fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags,
00919 nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf)));
00920 fprintf(ofd, " .nlmsg_seq = %d\n", nlh->nlmsg_seq);
00921 fprintf(ofd, " .nlmsg_pid = %d\n", nlh->nlmsg_pid);
00922
00923 }
00924
00925 static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
00926 int prefix)
00927 {
00928 int rem;
00929 struct nlattr *nla;
00930
00931 nla_for_each_attr(nla, attrs, attrlen, rem) {
00932 int padlen, alen = nla_len(nla);
00933
00934 prefix_line(ofd, prefix);
00935 fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla),
00936 nla->nla_type & NLA_F_NESTED ? " NESTED" : "",
00937 alen);
00938
00939 if (nla->nla_type & NLA_F_NESTED)
00940 dump_attrs(ofd, nla_data(nla), alen, prefix+1);
00941 else
00942 dump_hex(ofd, nla_data(nla), alen, prefix);
00943
00944 padlen = nla_padlen(alen);
00945 if (padlen > 0) {
00946 prefix_line(ofd, prefix);
00947 fprintf(ofd, " [PADDING] %d octets\n",
00948 padlen);
00949 dump_hex(ofd, nla_data(nla) + alen,
00950 padlen, prefix);
00951 }
00952 }
00953
00954 if (rem) {
00955 prefix_line(ofd, prefix);
00956 fprintf(ofd, " [LEFTOVER] %d octets\n", rem);
00957 }
00958 }
00959
00960
00961
00962
00963
00964
00965 void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
00966 {
00967 struct nlmsghdr *hdr = nlmsg_hdr(msg);
00968
00969 fprintf(ofd,
00970 "-------------------------- BEGIN NETLINK MESSAGE "
00971 "---------------------------\n");
00972
00973 fprintf(ofd, " [HEADER] %Zu octets\n", sizeof(struct nlmsghdr));
00974 print_hdr(ofd, msg);
00975
00976 if (hdr->nlmsg_type == NLMSG_ERROR &&
00977 hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) {
00978 struct nl_msg *errmsg;
00979 struct nlmsgerr *err = nlmsg_data(hdr);
00980
00981 fprintf(ofd, " [ERRORMSG] %Zu octets\n", sizeof(*err));
00982 fprintf(ofd, " .error = %d \"%s\"\n", err->error,
00983 strerror(-err->error));
00984 fprintf(ofd, " [ORIGINAL MESSAGE] %Zu octets\n", sizeof(*hdr));
00985
00986 errmsg = nlmsg_inherit(&err->msg);
00987 print_hdr(ofd, errmsg);
00988 nlmsg_free(errmsg);
00989 } else if (nlmsg_len(hdr) > 0) {
00990 struct nl_cache_ops *ops;
00991 int payloadlen = nlmsg_len(hdr);
00992 int attrlen = 0;
00993
00994 ops = nl_cache_ops_associate(nlmsg_get_proto(msg),
00995 hdr->nlmsg_type);
00996 if (ops) {
00997 attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize);
00998 payloadlen -= attrlen;
00999 }
01000
01001 fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen);
01002 dump_hex(ofd, nlmsg_data(hdr), payloadlen, 0);
01003
01004 if (attrlen) {
01005 struct nlattr *attrs;
01006 int attrlen;
01007
01008 attrs = nlmsg_attrdata(hdr, ops->co_hdrsize);
01009 attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize);
01010 dump_attrs(ofd, attrs, attrlen, 0);
01011 }
01012 }
01013
01014 fprintf(ofd,
01015 "--------------------------- END NETLINK MESSAGE "
01016 "---------------------------\n");
01017 }
01018
01019
01020
01021