forked from mirrors/linux
		
	sctp: add the sctp_diag.c file
This one will implement all the interface of inet_diag, inet_diag_handler. which includes sctp_diag_dump, sctp_diag_dump_one and sctp_diag_get_info. It will work as a module, and register inet_diag_handler when loading. v2->v3: - fix the mistake in inet_assoc_attr_size(). - change inet_diag_msg_laddrs_fill() name to inet_diag_msg_sctpladdrs_fill. - change inet_diag_msg_paddrs_fill() name to inet_diag_msg_sctpaddrs_fill. - add inet_diag_msg_sctpinfo_fill() to make asoc/ep fill code clearer. - add inet_diag_msg_sctpasoc_fill() to make asoc fill code clearer. - merge inet_asoc_diag_fill() and inet_ep_diag_fill() to inet_sctp_diag_fill(). - call sctp_diag_get_info() directly, instead by handler, cause the caller is in the same file with it. - call lock_sock in sctp_tsp_dump_one() to make sure we call get sctp info safely. - after lock_sock(sk), we should check sk != assoc->base.sk. - change mem[SK_MEMINFO_WMEM_ALLOC] to asoc->sndbuf_used for asoc dump when asoc->ep->sndbuf_policy is set. don't use INET_DIAG_MEMINFO attr any more. Signed-off-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									cb2050a7b8
								
							
						
					
					
						commit
						8f840e47f1
					
				
					 4 changed files with 504 additions and 0 deletions
				
			
		|  | @ -113,6 +113,8 @@ enum { | ||||||
| 	INET_DIAG_DCTCPINFO, | 	INET_DIAG_DCTCPINFO, | ||||||
| 	INET_DIAG_PROTOCOL,  /* response attribute only */ | 	INET_DIAG_PROTOCOL,  /* response attribute only */ | ||||||
| 	INET_DIAG_SKV6ONLY, | 	INET_DIAG_SKV6ONLY, | ||||||
|  | 	INET_DIAG_LOCALS, | ||||||
|  | 	INET_DIAG_PEERS, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define INET_DIAG_MAX INET_DIAG_SKV6ONLY | #define INET_DIAG_MAX INET_DIAG_SKV6ONLY | ||||||
|  |  | ||||||
|  | @ -99,5 +99,9 @@ config SCTP_COOKIE_HMAC_SHA1 | ||||||
| 	select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1 | 	select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1 | ||||||
| 	select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1 | 	select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1 | ||||||
| 
 | 
 | ||||||
|  | config INET_SCTP_DIAG | ||||||
|  | 	depends on INET_DIAG | ||||||
|  | 	def_tristate INET_DIAG | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| endif # IP_SCTP | endif # IP_SCTP | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_IP_SCTP) += sctp.o | obj-$(CONFIG_IP_SCTP) += sctp.o | ||||||
| obj-$(CONFIG_NET_SCTPPROBE) += sctp_probe.o | obj-$(CONFIG_NET_SCTPPROBE) += sctp_probe.o | ||||||
|  | obj-$(CONFIG_INET_SCTP_DIAG) += sctp_diag.o | ||||||
| 
 | 
 | ||||||
| sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
 | sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
 | ||||||
| 	  protocol.o endpointola.o associola.o \
 | 	  protocol.o endpointola.o associola.o \
 | ||||||
|  |  | ||||||
							
								
								
									
										497
									
								
								net/sctp/sctp_diag.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										497
									
								
								net/sctp/sctp_diag.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,497 @@ | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/inet_diag.h> | ||||||
|  | #include <linux/sock_diag.h> | ||||||
|  | #include <net/sctp/sctp.h> | ||||||
|  | 
 | ||||||
|  | extern void inet_diag_msg_common_fill(struct inet_diag_msg *r, | ||||||
|  | 				      struct sock *sk); | ||||||
|  | extern int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, | ||||||
|  | 				    struct inet_diag_msg *r, int ext, | ||||||
|  | 				    struct user_namespace *user_ns); | ||||||
|  | 
 | ||||||
