[TCPDIAG]: Implement cheapest way of supporting DCCPDIAG_GETSOCK

With ugly ifdefs, etc, but this actually:

1. keeps the existing ABI, i.e. no need to recompile the iproute2
   utilities if not interested in DCCP.

2. Provides all the tcp_diag functionality in DCCP, with just a
   small patch that makes iproute2 support DCCP.

Of course I'll get this cleaned-up in time, but for now I think its
OK to be this way to quickly get this functionality.

iproute2-ss050808 patch at:

http://vger.kernel.org/~acme/iproute2-ss050808.dccp.patch

Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/linux/tcp_diag.h b/include/linux/tcp_diag.h
index 7a59967..190494e 100644
--- a/include/linux/tcp_diag.h
+++ b/include/linux/tcp_diag.h
@@ -3,6 +3,7 @@
 
 /* Just some random number */
 #define TCPDIAG_GETSOCK 18
+#define DCCPDIAG_GETSOCK 19
 
 /* Socket identity */
 struct tcpdiag_sockid
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index f6da932..d3770ae 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -34,6 +34,8 @@
 	.port_rover	= 1024 - 1,
 };
 
+EXPORT_SYMBOL_GPL(dccp_hashinfo);
+
 static int dccp_v4_get_port(struct sock *sk, const unsigned short snum)
 {
 	return inet_csk_get_port(&dccp_hashinfo, sk, snum);
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
index 4288ecf..f5fc84a 100644
--- a/net/ipv4/tcp_diag.c
+++ b/net/ipv4/tcp_diag.c
@@ -45,11 +45,15 @@
 #define TCPDIAG_PUT(skb, attrtype, attrlen) \
 	RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
 
+#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+extern struct inet_hashinfo dccp_hashinfo;
+#endif
+
 static int tcpdiag_fill(struct sk_buff *skb, struct sock *sk,
-			int ext, u32 pid, u32 seq, u16 nlmsg_flags)
+			int ext, u32 pid, u32 seq, u16 nlmsg_flags,
+			const struct nlmsghdr *unlh)
 {
 	const struct inet_sock *inet = inet_sk(sk);
-	struct tcp_sock *tp = tcp_sk(sk);
 	const struct inet_connection_sock *icsk = inet_csk(sk);
 	struct tcpdiagmsg *r;
 	struct nlmsghdr  *nlh;
@@ -57,7 +61,7 @@
 	struct tcpdiag_meminfo  *minfo = NULL;
 	unsigned char	 *b = skb->tail;
 
-	nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r));
+	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
 	nlh->nlmsg_flags = nlmsg_flags;
 	r = NLMSG_DATA(nlh);
 	if (sk->sk_state != TCP_TIME_WAIT) {
@@ -147,8 +151,20 @@
 		r->tcpdiag_expires = 0;
 	}
 #undef EXPIRES_IN_MS
-	r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq;
-	r->tcpdiag_wqueue = tp->write_seq - tp->snd_una;
+	/*
+	 * Ahem... for now we'll have some knowledge about TCP -acme
+	 * But this is just one of two small exceptions, both in this
+	 * function, so lets close our eyes for some 15 lines or so... 8)
+	 * -acme
+	 */
+	if (sk->sk_protocol == IPPROTO_TCP) {
+		const struct tcp_sock *tp = tcp_sk(sk);
+
+		r->tcpdiag_rqueue = tp->rcv_nxt - tp->copied_seq;
+		r->tcpdiag_wqueue = tp->write_seq - tp->snd_una;
+	} else
+		r->tcpdiag_rqueue = r->tcpdiag_wqueue = 0;
+
 	r->tcpdiag_uid = sock_i_uid(sk);
 	r->tcpdiag_inode = sock_i_ino(sk);
 
@@ -159,8 +175,13 @@
 		minfo->tcpdiag_tmem = atomic_read(&sk->sk_wmem_alloc);
 	}
 
-	if (info) 
-		tcp_get_info(sk, info);
+	/* Ahem... for now we'll have some knowledge about TCP -acme */
+	if (info) {
+		if (sk->sk_protocol == IPPROTO_TCP) 
+			tcp_get_info(sk, info);
+		else
+			memset(info, 0, sizeof(*info));
+	}
 
 	if (sk->sk_state < TCP_TIME_WAIT &&
 	    icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info)
@@ -194,9 +215,13 @@
 	struct sock *sk;
 	struct tcpdiagreq *req = NLMSG_DATA(nlh);
 	struct sk_buff *rep;
