Skip to content

Commit

Permalink
[IPSEC]: Add ICMP host relookup support
Browse files Browse the repository at this point in the history
RFC 4301 requires us to relookup ICMP traffic that does not match any
policies using the reverse of its payload.  This patch implements this
for ICMP traffic that originates from or terminates on localhost.

This is activated on outbound with the new policy flag XFRM_POLICY_ICMP,
and on inbound by the new state flag XFRM_STATE_ICMP.

On inbound the policy check is now performed by the ICMP protocol so
that it can repeat the policy check where necessary.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Herbert Xu authored and David S. Miller committed Jan 28, 2008
1 parent d5422ef commit 8b7817f
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 10 deletions.
3 changes: 3 additions & 0 deletions include/linux/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ struct xfrm_usersa_info {
#define XFRM_STATE_DECAP_DSCP 2
#define XFRM_STATE_NOPMTUDISC 4
#define XFRM_STATE_WILDRECV 8
#define XFRM_STATE_ICMP 16
};

struct xfrm_usersa_id {
Expand Down Expand Up @@ -363,6 +364,8 @@ struct xfrm_userpolicy_info {
#define XFRM_POLICY_BLOCK 1
__u8 flags;
#define XFRM_POLICY_LOCALOK 1 /* Allow user to override global policy */
/* Automatically expand selector to include matching ICMP payloads. */
#define XFRM_POLICY_ICMP 2
__u8 share;
};

Expand Down
1 change: 1 addition & 0 deletions include/net/dst.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ extern void dst_init(void);
/* Flags for xfrm_lookup flags argument. */
enum {
XFRM_LOOKUP_WAIT = 1 << 0,
XFRM_LOOKUP_ICMP = 1 << 1,
};

struct flowi;
Expand Down
1 change: 1 addition & 0 deletions net/ipv4/af_inet.c
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,7 @@ static struct net_protocol udp_protocol = {

static struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.no_policy = 1,
};

static int __init init_ipv4_mibs(void)
Expand Down
82 changes: 79 additions & 3 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <net/checksum.h>
#include <net/xfrm.h>

/*
* Build xmit assembly blocks
Expand Down Expand Up @@ -563,11 +564,71 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
}
}
};
int err;
struct rtable *rt2;

security_skb_classify_flow(skb_in, &fl);
if (ip_route_output_key(&rt, &fl))
if (__ip_route_output_key(&rt, &fl))
goto out_unlock;

/* No need to clone since we're just using its address. */
rt2 = rt;

err = xfrm_lookup((struct dst_entry **)&rt, &fl, NULL, 0);
switch (err) {
case 0:
if (rt != rt2)
goto route_done;
break;
case -EPERM:
rt = NULL;
break;
default:
goto out_unlock;
}

if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET))
goto out_unlock;

if (inet_addr_type(fl.fl4_src) == RTN_LOCAL)
err = __ip_route_output_key(&rt2, &fl);
else {
struct flowi fl2 = {};
struct dst_entry *odst;

fl2.fl4_dst = fl.fl4_src;
if (ip_route_output_key(&rt2, &fl2))
goto out_unlock;

/* Ugh! */
odst = skb_in->dst;
err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
RT_TOS(tos), rt2->u.dst.dev);

dst_release(&rt2->u.dst);
rt2 = (struct rtable *)skb_in->dst;
skb_in->dst = odst;
}

if (err)
goto out_unlock;

err = xfrm_lookup((struct dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
if (err == -ENOENT) {
if (!rt)
goto out_unlock;
goto route_done;
}

dst_release(&rt->u.dst);
rt = rt2;

if (err)
goto out_unlock;
}

route_done:
if (!icmpv4_xrlim_allow(rt, type, code))
goto ende;

Expand Down Expand Up @@ -916,6 +977,22 @@ int icmp_rcv(struct sk_buff *skb)
struct icmphdr *icmph;
struct rtable *rt = (struct rtable *)skb->dst;

if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb) &&
skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
int nh;

if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
goto drop;

nh = skb_network_offset(skb);
skb_set_network_header(skb, sizeof(*icmph));

if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
goto drop;

skb_set_network_header(skb, nh);
}

ICMP_INC_STATS_BH(ICMP_MIB_INMSGS);

