Patchwork [RFC] xfrm: fix handling of XFRM policies mark and mask.

login
register
mail settings
Submitter Romain KUNTZ
Date Feb. 2, 2013, 5:27 p.m.
Message ID <9E57ADA1-5770-47A8-8EBF-7FC262EEF1C7@ipflavors.com>
Download mbox | patch
Permalink /patch/217697/
State Awaiting Upstream
Delegated to: David Miller
Headers show

Comments

Romain KUNTZ - Feb. 2, 2013, 5:27 p.m.
The current algorithm to insert XFRM policies with a mark and a mask
allows the insertion of more generic policies, but fails when trying
to install more specific policies.

For example, executing the below commands in that order succeed:
 ip -6 xfrm policy flush
 ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
 ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out

But it fails in the reverse order:
 ip -6 xfrm policy flush
 ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
 ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
 RTNETLINK answers: File exists

This patch modifies the tests on the marks and masks and takes the most
straightforward way: compare the mask and mark separately. It requires to 
give the xfrm_mark structure as an argument to some of the xfrm_policy_*
functions, instead of just the mark.

This patch covers the problem for XFRM policies, a similar problem
exists with XFRM states. If the proposed solution is satisfactory, I 
will propose a similar patch for the states. Comments are welcome.

Signed-off-by: Emmanuel Thierry <emmanuel.thierry@telecom-bretagne.eu>
Signed-off-by: Romain Kuntz <r.kuntz@ipflavors.com>
---
 include/net/xfrm.h     |    6 ++++--
 net/xfrm/xfrm_policy.c |   18 +++++++++++-------
 net/xfrm/xfrm_user.c   |   13 +++++++------
 3 files changed, 22 insertions(+), 15 deletions(-)
Steffen Klassert - Feb. 5, 2013, 8:12 a.m.
Cc Jamal, he introduced the xfrm_mark framework and knows it
probably the best.

On Sat, Feb 02, 2013 at 06:27:03PM +0100, Romain KUNTZ wrote:
> The current algorithm to insert XFRM policies with a mark and a mask
> allows the insertion of more generic policies, but fails when trying
> to install more specific policies.
> 

Hm, I think we will not match always the right policy if we allow both
orders. Lets take your example and assume we have a flow with mark 1.
The policy lookup is a linear search, so we use the first matching
policy. xfrm_policy_match() does the following check on the mark:

if (... || (fl->flowi_mark & pol->mark.m) != pol->mark.v || ...)
	return -ESRCH

> For example, executing the below commands in that order succeed:
>  ip -6 xfrm policy flush
>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out

The policy with mark 1 is the first we find. The policy passes the
mark check and if the flow matches the selectors, we use this policy.

> 
> But it fails in the reverse order:
>  ip -6 xfrm policy flush
>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>  RTNETLINK answers: File exists

With this scenario, we would find the policy with mark and mask 0 first.
This policy passes the mark check too. So we would use this policy if the
flow matches the selectors, but the flow asked for a policy with mark 1.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
jamal - Feb. 6, 2013, 1:14 p.m.
Hi Steffen,

On 13-02-05 03:12 AM, Steffen Klassert wrote:
>> For example, executing the below commands in that order succeed:
>>   ip -6 xfrm policy flush
>>   ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>>   ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
> The policy with mark 1 is the first we find. The policy passes the
> mark check and if the flow matches the selectors, we use this policy.
>
>> But it fails in the reverse order:
>>   ip -6 xfrm policy flush
>>   ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
>>   ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>>   RTNETLINK answers: File exists
> With this scenario, we would find the policy with mark and mask 0 first.
> This policy passes the mark check too. So we would use this policy if the
> flow matches the selectors, but the flow asked for a policy with mark 1.

I think the intent Romain is expressing is reasonable and should resolved at
insertion time(xfrm_policy_insert()).
i.e even though the policy (such as mark=1) is inserted afterwards, at
insertion time if it proves it is more specific and not duplicate, it 
should be
inserted ahead of the mark=0.
The runtime check will work then.

cheers,
jamal





--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Emmanuel Thierry - Feb. 6, 2013, 1:53 p.m.
Hello,

Le 6 févr. 2013 à 14:14, jamal a écrit :

