aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMasahide NAKAMURA <nakam@linux-ipv6.org>2006-08-23 20:41:00 -0700
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 15:08:29 -0700
commitdf0ba92a99ca757039dfa84a929281ea3f7a50e8 (patch)
tree26f1f562c513ad43f813dcf2c58d426a3649625b
parent2ce4272a699c731b9736d76126dc742353e381db (diff)
[XFRM]: Trace which secpath state is reject factor.
For Mobile IPv6 usage, it is required to trace which secpath state is reject factor in order to notify it to user space (to know the address which cannot be used route optimized communication). Based on MIPL2 kernel patch. This patch was also written by: Henrik Petander <petander@tcs.hut.fi> Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/xfrm.h1
-rw-r--r--net/xfrm/xfrm_policy.c55
2 files changed, 49 insertions, 7 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 13488e7ba68..9ebbdc1dd47 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -273,6 +273,7 @@ struct xfrm_type
void (*destructor)(struct xfrm_state *);
int (*input)(struct xfrm_state *, struct sk_buff *skb);
int (*output)(struct xfrm_state *, struct sk_buff *pskb);
+ int (*reject)(struct xfrm_state *, struct sk_buff *, struct flowi *);
int (*hdr_offset)(struct xfrm_state *, struct sk_buff *, u8 **);
xfrm_address_t *(*local_addr)(struct xfrm_state *, xfrm_address_t *);
xfrm_address_t *(*remote_addr)(struct xfrm_state *, xfrm_address_t *);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index ad2a5cba1f5..d125a264903 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -988,6 +988,23 @@ error:
}
EXPORT_SYMBOL(xfrm_lookup);
+static inline int
+xfrm_secpath_reject(int idx, struct sk_buff *skb, struct flowi *fl)
+{
+ struct xfrm_state *x;
+ int err;
+
+ if (!skb->sp || idx < 0 || idx >= skb->sp->len)
+ return 0;
+ x = skb->sp->xvec[idx];
+ if (!x->type->reject)
+ return 0;
+ xfrm_state_hold(x);
+ err = x->type->reject(x, skb, fl);
+ xfrm_state_put(x);
+ return err;
+}
+
/* When skb is transformed back to its "native" form, we have to
* check policy restrictions. At the moment we make this in maximally
* stupid way. Shame on me. :-) Of course, connected sockets must
@@ -1010,6 +1027,13 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
xfrm_state_addr_cmp(tmpl, x, family));
}
+/*
+ * 0 or more than 0 is returned when validation is succeeded (either bypass
+ * because of optional transport mode, or next index of the mathced secpath
+ * state with the template.
+ * -1 is returned when no matching template is found.
+ * Otherwise "-2 - errored_index" is returned.
+ */
static inline int
xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
unsigned short family)
@@ -1024,8 +1048,11 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
for (; idx < sp->len; idx++) {
if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
return ++idx;
- if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT)
+ if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
+ if (start == -1)
+ start = -2-idx;
break;
+ }
}
return start;
}
@@ -1046,11 +1073,14 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family
}
EXPORT_SYMBOL(xfrm_decode_session);
-static inline int secpath_has_nontransport(struct sec_path *sp, int k)
+static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp)
{
for (; k < sp->len; k++) {
- if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT)
+ if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) {
+ if (idxp)
+ *idxp = k;
return 1;
+ }
}
return 0;
@@ -1062,6 +1092,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
struct xfrm_policy *pol;
struct flowi fl;
u8 fl_dir = policy_to_flow_dir(dir);
+ int xerr_idx = -1;
+ int *xerr_idxp = &xerr_idx;
if (xfrm_decode_session(skb, &fl, family) < 0)
return 0;
@@ -1086,8 +1118,13 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
pol = flow_cache_lookup(&fl, family, fl_dir,
xfrm_policy_lookup);
- if (!pol)
- return !skb->sp || !secpath_has_nontransport(skb->sp, 0);
+ if (!pol) {
+ if (skb->sp && secpath_has_nontransport(skb->sp, 0, xerr_idxp)) {
+ xfrm_secpath_reject(xerr_idx, skb, &fl);
+ return 0;
+ }
+ return 1;
+ }
pol->curlft.use_time = (unsigned long)xtime.tv_sec;
@@ -1107,11 +1144,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
*/
for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
- if (k < 0)
+ if (k < 0) {
+ if (k < -1 && xerr_idxp)
+ *xerr_idxp = -(2+k);
goto reject;
+ }
}
- if (secpath_has_nontransport(sp, k))
+ if (secpath_has_nontransport(sp, k, xerr_idxp))
goto reject;
xfrm_pol_put(pol);
@@ -1119,6 +1159,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}
reject:
+ xfrm_secpath_reject(xerr_idx, skb, &fl);
xfrm_pol_put(pol);
return 0;
}