00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <netlink-local.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/utils.h>
00022 #include <netlink/route/rtnl.h>
00023 #include <netlink/route/rule.h>
00024 #include <inttypes.h>
00025
00026
00027 #define RULE_ATTR_FAMILY 0x0001
00028 #define RULE_ATTR_TABLE 0x0002
00029 #define RULE_ATTR_ACTION 0x0004
00030 #define RULE_ATTR_FLAGS 0x0008
00031 #define RULE_ATTR_IIFNAME 0x0010
00032 #define RULE_ATTR_OIFNAME 0x0020
00033 #define RULE_ATTR_PRIO 0x0040
00034 #define RULE_ATTR_MARK 0x0080
00035 #define RULE_ATTR_MASK 0x0100
00036 #define RULE_ATTR_GOTO 0x0200
00037 #define RULE_ATTR_SRC 0x0400
00038 #define RULE_ATTR_DST 0x0800
00039 #define RULE_ATTR_DSFIELD 0x1000
00040 #define RULE_ATTR_FLOW 0x2000
00041
00042 static struct nl_cache_ops rtnl_rule_ops;
00043 static struct nl_object_ops rule_obj_ops;
00044
00045
00046 static void rule_free_data(struct nl_object *c)
00047 {
00048 struct rtnl_rule *rule = nl_object_priv(c);
00049
00050 if (!rule)
00051 return;
00052
00053 nl_addr_put(rule->r_src);
00054 nl_addr_put(rule->r_dst);
00055 }
00056
00057 static int rule_clone(struct nl_object *_dst, struct nl_object *_src)
00058 {
00059 struct rtnl_rule *dst = nl_object_priv(_dst);
00060 struct rtnl_rule *src = nl_object_priv(_src);
00061
00062 if (src->r_src)
00063 if (!(dst->r_src = nl_addr_clone(src->r_src)))
00064 return -NLE_NOMEM;
00065
00066 if (src->r_dst)
00067 if (!(dst->r_dst = nl_addr_clone(src->r_dst)))
00068 return -NLE_NOMEM;
00069
00070 return 0;
00071 }
00072
00073 static struct nla_policy rule_policy[FRA_MAX+1] = {
00074 [FRA_TABLE] = { .type = NLA_U32 },
00075 [FRA_IIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ },
00076 [FRA_OIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ },
00077 [FRA_PRIORITY] = { .type = NLA_U32 },
00078 [FRA_FWMARK] = { .type = NLA_U32 },
00079 [FRA_FWMASK] = { .type = NLA_U32 },
00080 [FRA_GOTO] = { .type = NLA_U32 },
00081 [FRA_FLOW] = { .type = NLA_U32 },
00082 };
00083
00084 static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00085 struct nlmsghdr *n, struct nl_parser_param *pp)
00086 {
00087 struct rtnl_rule *rule;
00088 struct fib_rule_hdr *frh;
00089 struct nlattr *tb[FRA_MAX+1];
00090 int err = 1, family;
00091
00092 rule = rtnl_rule_alloc();
00093 if (!rule) {
00094 err = -NLE_NOMEM;
00095 goto errout;
00096 }
00097
00098 rule->ce_msgtype = n->nlmsg_type;
00099 frh = nlmsg_data(n);
00100
00101 err = nlmsg_parse(n, sizeof(*frh), tb, FRA_MAX, rule_policy);
00102 if (err < 0)
00103 goto errout;
00104
00105 rule->r_family = family = frh->family;
00106 rule->r_table = frh->table;
00107 rule->r_action = frh->action;
00108 rule->r_flags = frh->flags;
00109
00110 rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TABLE | RULE_ATTR_ACTION |
00111 RULE_ATTR_FLAGS);
00112
00113
00114 if (frh->tos) {
00115 rule->r_dsfield = frh->tos;
00116 rule->ce_mask |= RULE_ATTR_DSFIELD;
00117 }
00118
00119 if (tb[FRA_TABLE]) {
00120 rule->r_table = nla_get_u32(tb[FRA_TABLE]);
00121 rule->ce_mask |= RULE_ATTR_TABLE;
00122 }
00123
00124 if (tb[FRA_IIFNAME]) {
00125 nla_strlcpy(rule->r_iifname, tb[FRA_IIFNAME], IFNAMSIZ);
00126 rule->ce_mask |= RULE_ATTR_IIFNAME;
00127 }
00128
00129 if (tb[FRA_OIFNAME]) {
00130 nla_strlcpy(rule->r_oifname, tb[FRA_OIFNAME], IFNAMSIZ);
00131 rule->ce_mask |= RULE_ATTR_OIFNAME;
00132 }
00133
00134 if (tb[FRA_PRIORITY]) {
00135 rule->r_prio = nla_get_u32(tb[FRA_PRIORITY]);
00136 rule->ce_mask |= RULE_ATTR_PRIO;
00137 }
00138
00139 if (tb[FRA_FWMARK]) {
00140 rule->r_mark = nla_get_u32(tb[FRA_FWMARK]);
00141 rule->ce_mask |= RULE_ATTR_MARK;
00142 }
00143
00144 if (tb[FRA_FWMASK]) {
00145 rule->r_mask = nla_get_u32(tb[FRA_FWMASK]);
00146 rule->ce_mask |= RULE_ATTR_MASK;
00147 }
00148
00149 if (tb[FRA_GOTO]) {
00150 rule->r_goto = nla_get_u32(tb[FRA_GOTO]);
00151 rule->ce_mask |= RULE_ATTR_GOTO;
00152 }
00153
00154 if (tb[FRA_SRC]) {
00155 if (!(rule->r_src = nl_addr_alloc_attr(tb[FRA_SRC], family)))
00156 goto errout_enomem;
00157
00158 nl_addr_set_prefixlen(rule->r_src, frh->src_len);
00159 rule->ce_mask |= RULE_ATTR_SRC;
00160 }
00161
00162 if (tb[FRA_DST]) {
00163 if (!(rule->r_dst = nl_addr_alloc_attr(tb[FRA_DST], family)))
00164 goto errout_enomem;
00165 nl_addr_set_prefixlen(rule->r_dst, frh->dst_len);
00166 rule->ce_mask |= RULE_ATTR_DST;
00167 }
00168
00169
00170 if (tb[FRA_FLOW]) {
00171 rule->r_flow = nla_get_u32(tb[FRA_FLOW]);
00172 rule->ce_mask |= RULE_ATTR_FLOW;
00173 }
00174
00175 err = pp->pp_cb((struct nl_object *) rule, pp);
00176 errout:
00177 rtnl_rule_put(rule);
00178 return err;
00179
00180 errout_enomem:
00181 err = -NLE_NOMEM;
00182 goto errout;
00183 }
00184
00185 static int rule_request_update(struct nl_cache *c, struct nl_sock *h)
00186 {
00187 return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP);
00188 }
00189
00190 static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p)
00191 {
00192 struct rtnl_rule *r = (struct rtnl_rule *) o;
00193 char buf[128];
00194
00195 nl_dump_line(p, "%8d ", (r->ce_mask & RULE_ATTR_PRIO) ? r->r_prio : 0);
00196 nl_dump(p, "%s ", nl_af2str(r->r_family, buf, sizeof(buf)));
00197
00198 if (r->ce_mask & RULE_ATTR_SRC)
00199 nl_dump(p, "from %s ",
00200 nl_addr2str(r->r_src, buf, sizeof(buf)));
00201
00202 if (r->ce_mask & RULE_ATTR_DST)
00203 nl_dump(p, "to %s ",
00204 nl_addr2str(r->r_dst, buf, sizeof(buf)));
00205
00206 if (r->ce_mask & RULE_ATTR_DSFIELD)
00207 nl_dump(p, "tos %u ", r->r_dsfield);
00208
00209 if (r->ce_mask & (RULE_ATTR_MARK | RULE_ATTR_MASK))
00210 nl_dump(p, "mark %#x/%#x", r->r_mark, r->r_mask);
00211
00212 if (r->ce_mask & RULE_ATTR_IIFNAME)
00213 nl_dump(p, "iif %s ", r->r_iifname);
00214
00215 if (r->ce_mask & RULE_ATTR_OIFNAME)
00216 nl_dump(p, "oif %s ", r->r_oifname);
00217
00218 if (r->ce_mask & RULE_ATTR_TABLE)
00219 nl_dump(p, "lookup %s ",
00220 rtnl_route_table2str(r->r_table, buf, sizeof(buf)));
00221
00222 if (r->ce_mask & RULE_ATTR_FLOW)
00223 nl_dump(p, "flow %s ",
00224 rtnl_realms2str(r->r_flow, buf, sizeof(buf)));
00225
00226 if (r->ce_mask & RULE_ATTR_GOTO)
00227 nl_dump(p, "goto %u ", r->r_goto);
00228
00229 if (r->ce_mask & RULE_ATTR_ACTION)
00230 nl_dump(p, "action %s",
00231 nl_rtntype2str(r->r_action, buf, sizeof(buf)));
00232
00233 nl_dump(p, "\n");
00234 }
00235
00236 static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p)
00237 {
00238 rule_dump_line(obj, p);
00239 }
00240
00241 static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
00242 {
00243 rule_dump_details(obj, p);
00244 }
00245
00246 #define RULE_ATTR_FLAGS 0x0008
00247
00248 static int rule_compare(struct nl_object *_a, struct nl_object *_b,
00249 uint32_t attrs, int flags)
00250 {
00251 struct rtnl_rule *a = (struct rtnl_rule *) _a;
00252 struct rtnl_rule *b = (struct rtnl_rule *) _b;
00253 int diff = 0;
00254
00255 #define RULE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, RULE_ATTR_##ATTR, a, b, EXPR)
00256
00257 diff |= RULE_DIFF(FAMILY, a->r_family != b->r_family);
00258 diff |= RULE_DIFF(TABLE, a->r_table != b->r_table);
00259 diff |= RULE_DIFF(ACTION, a->r_action != b->r_action);
00260 diff |= RULE_DIFF(IIFNAME, strcmp(a->r_iifname, b->r_iifname));
00261 diff |= RULE_DIFF(OIFNAME, strcmp(a->r_oifname, b->r_oifname));
00262 diff |= RULE_DIFF(PRIO, a->r_prio != b->r_prio);
00263 diff |= RULE_DIFF(MARK, a->r_mark != b->r_mark);
00264 diff |= RULE_DIFF(MASK, a->r_mask != b->r_mask);
00265 diff |= RULE_DIFF(GOTO, a->r_goto != b->r_goto);
00266 diff |= RULE_DIFF(SRC, nl_addr_cmp(a->r_src, b->r_src));
00267 diff |= RULE_DIFF(DST, nl_addr_cmp(a->r_dst, b->r_dst));
00268 diff |= RULE_DIFF(DSFIELD, a->r_dsfield != b->r_dsfield);
00269 diff |= RULE_DIFF(FLOW, a->r_flow != b->r_flow);
00270
00271 #undef RULE_DIFF
00272
00273 return diff;
00274 }
00275
00276 static const struct trans_tbl rule_attrs[] = {
00277 __ADD(RULE_ATTR_FAMILY, family)
00278 __ADD(RULE_ATTR_TABLE, table)
00279 __ADD(RULE_ATTR_ACTION, action)
00280 __ADD(RULE_ATTR_IIFNAME, iifname)
00281 __ADD(RULE_ATTR_OIFNAME, oifname)
00282 __ADD(RULE_ATTR_PRIO, prio)
00283 __ADD(RULE_ATTR_MARK, mark)
00284 __ADD(RULE_ATTR_MASK, mask)
00285 __ADD(RULE_ATTR_GOTO, goto)
00286 __ADD(RULE_ATTR_SRC, src)
00287 __ADD(RULE_ATTR_DST, dst)
00288 __ADD(RULE_ATTR_DSFIELD, dsfield)
00289 __ADD(RULE_ATTR_FLOW, flow)
00290 };
00291
00292 static char *rule_attrs2str(int attrs, char *buf, size_t len)
00293 {
00294 return __flags2str(attrs, buf, len, rule_attrs,
00295 ARRAY_SIZE(rule_attrs));
00296 }
00297
00298
00299
00300
00301
00302
00303 struct rtnl_rule *rtnl_rule_alloc(void)
00304 {
00305 return (struct rtnl_rule *) nl_object_alloc(&rule_obj_ops);
00306 }
00307
00308 void rtnl_rule_put(struct rtnl_rule *rule)
00309 {
00310 nl_object_put((struct nl_object *) rule);
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331 int rtnl_rule_alloc_cache(struct nl_sock *sock, int family,
00332 struct nl_cache **result)
00333 {
00334 struct nl_cache * cache;
00335 int err;
00336
00337 if (!(cache = nl_cache_alloc(&rtnl_rule_ops)))
00338 return -NLE_NOMEM;
00339
00340 cache->c_iarg1 = family;
00341
00342 if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
00343 free(cache);
00344 return err;
00345 }
00346
00347 *result = cache;
00348 return 0;
00349 }
00350
00351
00352
00353
00354
00355
00356
00357
00358 static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags,
00359 struct nl_msg **result)
00360 {
00361 struct nl_msg *msg;
00362 struct fib_rule_hdr frh = {
00363 .family = tmpl->r_family,
00364 .table = tmpl->r_table,
00365 .action = tmpl->r_action,
00366 .flags = tmpl->r_flags,
00367 .tos = tmpl->r_dsfield,
00368 };
00369
00370 if (!(tmpl->ce_mask & RULE_ATTR_FAMILY))
00371 return -NLE_MISSING_ATTR;
00372
00373 msg = nlmsg_alloc_simple(cmd, flags);
00374 if (!msg)
00375 return -NLE_NOMEM;
00376
00377 if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0)
00378 goto nla_put_failure;
00379
00380 if (tmpl->ce_mask & RULE_ATTR_SRC) {
00381 frh.src_len = nl_addr_get_prefixlen(tmpl->r_src);
00382 NLA_PUT_ADDR(msg, FRA_SRC, tmpl->r_src);
00383 }
00384
00385 if (tmpl->ce_mask & RULE_ATTR_DST) {
00386 frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst);
00387 NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst);
00388 }
00389
00390 if (tmpl->ce_mask & RULE_ATTR_IIFNAME)
00391 NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname);
00392
00393 if (tmpl->ce_mask & RULE_ATTR_OIFNAME)
00394 NLA_PUT_STRING(msg, FRA_OIFNAME, tmpl->r_oifname);
00395
00396 if (tmpl->ce_mask & RULE_ATTR_PRIO)
00397 NLA_PUT_U32(msg, FRA_PRIORITY, tmpl->r_prio);
00398
00399 if (tmpl->ce_mask & RULE_ATTR_MARK)
00400 NLA_PUT_U32(msg, FRA_FWMARK, tmpl->r_mark);
00401
00402 if (tmpl->ce_mask & RULE_ATTR_MASK)
00403 NLA_PUT_U32(msg, FRA_FWMASK, tmpl->r_mask);
00404
00405 if (tmpl->ce_mask & RULE_ATTR_GOTO)
00406 NLA_PUT_U32(msg, FRA_GOTO, tmpl->r_goto);
00407
00408 if (tmpl->ce_mask & RULE_ATTR_FLOW)
00409 NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow);
00410
00411
00412 *result = msg;
00413 return 0;
00414
00415 nla_put_failure:
00416 nlmsg_free(msg);
00417 return -NLE_MSGSIZE;
00418 }
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags,
00434 struct nl_msg **result)
00435 {
00436 return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags,
00437 result);
00438 }
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452 int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags)
00453 {
00454 struct nl_msg *msg;
00455 int err;
00456
00457 if ((err = rtnl_rule_build_add_request(tmpl, flags, &msg)) < 0)
00458 return err;
00459
00460 err = nl_send_auto_complete(sk, msg);
00461 nlmsg_free(msg);
00462 if (err < 0)
00463 return err;
00464
00465 return wait_for_ack(sk);
00466 }
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488 int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags,
00489 struct nl_msg **result)
00490 {
00491 return build_rule_msg(rule, RTM_DELRULE, flags, result);
00492 }
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 int rtnl_rule_delete(struct nl_sock *sk, struct rtnl_rule *rule, int flags)
00507 {
00508 struct nl_msg *msg;
00509 int err;
00510
00511 if ((err = rtnl_rule_build_delete_request(rule, flags, &msg)) < 0)
00512 return err;
00513
00514 err = nl_send_auto_complete(sk, msg);
00515 nlmsg_free(msg);
00516 if (err < 0)
00517 return err;
00518
00519 return wait_for_ack(sk);
00520 }
00521
00522
00523
00524
00525
00526
00527
00528
00529 void rtnl_rule_set_family(struct rtnl_rule *rule, int family)
00530 {
00531 rule->r_family = family;
00532 rule->ce_mask |= RULE_ATTR_FAMILY;
00533 }
00534
00535 int rtnl_rule_get_family(struct rtnl_rule *rule)
00536 {
00537 if (rule->ce_mask & RULE_ATTR_FAMILY)
00538 return rule->r_family;
00539 else
00540 return AF_UNSPEC;
00541 }
00542
00543 void rtnl_rule_set_prio(struct rtnl_rule *rule, uint32_t prio)
00544 {
00545 rule->r_prio = prio;
00546 rule->ce_mask |= RULE_ATTR_PRIO;
00547 }
00548
00549 uint32_t rtnl_rule_get_prio(struct rtnl_rule *rule)
00550 {
00551 return rule->r_prio;
00552 }
00553
00554 void rtnl_rule_set_mark(struct rtnl_rule *rule, uint32_t mark)
00555 {
00556 rule->r_mark = mark;
00557 rule->ce_mask |= RULE_ATTR_MARK;
00558 }
00559
00560 uint32_t rtnl_rule_get_mark(struct rtnl_rule *rule)
00561 {
00562 return rule->r_mark;
00563 }
00564
00565 void rtnl_rule_set_mask(struct rtnl_rule *rule, uint32_t mask)
00566 {
00567 rule->r_mask = mask;
00568 rule->ce_mask |= RULE_ATTR_MASK;
00569 }
00570
00571 uint32_t rtnl_rule_get_mask(struct rtnl_rule *rule)
00572 {
00573 return rule->r_mask;
00574 }
00575
00576 void rtnl_rule_set_table(struct rtnl_rule *rule, uint32_t table)
00577 {
00578 rule->r_table = table;
00579 rule->ce_mask |= RULE_ATTR_TABLE;
00580 }
00581
00582 uint32_t rtnl_rule_get_table(struct rtnl_rule *rule)
00583 {
00584 return rule->r_table;
00585 }
00586
00587 void rtnl_rule_set_dsfield(struct rtnl_rule *rule, uint8_t dsfield)
00588 {
00589 rule->r_dsfield = dsfield;
00590 rule->ce_mask |= RULE_ATTR_DSFIELD;
00591 }
00592
00593 uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *rule)
00594 {
00595 return rule->r_dsfield;
00596 }
00597
00598 static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos,
00599 struct nl_addr *new, int flag)
00600 {
00601 if (rule->ce_mask & RULE_ATTR_FAMILY) {
00602 if (new->a_family != rule->r_family)
00603 return -NLE_AF_MISMATCH;
00604 } else
00605 rule->r_family = new->a_family;
00606
00607 if (*pos)
00608 nl_addr_put(*pos);
00609
00610 nl_addr_get(new);
00611 *pos = new;
00612
00613 rule->ce_mask |= (flag | RULE_ATTR_FAMILY);
00614
00615 return 0;
00616 }
00617
00618 int rtnl_rule_set_src(struct rtnl_rule *rule, struct nl_addr *src)
00619 {
00620 return __assign_addr(rule, &rule->r_src, src, RULE_ATTR_SRC);
00621 }
00622
00623 struct nl_addr *rtnl_rule_get_src(struct rtnl_rule *rule)
00624 {
00625 return rule->r_src;
00626 }
00627
00628 int rtnl_rule_set_dst(struct rtnl_rule *rule, struct nl_addr *dst)
00629 {
00630 return __assign_addr(rule, &rule->r_dst, dst, RULE_ATTR_DST);
00631 }
00632
00633 struct nl_addr *rtnl_rule_get_dst(struct rtnl_rule *rule)
00634 {
00635 return rule->r_dst;
00636 }
00637
00638 int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev)
00639 {
00640 if (strlen(dev) > IFNAMSIZ-1)
00641 return -NLE_RANGE;
00642
00643 strcpy(rule->r_iifname, dev);
00644 rule->ce_mask |= RULE_ATTR_IIFNAME;
00645 return 0;
00646 }
00647
00648 char *rtnl_rule_get_iif(struct rtnl_rule *rule)
00649 {
00650 if (rule->ce_mask & RULE_ATTR_IIFNAME)
00651 return rule->r_iifname;
00652 else
00653 return NULL;
00654 }
00655
00656 int rtnl_rule_set_oif(struct rtnl_rule *rule, const char *dev)
00657 {
00658 if (strlen(dev) > IFNAMSIZ-1)
00659 return -NLE_RANGE;
00660
00661 strcpy(rule->r_oifname, dev);
00662 rule->ce_mask |= RULE_ATTR_OIFNAME;
00663 return 0;
00664 }
00665
00666 char *rtnl_rule_get_oif(struct rtnl_rule *rule)
00667 {
00668 if (rule->ce_mask & RULE_ATTR_OIFNAME)
00669 return rule->r_oifname;
00670 else
00671 return NULL;
00672 }
00673
00674 void rtnl_rule_set_action(struct rtnl_rule *rule, uint8_t action)
00675 {
00676 rule->r_action = action;
00677 rule->ce_mask |= RULE_ATTR_ACTION;
00678 }
00679
00680 uint8_t rtnl_rule_get_action(struct rtnl_rule *rule)
00681 {
00682 return rule->r_action;
00683 }
00684
00685 void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
00686 {
00687 rule->r_flow = realms;
00688 rule->ce_mask |= RULE_ATTR_FLOW;
00689 }
00690
00691 uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule)
00692 {
00693 return rule->r_flow;
00694 }
00695
00696 void rtnl_rule_set_goto(struct rtnl_rule *rule, uint32_t ref)
00697 {
00698 rule->r_goto = ref;
00699 rule->ce_mask |= RULE_ATTR_GOTO;
00700 }
00701
00702 uint32_t rtnl_rule_get_goto(struct rtnl_rule *rule)
00703 {
00704 return rule->r_goto;
00705 }
00706
00707
00708
00709 static struct nl_object_ops rule_obj_ops = {
00710 .oo_name = "route/rule",
00711 .oo_size = sizeof(struct rtnl_rule),
00712 .oo_free_data = rule_free_data,
00713 .oo_clone = rule_clone,
00714 .oo_dump = {
00715 [NL_DUMP_LINE] = rule_dump_line,
00716 [NL_DUMP_DETAILS] = rule_dump_details,
00717 [NL_DUMP_STATS] = rule_dump_stats,
00718 },
00719 .oo_compare = rule_compare,
00720 .oo_attrs2str = rule_attrs2str,
00721 .oo_id_attrs = ~0,
00722 };
00723
00724 static struct nl_cache_ops rtnl_rule_ops = {
00725 .co_name = "route/rule",
00726 .co_hdrsize = sizeof(struct fib_rule_hdr),
00727 .co_msgtypes = {
00728 { RTM_NEWRULE, NL_ACT_NEW, "new" },
00729 { RTM_DELRULE, NL_ACT_DEL, "del" },
00730 { RTM_GETRULE, NL_ACT_GET, "get" },
00731 END_OF_MSGTYPES_LIST,
00732 },
00733 .co_protocol = NETLINK_ROUTE,
00734 .co_request_update = rule_request_update,
00735 .co_msg_parser = rule_msg_parser,
00736 .co_obj_ops = &rule_obj_ops,
00737 };
00738
00739 static void __init rule_init(void)
00740 {
00741 nl_cache_mngt_register(&rtnl_rule_ops);
00742 }
00743
00744 static void __exit rule_exit(void)
00745 {
00746 nl_cache_mngt_unregister(&rtnl_rule_ops);
00747 }
00748
00749