Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7d58aab authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller
Browse files

[INET_DIAG]: Introduce inet_twsk_diag_dump & inet_twsk_diag_fill



To properly dump TIME_WAIT sockets and to reduce complexity a bit by
having per socket class accessor routines.

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4e852c02
Loading
Loading
Loading
Loading
+111 −47
Original line number Original line Diff line number Diff line
@@ -70,20 +70,22 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
	nlh->nlmsg_flags = nlmsg_flags;
	nlh->nlmsg_flags = nlmsg_flags;


	r = NLMSG_DATA(nlh);
	r = NLMSG_DATA(nlh);
	if (sk->sk_state != TCP_TIME_WAIT) {
	BUG_ON(sk->sk_state == TCP_TIME_WAIT);

	if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
	if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
			minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO,
		minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, sizeof(*minfo));
					      sizeof(*minfo));

	if (ext & (1 << (INET_DIAG_INFO - 1)))
	if (ext & (1 << (INET_DIAG_INFO - 1)))
		info = INET_DIAG_PUT(skb, INET_DIAG_INFO,
		info = INET_DIAG_PUT(skb, INET_DIAG_INFO,
				     handler->idiag_info_size);
				     handler->idiag_info_size);


	if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
	if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
			size_t len = strlen(icsk->icsk_ca_ops->name);
		const size_t len = strlen(icsk->icsk_ca_ops->name);

		strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
		strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
		       icsk->icsk_ca_ops->name);
		       icsk->icsk_ca_ops->name);
	}
	}
	}

	r->idiag_family = sk->sk_family;
	r->idiag_family = sk->sk_family;
	r->idiag_state = sk->sk_state;
	r->idiag_state = sk->sk_state;
	r->idiag_timer = 0;
	r->idiag_timer = 0;
@@ -93,37 +95,6 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
	r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
	r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);


	if (r->idiag_state == TCP_TIME_WAIT) {
		const struct inet_timewait_sock *tw = inet_twsk(sk);
		long tmo = tw->tw_ttd - jiffies;
		if (tmo < 0)
			tmo = 0;

		r->id.idiag_sport = tw->tw_sport;
		r->id.idiag_dport = tw->tw_dport;
		r->id.idiag_src[0] = tw->tw_rcv_saddr;
		r->id.idiag_dst[0] = tw->tw_daddr;
		r->idiag_state = tw->tw_substate;
		r->idiag_timer = 3;
		r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ;
		r->idiag_rqueue = 0;
		r->idiag_wqueue = 0;
		r->idiag_uid = 0;
		r->idiag_inode = 0;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		if (r->idiag_family == AF_INET6) {
			const struct inet6_timewait_sock *tw6 = inet6_twsk(sk);

			ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
				       &tw6->tw_v6_rcv_saddr);
			ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
				       &tw6->tw_v6_daddr);
		}
#endif
		nlh->nlmsg_len = skb->tail - b;
		return skb->len;
	}

	r->id.idiag_sport = inet->sport;
	r->id.idiag_sport = inet->sport;
	r->id.idiag_dport = inet->dport;
	r->id.idiag_dport = inet->dport;
	r->id.idiag_src[0] = inet->rcv_saddr;
	r->id.idiag_src[0] = inet->rcv_saddr;
@@ -185,6 +156,62 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
	return -1;
	return -1;
}
}


