Skip to content

Commit

Permalink
ipv4: Make icmp route lookup code a bit clearer.
Browse files Browse the repository at this point in the history
The route lookup code in icmp_send() is slightly tricky as a result of
having to handle all of the requirements of RFC 4301 host relookups.

Pull the route resolution into a seperate function, so that the error
handling and route reference counting is hopefully easier to see and
contained wholly within this new routine.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Mar 1, 2011
1 parent 2774c13 commit f6d460c
Showing 1 changed file with 96 additions and 79 deletions.
175 changes: 96 additions & 79 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,98 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
icmp_xmit_unlock(sk);
}

static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in,
struct iphdr *iph,
__be32 saddr, u8 tos,
int type, int code,
struct icmp_bxm *param)
{
struct flowi fl = {
.fl4_dst = (param->replyopts.srr ?
param->replyopts.faddr : iph->saddr),
.fl4_src = saddr,
.fl4_tos = RT_TOS(tos),
.proto = IPPROTO_ICMP,
.fl_icmp_type = type,
.fl_icmp_code = code,
};
struct rtable *rt, *rt2;
int err;

security_skb_classify_flow(skb_in, &fl);
err = __ip_route_output_key(net, &rt, &fl);
if (err)
return ERR_PTR(err);

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

if (!fl.fl4_src)
fl.fl4_src = rt->rt_src;

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

err = xfrm_decode_session_reverse(skb_in, &fl, AF_INET);
if (err)
goto relookup_failed;

if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) {
err = __ip_route_output_key(net, &rt2, &fl);
} else {
struct flowi fl2 = {};
unsigned long orefdst;

fl2.fl4_dst = fl.fl4_src;
err = ip_route_output_key(net, &rt2, &fl2);
if (err)
goto relookup_failed;
/* Ugh! */
orefdst = skb_in->_skb_refdst; /* save old refdst */
err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
RT_TOS(tos), rt2->dst.dev);

dst_release(&rt2->dst);
rt2 = skb_rtable(skb_in);
skb_in->_skb_refdst = orefdst; /* restore old refdst */
}

if (err)
goto relookup_failed;

err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
switch (err) {
case 0:
dst_release(&rt->dst);
rt = rt2;
break;
case -EPERM:
return ERR_PTR(err);
default:
if (!rt)
return ERR_PTR(err);
break;
}


return rt;

relookup_failed:
if (rt)
return rt;
return ERR_PTR(err);
}

/*
* Send an ICMP message in response to a situation
Expand Down Expand Up @@ -506,86 +598,11 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info)
ipc.opt = &icmp_param.replyopts;
ipc.tx_flags = 0;

{
struct flowi fl = {
.fl4_dst = icmp_param.replyopts.srr ?
icmp_param.replyopts.faddr : iph->saddr,
.fl4_src = saddr,
.fl4_tos = RT_TOS(tos),
.proto = IPPROTO_ICMP,
.fl_icmp_type = type,
.fl_icmp_code = code,
};
int err;
struct rtable *rt2;

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

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

if (!fl.nl_u.ip4_u.saddr)
fl.nl_u.ip4_u.saddr = rt->rt_src;

err = xfrm_lookup(net, (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 relookup_failed;

if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL)
err = __ip_route_output_key(net, &rt2, &fl);
else {
struct flowi fl2 = {};
unsigned long orefdst;

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

/* Ugh! */
orefdst = skb_in->_skb_refdst; /* save old refdst */
err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src,
RT_TOS(tos), rt2->dst.dev);

dst_release(&rt2->dst);
rt2 = skb_rtable(skb_in);
skb_in->_skb_refdst = orefdst; /* restore old refdst */
}

if (err)
goto relookup_failed;

err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL,
XFRM_LOOKUP_ICMP);
switch (err) {
case 0:
dst_release(&rt->dst);
rt = rt2;
break;
case -EPERM:
goto ende;
default:
relookup_failed:
if (!rt)
goto out_unlock;
break;
}
}
rt = icmp_route_lookup(net, skb_in, iph, saddr, tos,
type, code, &icmp_param);
if (IS_ERR(rt))
goto out_unlock;

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

Expand Down

0 comments on commit f6d460c

Please sign in to comment.