|  | static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, | ||||||
|  | 			       void *info); | ||||||
|  | 
 | ||||||
|  | /* define some functions to make asoc/ep fill look clean */ | ||||||
|  | static void inet_diag_msg_sctpasoc_fill(struct inet_diag_msg *r, | ||||||
|  | 					struct sock *sk, | ||||||
|  | 					struct sctp_association *asoc) | ||||||
|  | { | ||||||
|  | 	union sctp_addr laddr, paddr; | ||||||
|  | 	struct dst_entry *dst; | ||||||
|  | 
 | ||||||
|  | 	laddr = list_entry(asoc->base.bind_addr.address_list.next, | ||||||
|  | 			   struct sctp_sockaddr_entry, list)->a; | ||||||
|  | 	paddr = asoc->peer.primary_path->ipaddr; | ||||||
|  | 	dst = asoc->peer.primary_path->dst; | ||||||
|  | 
 | ||||||
|  | 	r->idiag_family = sk->sk_family; | ||||||
|  | 	r->id.idiag_sport = htons(asoc->base.bind_addr.port); | ||||||
|  | 	r->id.idiag_dport = htons(asoc->peer.port); | ||||||
|  | 	r->id.idiag_if = dst ? dst->dev->ifindex : 0; | ||||||
|  | 	sock_diag_save_cookie(sk, r->id.idiag_cookie); | ||||||
|  | 
 | ||||||
|  | #if IS_ENABLED(CONFIG_IPV6) | ||||||
|  | 	if (sk->sk_family == AF_INET6) { | ||||||
|  | 		*(struct in6_addr *)r->id.idiag_src = laddr.v6.sin6_addr; | ||||||
|  | 		*(struct in6_addr *)r->id.idiag_dst = paddr.v6.sin6_addr; | ||||||
|  | 	} else | ||||||
|  | #endif | ||||||
|  | 	{ | ||||||
|  | 		memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); | ||||||
|  | 		memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); | ||||||
|  | 
 | ||||||
