[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: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Arnaldo Carvalho de Melo 2006-01-09 14:56:38 -08:00 committed by David S. Miller
parent 4e852c0279
commit c7d58aabdc
1 changed files with 110 additions and 46 deletions

View File

@ -70,20 +70,22 @@ static int inet_diag_fill(struct sk_buff *skb, struct sock *sk,
nlh->nlmsg_flags = nlmsg_flags;
r = NLMSG_DATA(nlh);
if (sk->sk_state != TCP_TIME_WAIT) {
if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO,
sizeof(*minfo));
if (ext & (1 << (INET_DIAG_INFO - 1)))
info = INET_DIAG_PUT(skb, INET_DIAG_INFO,
handler->idiag_info_size);
BUG_ON(sk->sk_state == TCP_TIME_WAIT);
if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
size_t len = strlen(icsk->icsk_ca_ops->name);
strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
icsk->icsk_ca_ops->name);
}
if (ext & (1 << (INET_DIAG_MEMINFO - 1)))
minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, sizeof(*minfo));
if (ext & (1 << (INET_DIAG_INFO - 1)))
info = INET_DIAG_PUT(skb, INET_DIAG_INFO,
handler->idiag_info_size);
if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) {
const size_t len = strlen(icsk->icsk_ca_ops->name);
strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1),
icsk->icsk_ca_ops->name);
}
r->idiag_family = sk->sk_family;
r->idiag_state = sk->sk_state;
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[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_dport = inet->dport;
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;
}
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,
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);
}
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,
struct request_sock *req, u32 pid, u32 seq,
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) {
sk_for_each(sk, node,
struct inet_timewait_sock *tw;
inet_twsk_for_each(tw, node,
&hashinfo->ehash[i + hashinfo->ehash_size].chain) {
const struct inet_timewait_sock *tw = inet_twsk(sk);
if (num < s_num)
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 &&
r->id.idiag_dport)
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);
goto done;
}