static int fib6_age(struct rt6_info *rt, void *arg) { unsigned long now = jiffies; /* Age clones. Note, that clones are aged out only if they are not in use now. */ if (rt->rt6i_flags & RTF_CACHE) { if (atomic_read(&rt->u.dst.use) == 0 && (long)(now - rt->u.dst.lastuse) >= gc_args.timeout) { RT6_TRACE("aging clone %p\n", rt); return -1; } gc_args.more++; } /* * check addrconf expiration here. * They are expired even if they are in use. */ if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) { if ((long)(now - rt->rt6i_expires) > 0) { RT6_TRACE("expiring %p\n", rt); return -1; } gc_args.more++; } return 0; }
static int fib6_prune_clone(struct rt6_info *rt, void *arg) { if (rt->rt6i_flags & RTF_CACHE) { RT6_TRACE("pruning clone %p\n", rt); return -1; } return 0; }
static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp) { struct fib6_walker_t *w; struct rt6_info *rt = *rtp; RT6_TRACE("fib6_del_route\n"); /* Unlink it */ *rtp = rt->u.next; rt->rt6i_node = NULL; rt6_stats.fib_rt_entries--; /* Adjust walkers */ FOR_WALKERS(w) { if (w->state == FWS_C && w->leaf == rt) { RT6_TRACE("walker %p adjusted by delroute\n", w); w->leaf = rt->u.next; if (w->leaf == NULL) w->state = FWS_U; } } rt->u.next = NULL; /* If it was last route, expunge its radix tree node */ if (fn->leaf == NULL) { fn->fn_flags &= ~RTN_RTINFO; rt6_stats.fib_route_nodes--; fib6_repair_tree(fn); } #ifdef CONFIG_RTNETLINK inet6_rt_notify(RTM_DELROUTE, rt); #endif rt6_release(rt); }
static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, int addrlen, int plen, int offset, int allow_create, int replace_required) { struct fib6_node *fn, *in, *ln; struct fib6_node *pn = NULL; struct rt6key *key; int bit; __be32 dir = 0; __u32 sernum = fib6_new_sernum(); RT6_TRACE("fib6_add_1\n"); /* insert node in tree */ fn = root; do { key = (struct rt6key *)((u8 *)fn->leaf + offset); /* * Prefix match */ if (plen < fn->fn_bit || !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) { if (!allow_create) { if (replace_required) { pr_warn("Can't replace route, no match found\n"); return ERR_PTR(-ENOENT); } pr_warn("NLM_F_CREATE should be set when creating new route\n"); } goto insert_above; } /* * Exact match ? */ if (plen == fn->fn_bit) { /* clean up an intermediate node */ if (!(fn->fn_flags & RTN_RTINFO)) { rt6_release(fn->leaf); fn->leaf = NULL; } fn->fn_sernum = sernum; return fn; } /* * We have more bits to go */ /* Try to walk down on tree. */ fn->fn_sernum = sernum; dir = addr_bit_set(addr, fn->fn_bit); pn = fn; fn = dir ? fn->right: fn->left; } while (fn); if (!allow_create) { /* We should not create new node because * NLM_F_REPLACE was specified without NLM_F_CREATE * I assume it is safe to require NLM_F_CREATE when * REPLACE flag is used! Later we may want to remove the * check for replace_required, because according * to netlink specification, NLM_F_CREATE * MUST be specified if new route is created. * That would keep IPv6 consistent with IPv4 */ if (replace_required) { pr_warn("Can't replace route, no match found\n"); return ERR_PTR(-ENOENT); } pr_warn("NLM_F_CREATE should be set when creating new route\n"); } /* * We walked to the bottom of tree. * Create new leaf node without children. */ ln = node_alloc(); if (!ln) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; return ln; insert_above: /* * split since we don't have a common prefix anymore or * we have a less significant route. * we've to insert an intermediate node on the list * this new node will point to the one we need to create * and the current */ pn = fn->parent; /* find 1st bit in difference between the 2 addrs. See comment in __ipv6_addr_diff: bit may be an invalid value, but if it is >= plen, the value is ignored in any case. */ bit = __ipv6_addr_diff(addr, &key->addr, addrlen); /* * (intermediate)[in] * / \ * (new leaf node)[ln] (old node)[fn] */ if (plen > bit) { in = node_alloc(); ln = node_alloc(); if (!in || !ln) { if (in) node_free(in); if (ln) node_free(ln); return NULL; } /* * new intermediate node. * RTN_RTINFO will * be off since that an address that chooses one of * the branches would not match less specific routes * in the other branch */ in->fn_bit = bit; in->parent = pn; in->leaf = fn->leaf; atomic_inc(&in->leaf->rt6i_ref); in->fn_sernum = sernum; /* update parent pointer */ if (dir) pn->right = in; else pn->left = in; ln->fn_bit = plen; ln->parent = in; fn->parent = in; ln->fn_sernum = sernum; if (addr_bit_set(addr, bit)) { in->right = ln; in->left = fn; } else { in->left = ln; in->right = fn; } } else { /* plen <= bit */ /* * (new leaf node)[ln] * / \ * (old node)[fn] NULL */ ln = node_alloc(); if (!ln) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; if (addr_bit_set(&key->addr, plen)) ln->right = fn; else ln->left = fn; fn->parent = ln; } return ln; }
static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt, struct in6_addr *daddr, struct in6_addr *saddr, struct fl_acc_args *args) { struct flow_rule *frule; struct rt6_info *nrt = NULL; struct pol_chain *pol; for (pol = rt6_pol_list; pol; pol = pol->next) { struct fib6_node *fn; struct rt6_info *sprt; fn = fib6_lookup(pol->rules, daddr, saddr); do { for (sprt = fn->leaf; sprt; sprt=sprt->u.next) { int res; frule = sprt->rt6i_flowr; #if RT6_DEBUG >= 2 if (frule == NULL) { printk(KERN_DEBUG "NULL flowr\n"); goto error; } #endif res = frule->ops->accept(rt, sprt, args, &nrt); switch (res) { case FLOWR_SELECT: goto found; case FLOWR_CLEAR: goto next_policy; case FLOWR_NODECISION: break; default: goto error; }; } fn = fn->parent; } while ((fn->fn_flags & RTN_TL_ROOT) == 0); next_policy: } error: dst_hold(&ip6_null_entry.u.dst); return &ip6_null_entry; found: if (nrt == NULL) goto error; nrt->rt6i_flags |= RTF_CACHE; dst_hold(&nrt->u.dst); err = rt6_ins(nrt, NULL); if (err) nrt->u.dst.error = err; return nrt; } #endif static int fib6_ifdown(struct rt6_info *rt, void *arg) { if (((void*)rt->rt6i_dev == arg || arg == NULL) && rt != &ip6_null_entry) { RT6_TRACE("deleted by ifdown %p\n", rt); return -1; } return 0; } void rt6_ifdown(struct net_device *dev) { write_lock_bh(&rt6_lock); fib6_clean_tree(&ip6_routing_table, fib6_ifdown, 0, dev); write_unlock_bh(&rt6_lock); }
static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, int addrlen, int plen, int offset) { struct fib6_node *fn, *in, *ln; struct fib6_node *pn = NULL; struct rt6key *key; int bit; int dir = 0; __u32 sernum = fib6_new_sernum(); RT6_TRACE("fib6_add_1\n"); /* insert node in tree */ fn = root; do { key = (struct rt6key *)((u8 *)fn->leaf + offset); /* * Prefix match */ if (plen < fn->fn_bit || !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) goto insert_above; /* * Exact match ? */ if (plen == fn->fn_bit) { /* clean up an intermediate node */ if ((fn->fn_flags & RTN_RTINFO) == 0) { rt6_release(fn->leaf); fn->leaf = NULL; } fn->fn_sernum = sernum; return fn; } /* * We have more bits to go */ /* Try to walk down on tree. */ fn->fn_sernum = sernum; dir = addr_bit_set(addr, fn->fn_bit); pn = fn; fn = dir ? fn->right: fn->left; } while (fn); /* * We walked to the bottom of tree. * Create new leaf node without children. */ ln = node_alloc(); if (ln == NULL) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; return ln; insert_above: /* * split since we don't have a common prefix anymore or * we have a less significant route. * we've to insert an intermediate node on the list * this new node will point to the one we need to create * and the current */ pn = fn->parent; /* find 1st bit in difference between the 2 addrs. See comment in __ipv6_addr_diff: bit may be an invalid value, but if it is >= plen, the value is ignored in any case. */ bit = __ipv6_addr_diff(addr, &key->addr, addrlen); /* * (intermediate)[in] * / \ * (new leaf node)[ln] (old node)[fn] */ if (plen > bit) { in = node_alloc(); ln = node_alloc(); if (in == NULL || ln == NULL) { if (in) node_free(in); if (ln) node_free(ln); return NULL; } /* * new intermediate node. * RTN_RTINFO will * be off since that an address that chooses one of * the branches would not match less specific routes * in the other branch */ in->fn_bit = bit; in->parent = pn; in->leaf = fn->leaf; atomic_inc(&in->leaf->rt6i_ref); in->fn_sernum = sernum; /* update parent pointer */ if (dir) pn->right = in; else pn->left = in; ln->fn_bit = plen; ln->parent = in; fn->parent = in; ln->fn_sernum = sernum; if (addr_bit_set(addr, bit)) { in->right = ln; in->left = fn; } else { in->left = ln; in->right = fn; } } else { /* plen <= bit */ /* * (new leaf node)[ln] * / \ * (old node)[fn] NULL */ ln = node_alloc(); if (ln == NULL) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; if (addr_bit_set(&key->addr, plen)) ln->right = fn; else ln->left = fn; fn->parent = ln; } return ln; }
static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, int addrlen, int plen, int offset, int allow_create, int replace_required) { struct fib6_node *fn, *in, *ln; struct fib6_node *pn = NULL; struct rt6key *key; int bit; __be32 dir = 0; __u32 sernum = fib6_new_sernum(); RT6_TRACE("fib6_add_1\n"); fn = root; do { key = (struct rt6key *)((u8 *)fn->leaf + offset); if (plen < fn->fn_bit || !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) { if (!allow_create) { if (replace_required) { pr_warn("IPv6: Can't replace route, " "no match found\n"); return ERR_PTR(-ENOENT); } pr_warn("IPv6: NLM_F_CREATE should be set " "when creating new route\n"); } goto insert_above; } if (plen == fn->fn_bit) { if (!(fn->fn_flags & RTN_RTINFO)) { rt6_release(fn->leaf); fn->leaf = NULL; } fn->fn_sernum = sernum; return fn; } fn->fn_sernum = sernum; dir = addr_bit_set(addr, fn->fn_bit); pn = fn; fn = dir ? fn->right: fn->left; } while (fn); if (!allow_create) { if (replace_required) { pr_warn("IPv6: Can't replace route, no match found\n"); return ERR_PTR(-ENOENT); } pr_warn("IPv6: NLM_F_CREATE should be set " "when creating new route\n"); } ln = node_alloc(); if (!ln) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; return ln; insert_above: pn = fn->parent; bit = __ipv6_addr_diff(addr, &key->addr, addrlen); if (plen > bit) { in = node_alloc(); ln = node_alloc(); if (!in || !ln) { if (in) node_free(in); if (ln) node_free(ln); return NULL; } in->fn_bit = bit; in->parent = pn; in->leaf = fn->leaf; atomic_inc(&in->leaf->rt6i_ref); in->fn_sernum = sernum; if (dir) pn->right = in; else pn->left = in; ln->fn_bit = plen; ln->parent = in; fn->parent = in; ln->fn_sernum = sernum; if (addr_bit_set(addr, bit)) { in->right = ln; in->left = fn; } else { in->left = ln; in->right = fn; } } else { ln = node_alloc(); if (!ln) return NULL; ln->fn_bit = plen; ln->parent = pn; ln->fn_sernum = sernum; if (dir) pn->right = ln; else pn->left = ln; if (addr_bit_set(&key->addr, plen)) ln->right = fn; else ln->left = fn; fn->parent = ln; } return ln; }
static void fib6_repair_tree(struct fib6_node *fn) { int children; int nstate; struct fib6_node *child, *pn; struct fib6_walker_t *w; int iter = 0; for (;;) { RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter); iter++; BUG_TRAP(!(fn->fn_flags&RTN_RTINFO)); BUG_TRAP(!(fn->fn_flags&RTN_TL_ROOT)); BUG_TRAP(fn->leaf==NULL); children = 0; child = NULL; if (fn->right) child = fn->right, children |= 1; if (fn->left) child = fn->left, children |= 2; if (children == 3 || SUBTREE(fn) #ifdef CONFIG_IPV6_SUBTREES /* Subtree root (i.e. fn) may have one child */ || (children && fn->fn_flags&RTN_ROOT) #endif ) { fn->leaf = fib6_find_prefix(fn); #if RT6_DEBUG >= 2 if (fn->leaf==NULL) { BUG_TRAP(fn->leaf); fn->leaf = &ip6_null_entry; } #endif atomic_inc(&fn->leaf->rt6i_ref); return; } pn = fn->parent; #ifdef CONFIG_IPV6_SUBTREES if (SUBTREE(pn) == fn) { BUG_TRAP(fn->fn_flags&RTN_ROOT); SUBTREE(pn) = NULL; nstate = FWS_L; } else { BUG_TRAP(!(fn->fn_flags&RTN_ROOT)); #endif if (pn->right == fn) pn->right = child; else if (pn->left == fn) pn->left = child; #if RT6_DEBUG >= 2 else BUG_TRAP(0); #endif if (child) child->parent = pn; nstate = FWS_R; #ifdef CONFIG_IPV6_SUBTREES } #endif FOR_WALKERS(w) { if (child == NULL) { if (w->root == fn) { w->root = w->node = NULL; RT6_TRACE("W %p adjusted by delroot 1\n", w); } else if (w->node == fn) { RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate); w->node = pn; w->state = nstate; } } else { if (w->root == fn) { w->root = child; RT6_TRACE("W %p adjusted by delroot 2\n", w); } if (w->node == fn) { w->node = child; if (children&2) { RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); w->state = w->state>=FWS_R ? FWS_U : FWS_INIT; } else { RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); w->state = w->state>=FWS_C ? FWS_U : FWS_INIT; } } } } node_free(fn); if (pn->fn_flags&RTN_RTINFO || SUBTREE(pn)) return; rt6_release(pn->leaf); pn->leaf = NULL; fn = pn; } }