* YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which
* Alexey Kuznetsov allow both IPv4 and IPv6 sockets to bind
* a single port at the same time.
+ * YOSHIFUJI Hideaki @USAGI: convert /proc/net/tcp6 to seq_file.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
static void tcp_v6_send_reset(struct sk_buff *skb);
static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req);
static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
}
/* Proc filesystem TCPv6 sock list dumping. */
-static void get_openreq6(struct sock *sk, struct open_request *req, char *tmpbuf, int i, int uid)
+static void get_openreq6(struct seq_file *seq,
+ struct sock *sk, struct open_request *req, int i, int uid)
{
struct in6_addr *dest, *src;
int ttd = req->expires - jiffies;
src = &req->af.v6_req.loc_addr;
dest = &req->af.v6_req.rmt_addr;
- sprintf(tmpbuf,
- "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p",
- i,
- src->s6_addr32[0], src->s6_addr32[1],
- src->s6_addr32[2], src->s6_addr32[3],
- ntohs(inet_sk(sk)->sport),
- dest->s6_addr32[0], dest->s6_addr32[1],
- dest->s6_addr32[2], dest->s6_addr32[3],
- ntohs(req->rmt_port),
- TCP_SYN_RECV,
- 0,0, /* could print option size, but that is af dependent. */
- 1, /* timers active (only the expire timer) */
- ttd,
- req->retrans,
- uid,
- 0, /* non standard timer */
- 0, /* open_requests have no inode */
- 0, req);
+ seq_printf(seq,
+ "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p\n",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3],
+ ntohs(inet_sk(sk)->sport),
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3],
+ ntohs(req->rmt_port),
+ TCP_SYN_RECV,
+ 0,0, /* could print option size, but that is af dependent. */
+ 1, /* timers active (only the expire timer) */
+ ttd,
+ req->retrans,
+ uid,
+ 0, /* non standard timer */
+ 0, /* open_requests have no inode */
+ 0, req);
}
-static void get_tcp6_sock(struct sock *sp, char *tmpbuf, int i)
+static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
{
struct in6_addr *dest, *src;
__u16 destp, srcp;
timer_expires = jiffies;
}
- sprintf(tmpbuf,
- "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %u %u %u %u %d",
- i,
- src->s6_addr32[0], src->s6_addr32[1],
- src->s6_addr32[2], src->s6_addr32[3], srcp,
- dest->s6_addr32[0], dest->s6_addr32[1],
- dest->s6_addr32[2], dest->s6_addr32[3], destp,
- sp->state,
- tp->write_seq-tp->snd_una, tp->rcv_nxt-tp->copied_seq,
- timer_active, timer_expires-jiffies,
- tp->retransmits,
- sock_i_uid(sp),
- tp->probes_out,
- sock_i_ino(sp),
- atomic_read(&sp->refcnt), sp,
- tp->rto, tp->ack.ato, (tp->ack.quick<<1)|tp->ack.pingpong,
- tp->snd_cwnd, tp->snd_ssthresh>=0xFFFF?-1:tp->snd_ssthresh
- );
+ seq_printf(seq,
+ "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %u %u %u %u %d\n",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3], srcp,
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3], destp,
+ sp->state,
+ tp->write_seq-tp->snd_una, tp->rcv_nxt-tp->copied_seq,
+ timer_active, timer_expires-jiffies,
+ tp->retransmits,
+ sock_i_uid(sp),
+ tp->probes_out,
+ sock_i_ino(sp),
+ atomic_read(&sp->refcnt), sp,
+ tp->rto, tp->ack.ato, (tp->ack.quick<<1)|tp->ack.pingpong,
+ tp->snd_cwnd, tp->snd_ssthresh>=0xFFFF?-1:tp->snd_ssthresh
+ );
}
-static void get_timewait6_sock(struct tcp_tw_bucket *tw, char *tmpbuf, int i)
+static void get_timewait6_sock(struct seq_file *seq,
+ struct tcp_tw_bucket *tw, int i)
{
struct in6_addr *dest, *src;
__u16 destp, srcp;
destp = ntohs(tw->dport);
srcp = ntohs(tw->sport);
- sprintf(tmpbuf,
- "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p",
- i,
- src->s6_addr32[0], src->s6_addr32[1],
- src->s6_addr32[2], src->s6_addr32[3], srcp,
- dest->s6_addr32[0], dest->s6_addr32[1],
- dest->s6_addr32[2], dest->s6_addr32[3], destp,
- tw->substate, 0, 0,
- 3, ttd, 0, 0, 0, 0,
- atomic_read(&tw->refcnt), tw);
+ seq_printf(seq,
+ "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08X %08X %5d %8d %d %d %p\n",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3], srcp,
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3], destp,
+ tw->substate, 0, 0,
+ 3, ttd, 0, 0, 0, 0,
+ atomic_read(&tw->refcnt), tw);
}
-#define LINE_LEN 190
-#define LINE_FMT "%-190s\n"
-
-int tcp6_get_info(char *buffer, char **start, off_t offset, int length)
+static int tcp6_seq_show(struct seq_file *seq, void *v)
{
- int len = 0, num = 0, i;
- off_t begin, pos = 0;
- char tmpbuf[LINE_LEN+2];
-
- if (offset < LINE_LEN+1)
- len += sprintf(buffer, LINE_FMT,
- " sl " /* 6 */
- "local_address " /* 38 */
- "remote_address " /* 38 */
- "st tx_queue rx_queue tr tm->when retrnsmt" /* 41 */
- " uid timeout inode"); /* 21 */
- /*----*/
- /*144 */
-
- pos = LINE_LEN+1;
-
- /* First, walk listening socket table. */
- tcp_listen_lock();
- for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
- struct sock *sk = tcp_listening_hash[i];
- struct tcp_listen_opt *lopt;
- int k;
-
- for (sk = tcp_listening_hash[i]; sk; sk = sk->next, num++) {
- struct open_request *req;
- int uid;
- struct tcp_opt *tp = tcp_sk(sk);
-
- if (sk->family != PF_INET6)
- continue;
- pos += LINE_LEN+1;
- if (pos >= offset) {
- get_tcp6_sock(sk, tmpbuf, num);
- len += sprintf(buffer+len, LINE_FMT, tmpbuf);
- if (pos >= offset + length) {
- tcp_listen_unlock();
- goto out_no_bh;
- }
- }
-
- uid = sock_i_uid(sk);
- read_lock_bh(&tp->syn_wait_lock);
- lopt = tp->listen_opt;
- if (lopt && lopt->qlen != 0) {
- for (k=0; k<TCP_SYNQ_HSIZE; k++) {
- for (req = lopt->syn_table[k]; req; req = req->dl_next, num++) {
- if (req->class->family != PF_INET6)
- continue;
- pos += LINE_LEN+1;
- if (pos <= offset)
- continue;
- get_openreq6(sk, req, tmpbuf, num, uid);
- len += sprintf(buffer+len, LINE_FMT, tmpbuf);
- if (pos >= offset + length) {
- read_unlock_bh(&tp->syn_wait_lock);
- tcp_listen_unlock();
- goto out_no_bh;
- }
- }
- }
- }
- read_unlock_bh(&tp->syn_wait_lock);
+ struct tcp_iter_state *st;
+
+ if (v == (void *)1) {
+ seq_printf(seq,
+ " sl "
+ "local_address "
+ "remote_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt"
+ " uid timeout inode\n");
+ goto out;
+ }
+ st = seq->private;
- /* Completed requests are in normal socket hash table */
- }
+ switch (st->state) {
+ case TCP_SEQ_STATE_LISTENING:
+ case TCP_SEQ_STATE_ESTABLISHED:
+ get_tcp6_sock(seq, v, st->num);
+ break;
+ case TCP_SEQ_STATE_OPENREQ:
+ get_openreq6(seq, st->syn_wait_sk, v, st->num, st->uid);
+ break;
+ case TCP_SEQ_STATE_TIME_WAIT:
+ get_timewait6_sock(seq, v, st->num);
+ break;
}
- tcp_listen_unlock();
+out:
+ return 0;
+}
- local_bh_disable();
+static struct file_operations tcp6_seq_fops;
+static struct tcp_seq_afinfo tcp6_seq_afinfo = {
+ .owner = THIS_MODULE,
+ .name = "tcp6",
+ .family = AF_INET6,
+ .seq_show = tcp6_seq_show,
+ .seq_fops = &tcp6_seq_fops,
+};
- /* Next, walk established hash chain. */
- for (i = 0; i < tcp_ehash_size; i++) {
- struct tcp_ehash_bucket *head = &tcp_ehash[i];
- struct sock *sk;
- struct tcp_tw_bucket *tw;
-
- read_lock(&head->lock);
- for(sk = head->chain; sk; sk = sk->next, num++) {
- if (sk->family != PF_INET6)
- continue;
- pos += LINE_LEN+1;
- if (pos <= offset)
- continue;
- get_tcp6_sock(sk, tmpbuf, num);
- len += sprintf(buffer+len, LINE_FMT, tmpbuf);
- if (pos >= offset + length) {
- read_unlock(&head->lock);
- goto out;
- }
- }
- for (tw = (struct tcp_tw_bucket *)tcp_ehash[i+tcp_ehash_size].chain;
- tw != NULL;
- tw = (struct tcp_tw_bucket *)tw->next, num++) {
- if (tw->family != PF_INET6)
- continue;
- pos += LINE_LEN+1;
- if (pos <= offset)
- continue;
- get_timewait6_sock(tw, tmpbuf, num);
- len += sprintf(buffer+len, LINE_FMT, tmpbuf);
- if (pos >= offset + length) {
- read_unlock(&head->lock);
- goto out;
- }
- }
- read_unlock(&head->lock);
- }
+int __init tcp6_proc_init(void)
+{
+ return tcp_proc_register(&tcp6_seq_afinfo);
+}
-out:
- local_bh_enable();
-out_no_bh:
-
- begin = len - (pos - offset);
- *start = buffer + begin;
- len -= begin;
- if (len > length)
- len = length;
- if (len < 0)
- len = 0;
- return len;
+void tcp6_proc_exit(void)
+{
+ tcp_proc_unregister(&tcp6_seq_afinfo);
}
struct proto tcpv6_prot = {