> 
> On 13-02-05 03:12 AM, Steffen Klassert wrote:
>>> For example, executing the below commands in that order succeed:
>>>  ip -6 xfrm policy flush
>>>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>>>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
>> The policy with mark 1 is the first we find. The policy passes the
>> mark check and if the flow matches the selectors, we use this policy.
>> 
>>> But it fails in the reverse order:
>>>  ip -6 xfrm policy flush
>>>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out
>>>  ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 1 mask 0xffffffff
>>>  RTNETLINK answers: File exists
>> With this scenario, we would find the policy with mark and mask 0 first.
>> This policy passes the mark check too. So we would use this policy if the
>> flow matches the selectors, but the flow asked for a policy with mark 1.
> 
> I think the intent Romain is expressing is reasonable and should resolved at
> insertion time(xfrm_policy_insert()).
> i.e even though the policy (such as mark=1) is inserted afterwards, at
> insertion time if it proves it is more specific and not duplicate, it should be
> inserted ahead of the mark=0.
> The runtime check will work then.

Actually, we didn't think about this problem since we work with priorities, putting the default policy (without a mark) at a minor priority than the marked one.
Your remark makes clearer the ideas behind the design of XFRM, but this leads to an interesting concern. If on policy insertion, the policy were inserted depending on the accuracy of the mark (the more the mask is specific, the more the mark must be put at the beginning of the list), how would we decide which is the more specific between these ones ?

ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000005

ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000003

Best regards
Emmanuel Thierry

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jamal Hadi Salim - Feb. 6, 2013, 2:30 p.m.
On 13-02-06 08:53 AM, Emmanuel Thierry wrote:
> Actually, we didn't think about this problem since we work with priorities, putting the default policy (without a mark) at a minor priority than the marked one.

I think priorities are the way to go in cases of ambiguity.

> Your remark makes clearer the ideas behind the design of XFRM, but this leads to an interesting concern. If on policy insertion, the policy were inserted depending on the accuracy of the mark (the more the mask is specific, the more the mark must be put at the beginning of the list), how would we decide which is the more specific between these ones ?
>
> ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000005
>
> ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000003

They look different to me, no? i.e i dont see a conflict - one has 
mark=5 and the other
has mark=3.

cheers,
jamal

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Emmanuel Thierry - Feb. 6, 2013, 2:39 p.m.
Le 6 févr. 2013 à 15:30, Jamal Hadi Salim a écrit :

> On 13-02-06 08:53 AM, Emmanuel Thierry wrote:
>> Actually, we didn't think about this problem since we work with priorities, putting the default policy (without a mark) at a minor priority than the marked one.
> 
> I think priorities are the way to go in cases of ambiguity.
> 
>> Your remark makes clearer the ideas behind the design of XFRM, but this leads to an interesting concern. If on policy insertion, the policy were inserted depending on the accuracy of the mark (the more the mask is specific, the more the mark must be put at the beginning of the list), how would we decide which is the more specific between these ones ?
>> 
>> ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000005
>> 
>> ip -6 xfrm policy add src fd00::1/128 dst fd00::2/128 dir out mark 0x00000001 mask 0x00000003
> 
> They look different to me, no? i.e i dont see a conflict - one has mark=5 and the other
> has mark=3.

I think you misread the example !
Marks are both 1, masks are different.

This case is more complex than a policy with no mark (so mark=0 and mask=0) versus a policy with an exact mark (so mark=1 and mask=0xffffffff), and i wanted to know if the algorithm would take these kind of cases into account.

Best regards
Emmanuel Thierry

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jamal Hadi Salim - Feb. 6, 2013, 3:50 p.m.
On 13-02-06 09:39 AM, Emmanuel Thierry wrote:
>

> I think you misread the example !

I did ;->

> Marks are both 1, masks are different.
>
> This case is more complex than a policy
> with no mark (so mark=0 and mask=0) versus
> a policy with an exact mark (so mark=1 and mask=0xffffffff),
> and i wanted to know if the algorithm would take these kind of cases into account.
>

Aha. I think this is pushing the envelope a little - are there good use 
cases for this?
certainly you could insert with most exact mask first. No such check is 
made at the moment.

cheers,
jamal
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 63445ed..cdd79b7 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -1529,12 +1529,14 @@  extern int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
 	int (*func)(struct xfrm_policy *, int, int, void*), void *);
 extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
-struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net,
+					  struct xfrm_mark *mark,
 					  u8 type, int dir,
 					  struct xfrm_selector *sel,
 					  struct xfrm_sec_ctx *ctx, int delete,
 					  int *err);
-struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err);
+struct xfrm_policy *xfrm_policy_byid(struct net *net, struct xfrm_mark *mark,
+				     u8, int dir, u32 id, int delete, int *err);
 int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info);
 u32 xfrm_get_acqseq(void);
 extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 07c5857..d9fe665 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -569,7 +569,6 @@  int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 	struct xfrm_policy *delpol;
 	struct hlist_head *chain;
 	struct hlist_node *entry, *newpos;
