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; }
int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) { struct fib6_node *fn, *pn = NULL; int err = -ENOMEM; int allow_create = 1; int replace_required = 0; if (info->nlh) { if (!(info->nlh->nlmsg_flags & NLM_F_CREATE)) allow_create = 0; if (info->nlh->nlmsg_flags & NLM_F_REPLACE) replace_required = 1; } if (!allow_create && !replace_required) pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, replace_required); if (IS_ERR(fn)) { err = PTR_ERR(fn); fn = NULL; } if (!fn) goto out; pn = fn; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen) { struct fib6_node *sn; if (!fn->subtree) { struct fib6_node *sfn; /* * Create subtree. * * fn[main tree] * | * sfn[subtree root] * \ * sn[new leaf node] */ /* Create subtree root node */ sfn = node_alloc(); if (!sfn) goto st_failure; sfn->leaf = info->nl_net->ipv6.ip6_null_entry; atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); /* Now add the first leaf node to new subtree */ sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required); if (IS_ERR(sn)) { err = PTR_ERR(sn); sn = NULL; } if (!sn) { /* If it is failed, discard just allocated root, and then (in st_failure) stale node in main tree. */ node_free(sfn); goto st_failure; } /* Now link new subtree to main tree */ sfn->parent = fn; fn->subtree = sfn; } else { sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required); if (IS_ERR(sn)) { err = PTR_ERR(sn); sn = NULL; } if (!sn) goto st_failure; } if (!fn->leaf) { fn->leaf = rt; atomic_inc(&rt->rt6i_ref); } fn = sn; }
int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh, void *_rtattr, struct netlink_skb_parms *req) { struct fib6_node *fn; int err = -ENOMEM; fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst)); if (fn == NULL) goto out; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen) { struct fib6_node *sn; if (fn->subtree == NULL) { struct fib6_node *sfn; /* * Create subtree. * * fn[main tree] * | * sfn[subtree root] * \ * sn[new leaf node] */ /* Create subtree root node */ sfn = node_alloc(); if (sfn == NULL) goto st_failure; sfn->leaf = &ip6_null_entry; atomic_inc(&ip6_null_entry.rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); /* Now add the first leaf node to new subtree */ sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src)); if (sn == NULL) { /* If it is failed, discard just allocated root, and then (in st_failure) stale node in main tree. */ node_free(sfn); goto st_failure; } /* Now link new subtree to main tree */ sfn->parent = fn; fn->subtree = sfn; if (fn->leaf == NULL) { fn->leaf = rt; atomic_inc(&rt->rt6i_ref); } } else { sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src)); if (sn == NULL) goto st_failure; } fn = sn; }
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; }
int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) { struct fib6_node *fn, *pn = NULL; int err = -ENOMEM; int allow_create = 1; int replace_required = 0; if (info->nlh) { if (!(info->nlh->nlmsg_flags & NLM_F_CREATE)) allow_create = 0; if (info->nlh->nlmsg_flags & NLM_F_REPLACE) replace_required = 1; } if (!allow_create && !replace_required) pr_warn("IPv6: RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, replace_required); if (IS_ERR(fn)) { err = PTR_ERR(fn); fn = NULL; } if (!fn) goto out; pn = fn; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen) { struct fib6_node *sn; if (!fn->subtree) { struct fib6_node *sfn; sfn = node_alloc(); if (!sfn) goto st_failure; sfn->leaf = info->nl_net->ipv6.ip6_null_entry; atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required); if (!sn) { node_free(sfn); goto st_failure; } sfn->parent = fn; fn->subtree = sfn; } else { sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required); if (IS_ERR(sn)) { err = PTR_ERR(sn); sn = NULL; } if (!sn) goto st_failure; } if (!fn->leaf) { fn->leaf = rt; atomic_inc(&rt->rt6i_ref); } fn = sn; }
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; }
int fib6_add(struct fib6_node *root, struct rt6_info *rt) { struct fib6_node *fn; int err = -ENOMEM; fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); if (fn == NULL) return -ENOMEM; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen) { struct fib6_node *sn; if (fn->subtree == NULL) { struct fib6_node *sfn; /* * Create subtree. * * fn[main tree] * | * sfn[subtree root] * \ * sn[new leaf node] */ /* Create subtree root node */ sfn = node_alloc(); if (sfn == NULL) goto st_failure; sfn->leaf = &ip6_null_entry; atomic_inc(&ip6_null_entry.rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); /* Now add the first leaf node to new subtree */ sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); if (sn == NULL) { /* If it is failed, discard just allocated root, and then (in st_failure) stale node in main tree. */ node_free(sfn); goto st_failure; } /* Now link new subtree to main tree */ sfn->parent = fn; fn->subtree = sfn; if (fn->leaf == NULL) { fn->leaf = rt; atomic_inc(&rt->rt6i_ref); } } else { sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); if (sn == NULL) goto st_failure; } fn = sn; } #endif err = fib6_add_rt2node(fn, rt); if (err == 0) { fib6_start_gc(rt); if (!(rt->rt6i_flags&RTF_CACHE)) fib6_prune_clones(fn, rt); } if (err) dst_free(&rt->u.dst); return err; #ifdef CONFIG_IPV6_SUBTREES /* Subtree creation failed, probably main tree node is orphan. If it is, shot it. */ st_failure: if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) fib_repair_tree(fn); dst_free(&rt->u.dst); return err; #endif }