Skip to contentSkip to conversation

net: ip, diag -- Add diag interface for raw sockets

PATCH Superseded Cyrill Gorcunov 2 participants 3 messages Sep 27, 2016

Subsystems (2)

v4 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

PATCH v4 1/1 net: ip, diag -- Add diag interface for raw sockets

In criu we are actively using diag interface to collect sockets present in the system when dumping applications. And while for unix, tcp, udp[lite], packet, netlink it works as expected, the raw sockets do not have. Thus add it.

v2:
- add missing sock_put calls in raw_diag_dump_one (by eric.dumazet@)
- implement @destroy for diag requests (by dsa@)

v3:
- add export of raw_abort for IPv6 (by dsa@)
- pass net-admin flag into inet_sk_diag_fill due to changes in net-next branch (by dsa@)

v4:
- use @pad in struct inet_diag_req_v2 for raw socket protocol specification: raw module carries sockets which may have custom protocol passed from socket() syscall and sole @sdiag_protocol is not enough to match underlied ones
- start reporting protocol specifed in socket() call when sockets are raw ones for the same reason: user space tools like ss may parse this attribute and use it for socket matching

CC David S. Miller <[email protected]>
CC Eric Dumazet <[email protected]>
CC David Ahern <[email protected]>
CC Alexey Kuznetsov <[email protected]>
CC James Morris <[email protected]>
CC Hideaki YOSHIFUJI <[email protected]>
CC Patrick McHardy <[email protected]>
CC Andrey Vagin <[email protected]>
CC Stephen Hemminger <[email protected]>

---

Guys, take a look please once time permit. I mostly worried about start reporint protocol in inet_sk_diag_fill, I think it should be safe but still.

 include/net/raw.h              |    6 +
 include/net/rawv6.h            |    7 +
 include/uapi/linux/inet_diag.h |    5 
 net/ipv4/Kconfig               |    8 +
 net/ipv4/Makefile              |    1 
 net/ipv4/inet_diag.c           |    9 +
 net/ipv4/raw.c                 |   21 +++
 net/ipv4/raw_diag.c            |  226 +++++++++++++++++++++++++++++++++++++++++
 net/ipv6/raw.c                 |    7 -
 9 files changed, 285 insertions(+), 5 deletions(-)
include/net/raw.h +6 -0
Index: linux-ml.git/include/net/raw.h
===================================================================
@@ -23,6 +23,12 @@
 
 extern struct proto raw_prot;
 
+extern struct raw_hashinfo raw_v4_hashinfo;
+struct sock *__raw_v4_lookup(struct net *net, struct sock *sk,
+			     unsigned short num, __be32 raddr,
+			     __be32 laddr, int dif);
+
+int raw_abort(struct sock *sk, int err);
 void raw_icmp_error(struct sk_buff *, int, u32);
 int raw_local_deliver(struct sk_buff *, int);
 
include/net/rawv6.h +7 -0
Index: linux-ml.git/include/net/rawv6.h
===================================================================
@@ -3,6 +3,13 @@
 
 #include <net/protocol.h>
 
+extern struct raw_hashinfo raw_v6_hashinfo;
+struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
+			     unsigned short num, const struct in6_addr *loc_addr,
+			     const struct in6_addr *rmt_addr, int dif);
+
+int raw_abort(struct sock *sk, int err);
+
 void raw6_icmp_error(struct sk_buff *, int nexthdr,
 		u8 type, u8 code, int inner_offset, __be32);
 bool raw6_local_deliver(struct sk_buff *, int);
include/uapi/linux/inet_diag.h +4 -1
Index: linux-ml.git/include/uapi/linux/inet_diag.h
===================================================================
@@ -38,7 +38,10 @@ struct inet_diag_req_v2 {
 	__u8	sdiag_family;
 	__u8	sdiag_protocol;
 	__u8	idiag_ext;
-	__u8	pad;
+	union {
+		__u8	pad;
+		__u8	sdiag_raw_protocol;	/* SOCK_RAW only, @pad for others */
+	};
 	__u32	idiag_states;
 	struct inet_diag_sockid id;
 };
net/ipv4/Kconfig +8 -0
Index: linux-ml.git/net/ipv4/Kconfig
===================================================================
@@ -430,6 +430,14 @@ config INET_UDP_DIAG
 	  Support for UDP socket monitoring interface used by the ss tool.
 	  If unsure, say Y.
 
+config INET_RAW_DIAG
+	tristate "RAW: socket monitoring interface"
+	depends on INET_DIAG && (IPV6 || IPV6=n)
+	default n
+	---help---
+	  Support for RAW socket monitoring interface used by the ss tool.
+	  If unsure, say Y.
+
 config INET_DIAG_DESTROY
 	bool "INET: allow privileged process to administratively close sockets"
 	depends on INET_DIAG
net/ipv4/Makefile +1 -0
Index: linux-ml.git/net/ipv4/Makefile
===================================================================
@@ -40,6 +40,7 @@ obj-$(CONFIG_NETFILTER)	+= netfilter.o n
 obj-$(CONFIG_INET_DIAG) += inet_diag.o 
 obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
 obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o
+obj-$(CONFIG_INET_RAW_DIAG) += raw_diag.o
 obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
 obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
 obj-$(CONFIG_TCP_CONG_CDG) += tcp_cdg.o
net/ipv4/inet_diag.c +9 -0
Index: linux-ml.git/net/ipv4/inet_diag.c
===================================================================
@@ -200,6 +200,15 @@ int inet_sk_diag_fill(struct sock *sk, s
 		if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO))
 			goto errout;
 
