Commit eba75c58 authored by Willem de Bruijn's avatar Willem de Bruijn Committed by David S. Miller
Browse files

icmp: support rfc 4884



Add setsockopt SOL_IP/IP_RECVERR_4884 to return the offset to an
extension struct if present.

ICMP messages may include an extension structure after the original
datagram. RFC 4884 standardized this behavior. It stores the offset
in words to the extension header in u8 icmphdr.un.reserved[1].

The field is valid only for ICMP types destination unreachable, time
exceeded and parameter problem, if length is at least 128 bytes and
entire packet does not exceed 576 bytes.

Return the offset to the start of the extension struct when reading an
ICMP error from the error queue, if it matches the above constraints.

Do not return the raw u8 field. Return the offset from the start of
the user buffer, in bytes. The kernel does not return the network and
transport headers, so subtract those.

Also validate the headers. Return the offset regardless of validation,
as an invalid extension must still not be misinterpreted as part of
the original datagram. Note that !invalid does not imply valid. If
the extension version does not match, no validation can take place,
for instance.

For backward compatibility, make this optional, set by setsockopt
SOL_IP/IP_RECVERR_RFC4884. For API example and feature test, see
github.com/wdebruij/kerneltools/blob/master/tests/recv_icmp_v2.c

For forward compatibility, reserve only setsockopt value 1, leaving
other bits for additional icmp extensions.

Changes
  v1->v2:
  - convert word offset to byte offset from start of user buffer
    - return in ee_data as u8 may be insufficient
  - define extension struct and object header structs
  - return len only if constraints met
  - if returning len, also validate

Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 930bc4cc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include <linux/skbuff.h>
#include <uapi/linux/icmp.h>
#include <uapi/linux/errqueue.h>

static inline struct icmphdr *icmp_hdr(const struct sk_buff *skb)
{
@@ -35,4 +36,7 @@ static inline bool icmp_is_err(int type)
	return false;
}

void ip_icmp_error_rfc4884(const struct sk_buff *skb,
			   struct sock_ee_data_rfc4884 *out);

#endif	/* _LINUX_ICMP_H */
+1 −0
Original line number Diff line number Diff line
@@ -225,6 +225,7 @@ struct inet_sock {
				mc_all:1,
				nodefrag:1;
	__u8			bind_address_no_port:1,
				recverr_rfc4884:1,
				defer_connect:1; /* Indicates that fastopen_connect is set
						  * and cookie exists so we defer connect
						  * until first data frame is written
+13 −1
Original line number Diff line number Diff line
@@ -5,6 +5,13 @@
#include <linux/types.h>
#include <linux/time_types.h>

/* RFC 4884: return offset to extension struct + validation */
struct sock_ee_data_rfc4884 {
	__u16	len;
	__u8	flags;
	__u8	reserved;
};

struct sock_extended_err {
	__u32	ee_errno;	
	__u8	ee_origin;
@@ -12,7 +19,10 @@ struct sock_extended_err {
	__u8	ee_code;
	__u8	ee_pad;
	__u32   ee_info;
	union	{
		__u32   ee_data;
		struct sock_ee_data_rfc4884 ee_rfc4884;
	};
};

#define SO_EE_ORIGIN_NONE	0
@@ -31,6 +41,8 @@ struct sock_extended_err {
#define SO_EE_CODE_TXTIME_INVALID_PARAM	1
#define SO_EE_CODE_TXTIME_MISSED	2

#define SO_EE_RFC4884_FLAG_INVALID	1

/**
 *	struct scm_timestamping - timestamps exposed through cmsg
 *
+22 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#define _UAPI_LINUX_ICMP_H

#include <linux/types.h>
#include <asm/byteorder.h>

#define ICMP_ECHOREPLY		0	/* Echo Reply			*/
#define ICMP_DEST_UNREACH	3	/* Destination Unreachable	*/
@@ -95,5 +96,26 @@ struct icmp_filter {
	__u32		data;
};

/* RFC 4884 extension struct: one per message */
struct icmp_ext_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8		reserved1:4,
			version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
	__u8		version:4,
			reserved1:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8		reserved2;
	__sum16		checksum;
};

/* RFC 4884 extension object header: one for each object */
struct icmp_extobj_hdr {
	__be16		length;
	__u8		class_num;
	__u8		class_type;
};

#endif /* _UAPI_LINUX_ICMP_H */
+1 −0
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ struct in_addr {
#define IP_CHECKSUM	23
#define IP_BIND_ADDRESS_NO_PORT	24
#define IP_RECVFRAGSIZE	25
#define IP_RECVERR_RFC4884	26

/* IP_MTU_DISCOVER values */
#define IP_PMTUDISC_DONT		0	/* Never send DF frames */
Loading