Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+94 −15
Original line number Original line Diff line number Diff line
@@ -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,
		 *	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,
		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,
{
{
	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,
			/*
			/*
			 *	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,
	/*
	/*
	 *	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,
			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)
{
{
	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)


			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)
		} 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;
		}
		}