+	/*
+	 * RAW sockets might have user-defined protocols assigned,
+	 * so report the one supplied on socket creation.
+	 */
+	if (sk->sk_type == SOCK_RAW) {
+		if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))
+			goto errout;
+	}
+
 	if (!icsk) {
 		handler->idiag_get_info(sk, r, NULL);
 		goto out;
net/ipv4/raw.c +19 -2
Index: linux-ml.git/net/ipv4/raw.c
===================================================================
@@ -89,9 +89,10 @@ struct raw_frag_vec {
 	int hlen;
 };
 
-static struct raw_hashinfo raw_v4_hashinfo = {
+struct raw_hashinfo raw_v4_hashinfo = {
 	.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
 };
+EXPORT_SYMBOL_GPL(raw_v4_hashinfo);
 
 int raw_hash_sk(struct sock *sk)
 {
@@ -120,7 +121,7 @@ void raw_unhash_sk(struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(raw_unhash_sk);
 
-static struct sock *__raw_v4_lookup(struct net *net, struct sock *sk,
+struct sock *__raw_v4_lookup(struct net *net, struct sock *sk,
 		unsigned short num, __be32 raddr, __be32 laddr, int dif)
 {
 	sk_for_each_from(sk) {
@@ -136,6 +137,7 @@ static struct sock *__raw_v4_lookup(stru
 found:
 	return sk;
 }
+EXPORT_SYMBOL_GPL(__raw_v4_lookup);
 
 /*
  *	0 - deliver
@@ -918,6 +920,20 @@ static int compat_raw_ioctl(struct sock
 }
 #endif
 
+int raw_abort(struct sock *sk, int err)
+{
+	lock_sock(sk);
+
+	sk->sk_err = err;
+	sk->sk_error_report(sk);
+	udp_disconnect(sk, 0);
+
+	release_sock(sk);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(raw_abort);
+
 struct proto raw_prot = {
 	.name		   = "RAW",
 	.owner		   = THIS_MODULE,
@@ -943,6 +959,7 @@ struct proto raw_prot = {
 	.compat_getsockopt = compat_raw_getsockopt,
 	.compat_ioctl	   = compat_raw_ioctl,
 #endif
+	.diag_destroy	   = raw_abort,
 };
 
 #ifdef CONFIG_PROC_FS
net/ipv4/raw_diag.c new file +226 -0

226 lines not on this page · raw message

net/ipv6/raw.c +5 -2
Index: linux-ml.git/net/ipv6/raw.c
===================================================================
@@ -65,11 +65,12 @@
 
 #define	ICMPV6_HDRLEN	4	/* ICMPv6 header, RFC 4443 Section 2.1 */
 
-static struct raw_hashinfo raw_v6_hashinfo = {
+struct raw_hashinfo raw_v6_hashinfo = {
 	.lock = __RW_LOCK_UNLOCKED(raw_v6_hashinfo.lock),
 };
+EXPORT_SYMBOL_GPL(raw_v6_hashinfo);
 
-static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
+struct sock *__raw_v6_lookup(struct net *net, struct sock *sk,
 		unsigned short num, const struct in6_addr *loc_addr,
 		const struct in6_addr *rmt_addr, int dif)
 {
@@ -102,6 +103,7 @@ static struct sock *__raw_v6_lookup(stru
 found:
 	return sk;
 }
+EXPORT_SYMBOL_GPL(__raw_v6_lookup);
 
 /*
  *	0 - deliver
@@ -1252,6 +1254,7 @@ struct proto rawv6_prot = {
 	.compat_getsockopt = compat_rawv6_getsockopt,
 	.compat_ioctl	   = compat_rawv6_ioctl,
 #endif
+	.diag_destroy	   = raw_abort,
 };
 
 #ifdef CONFIG_PROC_FS
#1 lore raw
Quoted 9 lines · Cyrill Gorcunov
net/ipv4/raw_diag.c new file +9 -0
+static struct sock *raw_sock_get(struct net *net, const struct inet_diag_req_v2 *r)
+{
+	struct raw_hashinfo *hashinfo = raw_get_hashinfo(r);
+	struct sock *sk = NULL, *s;
+	int slot;
+
+	if (IS_ERR(hashinfo))
+		return ERR_CAST(hashinfo);
+

net/ipv4/raw_diag.c line 70

Quoted 10 lines
net/ipv4/raw_diag.c new file +10 -0
+	read_lock(&hashinfo->lock);
+	for (slot = 0; slot < RAW_HTABLE_SIZE; slot++) {
+		sk_for_each(s, &hashinfo->ht[slot]) {
+			sk = raw_lookup(net, s, r);
+			if (sk)
+				break;
+		}
+	}
+	if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
+		sk = NULL;

Minor detail, but note that raw sockets do not use rcu (yet)

Since you have read_lock(&hashinfo->lock), no writer can suddenly change sk->sk_refcnt to a zero value.

Therefore, a mere "sock_hold(sk)" should be enough to get a reference on the socket.

Using atomic_inc_not_zero() also works, but might distract/confuse the next guy trying to understand this code ;)

Quoted 5 lines
net/ipv4/raw_diag.c new file +5 -0
+	read_unlock(&hashinfo->lock);
+
+	return sk ? sk : ERR_PTR(-ENOENT);
+}
+
#2 lore raw

On Tue, Sep 27, 2016 at 08:39:59AM -0700, Eric Dumazet wrote:
...

Quoted 6 lines

net/ipv4/raw_diag.c line 70

Quoted 3 lines
net/ipv4/raw_diag.c new file +3 -0
+	}
+	if (sk && !atomic_inc_not_zero(&sk->sk_refcnt))
+		sk = NULL;

Minor detail, but note that raw sockets do not use rcu (yet)

Quoted 8 lines

Since you have read_lock(&hashinfo->lock), no writer can suddenly change sk->sk_refcnt to a zero value.

Therefore, a mere "sock_hold(sk)" should be enough to get a reference on the socket.

Using atomic_inc_not_zero() also works, but might distract/confuse the next guy trying to understand this code ;)

Oh, I see. Thanks Eric! I'll update the patch (hopefully tonight) and send it out (need to finish urgent task atm).