|  | 		r->id.idiag_src[0] = laddr.v4.sin_addr.s_addr; | ||||||
|  | 		r->id.idiag_dst[0] = paddr.v4.sin_addr.s_addr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	r->idiag_state = asoc->state; | ||||||
|  | 	r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX; | ||||||
|  | 	r->idiag_retrans = asoc->rtx_data_chunks; | ||||||
|  | #define EXPIRES_IN_MS(tmo)  DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) | ||||||
|  | 	r->idiag_expires = | ||||||
|  | 		EXPIRES_IN_MS(asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX]); | ||||||
|  | #undef EXPIRES_IN_MS | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, | ||||||
|  | 					 struct list_head *address_list) | ||||||
|  | { | ||||||
|  | 	struct sctp_sockaddr_entry *laddr; | ||||||
|  | 	int addrlen = sizeof(struct sockaddr_storage); | ||||||
|  | 	int addrcnt = 0; | ||||||
|  | 	struct nlattr *attr; | ||||||
|  | 	void *info = NULL; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry_rcu(laddr, address_list, list) | ||||||
|  | 		addrcnt++; | ||||||
|  | 
 | ||||||
|  | 	attr = nla_reserve(skb, INET_DIAG_LOCALS, addrlen * addrcnt); | ||||||
|  | 	if (!attr) | ||||||
|  | 		return -EMSGSIZE; | ||||||
|  | 
 | ||||||
|  | 	info = nla_data(attr); | ||||||
|  | 	list_for_each_entry_rcu(laddr, address_list, list) { | ||||||
|  | 		memcpy(info, &laddr->a, addrlen); | ||||||
|  | 		info += addrlen; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb, | ||||||
|  | 					struct sctp_association *asoc) | ||||||
|  | { | ||||||
|  | 	int addrlen = sizeof(struct sockaddr_storage); | ||||||
|  | 	struct sctp_transport *from; | ||||||
|  | 	struct nlattr *attr; | ||||||
|  | 	void *info = NULL; | ||||||
|  | 
 | ||||||
|  | 	attr = nla_reserve(skb, INET_DIAG_PEERS, | ||||||
|  | 			   addrlen * asoc->peer.transport_count); | ||||||
|  | 	if (!attr) | ||||||
|  | 		return -EMSGSIZE; | ||||||
|  | 
 | ||||||
|  | 	info = nla_data(attr); | ||||||
|  | 	list_for_each_entry(from, &asoc->peer.transport_addr_list, | ||||||
|  | 			    transports) { | ||||||
|  | 		memcpy(info, &from->ipaddr, addrlen); | ||||||
|  | 		info += addrlen; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* sctp asoc/ep fill*/ | ||||||
|  | static int inet_sctp_diag_fill(struct sock *sk, struct sctp_association *asoc, | ||||||
|  | 			       struct sk_buff *skb, | ||||||
|  | 			       const struct inet_diag_req_v2 *req, | ||||||
|  | 			       struct user_namespace *user_ns, | ||||||
|  | 			       int portid, u32 seq, u16 nlmsg_flags, | ||||||
|  | 			       const struct nlmsghdr *unlh) | ||||||
|  | { | ||||||
|  | 	struct sctp_endpoint *ep = sctp_sk(sk)->ep; | ||||||
|  | 	struct list_head *addr_list; | ||||||
|  | 	struct inet_diag_msg *r; | ||||||
|  | 	struct nlmsghdr  *nlh; | ||||||
|  | 	int ext = req->idiag_ext; | ||||||
|  | 	struct sctp_infox infox; | ||||||
|  | 	void *info = NULL; | ||||||
|  | 
 | ||||||
|  | 	nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), | ||||||
|  | 			nlmsg_flags); | ||||||
|  | 	if (!nlh) | ||||||
|  | 		return -EMSGSIZE; | ||||||
|  | 
 | ||||||
|  | 	r = nlmsg_data(nlh); | ||||||
|  | 	BUG_ON(!sk_fullsock(sk)); | ||||||
|  | 
 | ||||||
|  | 	if (asoc) { | ||||||
|  | 		inet_diag_msg_sctpasoc_fill(r, sk, asoc); | ||||||
|  | 	} else { | ||||||
|  | 		inet_diag_msg_common_fill(r, sk); | ||||||
|  | 		r->idiag_state = sk->sk_state; | ||||||
|  | 		r->idiag_timer = 0; | ||||||
|  | 		r->idiag_retrans = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns)) | ||||||
|  | 		goto errout; | ||||||
|  | 
 | ||||||
|  | 	if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) { | ||||||
|  | 		u32 mem[SK_MEMINFO_VARS]; | ||||||
|  | 		int amt; | ||||||
|  | 
 | ||||||
|  | 		if (asoc && asoc->ep->sndbuf_policy) | ||||||
|  | 			amt = asoc->sndbuf_used; | ||||||
|  | 		else | ||||||
|  | 			amt = sk_wmem_alloc_get(sk); | ||||||
|  | 		mem[SK_MEMINFO_WMEM_ALLOC] = amt; | ||||||
|  | 		mem[SK_MEMINFO_RMEM_ALLOC] = sk_rmem_alloc_get(sk); | ||||||
|  | 		mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf; | ||||||
|  | 		mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf; | ||||||
|  | 		mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; | ||||||
|  | 		mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; | ||||||
|  | 		mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); | ||||||
|  | 		mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len; | ||||||
|  | 		mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops); | ||||||
|  | 
 | ||||||
|  | 		if (nla_put(skb, INET_DIAG_SKMEMINFO, sizeof(mem), &mem) < 0) | ||||||
|  | 			goto errout; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (ext & (1 << (INET_DIAG_INFO - 1))) { | ||||||
|  | 		struct nlattr *attr; | ||||||
|  | 
 | ||||||
|  | 		attr = nla_reserve(skb, INET_DIAG_INFO, | ||||||
|  | 				   sizeof(struct sctp_info)); | ||||||
|  | 		if (!attr) | ||||||
|  | 			goto errout; | ||||||
|  | 
 | ||||||
|  | 		info = nla_data(attr); | ||||||
|  | 	} | ||||||
|  | 	infox.sctpinfo = (struct sctp_info *)info; | ||||||
|  | 	infox.asoc = asoc; | ||||||
|  | 	sctp_diag_get_info(sk, r, &infox); | ||||||
|  | 
 | ||||||
