net: ip, diag -- Add diag interface for raw sockets
Subsystems (2)
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
---
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -200,6 +200,15 @@ int inet_sk_diag_fill(struct sock *sk, sif (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
@@ -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
@@ -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
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 ); +} +
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 changesk->sk_refcntto 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).