-	u32 mark = policy->mark.v & policy->mark.m;
 
 	write_lock_bh(&xfrm_policy_lock);
 	chain = policy_hash_bysel(net, &policy->selector, policy->family, dir);
@@ -578,7 +577,8 @@  int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 	hlist_for_each_entry(pol, entry, chain, bydst) {
 		if (pol->type == policy->type &&
 		    !selector_cmp(&pol->selector, &policy->selector) &&
-		    (mark & pol->mark.m) == pol->mark.v &&
+		    policy->mark.v == pol->mark.v &&
+		    policy->mark.m == pol->mark.m &&
 		    xfrm_sec_ctx_match(pol->security, policy->security) &&
 		    !WARN_ON(delpol)) {
 			if (excl) {
@@ -623,7 +623,8 @@  int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 }
 EXPORT_SYMBOL(xfrm_policy_insert);
 
-struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
+struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net,
+					  struct xfrm_mark *mark, u8 type,
 					  int dir, struct xfrm_selector *sel,
 					  struct xfrm_sec_ctx *ctx, int delete,
 					  int *err)
@@ -638,7 +639,8 @@  struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
 	ret = NULL;
 	hlist_for_each_entry(pol, entry, chain, bydst) {
 		if (pol->type == type &&
-		    (mark & pol->mark.m) == pol->mark.v &&
+		    mark->v == pol->mark.v &&
+		    mark->m == pol->mark.m &&
 		    !selector_cmp(sel, &pol->selector) &&
 		    xfrm_sec_ctx_match(ctx, pol->security)) {
 			xfrm_pol_hold(pol);
@@ -663,8 +665,9 @@  struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
 }
 EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
 
-struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
-				     int dir, u32 id, int delete, int *err)
+struct xfrm_policy *xfrm_policy_byid(struct net *net, struct xfrm_mark *mark,
+				     u8 type, int dir, u32 id,
+				     int delete, int *err)
 {
 	struct xfrm_policy *pol, *ret;
 	struct hlist_head *chain;
@@ -680,7 +683,8 @@  struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
 	ret = NULL;
 	hlist_for_each_entry(pol, entry, chain, byidx) {
 		if (pol->type == type && pol->index == id &&
-		    (mark & pol->mark.m) == pol->mark.v) {
+		    mark->v == pol->mark.v &&
+		    mark->m == pol->mark.m) {
 			xfrm_pol_hold(pol);
 			if (delete) {
 				*err = security_xfrm_policy_delete(
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index eb872b2..2d41902 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -1602,7 +1602,7 @@  static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 	struct km_event c;
 	int delete;
 	struct xfrm_mark m;
-	u32 mark = xfrm_mark_get(attrs, &m);
+	xfrm_mark_get(attrs, &m);
 
 	p = nlmsg_data(nlh);
 	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
@@ -1616,7 +1616,8 @@  static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 		return err;
 
 	if (p->index)
-		xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err);
+		xp = xfrm_policy_byid(net, &m, type, p->dir,
+				      p->index, delete, &err);
 	else {
 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
 		struct xfrm_sec_ctx *ctx;
@@ -1633,7 +1634,7 @@  static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 			if (err)
 				return err;
 		}
-		xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel,
+		xp = xfrm_policy_bysel_ctx(net, &m, type, p->dir, &p->sel,
 					   ctx, delete, &err);
 		security_xfrm_policy_free(ctx);
 	}
@@ -1905,7 +1906,7 @@  static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err = -ENOENT;
 	struct xfrm_mark m;
-	u32 mark = xfrm_mark_get(attrs, &m);
+	xfrm_mark_get(attrs, &m);
 
 	err = copy_from_user_policy_type(&type, attrs);
 	if (err)
@@ -1916,7 +1917,7 @@  static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 		return err;
 
 	if (p->index)
-		xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err);
+		xp = xfrm_policy_byid(net, &m, type, p->dir, p->index, 0, &err);
 	else {
 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
 		struct xfrm_sec_ctx *ctx;
@@ -1933,7 +1934,7 @@  static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 			if (err)
 				return err;
 		}
-		xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir,
+		xp = xfrm_policy_bysel_ctx(net, &m, type, p->dir,
 					   &p->sel, ctx, 0, &err);
 		security_xfrm_policy_free(ctx);
 	}