switch (skb->ip_summed) {
Expand All @@ -929,8 +1006,7 @@ int icmp_rcv(struct sk_buff *skb)
goto error;
}

if (!pskb_pull(skb, sizeof(struct icmphdr)))
goto error;
__skb_pull(skb, sizeof(*icmph));

icmph = icmp_hdr(skb);

Expand Down
60 changes: 56 additions & 4 deletions net/ipv6/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/icmp.h>
#include <net/xfrm.h>

#include <asm/uaccess.h>
#include <asm/system.h>
Expand All @@ -86,7 +87,7 @@ static int icmpv6_rcv(struct sk_buff *skb);

static struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
.flags = INET6_PROTO_FINAL,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};

static __inline__ int icmpv6_xmit_lock(void)
Expand Down Expand Up @@ -310,8 +311,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct ipv6_pinfo *np;
struct in6_addr *saddr = NULL;
struct dst_entry *dst;
struct dst_entry *dst2;
struct icmp6hdr tmp_hdr;
struct flowi fl;
struct flowi fl2;
struct icmpv6_msg msg;
int iif = 0;
int addr_type = 0;
Expand Down Expand Up @@ -418,9 +421,42 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
goto out_dst_release;
}

if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
/* No need to clone since we're just using its address. */
dst2 = dst;

err = xfrm_lookup(&dst, &fl, sk, 0);
switch (err) {
case 0:
if (dst != dst2)
goto route_done;
break;
case -EPERM:
dst = NULL;
break;
default:
goto out;
}

if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6))
goto out;

if (ip6_dst_lookup(sk, &dst2, &fl))
goto out;

err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP);
if (err == -ENOENT) {
if (!dst)
goto out;
goto route_done;
}

dst_release(dst);
dst = dst2;

if (err)
goto out;

route_done:
if (ipv6_addr_is_multicast(&fl.fl6_dst))
hlimit = np->mcast_hops;
else
Expand Down Expand Up @@ -608,6 +644,22 @@ static int icmpv6_rcv(struct sk_buff *skb)
struct icmp6hdr *hdr;
int type;

if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb) &&
skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) {
int nh;

if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr)))
goto drop_no_count;

nh = skb_network_offset(skb);
skb_set_network_header(skb, sizeof(*hdr));

if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb))
goto drop_no_count;

skb_set_network_header(skb, nh);
}

ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS);

saddr = &ipv6_hdr(skb)->saddr;
Expand All @@ -630,8 +682,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
}
}

if (!pskb_pull(skb, sizeof(struct icmp6hdr)))
goto discard_it;
__skb_pull(skb, sizeof(*hdr));

hdr = icmp6_hdr(skb);

Expand Down Expand Up @@ -717,6 +768,7 @@ static int icmpv6_rcv(struct sk_buff *skb)

discard_it:
ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);
drop_no_count:
kfree_skb(skb);
return 0;
}
Expand Down
17 changes: 14 additions & 3 deletions net/xfrm/xfrm_policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1469,11 +1469,13 @@ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
goto dropdst;
}

err = -ENOENT;

if (!policy) {
/* To accelerate a bit... */
if ((dst_orig->flags & DST_NOXFRM) ||
!xfrm_policy_count[XFRM_POLICY_OUT])
return 0;
goto nopol;

policy = flow_cache_lookup(fl, dst_orig->ops->family,
dir, xfrm_policy_lookup);
Expand All @@ -1483,14 +1485,18 @@ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
}

if (!policy)
return 0;
goto nopol;

family = dst_orig->ops->family;
policy->curlft.use_time = get_seconds();
pols[0] = policy;
npols ++;
xfrm_nr += pols[0]->xfrm_nr;

if ((flags & XFRM_LOOKUP_ICMP) && !(policy->flags & XFRM_POLICY_ICMP))
goto error;

policy->curlft.use_time = get_seconds();

switch (policy->action) {
default:
case XFRM_POLICY_BLOCK:
Expand Down Expand Up @@ -1649,6 +1655,11 @@ int __xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
dst_release(dst_orig);
*dst_p = NULL;
return err;

nopol:
if (flags & XFRM_LOOKUP_ICMP)
goto dropdst;
return 0;
}
EXPORT_SYMBOL(__xfrm_lookup);

Expand Down

0 comments on commit 8b7817f

Please sign in to comment.