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:
Andreas Roeseler 2021-06-26 09:07:46 -05:00 committed by David S. Miller
parent 83300c69e7
commit 1fd07f33c3
3 changed files with 60 additions and 25 deletions

View File

@ -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 */

View File

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

View File

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