|  | 	addr_list = asoc ? &asoc->base.bind_addr.address_list | ||||||
|  | 			 : &ep->base.bind_addr.address_list; | ||||||
|  | 	if (inet_diag_msg_sctpladdrs_fill(skb, addr_list)) | ||||||
|  | 		goto errout; | ||||||
|  | 
 | ||||||
|  | 	if (asoc && (ext & (1 << (INET_DIAG_CONG - 1)))) | ||||||
|  | 		if (nla_put_string(skb, INET_DIAG_CONG, "reno") < 0) | ||||||
|  | 			goto errout; | ||||||
|  | 
 | ||||||
|  | 	if (asoc && inet_diag_msg_sctpaddrs_fill(skb, asoc)) | ||||||
|  | 		goto errout; | ||||||
|  | 
 | ||||||
|  | 	nlmsg_end(skb, nlh); | ||||||
|  | 	return 0; | ||||||
|  | 
 | ||||||
|  | errout: | ||||||
|  | 	nlmsg_cancel(skb, nlh); | ||||||
|  | 	return -EMSGSIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* callback and param */ | ||||||
|  | struct sctp_comm_param { | ||||||
|  | 	struct sk_buff *skb; | ||||||
|  | 	struct netlink_callback *cb; | ||||||
|  | 	const struct inet_diag_req_v2 *r; | ||||||
|  | 	const struct nlmsghdr *nlh; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static size_t inet_assoc_attr_size(struct sctp_association *asoc) | ||||||
|  | { | ||||||
|  | 	int addrlen = sizeof(struct sockaddr_storage); | ||||||
|  | 	int addrcnt = 0; | ||||||
|  | 	struct sctp_sockaddr_entry *laddr; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry_rcu(laddr, &asoc->base.bind_addr.address_list, | ||||||
|  | 				list) | ||||||
|  | 		addrcnt++; | ||||||
|  | 
 | ||||||
|  | 	return	  nla_total_size(sizeof(struct sctp_info)) | ||||||
|  | 		+ nla_total_size(1) /* INET_DIAG_SHUTDOWN */ | ||||||
|  | 		+ nla_total_size(1) /* INET_DIAG_TOS */ | ||||||
|  | 		+ nla_total_size(1) /* INET_DIAG_TCLASS */ | ||||||
|  | 		+ nla_total_size(addrlen * asoc->peer.transport_count) | ||||||
|  | 		+ nla_total_size(addrlen * addrcnt) | ||||||
|  | 		+ nla_total_size(sizeof(struct inet_diag_meminfo)) | ||||||
|  | 		+ nla_total_size(sizeof(struct inet_diag_msg)) | ||||||
|  | 		+ 64; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p) | ||||||
|  | { | ||||||
|  | 	struct sctp_association *assoc = tsp->asoc; | ||||||
|  | 	struct sock *sk = tsp->asoc->base.sk; | ||||||
|  | 	struct sctp_comm_param *commp = p; | ||||||
|  | 	struct sk_buff *in_skb = commp->skb; | ||||||
|  | 	const struct inet_diag_req_v2 *req = commp->r; | ||||||
|  | 	const struct nlmsghdr *nlh = commp->nlh; | ||||||
|  | 	struct net *net = sock_net(in_skb->sk); | ||||||
|  | 	struct sk_buff *rep; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = sock_diag_check_cookie(sk, req->id.idiag_cookie); | ||||||
|  | 	if (err) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	err = -ENOMEM; | ||||||
|  | 	rep = nlmsg_new(inet_assoc_attr_size(assoc), GFP_KERNEL); | ||||||
|  | 	if (!rep) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	lock_sock(sk); | ||||||
|  | 	if (sk != assoc->base.sk) { | ||||||
|  | 		release_sock(sk); | ||||||
|  | 		sk = assoc->base.sk; | ||||||
|  | 		lock_sock(sk); | ||||||
|  | 	} | ||||||
|  | 	err = inet_sctp_diag_fill(sk, assoc, rep, req, | ||||||
|  | 				  sk_user_ns(NETLINK_CB(in_skb).sk), | ||||||
|  | 				  NETLINK_CB(in_skb).portid, | ||||||
|  | 				  nlh->nlmsg_seq, 0, nlh); | ||||||
|  | 	release_sock(sk); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		WARN_ON(err == -EMSGSIZE); | ||||||
|  | 		kfree_skb(rep); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, | ||||||
|  | 			      MSG_DONTWAIT); | ||||||
|  | 	if (err > 0) | ||||||
|  | 		err = 0; | ||||||
|  | out: | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sctp_tsp_dump(struct sctp_transport *tsp, void *p) | ||||||
|  | { | ||||||
|  | 	struct sctp_endpoint *ep = tsp->asoc->ep; | ||||||
|  | 	struct sctp_comm_param *commp = p; | ||||||
|  | 	struct sock *sk = ep->base.sk; | ||||||
|  | 	struct sk_buff *skb = commp->skb; | ||||||
|  | 	struct netlink_callback *cb = commp->cb; | ||||||
|  | 	const struct inet_diag_req_v2 *r = commp->r; | ||||||
|  | 	struct sctp_association *assoc = | ||||||
|  | 		list_entry(ep->asocs.next, struct sctp_association, asocs); | ||||||
|  | 	int err = 0; | ||||||
|  | 
 | ||||||
|  | 	/* find the ep only once through the transports by this condition */ | ||||||
|  | 	if (tsp->asoc != assoc) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	lock_sock(sk); | ||||||
|  | 	if (sk != assoc->base.sk) | ||||||
|  | 		goto release; | ||||||
|  | 	list_for_each_entry(assoc, &ep->asocs, asocs) { | ||||||
|  | 		if (cb->args[4] < cb->args[1]) | ||||||
|  | 			goto next; | ||||||
|  | 
 | ||||||
|  | 		if (r->id.idiag_sport != htons(assoc->base.bind_addr.port) && | ||||||
|  | 		    r->id.idiag_sport) | ||||||
|  | 			goto next; | ||||||
|  | 		if (r->id.idiag_dport != htons(assoc->peer.port) && | ||||||
|  | 		    r->id.idiag_dport) | ||||||
|  | 			goto next; | ||||||
|  | 
 | ||||||
|  | 		if (!cb->args[3] && | ||||||
|  | 		    inet_sctp_diag_fill(sk, NULL, skb, r, | ||||||
|  | 					sk_user_ns(NETLINK_CB(cb->skb).sk), | ||||||
|  | 					NETLINK_CB(cb->skb).portid, | ||||||
|  | 					cb->nlh->nlmsg_seq, | ||||||
|  | 					NLM_F_MULTI, cb->nlh) < 0) { | ||||||
|  | 			cb->args[3] = 1; | ||||||
|  | 			err = 2; | ||||||
|  | 			goto release; | ||||||
|  | 		} | ||||||
|  | 		cb->args[3] = 1; | ||||||
|  | 
 | ||||||
|  | 		if (inet_sctp_diag_fill(sk, assoc, skb, r, | ||||||
|  | 					sk_user_ns(NETLINK_CB(cb->skb).sk), | ||||||
|  | 					NETLINK_CB(cb->skb).portid, | ||||||
|  | 					cb->nlh->nlmsg_seq, 0, cb->nlh) < 0) { | ||||||
|  | 			err = 2; | ||||||
|  | 			goto release; | ||||||
|  | 		} | ||||||
|  | next: | ||||||
|  | 		cb->args[4]++; | ||||||
|  | 	} | ||||||
|  | 	cb->args[1] = 0; | ||||||
|  | 	cb->args[2]++; | ||||||
|  | 	cb->args[3] = 0; | ||||||
|  | 	cb->args[4] = 0; | ||||||
|  | release: | ||||||
|  | 	release_sock(sk); | ||||||
|  | 	return err; | ||||||
|  | out: | ||||||
|  | 	cb->args[2]++; | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sctp_ep_dump(struct sctp_endpoint *ep, void *p) | ||||||
|  | { | ||||||
|  | 	struct sctp_comm_param *commp = p; | ||||||
|  | 	struct sock *sk = ep->base.sk; | ||||||
|  | 	struct sk_buff *skb = commp->skb; | ||||||
|  | 	struct netlink_callback *cb = commp->cb; | ||||||
|  | 	const struct inet_diag_req_v2 *r = commp->r; | ||||||
|  | 	struct net *net = sock_net(skb->sk); | ||||||
|  | 	struct inet_sock *inet = inet_sk(sk); | ||||||
|  | 	int err = 0; | ||||||
|  | 
 | ||||||
|  | 	if (!net_eq(sock_net(sk), net)) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	if (cb->args[4] < cb->args[1]) | ||||||
|  | 		goto next; | ||||||
|  | 
 | ||||||
|  | 	if (r->sdiag_family != AF_UNSPEC && | ||||||
|  | 	    sk->sk_family != r->sdiag_family) | ||||||
|  | 		goto next; | ||||||
|  | 
 | ||||||
|  | 	if (r->id.idiag_sport != inet->inet_sport && | ||||||
|  | 	    r->id.idiag_sport) | ||||||
|  | 		goto next; | ||||||
|  | 
 | ||||||
|  | 	if (r->id.idiag_dport != inet->inet_dport && | ||||||
|  | 	    r->id.idiag_dport) | ||||||
|  | 		goto next; | ||||||
|  | 
 | ||||||
|  | 	if (inet_sctp_diag_fill(sk, NULL, skb, r, | ||||||
|  | 				sk_user_ns(NETLINK_CB(cb->skb).sk), | ||||||
|  | 				NETLINK_CB(cb->skb).portid, | ||||||
|  | 				cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||||||
|  | 				cb->nlh) < 0) { | ||||||
|  | 		err = 2; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | next: | ||||||
|  | 	cb->args[4]++; | ||||||
|  | out: | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* define the functions for sctp_diag_handler*/ | ||||||
|  | static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, | ||||||
|  | 			       void *info) | ||||||
|  | { | ||||||
|  | 	struct sctp_infox *infox = (struct sctp_infox *)info; | ||||||
|  | 
 | ||||||
|  | 	if (infox->asoc) { | ||||||
|  | 		r->idiag_rqueue = atomic_read(&infox->asoc->rmem_alloc); | ||||||
|  | 		r->idiag_wqueue = infox->asoc->sndbuf_used; | ||||||
|  | 	} else { | ||||||
|  | 		r->idiag_rqueue = sk->sk_ack_backlog; | ||||||
|  | 		r->idiag_wqueue = sk->sk_max_ack_backlog; | ||||||
|  | 	} | ||||||
|  | 	if (infox->sctpinfo) | ||||||
|  | 		sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sctp_diag_dump_one(struct sk_buff *in_skb, | ||||||
|  | 			      const struct nlmsghdr *nlh, | ||||||
|  | 			      const struct inet_diag_req_v2 *req) | ||||||
|  | { | ||||||
|  | 	struct net *net = sock_net(in_skb->sk); | ||||||
|  | 	union sctp_addr laddr, paddr; | ||||||
|  | 	struct sctp_comm_param commp = { | ||||||
|  | 		.skb = in_skb, | ||||||
|  | 		.r = req, | ||||||
|  | 		.nlh = nlh, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	if (req->sdiag_family == AF_INET) { | ||||||
|  | 		laddr.v4.sin_port = req->id.idiag_sport; | ||||||
|  | 		laddr.v4.sin_addr.s_addr = req->id.idiag_src[0]; | ||||||
|  | 		laddr.v4.sin_family = AF_INET; | ||||||
|  | 
 | ||||||
|  | 		paddr.v4.sin_port = req->id.idiag_dport; | ||||||
|  | 		paddr.v4.sin_addr.s_addr = req->id.idiag_dst[0]; | ||||||
|  | 		paddr.v4.sin_family = AF_INET; | ||||||
|  | 	} else { | ||||||
|  | 		laddr.v6.sin6_port = req->id.idiag_sport; | ||||||
|  | 		memcpy(&laddr.v6.sin6_addr, req->id.idiag_src, 64); | ||||||
|  | 		laddr.v6.sin6_family = AF_INET6; | ||||||
|  | 
 | ||||||
|  | 		paddr.v6.sin6_port = req->id.idiag_dport; | ||||||
|  | 		memcpy(&paddr.v6.sin6_addr, req->id.idiag_dst, 64); | ||||||
|  | 		paddr.v6.sin6_family = AF_INET6; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return sctp_transport_lookup_process(sctp_tsp_dump_one, | ||||||
|  | 					     net, &laddr, &paddr, &commp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, | ||||||
|  | 			   const struct inet_diag_req_v2 *r, struct nlattr *bc) | ||||||
|  | { | ||||||
|  | 	u32 idiag_states = r->idiag_states; | ||||||
|  | 	struct net *net = sock_net(skb->sk); | ||||||
|  | 	struct sctp_comm_param commp = { | ||||||
|  | 		.skb = skb, | ||||||
|  | 		.cb = cb, | ||||||
|  | 		.r = r, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	/* eps hashtable dumps
 | ||||||
|  | 	 * args: | ||||||
|  | 	 * 0 : if it will traversal listen sock | ||||||
|  | 	 * 1 : to record the sock pos of this time's traversal | ||||||
|  | 	 * 4 : to work as a temporary variable to traversal list | ||||||
|  | 	 */ | ||||||
|  | 	if (cb->args[0] == 0) { | ||||||
|  | 		if (!(idiag_states & TCPF_LISTEN)) | ||||||
|  | 			goto skip; | ||||||
|  | 		if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) | ||||||
|  | 			goto done; | ||||||
|  | skip: | ||||||
|  | 		cb->args[0] = 1; | ||||||
|  | 		cb->args[1] = 0; | ||||||
|  | 		cb->args[4] = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* asocs by transport hashtable dump
 | ||||||
|  | 	 * args: | ||||||
|  | 	 * 1 : to record the assoc pos of this time's traversal | ||||||
|  | 	 * 2 : to record the transport pos of this time's traversal | ||||||
|  | 	 * 3 : to mark if we have dumped the ep info of the current asoc | ||||||
|  | 	 * 4 : to work as a temporary variable to traversal list | ||||||
|  | 	 */ | ||||||
|  | 	if (!(idiag_states & ~TCPF_LISTEN)) | ||||||
|  | 		goto done; | ||||||
|  | 	sctp_for_each_transport(sctp_tsp_dump, net, cb->args[2], &commp); | ||||||
|  | done: | ||||||
|  | 	cb->args[1] = cb->args[4]; | ||||||
|  | 	cb->args[4] = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct inet_diag_handler sctp_diag_handler = { | ||||||
|  | 	.dump		 = sctp_diag_dump, | ||||||
|  | 	.dump_one	 = sctp_diag_dump_one, | ||||||
|  | 	.idiag_get_info  = sctp_diag_get_info, | ||||||
|  | 	.idiag_type	 = IPPROTO_SCTP, | ||||||
|  | 	.idiag_info_size = sizeof(struct sctp_info), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int __init sctp_diag_init(void) | ||||||
|  | { | ||||||
|  | 	return inet_diag_register(&sctp_diag_handler); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void __exit sctp_diag_exit(void) | ||||||
|  | { | ||||||
|  | 	inet_diag_unregister(&sctp_diag_handler); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module_init(sctp_diag_init); | ||||||
|  | module_exit(sctp_diag_exit); | ||||||
|  | MODULE_LICENSE("GPL"); | ||||||
|  | MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132); | ||||||
		Loading…
	
		Reference in a new issue
	
	 Xin Long
						Xin Long