Commit 0d2cfd9d authored by Stephen Hemminger's avatar Stephen Hemminger

[IPVS]: Convert to seq_file.

parent c31c7015
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/ip_vs.h> #include <net/ip_vs.h>
...@@ -480,55 +481,104 @@ int ip_vs_app_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -480,55 +481,104 @@ int ip_vs_app_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb)
} }
#ifdef CONFIG_PROC_FS
/* /*
* /proc/net/ip_vs_app entry function * /proc/net/ip_vs_app entry function
*/ */
static int #define SEQ_START_TOKEN ((void *)1)
ip_vs_app_getinfo(char *buffer, char **start, off_t offset, int length)
static struct ip_vs_app *ip_vs_app_idx(loff_t pos)
{ {
off_t pos=0; loff_t off = 0;
int len=0;
char temp[64];
struct ip_vs_app *app, *inc;
struct list_head *e, *i; struct list_head *e, *i;
pos = 64; list_for_each(e, &ip_vs_app_list) {
if (pos > offset) { struct ip_vs_app *app
len += sprintf(buffer+len, "%-63s\n", = list_entry(e, struct ip_vs_app, a_list);
"prot port usecnt name"); list_for_each (i, &app->incs_list) {
if (off == pos)
return list_entry(i, struct ip_vs_app, a_list);
++off;
}
} }
return NULL;
}
static void *ip_vs_app_seq_start(struct seq_file *seq, loff_t *pos)
{
down(&__ip_vs_app_mutex); down(&__ip_vs_app_mutex);
list_for_each (e, &ip_vs_app_list) {
return *pos ? ip_vs_app_idx(*pos - 1) : SEQ_START_TOKEN;
}
static void *ip_vs_app_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct ip_vs_app *inc, *app;
struct list_head *i, *e;
++*pos;
if (v == SEQ_START_TOKEN)
return ip_vs_app_idx(0);
inc = v;
app = inc->app;
if ((i = inc->a_list.next) != &app->incs_list)
return list_entry(i, struct ip_vs_app, a_list);
/* go on to next application */
for (e = app->a_list.next; e != &ip_vs_app_list; e = e->next) {
app = list_entry(e, struct ip_vs_app, a_list); app = list_entry(e, struct ip_vs_app, a_list);
list_for_each (i, &app->incs_list) { list_for_each (i, &app->incs_list) {
inc = list_entry(i, struct ip_vs_app, a_list); return list_entry(i, struct ip_vs_app, a_list);
pos += 64;
if (pos <= offset)
continue;
sprintf(temp, "%-3s %-7u %-6d %-17s",
ip_vs_proto_name(inc->protocol),
ntohs(inc->port),
atomic_read(&inc->usecnt),
inc->name);
len += sprintf(buffer+len, "%-63s\n", temp);
if (pos >= offset+length)
goto done;
} }
} }
done: return NULL;
}
static void ip_vs_app_seq_stop(struct seq_file *seq, void *v)
{
up(&__ip_vs_app_mutex); up(&__ip_vs_app_mutex);
}
static int ip_vs_app_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "prot port usecnt name\n");
else {
const struct ip_vs_app *inc = v;
seq_printf(seq, "%-3s %-7u %-6d %-17s\n",
ip_vs_proto_name(inc->protocol),
ntohs(inc->port),
atomic_read(&inc->usecnt),
inc->name);
}
return 0;
}
static struct seq_operations ip_vs_app_seq_ops = {
.start = ip_vs_app_seq_start,
.next = ip_vs_app_seq_next,
.stop = ip_vs_app_seq_stop,
.show = ip_vs_app_seq_show,
};
*start = buffer+len-(pos-offset); /* Start of wanted data */ static int ip_vs_app_open(struct inode *inode, struct file *file)
len = pos-offset; {
if (len > length) return seq_open(file, &ip_vs_app_seq_ops);
len = length;
if (len < 0)
len = 0;
return len;
} }
static struct file_operations ip_vs_app_fops = {
.owner = THIS_MODULE,
.open = ip_vs_app_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif
/* /*
* Replace a segment of data with a new segment * Replace a segment of data with a new segment
...@@ -577,7 +627,7 @@ int ip_vs_skb_replace(struct sk_buff *skb, int pri, ...@@ -577,7 +627,7 @@ int ip_vs_skb_replace(struct sk_buff *skb, int pri,
int ip_vs_app_init(void) int ip_vs_app_init(void)
{ {
/* we will replace it with proc_net_ipvs_create() soon */ /* we will replace it with proc_net_ipvs_create() soon */
proc_net_create("ip_vs_app", 0, ip_vs_app_getinfo); proc_net_fops_create("ip_vs_app", 0, &ip_vs_app_fops);
return 0; return 0;
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/proc_fs.h> /* for proc_net_* */ #include <linux/proc_fs.h> /* for proc_net_* */
#include <linux/seq_file.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/random.h> #include <linux/random.h>
...@@ -188,14 +189,13 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get ...@@ -188,14 +189,13 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get
{ {
unsigned hash; unsigned hash;
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct list_head *l,*e; struct list_head *e;
hash = ip_vs_conn_hashkey(protocol, s_addr, s_port); hash = ip_vs_conn_hashkey(protocol, s_addr, s_port);
l = &ip_vs_conn_tab[hash];
ct_read_lock(hash); ct_read_lock(hash);
for (e=l->next; e!=l; e=e->next) { list_for_each(e, &ip_vs_conn_tab[hash]) {
cp = list_entry(e, struct ip_vs_conn, c_list); cp = list_entry(e, struct ip_vs_conn, c_list);
if (s_addr==cp->caddr && s_port==cp->cport && if (s_addr==cp->caddr && s_port==cp->cport &&
d_port==cp->vport && d_addr==cp->vaddr && d_port==cp->vport && d_addr==cp->vaddr &&
...@@ -242,17 +242,16 @@ struct ip_vs_conn *ip_vs_conn_out_get ...@@ -242,17 +242,16 @@ struct ip_vs_conn *ip_vs_conn_out_get
{ {
unsigned hash; unsigned hash;
struct ip_vs_conn *cp, *ret=NULL; struct ip_vs_conn *cp, *ret=NULL;
struct list_head *l,*e; struct list_head *e;
/* /*
* Check for "full" addressed entries * Check for "full" addressed entries
*/ */
hash = ip_vs_conn_hashkey(protocol, d_addr, d_port); hash = ip_vs_conn_hashkey(protocol, d_addr, d_port);
l = &ip_vs_conn_tab[hash];
ct_read_lock(hash); ct_read_lock(hash);
for (e=l->next; e!=l; e=e->next) { list_for_each(e, &ip_vs_conn_tab[hash]) {
cp = list_entry(e, struct ip_vs_conn, c_list); cp = list_entry(e, struct ip_vs_conn, c_list);
if (d_addr == cp->caddr && d_port == cp->cport && if (d_addr == cp->caddr && d_port == cp->cport &&
s_port == cp->dport && s_addr == cp->daddr && s_port == cp->dport && s_addr == cp->daddr &&
...@@ -615,61 +614,116 @@ ip_vs_conn_new(int proto, __u32 caddr, __u16 cport, __u32 vaddr, __u16 vport, ...@@ -615,61 +614,116 @@ ip_vs_conn_new(int proto, __u32 caddr, __u16 cport, __u32 vaddr, __u16 vport,
/* /*
* /proc/net/ip_vs_conn entries * /proc/net/ip_vs_conn entries
*/ */
static int #ifdef CONFIG_PROC_FS
ip_vs_conn_getinfo(char *buffer, char **start, off_t offset, int length)
{
off_t pos=0;
int idx, len=0;
char temp[70];
struct ip_vs_conn *cp;
struct list_head *l, *e;
pos = 128; #define SEQ_START_TOKEN ((void *)1)
if (pos > offset) {
len += sprintf(buffer+len, "%-127s\n",
"Pro FromIP FPrt ToIP TPrt DestIP DPrt State Expires");
}
static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos)
{
struct list_head *e;
int idx;
loff_t off = 0;
for(idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) { for(idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {
/*
* Lock is actually only need in next loop
* we are called from uspace: must stop bh.
*/
ct_read_lock_bh(idx); ct_read_lock_bh(idx);
list_for_each(e, &ip_vs_conn_tab[idx]) {
if (off == pos) {
seq->private = &ip_vs_conn_tab[idx];
return list_entry(e, struct ip_vs_conn, c_list);
}
++off;
}
ct_read_unlock_bh(idx);
}
l = &ip_vs_conn_tab[idx]; return NULL;
for (e=l->next; e!=l; e=e->next) { }
cp = list_entry(e, struct ip_vs_conn, c_list);
pos += 128; static void *ip_vs_conn_seq_start(struct seq_file *seq, loff_t *pos)
if (pos <= offset) {
continue; seq->private = NULL;
sprintf(temp, return *pos ? ip_vs_conn_array(seq, *pos - 1) :SEQ_START_TOKEN;
"%-3s %08X %04X %08X %04X %08X %04X %-11s %7lu", }
static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct ip_vs_conn *cp = v;
struct list_head *e, *l = seq->private;
int idx;
++*pos;
if (v == SEQ_START_TOKEN)
return ip_vs_conn_array(seq, 0);
/* more on same hash chain? */
if ((e = cp->c_list.next) != l)
return list_entry(e, struct ip_vs_conn, c_list);
idx = l - ip_vs_conn_tab;
ct_read_unlock_bh(idx);
while (++idx < IP_VS_CONN_TAB_SIZE) {
ct_read_lock_bh(idx);
list_for_each(e, &ip_vs_conn_tab[idx]) {
seq->private = &ip_vs_conn_tab[idx];
return list_entry(e, struct ip_vs_conn, c_list);
}
ct_read_unlock_bh(idx);
}
seq->private = NULL;
return NULL;
}
static void ip_vs_conn_seq_stop(struct seq_file *seq, void *v)
{
struct list_head *l = seq->private;
if (l)
ct_read_unlock(l - ip_vs_conn_tab);
}
static int ip_vs_conn_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq,
"Pro FromIP FPrt ToIP TPrt DestIP DPrt State Expires\n");
else {
const struct ip_vs_conn *cp = v;
seq_printf(seq,
"%-3s %08X %04X %08X %04X %08X %04X %-11s %7lu\n",
ip_vs_proto_name(cp->protocol), ip_vs_proto_name(cp->protocol),
ntohl(cp->caddr), ntohs(cp->cport), ntohl(cp->caddr), ntohs(cp->cport),
ntohl(cp->vaddr), ntohs(cp->vport), ntohl(cp->vaddr), ntohs(cp->vport),
ntohl(cp->daddr), ntohs(cp->dport), ntohl(cp->daddr), ntohs(cp->dport),
ip_vs_state_name(cp->protocol, cp->state), ip_vs_state_name(cp->protocol, cp->state),
(cp->timer.expires-jiffies)/HZ); (cp->timer.expires-jiffies)/HZ);
len += sprintf(buffer+len, "%-127s\n", temp);
if (pos >= offset+length) {
ct_read_unlock_bh(idx);
goto done;
}
}
ct_read_unlock_bh(idx);
} }
return 0;
}
static struct seq_operations ip_vs_conn_seq_ops = {
.start = ip_vs_conn_seq_start,
.next = ip_vs_conn_seq_next,
.stop = ip_vs_conn_seq_stop,
.show = ip_vs_conn_seq_show,
};
done: static int ip_vs_conn_open(struct inode *inode, struct file *file)
*start = buffer+len-(pos-offset); /* Start of wanted data */ {
len = pos-offset; return seq_open(file, &ip_vs_conn_seq_ops);
if (len > length)
len = length;
if (len < 0)
len = 0;
return len;
} }
static struct file_operations ip_vs_conn_fops = {
.owner = THIS_MODULE,
.open = ip_vs_conn_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif
/* /*
* Randomly drop connection entries before running out of memory * Randomly drop connection entries before running out of memory
...@@ -707,7 +761,7 @@ void ip_vs_random_dropentry(void) ...@@ -707,7 +761,7 @@ void ip_vs_random_dropentry(void)
{ {
int idx; int idx;
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct list_head *l,*e; struct list_head *e;
struct ip_vs_conn *ct; struct ip_vs_conn *ct;
/* /*
...@@ -721,8 +775,7 @@ void ip_vs_random_dropentry(void) ...@@ -721,8 +775,7 @@ void ip_vs_random_dropentry(void)
*/ */
ct_write_lock(hash); ct_write_lock(hash);
l = &ip_vs_conn_tab[hash]; list_for_each(e, &ip_vs_conn_tab[hash]) {
for (e=l->next; e!=l; e=e->next) {
cp = list_entry(e, struct ip_vs_conn, c_list); cp = list_entry(e, struct ip_vs_conn, c_list);
if (!cp->cport && !(cp->flags & IP_VS_CONN_F_NO_CPORT)) if (!cp->cport && !(cp->flags & IP_VS_CONN_F_NO_CPORT))
/* connection template */ /* connection template */
...@@ -775,7 +828,7 @@ static void ip_vs_conn_flush(void) ...@@ -775,7 +828,7 @@ static void ip_vs_conn_flush(void)
{ {
int idx; int idx;
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct list_head *l,*e; struct list_head *e;
struct ip_vs_conn *ct; struct ip_vs_conn *ct;
flush_again: flush_again:
...@@ -785,8 +838,7 @@ static void ip_vs_conn_flush(void) ...@@ -785,8 +838,7 @@ static void ip_vs_conn_flush(void)
*/ */
ct_write_lock_bh(idx); ct_write_lock_bh(idx);
l = &ip_vs_conn_tab[idx]; list_for_each(e, &ip_vs_conn_tab[idx]) {
for (e=l->next; e!=l; e=e->next) {
cp = list_entry(e, struct ip_vs_conn, c_list); cp = list_entry(e, struct ip_vs_conn, c_list);
atomic_inc(&cp->refcnt); atomic_inc(&cp->refcnt);
ct_write_unlock(idx); ct_write_unlock(idx);
...@@ -848,7 +900,7 @@ int ip_vs_conn_init(void) ...@@ -848,7 +900,7 @@ int ip_vs_conn_init(void)
__ip_vs_conntbl_lock_array[idx].l = RW_LOCK_UNLOCKED; __ip_vs_conntbl_lock_array[idx].l = RW_LOCK_UNLOCKED;
} }
proc_net_create("ip_vs_conn", 0, ip_vs_conn_getinfo); proc_net_fops_create("ip_vs_conn", 0, &ip_vs_conn_fops);
/* calculate the random value for connection hash */ /* calculate the random value for connection hash */
get_random_bytes(&ip_vs_conn_rnd, sizeof(ip_vs_conn_rnd)); get_random_bytes(&ip_vs_conn_rnd, sizeof(ip_vs_conn_rnd));
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
...@@ -1507,207 +1509,253 @@ static struct ip_vs_sysctl_table ipv4_vs_table = { ...@@ -1507,207 +1509,253 @@ static struct ip_vs_sysctl_table ipv4_vs_table = {
{0}} {0}}
}; };
#ifdef CONFIG_PROC_FS
struct ip_vs_iter {
struct list_head *table;
int bucket;
};
#define SEQ_START_TOKEN ((void *)1)
/* /*
* Write the contents of the VS rule table to a PROCfs file. * Write the contents of the VS rule table to a PROCfs file.
* (It is kept just for backward compatibility) * (It is kept just for backward compatibility)
*/ */
static inline char *ip_vs_fwd_name(unsigned flags) static inline const char *ip_vs_fwd_name(unsigned flags)
{ {
char *fwd;
switch (flags & IP_VS_CONN_F_FWD_MASK) { switch (flags & IP_VS_CONN_F_FWD_MASK) {
case IP_VS_CONN_F_LOCALNODE: case IP_VS_CONN_F_LOCALNODE:
fwd = "Local"; return "Local";
break;
case IP_VS_CONN_F_TUNNEL: case IP_VS_CONN_F_TUNNEL:
fwd = "Tunnel"; return "Tunnel";
break;
case IP_VS_CONN_F_DROUTE: case IP_VS_CONN_F_DROUTE:
fwd = "Route"; return "Route";
break;
default: default:
fwd = "Masq"; return "Masq";
} }
return fwd;
} }
static inline int sprintf_dest(char *str, struct ip_vs_dest *dest)
{
return sprintf(str, " -> %08X:%04X %-7s %-6d %-10d %-10d",
ntohl(dest->addr), ntohs(dest->port),
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
atomic_read(&dest->inactconns));
}
static int ip_vs_get_info(char *buf, char **start, off_t offset, int length) /* Get the Nth entry in the two lists */
static struct ip_vs_service *ip_vs_info_array(struct seq_file *seq, loff_t pos)
{ {
int len=0; struct ip_vs_iter *iter = seq->private;
off_t pos=0;
char temp[64], temp2[32];
int idx; int idx;
struct ip_vs_service *svc; struct list_head *e;
struct ip_vs_dest *dest;
struct list_head *l, *e, *p, *q;
/*
* Note: since the length of the buffer is usually the multiple
* of 512, it is good to use fixed record of the divisor of 512,
* so that records won't be truncated at buffer boundary.
*/
pos = 192;
if (pos > offset) {
sprintf(temp,
"IP Virtual Server version %d.%d.%d (size=%d)",
NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
len += sprintf(buf+len, "%-63s\n", temp);
len += sprintf(buf+len, "%-63s\n",
"Prot LocalAddress:Port Scheduler Flags");
len += sprintf(buf+len, "%-63s\n",
" -> RemoteAddress:Port Forward Weight ActiveConn InActConn");
}
read_lock_bh(&__ip_vs_svc_lock); /* look in hash by protocol */
/* print the service table hashed by <protocol,addr,port> */
for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
l = &ip_vs_svc_table[idx]; list_for_each(e, &ip_vs_svc_table[idx]) {
for (e=l->next; e!=l; e=e->next) { if (pos-- == 0){
svc = list_entry(e, struct ip_vs_service, s_list); iter->table = ip_vs_svc_table;
pos += 64; iter->bucket = idx;
if (pos > offset) { return list_entry(e, struct ip_vs_service, s_list);
if (svc->flags & IP_VS_SVC_F_PERSISTENT)
sprintf(temp2, "persistent %d %08X",
svc->timeout,
ntohl(svc->netmask));
else
temp2[0] = '\0';
sprintf(temp, "%s %08X:%04X %s %s",
ip_vs_proto_name(svc->protocol),
ntohl(svc->addr),
ntohs(svc->port),
svc->scheduler->name, temp2);
len += sprintf(buf+len, "%-63s\n", temp);
if (len >= length)
goto done;
}
p = &svc->destinations;
for (q=p->next; q!=p; q=q->next) {
dest = list_entry(q, struct ip_vs_dest, n_list);
pos += 64;
if (pos <= offset)
continue;
sprintf_dest(temp, dest);
len += sprintf(buf+len, "%-63s\n", temp);
if (len >= length)
goto done;
} }
} }
} }
/* print the service table hashed by fwmark */ /* keep looking in fwmark */
for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) { for (idx = 0; idx < IP_VS_SVC_TAB_SIZE; idx++) {
l = &ip_vs_svc_fwm_table[idx]; list_for_each(e, &ip_vs_svc_fwm_table[idx]) {
for (e=l->next; e!=l; e=e->next) { if (pos-- == 0) {
svc = list_entry(e, struct ip_vs_service, f_list); iter->table = ip_vs_svc_fwm_table;
pos += 64; iter->bucket = idx;
if (pos > offset) { return list_entry(e, struct ip_vs_service, f_list);
if (svc->flags & IP_VS_SVC_F_PERSISTENT)
sprintf(temp2, "persistent %d %08X",
svc->timeout,
ntohl(svc->netmask));
else
temp2[0] = '\0';
sprintf(temp, "FWM %08X %s %s",
svc->fwmark,
svc->scheduler->name, temp2);
len += sprintf(buf+len, "%-63s\n", temp);
if (len >= length)
goto done;
} }
}
}
return NULL;
}
p = &svc->destinations; static void *ip_vs_info_seq_start(struct seq_file *seq, loff_t *pos)
for (q=p->next; q!=p; q=q->next) { {
dest = list_entry(q, struct ip_vs_dest, n_list);
pos += 64; read_lock_bh(&__ip_vs_svc_lock);
if (pos <= offset) return *pos ? ip_vs_info_array(seq, *pos - 1) : SEQ_START_TOKEN;
continue; }
sprintf_dest(temp, dest);
len += sprintf(buf+len, "%-63s\n", temp);
if (len >= length) static void *ip_vs_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
goto done; {
struct list_head *e;
struct ip_vs_iter *iter;
struct ip_vs_service *svc;
++*pos;
if (v == SEQ_START_TOKEN)
return ip_vs_info_array(seq,0);
svc = v;
iter = seq->private;
if (iter->table == ip_vs_svc_table) {
/* next service in table hashed by protocol */
if ((e = svc->s_list.next) != &ip_vs_svc_table[iter->bucket])
return list_entry(e, struct ip_vs_service, s_list);
while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
list_for_each(e, &ip_vs_svc_table[iter->bucket]) {
return list_entry(e, struct ip_vs_service, s_list);
} }
} }
iter->table = ip_vs_svc_fwm_table;
iter->bucket = -1;
goto scan_fwmark;
} }
done: /* next service in hashed by fwmark */
if ((e = svc->f_list.next) != &ip_vs_svc_fwm_table[iter->bucket])
return list_entry(e, struct ip_vs_service, f_list);
scan_fwmark:
while (++iter->bucket < IP_VS_SVC_TAB_SIZE) {
list_for_each(e, &ip_vs_svc_fwm_table[iter->bucket])
return list_entry(e, struct ip_vs_service, f_list);
}
return NULL;
}
static void ip_vs_info_seq_stop(struct seq_file *seq, void *v)
{
read_unlock_bh(&__ip_vs_svc_lock); read_unlock_bh(&__ip_vs_svc_lock);
}
static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN) {
seq_printf(seq,
"IP Virtual Server version %d.%d.%d (size=%d)\n",
NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
seq_puts(seq,
"Prot LocalAddress:Port Scheduler Flags\n");
seq_puts(seq,
" -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n");
} else {
const struct ip_vs_service *svc = v;
const struct ip_vs_iter *iter = seq->private;
const struct ip_vs_dest *dest;
if (iter->table == ip_vs_svc_table)
seq_printf(seq, "%s %08X:%04X %s ",
ip_vs_proto_name(svc->protocol),
ntohl(svc->addr),
ntohs(svc->port),
svc->scheduler->name);
else
seq_printf(seq, "FWM %08X %s ",
svc->fwmark, svc->scheduler->name);
if (svc->flags & IP_VS_SVC_F_PERSISTENT)
seq_printf(seq, "persistent %d %08X\n",
svc->timeout,
ntohl(svc->netmask));
else
seq_putc(seq, '\n');
list_for_each_entry(dest, &svc->destinations, n_list) {
seq_printf(seq,
" -> %08X:%04X %-7s %-6d %-10d %-10d\n",
ntohl(dest->addr), ntohs(dest->port),
ip_vs_fwd_name(atomic_read(&dest->conn_flags)),
atomic_read(&dest->weight),
atomic_read(&dest->activeconns),
atomic_read(&dest->inactconns));
}
}
return 0;
}
*start = buf+len-(pos-offset); /* Start of wanted data */ static struct seq_operations ip_vs_info_seq_ops = {
len = pos-offset; .start = ip_vs_info_seq_start,
if (len > length) .next = ip_vs_info_seq_next,
len = length; .stop = ip_vs_info_seq_stop,
if (len < 0) .show = ip_vs_info_seq_show,
len = 0; };
return len;
static int ip_vs_info_open(struct inode *inode, struct file *file)
{
struct seq_file *seq;
int rc = -ENOMEM;
struct ip_vs_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
goto out;
rc = seq_open(file, &ip_vs_info_seq_ops);
if (rc)
goto out_kfree;
seq = file->private_data;
seq->private = s;
memset(s, 0, sizeof(*s));
out:
return rc;
out_kfree:
kfree(s);
goto out;
} }
static struct file_operations ip_vs_info_fops = {
.owner = THIS_MODULE,
.open = ip_vs_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#endif
struct ip_vs_stats ip_vs_stats; struct ip_vs_stats ip_vs_stats;
static int #ifdef CONFIG_PROC_FS
ip_vs_stats_get_info(char *buf, char **start, off_t offset, int length) static int ip_vs_stats_show(struct seq_file *seq, void *v)
{ {
int len=0;
off_t pos=0; /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
char temp[64]; seq_puts(seq,
" Total Incoming Outgoing Incoming Outgoing\n");
pos += 320; seq_printf(seq,
if (pos > offset) { " Conns Packets Packets Bytes Bytes\n");
len += sprintf(buf+len, "%-63s\n%-63s\n",
/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ spin_lock_bh(&ip_vs_stats.lock);
" Total Incoming Outgoing Incoming Outgoing", seq_printf(seq, "%8X %8X %8X %16LX %16LX\n\n", ip_vs_stats.conns,
" Conns Packets Packets Bytes Bytes"); ip_vs_stats.inpkts, ip_vs_stats.outpkts,
ip_vs_stats.inbytes, ip_vs_stats.outbytes);
spin_lock_bh(&ip_vs_stats.lock);
sprintf(temp, "%8X %8X %8X %8X%08X %8X%08X", /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
ip_vs_stats.conns, seq_puts(seq,
ip_vs_stats.inpkts, " Conns/s Pkts/s Pkts/s Bytes/s Bytes/s\n");
ip_vs_stats.outpkts, seq_printf(seq,"%8X %8X %8X %16X %16X\n",
(__u32)(ip_vs_stats.inbytes>>32),
(__u32)ip_vs_stats.inbytes,
(__u32)(ip_vs_stats.outbytes>>32),
(__u32)ip_vs_stats.outbytes);
len += sprintf(buf+len, "%-62s\n\n", temp);
len += sprintf(buf+len, "%-63s\n",
/* 01234567 01234567 01234567 0123456701234567 0123456701234567 */
" Conns/s Pkts/s Pkts/s Bytes/s Bytes/s");
sprintf(temp, "%8X %8X %8X %16X %16X",
ip_vs_stats.cps, ip_vs_stats.cps,
ip_vs_stats.inpps, ip_vs_stats.inpps,
ip_vs_stats.outpps, ip_vs_stats.outpps,
ip_vs_stats.inbps, ip_vs_stats.inbps,
ip_vs_stats.outbps); ip_vs_stats.outbps);
len += sprintf(buf+len, "%-63s\n", temp); spin_unlock_bh(&ip_vs_stats.lock);
spin_unlock_bh(&ip_vs_stats.lock); return 0;
} }
*start = buf+len-(pos-offset); /* Start of wanted data */ static int ip_vs_stats_seq_open(struct inode *inode, struct file *file)
len = pos-offset; {
if (len > length) return single_open(file, ip_vs_stats_show, NULL);
len = length;
if (len < 0)
len = 0;
return len;
} }
static struct file_operations ip_vs_stats_fops = {
.owner = THIS_MODULE,
.open = ip_vs_stats_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
/* /*
* Set timeout values for tcp tcpfin udp in the timeout_table. * Set timeout values for tcp tcpfin udp in the timeout_table.
...@@ -2195,8 +2243,8 @@ int ip_vs_control_init(void) ...@@ -2195,8 +2243,8 @@ int ip_vs_control_init(void)
return ret; return ret;
} }
proc_net_create("ip_vs", 0, ip_vs_get_info); proc_net_fops_create("ip_vs", 0, &ip_vs_info_fops);
proc_net_create("ip_vs_stats", 0, ip_vs_stats_get_info); proc_net_fops_create("ip_vs_stats",0, &ip_vs_stats_fops);
ipv4_vs_table.sysctl_header = ipv4_vs_table.sysctl_header =
register_sysctl_table(ipv4_vs_table.root_dir, 0); register_sysctl_table(ipv4_vs_table.root_dir, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment