|
@@ -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;
|
|
|
-
|
|
|
- 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;
|
|
|
|