static int inet_twsk_diag_fill(struct inet_timewait_sock *tw,
			       struct sk_buff *skb, int ext, u32 pid,
			       u32 seq, u16 nlmsg_flags,
			       const struct nlmsghdr *unlh)
{
	long tmo;
	struct inet_diag_msg *r;
	const unsigned char *previous_tail = skb->tail;
	struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq,
					 unlh->nlmsg_type, sizeof(*r));

	r = NLMSG_DATA(nlh);
	BUG_ON(tw->tw_state != TCP_TIME_WAIT);

	nlh->nlmsg_flags = nlmsg_flags;

	tmo = tw->tw_ttd - jiffies;
	if (tmo < 0)
		tmo = 0;

	r->idiag_family	      = tw->tw_family;
	r->idiag_state	      = tw->tw_state;
	r->idiag_timer	      = 0;
	r->idiag_retrans      = 0;
	r->id.idiag_if	      = tw->tw_bound_dev_if;
	r->id.idiag_cookie[0] = (u32)(unsigned long)tw;
	r->id.idiag_cookie[1] = (u32)(((unsigned long)tw >> 31) >> 1);
	r->id.idiag_sport     = tw->tw_sport;
	r->id.idiag_dport     = tw->tw_dport;
	r->id.idiag_src[0]    = tw->tw_rcv_saddr;
	r->id.idiag_dst[0]    = tw->tw_daddr;
	r->idiag_state	      = tw->tw_substate;
	r->idiag_timer	      = 3;
	r->idiag_expires      = (tmo * 1000 + HZ - 1) / HZ;
	r->idiag_rqueue	      = 0;
	r->idiag_wqueue	      = 0;
	r->idiag_uid	      = 0;
	r->idiag_inode	      = 0;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
	if (tw->tw_family == AF_INET6) {
		const struct inet6_timewait_sock *tw6 =
						inet6_twsk((struct sock *)tw);

		ipv6_addr_copy((struct in6_addr *)r->id.idiag_src,
			       &tw6->tw_v6_rcv_saddr);
		ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst,
			       &tw6->tw_v6_daddr);
	}
#endif
	nlh->nlmsg_len = skb->tail - previous_tail;
	return skb->len;
nlmsg_failure:
	skb_trim(skb, previous_tail - skb->data);
	return -1;
}

static int inet_diag_get_exact(struct sk_buff *in_skb,
static int inet_diag_get_exact(struct sk_buff *in_skb,
			       const struct nlmsghdr *nlh)
			       const struct nlmsghdr *nlh)
{
{
@@ -450,6 +477,42 @@ static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk,
			      cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
			      cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}
}


static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
			       struct sk_buff *skb,
			       struct netlink_callback *cb)
{
	struct inet_diag_req *r = NLMSG_DATA(cb->nlh);

	if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
		struct inet_diag_entry entry;
		struct rtattr *bc = (struct rtattr *)(r + 1);

		entry.family = tw->tw_family;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		if (tw->tw_family == AF_INET6) {
			struct inet6_timewait_sock *tw6 =
						inet6_twsk((struct sock *)tw);
			entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32;
			entry.daddr = tw6->tw_v6_daddr.s6_addr32;
		} else
#endif
		{
			entry.saddr = &tw->tw_rcv_saddr;
			entry.daddr = &tw->tw_daddr;
		}
		entry.sport = tw->tw_num;
		entry.dport = ntohs(tw->tw_dport);
		entry.userlocks = 0; 

		if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
			return 0;
	}

	return inet_twsk_diag_fill(tw, skb, r->idiag_ext,
				   NETLINK_CB(cb->skb).pid,
				   cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}

static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
			      struct request_sock *req, u32 pid, u32 seq,
			      struct request_sock *req, u32 pid, u32 seq,
			      const struct nlmsghdr *unlh)
			      const struct nlmsghdr *unlh)
@@ -696,9 +759,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
		}
		}


		if (r->idiag_states & TCPF_TIME_WAIT) {
		if (r->idiag_states & TCPF_TIME_WAIT) {
			sk_for_each(sk, node,
			struct inet_timewait_sock *tw;

			inet_twsk_for_each(tw, node,
				    &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
				    &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
				const struct inet_timewait_sock *tw = inet_twsk(sk);


				if (num < s_num)
				if (num < s_num)
					goto next_dying;
					goto next_dying;
@@ -708,7 +772,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
				if (r->id.idiag_dport != tw->tw_dport &&
				if (r->id.idiag_dport != tw->tw_dport &&
				    r->id.idiag_dport)
				    r->id.idiag_dport)
					goto next_dying;
					goto next_dying;
				if (inet_diag_dump_sock(skb, sk, cb) < 0) {
				if (inet_twsk_diag_dump(tw, skb, cb) < 0) {
					read_unlock_bh(&head->lock);
					read_unlock_bh(&head->lock);
					goto done;
					goto done;
				}
				}