Skip to content

Commit

Permalink
ipv6: ICMPV6: add response to ICMPV6 RFC 8335 PROBE messages
Browse files Browse the repository at this point in the history
This patch builds off of commit 2b246b2
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 <[email protected]>
Reviewed-by: Willem de Bruijn <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
aroeseler authored and davem330 committed Jun 28, 2021
1 parent 83300c6 commit 1fd07f3
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 25 deletions.
1 change: 1 addition & 0 deletions include/net/icmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,6 @@ int icmp_rcv(struct sk_buff *skb);
int icmp_err(struct sk_buff *skb, u32 info);
int icmp_init(void);
void icmp_out_count(struct net *net, unsigned char type);
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr);

#endif /* _ICMP_H */
63 changes: 41 additions & 22 deletions net/ipv4/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -993,14 +993,8 @@ static bool icmp_redirect(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 net_device *dev;
char buff[IFNAMSIZ];
struct net *net;
u16 ident_len;
u8 status;

net = dev_net(skb_dst(skb)->dev);
/* should there be an ICMP stat for ignored echos? */
Expand All @@ -1013,20 +1007,46 @@ static bool icmp_echo(struct sk_buff *skb)
icmp_param.data_len = skb->len;
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;
goto send_reply;
}
if (!net->ipv4.sysctl_icmp_echo_enable_probe)
else if (!icmp_build_probe(skb, &icmp_param.data.icmph))
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
* Check to ensure L-bit is set
*/
if (!(ntohs(icmp_param.data.icmph.un.echo.sequence) & 1))
return true;
if (!(ntohs(icmphdr->un.echo.sequence) & 1))
return false;
/* Clear status bits in reply message */
icmp_param.data.icmph.un.echo.sequence &= htons(0xFF00);
icmp_param.data.icmph.type = ICMP_EXT_ECHOREPLY;
icmphdr->un.echo.sequence &= htons(0xFF00);
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);
/* Size of iio is class_type dependent.
* Only check header here and assign length based on ctype in the switch statement
Expand Down Expand Up @@ -1087,8 +1107,8 @@ static bool icmp_echo(struct sk_buff *skb)
goto send_mal_query;
}
if (!dev) {
icmp_param.data.icmph.code = ICMP_EXT_CODE_NO_IF;
goto send_reply;
icmphdr->code = ICMP_EXT_CODE_NO_IF;
return true;
}
/* Fill bits in reply message */
if (dev->flags & IFF_UP)
Expand All @@ -1098,14 +1118,13 @@ static bool icmp_echo(struct sk_buff *skb)
if (!list_empty(&rcu_dereference(dev->ip6_ptr)->addr_list))
status |= ICMP_EXT_ECHOREPLY_IPV6;
dev_put(dev);
icmp_param.data.icmph.un.echo.sequence |= htons(status);
send_reply:
icmp_reply(&icmp_param, skb);
return true;
icmphdr->un.echo.sequence |= htons(status);
return true;
send_mal_query:
icmp_param.data.icmph.code = ICMP_EXT_CODE_MAL_QUERY;
goto send_reply;
icmphdr->code = ICMP_EXT_CODE_MAL_QUERY;
return true;
}
EXPORT_SYMBOL_GPL(icmp_build_probe);

/*
* Handle ICMP Timestamp requests.
Expand Down
21 changes: 18 additions & 3 deletions net/ipv6/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
struct ipcm6_cookie ipc6;
u32 mark = IP6_REPLY_MARK(net, skb->mark);
bool acast;
u8 type;

if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
net->ipv6.sysctl.icmpv6_echo_ignore_multicast)
Expand All @@ -740,8 +741,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
!(net->ipv6.sysctl.anycast_src_echo_reply && acast))
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));
tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;
tmp_hdr.icmp6_type = type;

memset(&fl6, 0, sizeof(fl6));
if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES)
Expand All @@ -752,7 +758,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (saddr)
fl6.saddr = *saddr;
fl6.flowi6_oif = icmp6_iif(skb);
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.fl6_icmp_type = type;
fl6.flowi6_mark = mark;
fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi_common(&fl6));
Expand Down Expand Up @@ -783,13 +789,17 @@ static void icmpv6_echo_reply(struct sk_buff *skb)

msg.skb = skb;
msg.offset = 0;
msg.type = ICMPV6_ECHO_REPLY;
msg.type = type;

ipcm6_init_sk(&ipc6, np);
ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst);
ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb));
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,
skb->len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), &ipc6, &fl6,
Expand Down Expand Up @@ -911,6 +921,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
if (!net->ipv6.sysctl.icmpv6_echo_ignore_all)
icmpv6_echo_reply(skb);
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:
success = ping_rcv(skb);
Expand Down

0 comments on commit 1fd07f3

Please sign in to comment.