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 #include <netlink-local.h>
00152 #include <netlink/netlink.h>
00153 #include <netlink/utils.h>
00154 #include <netlink/route/rtnl.h>
00155 #include <netlink/route/neighbour.h>
00156 #include <netlink/route/link.h>
00157
00158
00159 #define NEIGH_ATTR_FLAGS 0x01
00160 #define NEIGH_ATTR_STATE 0x02
00161 #define NEIGH_ATTR_LLADDR 0x04
00162 #define NEIGH_ATTR_DST 0x08
00163 #define NEIGH_ATTR_CACHEINFO 0x10
00164 #define NEIGH_ATTR_IFINDEX 0x20
00165 #define NEIGH_ATTR_FAMILY 0x40
00166 #define NEIGH_ATTR_TYPE 0x80
00167 #define NEIGH_ATTR_PROBES 0x100
00168
00169 static struct nl_cache_ops rtnl_neigh_ops;
00170 static struct nl_object_ops neigh_obj_ops;
00171
00172
00173 static void neigh_free_data(struct nl_object *c)
00174 {
00175 struct rtnl_neigh *neigh = nl_object_priv(c);
00176
00177 if (!neigh)
00178 return;
00179
00180 nl_addr_put(neigh->n_lladdr);
00181 nl_addr_put(neigh->n_dst);
00182 }
00183
00184 static int neigh_clone(struct nl_object *_dst, struct nl_object *_src)
00185 {
00186 struct rtnl_neigh *dst = nl_object_priv(_dst);
00187 struct rtnl_neigh *src = nl_object_priv(_src);
00188
00189 if (src->n_lladdr)
00190 if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr)))
00191 return -NLE_NOMEM;
00192
00193 if (src->n_dst)
00194 if (!(dst->n_dst = nl_addr_clone(src->n_dst)))
00195 return -NLE_NOMEM;
00196
00197 return 0;
00198 }
00199
00200 static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
00201 uint32_t attrs, int flags)
00202 {
00203 struct rtnl_neigh *a = (struct rtnl_neigh *) _a;
00204 struct rtnl_neigh *b = (struct rtnl_neigh *) _b;
00205 int diff = 0;
00206
00207 #define NEIGH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGH_ATTR_##ATTR, a, b, EXPR)
00208
00209 diff |= NEIGH_DIFF(IFINDEX, a->n_ifindex != b->n_ifindex);
00210 diff |= NEIGH_DIFF(FAMILY, a->n_family != b->n_family);
00211 diff |= NEIGH_DIFF(TYPE, a->n_type != b->n_type);
00212 diff |= NEIGH_DIFF(LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr));
00213 diff |= NEIGH_DIFF(DST, nl_addr_cmp(a->n_dst, b->n_dst));
00214
00215 if (flags & LOOSE_COMPARISON) {
00216 diff |= NEIGH_DIFF(STATE,
00217 (a->n_state ^ b->n_state) & b->n_state_mask);
00218 diff |= NEIGH_DIFF(FLAGS,
00219 (a->n_flags ^ b->n_flags) & b->n_flag_mask);
00220 } else {
00221 diff |= NEIGH_DIFF(STATE, a->n_state != b->n_state);
00222 diff |= NEIGH_DIFF(FLAGS, a->n_flags != b->n_flags);
00223 }
00224
00225 #undef NEIGH_DIFF
00226
00227 return diff;
00228 }
00229
00230 static const struct trans_tbl neigh_attrs[] = {
00231 __ADD(NEIGH_ATTR_FLAGS, flags)
00232 __ADD(NEIGH_ATTR_STATE, state)
00233 __ADD(NEIGH_ATTR_LLADDR, lladdr)
00234 __ADD(NEIGH_ATTR_DST, dst)
00235 __ADD(NEIGH_ATTR_CACHEINFO, cacheinfo)
00236 __ADD(NEIGH_ATTR_IFINDEX, ifindex)
00237 __ADD(NEIGH_ATTR_FAMILY, family)
00238 __ADD(NEIGH_ATTR_TYPE, type)
00239 __ADD(NEIGH_ATTR_PROBES, probes)
00240 };
00241
00242 static char *neigh_attrs2str(int attrs, char *buf, size_t len)
00243 {
00244 return __flags2str(attrs, buf, len, neigh_attrs,
00245 ARRAY_SIZE(neigh_attrs));
00246 }
00247
00248 static struct nla_policy neigh_policy[NDA_MAX+1] = {
00249 [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) },
00250 [NDA_PROBES] = { .type = NLA_U32 },
00251 };
00252
00253 static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00254 struct nlmsghdr *n, struct nl_parser_param *pp)
00255 {
00256 struct rtnl_neigh *neigh;
00257 struct nlattr *tb[NDA_MAX + 1];
00258 struct ndmsg *nm;
00259 int err;
00260
00261 neigh = rtnl_neigh_alloc();
00262 if (!neigh) {
00263 err = -NLE_NOMEM;
00264 goto errout;
00265 }
00266
00267 neigh->ce_msgtype = n->nlmsg_type;
00268 nm = nlmsg_data(n);
00269
00270 err = nlmsg_parse(n, sizeof(*nm), tb, NDA_MAX, neigh_policy);
00271 if (err < 0)
00272 goto errout;
00273
00274 neigh->n_family = nm->ndm_family;
00275 neigh->n_ifindex = nm->ndm_ifindex;
00276 neigh->n_state = nm->ndm_state;
00277 neigh->n_flags = nm->ndm_flags;
00278 neigh->n_type = nm->ndm_type;
00279
00280 neigh->ce_mask |= (NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX |
00281 NEIGH_ATTR_STATE | NEIGH_ATTR_FLAGS |
00282 NEIGH_ATTR_TYPE);
00283
00284 if (tb[NDA_LLADDR]) {
00285 neigh->n_lladdr = nl_addr_alloc_attr(tb[NDA_LLADDR], AF_UNSPEC);
00286 if (!neigh->n_lladdr) {
00287 err = -NLE_NOMEM;
00288 goto errout;
00289 }
00290 nl_addr_set_family(neigh->n_lladdr,
00291 nl_addr_guess_family(neigh->n_lladdr));
00292 neigh->ce_mask |= NEIGH_ATTR_LLADDR;
00293 }
00294
00295 if (tb[NDA_DST]) {
00296 neigh->n_dst = nl_addr_alloc_attr(tb[NDA_DST], neigh->n_family);
00297 if (!neigh->n_dst) {
00298 err = -NLE_NOMEM;
00299 goto errout;
00300 }
00301 neigh->ce_mask |= NEIGH_ATTR_DST;
00302 }
00303
00304 if (tb[NDA_CACHEINFO]) {
00305 struct nda_cacheinfo *ci = nla_data(tb[NDA_CACHEINFO]);
00306
00307 neigh->n_cacheinfo.nci_confirmed = ci->ndm_confirmed;
00308 neigh->n_cacheinfo.nci_used = ci->ndm_used;
00309 neigh->n_cacheinfo.nci_updated = ci->ndm_updated;
00310 neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt;
00311
00312 neigh->ce_mask |= NEIGH_ATTR_CACHEINFO;
00313 }
00314
00315 if (tb[NDA_PROBES]) {
00316 neigh->n_probes = nla_get_u32(tb[NDA_PROBES]);
00317 neigh->ce_mask |= NEIGH_ATTR_PROBES;
00318 }
00319
00320 err = pp->pp_cb((struct nl_object *) neigh, pp);
00321 errout:
00322 rtnl_neigh_put(neigh);
00323 return err;
00324 }
00325
00326 static int neigh_request_update(struct nl_cache *c, struct nl_sock *h)
00327 {
00328 return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP);
00329 }
00330
00331
00332 static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p)
00333 {
00334 char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5];
00335 struct rtnl_neigh *n = (struct rtnl_neigh *) a;
00336 struct nl_cache *link_cache;
00337 char state[128], flags[64];
00338
00339 link_cache = nl_cache_mngt_require("route/link");
00340
00341 nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
00342
00343 if (link_cache)
00344 nl_dump(p, "dev %s ",
00345 rtnl_link_i2name(link_cache, n->n_ifindex,
00346 state, sizeof(state)));
00347 else
00348 nl_dump(p, "dev %d ", n->n_ifindex);
00349
00350 if (n->ce_mask & NEIGH_ATTR_LLADDR)
00351 nl_dump(p, "lladdr %s ",
00352 nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr)));
00353
00354 rtnl_neigh_state2str(n->n_state, state, sizeof(state));
00355 rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags));
00356
00357 if (state[0])
00358 nl_dump(p, "<%s", state);
00359 if (flags[0])
00360 nl_dump(p, "%s%s", state[0] ? "," : "<", flags);
00361 if (state[0] || flags[0])
00362 nl_dump(p, ">");
00363 nl_dump(p, "\n");
00364 }
00365
00366 static void neigh_dump_details(struct nl_object *a, struct nl_dump_params *p)
00367 {
00368 char rtn_type[32];
00369 struct rtnl_neigh *n = (struct rtnl_neigh *) a;
00370 int hz = nl_get_user_hz();
00371
00372 neigh_dump_line(a, p);
00373
00374 nl_dump_line(p, " refcnt %u type %s confirmed %u used "
00375 "%u updated %u\n",
00376 n->n_cacheinfo.nci_refcnt,
00377 nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)),
00378 n->n_cacheinfo.nci_confirmed/hz,
00379 n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz);
00380 }
00381
00382 static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p)
00383 {
00384 neigh_dump_details(a, p);
00385 }
00386
00387
00388
00389
00390
00391
00392 struct rtnl_neigh *rtnl_neigh_alloc(void)
00393 {
00394 return (struct rtnl_neigh *) nl_object_alloc(&neigh_obj_ops);
00395 }
00396
00397 void rtnl_neigh_put(struct rtnl_neigh *neigh)
00398 {
00399 nl_object_put((struct nl_object *) neigh);
00400 }
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419 int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
00420 {
00421 return nl_cache_alloc_and_fill(&rtnl_neigh_ops, sock, result);
00422 }
00423
00424
00425
00426
00427
00428
00429
00430
00431 struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex,
00432 struct nl_addr *dst)
00433 {
00434 struct rtnl_neigh *neigh;
00435
00436 nl_list_for_each_entry(neigh, &cache->c_items, ce_list) {
00437 if (neigh->n_ifindex == ifindex &&
00438 !nl_addr_cmp(neigh->n_dst, dst)) {
00439 nl_object_get((struct nl_object *) neigh);
00440 return neigh;
00441 }
00442 }
00443
00444 return NULL;
00445 }
00446
00447
00448
00449
00450
00451
00452
00453
00454 static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags,
00455 struct nl_msg **result)
00456 {
00457 struct nl_msg *msg;
00458 struct ndmsg nhdr = {
00459 .ndm_ifindex = tmpl->n_ifindex,
00460 .ndm_state = NUD_PERMANENT,
00461 };
00462
00463 if (!(tmpl->ce_mask & NEIGH_ATTR_DST))
00464 return -NLE_MISSING_ATTR;
00465
00466 nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst);
00467
00468 if (tmpl->ce_mask & NEIGH_ATTR_FLAGS)
00469 nhdr.ndm_flags = tmpl->n_flags;
00470
00471 if (tmpl->ce_mask & NEIGH_ATTR_STATE)
00472 nhdr.ndm_state = tmpl->n_state;
00473
00474 msg = nlmsg_alloc_simple(cmd, flags);
00475 if (!msg)
00476 return -NLE_NOMEM;
00477
00478 if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0)
00479 goto nla_put_failure;
00480
00481 NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst);
00482
00483 if (tmpl->ce_mask & NEIGH_ATTR_LLADDR)
00484 NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr);
00485
00486 *result = msg;
00487 return 0;
00488
00489 nla_put_failure:
00490 nlmsg_free(msg);
00491 return -NLE_MSGSIZE;
00492 }
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514 int rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags,
00515 struct nl_msg **result)
00516 {
00517 return build_neigh_msg(tmpl, RTM_NEWNEIGH, flags, result);
00518 }
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 int rtnl_neigh_add(struct nl_sock *sk, struct rtnl_neigh *tmpl, int flags)
00539 {
00540 int err;
00541 struct nl_msg *msg;
00542
00543 if ((err = rtnl_neigh_build_add_request(tmpl, flags, &msg)) < 0)
00544 return err;
00545
00546 err = nl_send_auto_complete(sk, msg);
00547 nlmsg_free(msg);
00548 if (err < 0)
00549 return err;
00550
00551 return wait_for_ack(sk);
00552 }
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575 int rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, int flags,
00576 struct nl_msg **result)
00577 {
00578 return build_neigh_msg(neigh, RTM_DELNEIGH, flags, result);
00579 }
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593 int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh,
00594 int flags)
00595 {
00596 struct nl_msg *msg;
00597 int err;
00598
00599 if ((err = rtnl_neigh_build_delete_request(neigh, flags, &msg)) < 0)
00600 return err;
00601
00602 err = nl_send_auto_complete(sk, msg);
00603 nlmsg_free(msg);
00604 if (err < 0)
00605 return err;
00606
00607 return wait_for_ack(sk);
00608 }
00609
00610
00611
00612
00613
00614
00615
00616
00617 static const struct trans_tbl neigh_states[] = {
00618 __ADD(NUD_INCOMPLETE, incomplete)
00619 __ADD(NUD_REACHABLE, reachable)
00620 __ADD(NUD_STALE, stale)
00621 __ADD(NUD_DELAY, delay)
00622 __ADD(NUD_PROBE, probe)
00623 __ADD(NUD_FAILED, failed)
00624 __ADD(NUD_NOARP, norarp)
00625 __ADD(NUD_PERMANENT, permanent)
00626 };
00627
00628 char * rtnl_neigh_state2str(int state, char *buf, size_t len)
00629 {
00630 return __flags2str(state, buf, len, neigh_states,
00631 ARRAY_SIZE(neigh_states));
00632 }
00633
00634 int rtnl_neigh_str2state(const char *name)
00635 {
00636 return __str2type(name, neigh_states, ARRAY_SIZE(neigh_states));
00637 }
00638
00639
00640
00641
00642
00643
00644
00645
00646 static const struct trans_tbl neigh_flags[] = {
00647 __ADD(NTF_USE, use)
00648 __ADD(NTF_PROXY, proxy)
00649 __ADD(NTF_ROUTER, router)
00650 };
00651
00652 char * rtnl_neigh_flags2str(int flags, char *buf, size_t len)
00653 {
00654 return __flags2str(flags, buf, len, neigh_flags,
00655 ARRAY_SIZE(neigh_flags));
00656 }
00657
00658 int rtnl_neigh_str2flag(const char *name)
00659 {
00660 return __str2type(name, neigh_flags, ARRAY_SIZE(neigh_flags));
00661 }
00662
00663
00664
00665
00666
00667
00668
00669
00670 void rtnl_neigh_set_state(struct rtnl_neigh *neigh, int state)
00671 {
00672 neigh->n_state_mask |= state;
00673 neigh->n_state |= state;
00674 neigh->ce_mask |= NEIGH_ATTR_STATE;
00675 }
00676
00677 int rtnl_neigh_get_state(struct rtnl_neigh *neigh)
00678 {
00679 if (neigh->ce_mask & NEIGH_ATTR_STATE)
00680 return neigh->n_state;
00681 else
00682 return -1;
00683 }
00684
00685 void rtnl_neigh_unset_state(struct rtnl_neigh *neigh, int state)
00686 {
00687 neigh->n_state_mask |= state;
00688 neigh->n_state &= ~state;
00689 neigh->ce_mask |= NEIGH_ATTR_STATE;
00690 }
00691
00692 void rtnl_neigh_set_flags(struct rtnl_neigh *neigh, unsigned int flags)
00693 {
00694 neigh->n_flag_mask |= flags;
00695 neigh->n_flags |= flags;
00696 neigh->ce_mask |= NEIGH_ATTR_FLAGS;
00697 }
00698
00699 unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *neigh)
00700 {
00701 return neigh->n_flags;
00702 }
00703
00704 void rtnl_neigh_unset_flags(struct rtnl_neigh *neigh, unsigned int flags)
00705 {
00706 neigh->n_flag_mask |= flags;
00707 neigh->n_flags &= ~flags;
00708 neigh->ce_mask |= NEIGH_ATTR_FLAGS;
00709 }
00710
00711 void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex)
00712 {
00713 neigh->n_ifindex = ifindex;
00714 neigh->ce_mask |= NEIGH_ATTR_IFINDEX;
00715 }
00716
00717 int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh)
00718 {
00719 return neigh->n_ifindex;
00720 }
00721
00722 static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos,
00723 struct nl_addr *new, int flag, int nocheck)
00724 {
00725 if (!nocheck) {
00726 if (neigh->ce_mask & NEIGH_ATTR_FAMILY) {
00727 if (new->a_family != neigh->n_family)
00728 return -NLE_AF_MISMATCH;
00729 } else {
00730 neigh->n_family = new->a_family;
00731 neigh->ce_mask |= NEIGH_ATTR_FAMILY;
00732 }
00733 }
00734
00735 if (*pos)
00736 nl_addr_put(*pos);
00737
00738 nl_addr_get(new);
00739 *pos = new;
00740
00741 neigh->ce_mask |= flag;
00742
00743 return 0;
00744 }
00745
00746 void rtnl_neigh_set_lladdr(struct rtnl_neigh *neigh, struct nl_addr *addr)
00747 {
00748 __assign_addr(neigh, &neigh->n_lladdr, addr, NEIGH_ATTR_LLADDR, 1);
00749 }
00750
00751 struct nl_addr *rtnl_neigh_get_lladdr(struct rtnl_neigh *neigh)
00752 {
00753 if (neigh->ce_mask & NEIGH_ATTR_LLADDR)
00754 return neigh->n_lladdr;
00755 else
00756 return NULL;
00757 }
00758
00759 int rtnl_neigh_set_dst(struct rtnl_neigh *neigh, struct nl_addr *addr)
00760 {
00761 return __assign_addr(neigh, &neigh->n_dst, addr,
00762 NEIGH_ATTR_DST, 0);
00763 }
00764
00765 struct nl_addr *rtnl_neigh_get_dst(struct rtnl_neigh *neigh)
00766 {
00767 if (neigh->ce_mask & NEIGH_ATTR_DST)
00768 return neigh->n_dst;
00769 else
00770 return NULL;
00771 }
00772
00773 void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family)
00774 {
00775 neigh->n_family = family;
00776 neigh->ce_mask |= NEIGH_ATTR_FAMILY;
00777 }
00778
00779 int rtnl_neigh_get_family(struct rtnl_neigh *neigh)
00780 {
00781 return neigh->n_family;
00782 }
00783
00784 void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type)
00785 {
00786 neigh->n_type = type;
00787 neigh->ce_mask = NEIGH_ATTR_TYPE;
00788 }
00789
00790 int rtnl_neigh_get_type(struct rtnl_neigh *neigh)
00791 {
00792 if (neigh->ce_mask & NEIGH_ATTR_TYPE)
00793 return neigh->n_type;
00794 else
00795 return -1;
00796 }
00797
00798
00799
00800 static struct nl_object_ops neigh_obj_ops = {
00801 .oo_name = "route/neigh",
00802 .oo_size = sizeof(struct rtnl_neigh),
00803 .oo_free_data = neigh_free_data,
00804 .oo_clone = neigh_clone,
00805 .oo_dump = {
00806 [NL_DUMP_LINE] = neigh_dump_line,
00807 [NL_DUMP_DETAILS] = neigh_dump_details,
00808 [NL_DUMP_STATS] = neigh_dump_stats,
00809 },
00810 .oo_compare = neigh_compare,
00811 .oo_attrs2str = neigh_attrs2str,
00812 .oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY),
00813 };
00814
00815 static struct nl_af_group neigh_groups[] = {
00816 { AF_UNSPEC, RTNLGRP_NEIGH },
00817 { END_OF_GROUP_LIST },
00818 };
00819
00820 static struct nl_cache_ops rtnl_neigh_ops = {
00821 .co_name = "route/neigh",
00822 .co_hdrsize = sizeof(struct ndmsg),
00823 .co_msgtypes = {
00824 { RTM_NEWNEIGH, NL_ACT_NEW, "new" },
00825 { RTM_DELNEIGH, NL_ACT_DEL, "del" },
00826 { RTM_GETNEIGH, NL_ACT_GET, "get" },
00827 END_OF_MSGTYPES_LIST,
00828 },
00829 .co_protocol = NETLINK_ROUTE,
00830 .co_groups = neigh_groups,
00831 .co_request_update = neigh_request_update,
00832 .co_msg_parser = neigh_msg_parser,
00833 .co_obj_ops = &neigh_obj_ops,
00834 };
00835
00836 static void __init neigh_init(void)
00837 {
00838 nl_cache_mngt_register(&rtnl_neigh_ops);
00839 }
00840
00841 static void __exit neigh_exit(void)
00842 {
00843 nl_cache_mngt_unregister(&rtnl_neigh_ops);
00844 }
00845
00846