mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
ipv6: ICMPV6: add response to ICMPV6 RFC 8335 PROBE messages
This patch builds off of commit 2b246b2569
and adds functionality to respond to ICMPV6 PROBE requests.
Add icmp_build_probe function to construct PROBE requests for both
ICMPV4 and ICMPV6.
Modify icmpv6_rcv to detect ICMPV6 PROBE messages and call the
icmpv6_echo_reply handler.
Modify icmpv6_echo_reply to build a PROBE response message based on the
queried interface.
This patch has been tested using a branch of the iputils git repo which can
be found here: https://github.com/Juniper-Clinic-2020/iputils/tree/probe-request
Signed-off-by: Andreas Roeseler <andreas.a.roeseler@gmail.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
83300c69e7
commit
1fd07f33c3
@ -57,5 +57,6 @@ int icmp_rcv(struct sk_buff *skb);
|
|||||||
int icmp_err(struct sk_buff *skb, u32 info);
|
int icmp_err(struct sk_buff *skb, u32 info);
|
||||||
int icmp_init(void);
|
int icmp_init(void);
|
||||||
void icmp_out_count(struct net *net, unsigned char type);
|
void icmp_out_count(struct net *net, unsigned char type);
|
||||||
|
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr);
|
||||||
|
|
||||||
#endif /* _ICMP_H */
|
#endif /* _ICMP_H */
|
||||||
|
@ -993,14 +993,8 @@ static bool icmp_redirect(struct sk_buff *skb)
|
|||||||
|
|
||||||
static bool icmp_echo(struct sk_buff *skb)
|
static bool icmp_echo(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct icmp_ext_hdr *ext_hdr, _ext_hdr;
|
|
||||||
struct icmp_ext_echo_iio *iio, _iio;
|
|
||||||
struct icmp_bxm icmp_param;
|
struct icmp_bxm icmp_param;
|
||||||
struct net_device *dev;
|
|
||||||
char buff[IFNAMSIZ];
|
|
||||||
struct net *net;
|
struct net *net;
|
||||||
u16 ident_len;
|
|
||||||
u8 status;
|
|
||||||
|
|
||||||
net = dev_net(skb_dst(skb)->dev);
|
net = dev_net(skb_dst(skb)->dev);
|
||||||
/* should there be an ICMP stat for ignored echos? */
|
/* should there be an ICMP stat for ignored echos? */
|
||||||
@ -1013,20 +1007,46 @@ static bool icmp_echo(struct sk_buff *skb)
|
|||||||
icmp_param.data_len = skb->len;
|
icmp_param.data_len = skb->len;
|
||||||
icmp_param.head_len = sizeof(struct icmphdr);
|
icmp_param.head_len = sizeof(struct icmphdr);
|
||||||
|
|
||||||
if (icmp_param.data.icmph.type == ICMP_ECHO) {
|
if (icmp_param.data.icmph.type == ICMP_ECHO)
|
||||||
icmp_param.data.icmph.type = ICMP_ECHOREPLY;
|
icmp_param.data.icmph.type = ICMP_ECHOREPLY;
|
||||||
goto send_reply;
|
else if (!icmp_build_probe(skb, &icmp_param.data.icmph))
|
||||||
}
|
|
||||||
if (!net->ipv4.sysctl_icmp_echo_enable_probe)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
icmp_reply(&icmp_param, skb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper for icmp_echo and icmpv6_echo_reply.
|
||||||
|
* Searches for net_device that matches PROBE interface identifier
|
||||||
|
* and builds PROBE reply message in icmphdr.
|
||||||
|
*
|
||||||
|
* Returns false if PROBE responses are disabled via sysctl
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
|
||||||
|
{
|
||||||
|
struct icmp_ext_hdr *ext_hdr, _ext_hdr;
|
||||||
|
struct icmp_ext_echo_iio *iio, _iio;
|
||||||
|
struct net *net = dev_net(skb->dev);
|
||||||
|
struct net_device *dev;
|
||||||
|
char buff[IFNAMSIZ];
|
||||||
|
u16 ident_len;
|
||||||
|
u8 status;
|
||||||
|
|
||||||
|
if (!net->ipv4.sysctl_icmp_echo_enable_probe)
|
||||||
|
return false;
|
||||||
|
|
||||||
/* We currently only support probing interfaces on the proxy node
|
/* We currently only support probing interfaces on the proxy node
|
||||||
* Check to ensure L-bit is set
|
* Check to ensure L-bit is set
|
||||||
*/
|
*/
|
||||||
if (!(ntohs(icmp_param.data.icmph.un.echo.sequence) & 1))
|
if (!(ntohs(icmphdr->un.echo.sequence) & 1))
|
||||||
return true;
|
return false;
|
||||||
/* Clear status bits in reply message */
|
/* Clear status bits in reply message */
|
||||||
icmp_param.data.icmph.un.echo.sequence &= htons(0xFF00);
|
icmphdr->un.echo.sequence &= htons(0xFF00);
|
||||||
icmp_param.data.icmph.type = ICMP_EXT_ECHOREPLY;
|
if (icmphdr->type == ICMP_EXT_ECHO)
|
||||||
|
icmphdr->type = ICMP_EXT_ECHOREPLY;
|
||||||
|
else
|
||||||
|
icmphdr->type = ICMPV6_EXT_ECHO_REPLY;
|
||||||
ext_hdr = skb_header_pointer(skb, 0, sizeof(_ext_hdr), &_ext_hdr);
|
ext_hdr = skb_header_pointer(skb, 0, sizeof(_ext_hdr), &_ext_hdr);
|
||||||
/* Size of iio is class_type dependent.
|
/* Size of iio is class_type dependent.
|
||||||
* Only check header here and assign length based on ctype in the switch statement
|
* Only check header here and assign length based on ctype in the switch statement
|
||||||
@ -1087,8 +1107,8 @@ static bool icmp_echo(struct sk_buff *skb)
|
|||||||
goto send_mal_query;
|
goto send_mal_query;
|
||||||
}
|
}
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
icmp_param.data.icmph.code = ICMP_EXT_CODE_NO_IF;
|
icmphdr->code = ICMP_EXT_CODE_NO_IF;
|
||||||
goto send_reply;
|
return true;
|
||||||
}
|
}
|
||||||
/* Fill bits in reply message */
|
/* Fill bits in reply message */
|
||||||
if (dev->flags & IFF_UP)
|
if (dev->flags & IFF_UP)
|
||||||
@ -1098,14 +1118,13 @@ static bool icmp_echo(struct sk_buff *skb)
|
|||||||
if (!list_empty(&rcu_dereference(dev->ip6_ptr)->addr_list))
|
if (!list_empty(&rcu_dereference(dev->ip6_ptr)->addr_list))
|
||||||
status |= ICMP_EXT_ECHOREPLY_IPV6;
|
status |= ICMP_EXT_ECHOREPLY_IPV6;
|
||||||
dev_put(dev);
|
dev_put(dev);
|
||||||
icmp_param.data.icmph.un.echo.sequence |= htons(status);
|
icmphdr->un.echo.sequence |= htons(status);
|
||||||
send_reply:
|
return true;
|
||||||
icmp_reply(&icmp_param, skb);
|
|
||||||
return true;
|
|
||||||
send_mal_query:
|
send_mal_query:
|
||||||
icmp_param.data.icmph.code = ICMP_EXT_CODE_MAL_QUERY;
|
icmphdr->code = ICMP_EXT_CODE_MAL_QUERY;
|
||||||
goto send_reply;
|
return true;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(icmp_build_probe);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle ICMP Timestamp requests.
|
* Handle ICMP Timestamp requests.
|
||||||
|
@ -725,6 +725,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
struct ipcm6_cookie ipc6;
|
struct ipcm6_cookie ipc6;
|
||||||
u32 mark = IP6_REPLY_MARK(net, skb->mark);
|
u32 mark = IP6_REPLY_MARK(net, skb->mark);
|
||||||
bool acast;
|
bool acast;
|
||||||
|
u8 type;
|
||||||
|
|
||||||
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
|
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
|
||||||
net->ipv6.sysctl.icmpv6_echo_ignore_multicast)
|
net->ipv6.sysctl.icmpv6_echo_ignore_multicast)
|
||||||
@ -740,8 +741,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
!(net->ipv6.sysctl.anycast_src_echo_reply && acast))
|
!(net->ipv6.sysctl.anycast_src_echo_reply && acast))
|
||||||
saddr = NULL;
|
saddr = NULL;
|
||||||
|
|
||||||
|
if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST)
|
||||||
|
type = ICMPV6_EXT_ECHO_REPLY;
|
||||||
|
else
|
||||||
|
type = ICMPV6_ECHO_REPLY;
|
||||||
|
|
||||||
memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr));
|
memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr));
|
||||||
tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;
|
tmp_hdr.icmp6_type = type;
|
||||||
|
|
||||||
memset(&fl6, 0, sizeof(fl6));
|
memset(&fl6, 0, sizeof(fl6));
|
||||||
if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES)
|
if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES)
|
||||||
@ -752,7 +758,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
if (saddr)
|
if (saddr)
|
||||||
fl6.saddr = *saddr;
|
fl6.saddr = *saddr;
|
||||||
fl6.flowi6_oif = icmp6_iif(skb);
|
fl6.flowi6_oif = icmp6_iif(skb);
|
||||||
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
|
fl6.fl6_icmp_type = type;
|
||||||
fl6.flowi6_mark = mark;
|
fl6.flowi6_mark = mark;
|
||||||
fl6.flowi6_uid = sock_net_uid(net, NULL);
|
fl6.flowi6_uid = sock_net_uid(net, NULL);
|
||||||
security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6));
|
security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6));
|
||||||
@ -783,13 +789,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
|
|||||||
|
|
||||||
msg.skb = skb;
|
msg.skb = skb;
|
||||||
msg.offset = 0;
|
msg.offset = 0;
|
||||||
msg.type = ICMPV6_ECHO_REPLY;
|
msg.type = type;
|
||||||
|
|
||||||
ipcm6_init_sk(&ipc6, np);
|
ipcm6_init_sk(&ipc6, np);
|
||||||
ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
|
ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
|
||||||
ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb));
|
ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb));
|
||||||
ipc6.sockc.mark = mark;
|
ipc6.sockc.mark = mark;
|
||||||
|
|
||||||
|
if (icmph->icmp6_type == ICMPV6_EXT_ECHO_REQUEST)
|
||||||
|
if (!icmp_build_probe(skb, (struct icmphdr *)&tmp_hdr))
|
||||||
|
goto out_dst_release;
|
||||||
|
|
||||||
if (ip6_append_data(sk, icmpv6_getfrag, &msg,
|
if (ip6_append_data(sk, icmpv6_getfrag, &msg,
|
||||||
skb->len + sizeof(struct icmp6hdr),
|
skb->len + sizeof(struct icmp6hdr),
|
||||||
sizeof(struct icmp6hdr), &ipc6, &fl6,
|
sizeof(struct icmp6hdr), &ipc6, &fl6,
|
||||||
@ -911,6 +921,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
|
|||||||
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all)
|
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all)
|
||||||
icmpv6_echo_reply(skb);
|
icmpv6_echo_reply(skb);
|
||||||
break;
|
break;
|
||||||
|
case ICMPV6_EXT_ECHO_REQUEST:
|
||||||
|
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all &&
|
||||||
|
net->ipv4.sysctl_icmp_echo_enable_probe)
|
||||||
|
icmpv6_echo_reply(skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case ICMPV6_ECHO_REPLY:
|
case ICMPV6_ECHO_REPLY:
|
||||||
success = ping_rcv(skb);
|
success = ping_rcv(skb);
|
||||||
|
Loading…
Reference in New Issue
Block a user