ipv4: Make icmp route lookup code a bit clearer.

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>
This commit is contained in:
David S. Miller 2011-03-01 15:49:55 -08:00
parent 2774c131b1
commit f6d460cf0e
1 changed files with 96 additions and 79 deletions

View File

@ -369,6 +369,98 @@ out_unlock:
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
@ -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;
rt = icmp_route_lookup(net, skb_in, iph, saddr, tos,
type, code, &icmp_param);
if (IS_ERR(rt))
goto out_unlock;
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;
}
}
route_done:
if (!icmpv4_xrlim_allow(net, rt, type, code))
goto ende;