Commit 1fd07f33 authored by Andreas Roeseler's avatar Andreas Roeseler Committed by David S. Miller
Browse files

ipv6: ICMPV6: add response to ICMPV6 RFC 8335 PROBE messages

This patch builds off of commit 2b246b25
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: default avatarAndreas Roeseler <andreas.a.roeseler@gmail.com>
Reviewed-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 83300c69
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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 */
+41 −22
Original line number Original line Diff line number Diff line
@@ -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))
		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)
	if (!net->ipv4.sysctl_icmp_echo_enable_probe)
		return true;
		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:
	icmp_reply(&icmp_param, skb);
	return true;
	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.
+18 −3
Original line number Original line Diff line number Diff line
@@ -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);