+static int tcpdiag_dump_sock(struct sk_buff *skb, struct sock *sk,
+ struct netlink_callback *cb)
+{
+ struct tcpdiagreq *r = NLMSG_DATA(cb->nlh);
+
+ if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
+ struct tcpdiag_entry entry;
+ struct rtattr *bc = (struct rtattr *)(r + 1);
+ struct inet_sock *inet = inet_sk(sk);
+
+ entry.family = sk->sk_family;
+#ifdef CONFIG_IP_TCPDIAG_IPV6
+ if (entry.family == AF_INET6) {
+ struct ipv6_pinfo *np = inet6_sk(sk);
+
+ entry.saddr = np->rcv_saddr.s6_addr32;
+ entry.daddr = np->daddr.s6_addr32;
+ } else
+#endif
+ {
+ entry.saddr = &inet->rcv_saddr;
+ entry.daddr = &inet->daddr;
+ }
+ entry.sport = inet->num;
+ entry.dport = ntohs(inet->dport);
+ entry.userlocks = sk->sk_userlocks;
+
+ if (!tcpdiag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry))
+ return 0;
+ }
+
+ return tcpdiag_fill(skb, sk, r->tcpdiag_ext, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI);
+}
+
+static int tcpdiag_fill_req(struct sk_buff *skb, struct sock *sk,
+ struct open_request *req,
+ u32 pid, u32 seq)
+{
+ struct inet_sock *inet = inet_sk(sk);
+ unsigned char *b = skb->tail;
+ struct tcpdiagmsg *r;
+ struct nlmsghdr *nlh;
+ long tmo;
+
+ nlh = NLMSG_PUT(skb, pid, seq, TCPDIAG_GETSOCK, sizeof(*r));
+ nlh->nlmsg_flags = NLM_F_MULTI;
+ r = NLMSG_DATA(nlh);
+
+ r->tcpdiag_family = sk->sk_family;
+ r->tcpdiag_state = TCP_SYN_RECV;
+ r->tcpdiag_timer = 1;
+ r->tcpdiag_retrans = req->retrans;
+
+ r->id.tcpdiag_if = sk->sk_bound_dev_if;
+ r->id.tcpdiag_cookie[0] = (u32)(unsigned long)req;
+ r->id.tcpdiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1);
+
+ tmo = req->expires - jiffies;
+ if (tmo < 0)
+ tmo = 0;
+
+ r->id.tcpdiag_sport = inet->sport;
+ r->id.tcpdiag_dport = req->rmt_port;
+ r->id.tcpdiag_src[0] = req->af.v4_req.loc_addr;
+ r->id.tcpdiag_dst[0] = req->af.v4_req.rmt_addr;
+ r->tcpdiag_expires = jiffies_to_msecs(tmo),
+ r->tcpdiag_rqueue = 0;
+ r->tcpdiag_wqueue = 0;
+ r->tcpdiag_uid = sock_i_uid(sk);
+ r->tcpdiag_inode = 0;
+#ifdef CONFIG_IP_TCPDIAG_IPV6
+ if (r->tcpdiag_family == AF_INET6) {
+ ipv6_addr_copy((struct in6_addr *)r->id.tcpdiag_src,
+ &req->af.v6_req.loc_addr);
+ ipv6_addr_copy((struct in6_addr *)r->id.tcpdiag_dst,
+ &req->af.v6_req.rmt_addr);
+ }
+#endif
+ nlh->nlmsg_len = skb->tail - b;
+
+ return skb->len;
+
+nlmsg_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int tcpdiag_dump_reqs(struct sk_buff *skb, struct sock *sk,
+ struct netlink_callback *cb)
+{
+ struct tcpdiag_entry entry;
+ struct tcpdiagreq *r = NLMSG_DATA(cb->nlh);
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_listen_opt *lopt;
+ struct rtattr *bc = NULL;
+ struct inet_sock *inet = inet_sk(sk);
+ int j, s_j;
+ int reqnum, s_reqnum;
+ int err = 0;
+
+ s_j = cb->args[3];
+ s_reqnum = cb->args[4];
+
+ if (s_j > 0)
+ s_j--;
+
+ entry.family = sk->sk_family;
+
+ read_lock_bh(&tp->syn_wait_lock);
+
+ lopt = tp->listen_opt;
+ if (!lopt || !lopt->qlen)
+ goto out;
+
+ if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) {
+ bc = (struct rtattr *)(r + 1);
+ entry.sport = inet->num;
+ entry.userlocks = sk->sk_userlocks;
+ }
+
+ for (j = s_j; j < TCP_SYNQ_HSIZE; j++) {
+ struct open_request *req, *head = lopt->syn_table[j];
+
+ reqnum = 0;
+ for (req = head; req; reqnum++, req = req->dl_next) {
+ if (reqnum < s_reqnum)
+ continue;
+ if (r->id.tcpdiag_dport != req->rmt_port &&
+ r->id.tcpdiag_dport)
+ continue;
+
+ if (bc) {
+ entry.saddr =
+#ifdef CONFIG_IP_TCPDIAG_IPV6
+ (entry.family == AF_INET6) ?
+ req->af.v6_req.loc_addr.s6_addr32 :
+#endif
+ &req->af.v4_req.loc_addr;
+ entry.daddr =
+#ifdef CONFIG_IP_TCPDIAG_IPV6
+ (entry.family == AF_INET6) ?
+ req->af.v6_req.rmt_addr.s6_addr32 :
+#endif
+ &req->af.v4_req.rmt_addr;
+ entry.dport = ntohs(req->rmt_port);
+
+ if (!tcpdiag_bc_run(RTA_DATA(bc),
+ RTA_PAYLOAD(bc), &entry))
+ continue;
+ }
+
+ err = tcpdiag_fill_req(skb, sk, req,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq);
+ if (err < 0) {
+ cb->args[3] = j + 1;
+ cb->args[4] = reqnum;
+ goto out;
+ }
+ }
+
+ s_reqnum = 0;
+ }
+
+out:
+ read_unlock_bh(&tp->syn_wait_lock);
+
+ return err;
+}