Commit 4a287eba authored by Matti Vaittinen's avatar Matti Vaittinen Committed by David S. Miller

IPv6 routing, NLM_F_* flag support: REPLACE and EXCL flags support, warn about missing CREATE flag

The support for NLM_F_* flags at IPv6 routing requests.

If NLM_F_CREATE flag is not defined for RTM_NEWROUTE request,
warning is printed, but no error is returned. Instead new route is
added. Later NLM_F_CREATE may be required for
new route creation.

Exception is when NLM_F_REPLACE flag is given without NLM_F_CREATE, and
no matching route is found. In this case it should be safe to assume
that the request issuer is familiar with NLM_F_* flags, and does really
not want route to be created.

Specifying NLM_F_REPLACE flag will now make the kernel to search for
matching route, and replace it with new one. If no route is found and
NLM_F_CREATE is specified as well, then new route is created.

Also, specifying NLM_F_EXCL will yield returning of error if matching
route is found.

Patch created against linux-3.2-rc1
Signed-off-by: default avatarMatti Vaittinen <Mazziesaccount@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d71314b4
...@@ -425,7 +425,8 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -425,7 +425,8 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
int addrlen, int plen, int addrlen, int plen,
int offset) int offset, int allow_create,
int replace_required)
{ {
struct fib6_node *fn, *in, *ln; struct fib6_node *fn, *in, *ln;
struct fib6_node *pn = NULL; struct fib6_node *pn = NULL;
...@@ -447,8 +448,12 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, ...@@ -447,8 +448,12 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
* Prefix match * Prefix match
*/ */
if (plen < fn->fn_bit || if (plen < fn->fn_bit ||
!ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) {
if (!allow_create)
printk(KERN_WARNING
"IPv6: NLM_F_CREATE should be set when creating new route\n");
goto insert_above; goto insert_above;
}
/* /*
* Exact match ? * Exact match ?
...@@ -477,10 +482,26 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr, ...@@ -477,10 +482,26 @@ static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
fn = dir ? fn->right: fn->left; fn = dir ? fn->right: fn->left;
} while (fn); } while (fn);
if (replace_required && !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
*/
printk(KERN_WARNING
"IPv6: NLM_F_CREATE should be set when creating new route - ignoring request\n");
return ERR_PTR(-ENOENT);
}
/* /*
* We walked to the bottom of tree. * We walked to the bottom of tree.
* Create new leaf node without children. * Create new leaf node without children.
*/ */
if (!allow_create)
printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n");
ln = node_alloc(); ln = node_alloc();
...@@ -614,6 +635,12 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -614,6 +635,12 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
{ {
struct rt6_info *iter = NULL; struct rt6_info *iter = NULL;
struct rt6_info **ins; struct rt6_info **ins;
int replace = (NULL != info &&
NULL != info->nlh &&
(info->nlh->nlmsg_flags&NLM_F_REPLACE));
int add = ((NULL == info || NULL == info->nlh) ||
(info->nlh->nlmsg_flags&NLM_F_CREATE));
int found = 0;
ins = &fn->leaf; ins = &fn->leaf;
...@@ -626,6 +653,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -626,6 +653,13 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
/* /*
* Same priority level * Same priority level
*/ */
if (NULL != info->nlh &&
(info->nlh->nlmsg_flags&NLM_F_EXCL))
return -EEXIST;
if (replace) {
found++;
break;
}
if (iter->rt6i_dev == rt->rt6i_dev && if (iter->rt6i_dev == rt->rt6i_dev &&
iter->rt6i_idev == rt->rt6i_idev && iter->rt6i_idev == rt->rt6i_idev &&
...@@ -655,7 +689,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -655,7 +689,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
/* /*
* insert node * insert node
*/ */
if (!replace) {
if (!add)
printk(KERN_WARNING "IPv6: NLM_F_CREATE should be set when creating new route\n");
add:
rt->dst.rt6_next = iter; rt->dst.rt6_next = iter;
*ins = rt; *ins = rt;
rt->rt6i_node = fn; rt->rt6i_node = fn;
...@@ -668,6 +706,25 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ...@@ -668,6 +706,25 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
fn->fn_flags |= RTN_RTINFO; fn->fn_flags |= RTN_RTINFO;
} }
} else {
if (!found) {
if (add)
goto add;
printk(KERN_WARNING "IPv6: NLM_F_REPLACE set, but no existing node found!\n");
return -ENOENT;
}
*ins = rt;
rt->rt6i_node = fn;
rt->dst.rt6_next = iter->dst.rt6_next;
atomic_inc(&rt->rt6i_ref);
inet6_rt_notify(RTM_NEWROUTE, rt, info);
rt6_release(iter);
if ((fn->fn_flags & RTN_RTINFO) == 0) {
info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
fn->fn_flags |= RTN_RTINFO;
}
}
return 0; return 0;
} }
...@@ -696,9 +753,25 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) ...@@ -696,9 +753,25 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
{ {
struct fib6_node *fn, *pn = NULL; struct fib6_node *fn, *pn = NULL;
int err = -ENOMEM; int err = -ENOMEM;
int allow_create = 1;
int replace_required = 0;
if (NULL != info && NULL != 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)
printk(KERN_WARNING "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), fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst)); 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 == NULL) if (fn == NULL)
goto out; goto out;
...@@ -736,7 +809,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) ...@@ -736,7 +809,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
sizeof(struct in6_addr), rt->rt6i_src.plen, sizeof(struct in6_addr), rt->rt6i_src.plen,
offsetof(struct rt6_info, rt6i_src)); offsetof(struct rt6_info, rt6i_src),
allow_create, replace_required);
if (sn == NULL) { if (sn == NULL) {
/* If it is failed, discard just allocated /* If it is failed, discard just allocated
...@@ -753,8 +827,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) ...@@ -753,8 +827,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
} else { } else {
sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
sizeof(struct in6_addr), rt->rt6i_src.plen, sizeof(struct in6_addr), rt->rt6i_src.plen,
offsetof(struct rt6_info, rt6i_src)); offsetof(struct rt6_info, rt6i_src),
allow_create, replace_required);
if (IS_ERR(sn)) {
err = PTR_ERR(sn);
sn = NULL;
}
if (sn == NULL) if (sn == NULL)
goto st_failure; goto st_failure;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment