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:
parent
2774c131b1
commit
f6d460cf0e
175
net/ipv4/icmp.c
175
net/ipv4/icmp.c
@ -369,6 +369,98 @@ out_unlock:
|
|||||||
icmp_xmit_unlock(sk);
|
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
|
* 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.opt = &icmp_param.replyopts;
|
||||||
ipc.tx_flags = 0;
|
ipc.tx_flags = 0;
|
||||||
|
|
||||||
{
|
rt = icmp_route_lookup(net, skb_in, iph, saddr, tos,
|
||||||
struct flowi fl = {
|
type, code, &icmp_param);
|
||||||
.fl4_dst = icmp_param.replyopts.srr ?
|
if (IS_ERR(rt))
|
||||||
icmp_param.replyopts.faddr : iph->saddr,
|
goto out_unlock;
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
route_done:
|
|
||||||
if (!icmpv4_xrlim_allow(net, rt, type, code))
|
if (!icmpv4_xrlim_allow(net, rt, type, code))
|
||||||
goto ende;
|
goto ende;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user