-
+	struct inet_hashinfo *hashinfo = &tcp_hashinfo;
+#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+	if (nlh->nlmsg_type == DCCPDIAG_GETSOCK)
+		hashinfo = &dccp_hashinfo;
+#endif
 	if (req->tcpdiag_family == AF_INET) {
-		sk = inet_lookup(&tcp_hashinfo, req->id.tcpdiag_dst[0],
+		sk = inet_lookup(hashinfo, req->id.tcpdiag_dst[0],
 				 req->id.tcpdiag_dport, req->id.tcpdiag_src[0],
 				 req->id.tcpdiag_sport, req->id.tcpdiag_if);
 	}
@@ -230,7 +255,7 @@
 
 	if (tcpdiag_fill(rep, sk, req->tcpdiag_ext,
 			 NETLINK_CB(in_skb).pid,
-			 nlh->nlmsg_seq, 0) <= 0)
+			 nlh->nlmsg_seq, 0, nlh) <= 0)
 		BUG();
 
 	err = netlink_unicast(tcpnl, rep, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
@@ -436,12 +461,13 @@
 	}
 
 	return tcpdiag_fill(skb, sk, r->tcpdiag_ext, NETLINK_CB(cb->skb).pid,
-			    cb->nlh->nlmsg_seq, NLM_F_MULTI);
+			    cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
 }
 
 static int tcpdiag_fill_req(struct sk_buff *skb, struct sock *sk,
 			    struct request_sock *req,
-			    u32 pid, u32 seq)
+			    u32 pid, u32 seq,
+			    const struct nlmsghdr *unlh)
 {
 	const struct inet_request_sock *ireq = inet_rsk(req);
 	struct inet_sock *inet = inet_sk(sk);
@@ -450,7 +476,7 @@
 	struct nlmsghdr *nlh;
 	long tmo;
 
-	nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r));
+	nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r));
 	nlh->nlmsg_flags = NLM_F_MULTI;
 	r = NLMSG_DATA(nlh);
 
@@ -526,7 +552,7 @@
 		entry.userlocks = sk->sk_userlocks;
 	}
 
-	for (j = s_j; j < TCP_SYNQ_HSIZE; j++) {
+	for (j = s_j; j < lopt->nr_table_entries; j++) {
 		struct request_sock *req, *head = lopt->syn_table[j];
 
 		reqnum = 0;
@@ -561,7 +587,7 @@
 
 			err = tcpdiag_fill_req(skb, sk, req,
 					       NETLINK_CB(cb->skb).pid,
-					       cb->nlh->nlmsg_seq);
+					       cb->nlh->nlmsg_seq, cb->nlh);
 			if (err < 0) {
 				cb->args[3] = j + 1;
 				cb->args[4] = reqnum;
@@ -583,20 +609,26 @@
 	int i, num;
 	int s_i, s_num;
 	struct tcpdiagreq *r = NLMSG_DATA(cb->nlh);
+	struct inet_hashinfo *hashinfo;
 
 	s_i = cb->args[1];
 	s_num = num = cb->args[2];
-
+		hashinfo = &tcp_hashinfo;
+#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+	if (cb->nlh->nlmsg_type == DCCPDIAG_GETSOCK)
+		hashinfo = &dccp_hashinfo;
+#endif
 	if (cb->args[0] == 0) {
 		if (!(r->tcpdiag_states&(TCPF_LISTEN|TCPF_SYN_RECV)))
 			goto skip_listen_ht;
-		inet_listen_lock(&tcp_hashinfo);
+
+		inet_listen_lock(hashinfo);
 		for (i = s_i; i < INET_LHTABLE_SIZE; i++) {
 			struct sock *sk;
 			struct hlist_node *node;
 
 			num = 0;
-			sk_for_each(sk, node, &tcp_hashinfo.listening_hash[i]) {
+			sk_for_each(sk, node, &hashinfo->listening_hash[i]) {
 				struct inet_sock *inet = inet_sk(sk);
 
 				if (num < s_num) {
@@ -614,7 +646,7 @@
 					goto syn_recv;
 
 				if (tcpdiag_dump_sock(skb, sk, cb) < 0) {
-					inet_listen_unlock(&tcp_hashinfo);
+					inet_listen_unlock(hashinfo);
 					goto done;
 				}
 
@@ -623,7 +655,7 @@
 					goto next_listen;
 
 				if (tcpdiag_dump_reqs(skb, sk, cb) < 0) {
-					inet_listen_unlock(&tcp_hashinfo);
+					inet_listen_unlock(hashinfo);
 					goto done;
 				}
 
@@ -637,7 +669,7 @@
 			cb->args[3] = 0;
 			cb->args[4] = 0;
 		}
-		inet_listen_unlock(&tcp_hashinfo);
+		inet_listen_unlock(hashinfo);
 skip_listen_ht:
 		cb->args[0] = 1;
 		s_i = num = s_num = 0;
@@ -646,8 +678,8 @@
 	if (!(r->tcpdiag_states&~(TCPF_LISTEN|TCPF_SYN_RECV)))
 		return skb->len;
 
-	for (i = s_i; i < tcp_hashinfo.ehash_size; i++) {
-		struct inet_ehash_bucket *head = &tcp_hashinfo.ehash[i];
+	for (i = s_i; i < hashinfo->ehash_size; i++) {
+		struct inet_ehash_bucket *head = &hashinfo->ehash[i];
 		struct sock *sk;
 		struct hlist_node *node;
 
@@ -679,7 +711,7 @@
 
 		if (r->tcpdiag_states&TCPF_TIME_WAIT) {
 			sk_for_each(sk, node,
-				    &tcp_hashinfo.ehash[i + tcp_hashinfo.ehash_size].chain) {
+				    &hashinfo->ehash[i + hashinfo->ehash_size].chain) {
 				struct inet_sock *inet = inet_sk(sk);
 
 				if (num < s_num)
@@ -719,7 +751,11 @@
 	if (!(nlh->nlmsg_flags&NLM_F_REQUEST))
 		return 0;
 
-	if (nlh->nlmsg_type != TCPDIAG_GETSOCK)
+	if (nlh->nlmsg_type != TCPDIAG_GETSOCK
+#if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE)
+	    && nlh->nlmsg_type != DCCPDIAG_GETSOCK
+#endif
+	   )
 		goto err_inval;
 
 	if (NLMSG_LENGTH(sizeof(struct tcpdiagreq)) > skb->len)