Commit 97b085e2 authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Fix conntrack seq_file handling.

Am travelling, but this passed simple tests here.  If this isn't going
in, the current seqfile stuff should be ripped out; it's a mess.

/proc/net/ip_conntrack was changed over to seq_file.  However,
seq_file isn't a great fit (a linked list which is changing is not a
good candidate for seq file), and the conversion was done badly.

1) Don't do allocation: simply hand the pointer head of the correct chain.
2) Actually output the original tuple.
3) Lock only when actually traversing hash chain.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7066e0b7
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
#define _IP_CONNTRACK_PROTOCOL_H #define _IP_CONNTRACK_PROTOCOL_H
#include <linux/netfilter_ipv4/ip_conntrack.h> #include <linux/netfilter_ipv4/ip_conntrack.h>
/* length of buffer to which print_tuple/print_conntrack members are struct seq_file;
* writing */
#define IP_CT_PRINT_BUFLEN 100
struct ip_conntrack_protocol struct ip_conntrack_protocol
{ {
...@@ -31,13 +28,12 @@ struct ip_conntrack_protocol ...@@ -31,13 +28,12 @@ struct ip_conntrack_protocol
int (*invert_tuple)(struct ip_conntrack_tuple *inverse, int (*invert_tuple)(struct ip_conntrack_tuple *inverse,
const struct ip_conntrack_tuple *orig); const struct ip_conntrack_tuple *orig);
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. Return like seq_* */
unsigned int (*print_tuple)(char *buffer, int (*print_tuple)(struct seq_file *,
const struct ip_conntrack_tuple *); const struct ip_conntrack_tuple *);
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
unsigned int (*print_conntrack)(char *buffer, int (*print_conntrack)(struct seq_file *, const struct ip_conntrack *);
const struct ip_conntrack *);
/* Returns verdict for packet, or -1 for invalid. */ /* Returns verdict for packet, or -1 for invalid. */
int (*packet)(struct ip_conntrack *conntrack, int (*packet)(struct ip_conntrack *conntrack,
......
...@@ -34,15 +34,15 @@ static int generic_invert_tuple(struct ip_conntrack_tuple *tuple, ...@@ -34,15 +34,15 @@ static int generic_invert_tuple(struct ip_conntrack_tuple *tuple,
} }
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. */
static unsigned int generic_print_tuple(char *buffer, static int generic_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
{ {
return 0; return 0;
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static unsigned int generic_print_conntrack(char *buffer, static int generic_print_conntrack(struct seq_file *s,
const struct ip_conntrack *state) const struct ip_conntrack *state)
{ {
return 0; return 0;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/icmp.h> #include <linux/icmp.h>
#include <linux/seq_file.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
...@@ -70,18 +71,18 @@ static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple, ...@@ -70,18 +71,18 @@ static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
} }
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. */
static unsigned int icmp_print_tuple(char *buffer, static int icmp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
{ {
return sprintf(buffer, "type=%u code=%u id=%u ", return seq_printf(s, "type=%u code=%u id=%u ",
tuple->dst.u.icmp.type, tuple->dst.u.icmp.type,
tuple->dst.u.icmp.code, tuple->dst.u.icmp.code,
ntohs(tuple->src.u.icmp.id)); ntohs(tuple->src.u.icmp.id));
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static unsigned int icmp_print_conntrack(char *buffer, static int icmp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack) const struct ip_conntrack *conntrack)
{ {
return 0; return 0;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/sctp.h> #include <linux/sctp.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/netfilter_ipv4/ip_conntrack.h> #include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
...@@ -178,20 +179,20 @@ static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple, ...@@ -178,20 +179,20 @@ static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple,
} }
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. */
static unsigned int sctp_print_tuple(char *buffer, static int sctp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
{ {
DEBUGP(__FUNCTION__); DEBUGP(__FUNCTION__);
DEBUGP("\n"); DEBUGP("\n");
return sprintf(buffer, "sport=%hu dport=%hu ", return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.sctp.port), ntohs(tuple->src.u.sctp.port),
ntohs(tuple->dst.u.sctp.port)); ntohs(tuple->dst.u.sctp.port));
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static unsigned int sctp_print_conntrack(char *buffer, static int sctp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack) const struct ip_conntrack *conntrack)
{ {
enum sctp_conntrack state; enum sctp_conntrack state;
...@@ -202,7 +203,7 @@ static unsigned int sctp_print_conntrack(char *buffer, ...@@ -202,7 +203,7 @@ static unsigned int sctp_print_conntrack(char *buffer,
state = conntrack->proto.sctp.state; state = conntrack->proto.sctp.state;
READ_UNLOCK(&sctp_lock); READ_UNLOCK(&sctp_lock);
return sprintf(buffer, "%s ", sctp_conntrack_names[state]); return seq_printf(s, "%s ", sctp_conntrack_names[state]);
} }
#define for_each_sctp_chunk(skb, sch, offset, count) \ #define for_each_sctp_chunk(skb, sch, offset, count) \
......
...@@ -316,17 +316,17 @@ static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple, ...@@ -316,17 +316,17 @@ static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple,
} }
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. */
static unsigned int tcp_print_tuple(char *buffer, static int tcp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
{ {
return sprintf(buffer, "sport=%hu dport=%hu ", return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.tcp.port), ntohs(tuple->src.u.tcp.port),
ntohs(tuple->dst.u.tcp.port)); ntohs(tuple->dst.u.tcp.port));
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static unsigned int tcp_print_conntrack(char *buffer, static int tcp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack) const struct ip_conntrack *conntrack)
{ {
enum tcp_conntrack state; enum tcp_conntrack state;
...@@ -334,7 +334,7 @@ static unsigned int tcp_print_conntrack(char *buffer, ...@@ -334,7 +334,7 @@ static unsigned int tcp_print_conntrack(char *buffer,
state = conntrack->proto.tcp.state; state = conntrack->proto.tcp.state;
READ_UNLOCK(&tcp_lock); READ_UNLOCK(&tcp_lock);
return sprintf(buffer, "%s ", tcp_conntrack_names[state]); return seq_printf(s, "%s ", tcp_conntrack_names[state]);
} }
static unsigned int get_conntrack_index(const struct tcphdr *tcph) static unsigned int get_conntrack_index(const struct tcphdr *tcph)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/seq_file.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
...@@ -46,17 +47,17 @@ static int udp_invert_tuple(struct ip_conntrack_tuple *tuple, ...@@ -46,17 +47,17 @@ static int udp_invert_tuple(struct ip_conntrack_tuple *tuple,
} }
/* Print out the per-protocol part of the tuple. */ /* Print out the per-protocol part of the tuple. */
static unsigned int udp_print_tuple(char *buffer, static int udp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple) const struct ip_conntrack_tuple *tuple)
{ {
return sprintf(buffer, "sport=%hu dport=%hu ", return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.udp.port), ntohs(tuple->src.u.udp.port),
ntohs(tuple->dst.u.udp.port)); ntohs(tuple->dst.u.udp.port));
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static unsigned int udp_print_conntrack(char *buffer, static int udp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack) const struct ip_conntrack *conntrack)
{ {
return 0; return 0;
} }
......
...@@ -57,18 +57,13 @@ static int kill_proto(const struct ip_conntrack *i, void *data) ...@@ -57,18 +57,13 @@ static int kill_proto(const struct ip_conntrack *i, void *data)
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
static unsigned int static int
print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple, print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *proto) struct ip_conntrack_protocol *proto)
{ {
int len; seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
len = sprintf(buffer, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ", return proto->print_tuple(s, tuple);
NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
len += proto->print_tuple(buffer + len, tuple);
return len;
} }
#ifdef CONFIG_IP_NF_CT_ACCT #ifdef CONFIG_IP_NF_CT_ACCT
...@@ -85,48 +80,29 @@ seq_print_counters(struct seq_file *s, struct ip_conntrack_counter *counter) ...@@ -85,48 +80,29 @@ seq_print_counters(struct seq_file *s, struct ip_conntrack_counter *counter)
static void *ct_seq_start(struct seq_file *s, loff_t *pos) static void *ct_seq_start(struct seq_file *s, loff_t *pos)
{ {
unsigned int *bucket;
/* strange seq_file api calls stop even if we fail,
* thus we need to grab lock since stop unlocks */
READ_LOCK(&ip_conntrack_lock);
if (*pos >= ip_conntrack_htable_size) if (*pos >= ip_conntrack_htable_size)
return NULL; return NULL;
return &ip_conntrack_hash[*pos];
bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
if (!bucket) {
return ERR_PTR(-ENOMEM);
}
*bucket = *pos;
return bucket;
} }
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) static void ct_seq_stop(struct seq_file *s, void *v)
{ {
unsigned int *bucket = (unsigned int *) v; }
*pos = ++(*bucket); static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
if (*pos >= ip_conntrack_htable_size) { {
kfree(v); (*pos)++;
if (*pos >= ip_conntrack_htable_size)
return NULL; return NULL;
} return &ip_conntrack_hash[*pos];
return bucket;
} }
static void ct_seq_stop(struct seq_file *s, void *v)
{
READ_UNLOCK(&ip_conntrack_lock);
}
/* return 0 on success, 1 in case of error */ /* return 0 on success, 1 in case of error */
static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash, static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
struct seq_file *s) struct seq_file *s)
{ {
struct ip_conntrack *conntrack = hash->ctrack; struct ip_conntrack *conntrack = hash->ctrack;
struct ip_conntrack_protocol *proto; struct ip_conntrack_protocol *proto;
char buffer[IP_CT_PRINT_BUFLEN];
MUST_BE_READ_LOCKED(&ip_conntrack_lock); MUST_BE_READ_LOCKED(&ip_conntrack_lock);
...@@ -147,12 +123,12 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash, ...@@ -147,12 +123,12 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0) ? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
return 1; return 1;
proto->print_conntrack(buffer, conntrack); if (proto->print_conntrack(s, conntrack))
if (seq_puts(s, buffer))
return 1; return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple, if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto); proto))
return 1;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL])) if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
return 1; return 1;
...@@ -161,9 +137,8 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash, ...@@ -161,9 +137,8 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
if (seq_printf(s, "[UNREPLIED] ")) if (seq_printf(s, "[UNREPLIED] "))
return 1; return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple, if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto); proto))
if (seq_puts(s, buffer))
return 1; return 1;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY])) if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
...@@ -179,17 +154,18 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash, ...@@ -179,17 +154,18 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
return 0; return 0;
} }
static int ct_seq_show(struct seq_file *s, void *v) static int ct_seq_show(struct seq_file *s, void *v)
{ {
unsigned int *bucket = (unsigned int *) v; struct list_head *list = v;
int ret = 0;
if (LIST_FIND(&ip_conntrack_hash[*bucket], ct_seq_real_show, /* FIXME: Simply truncates if hash chain too long. */
struct ip_conntrack_tuple_hash *, s)) { READ_LOCK(&ip_conntrack_lock);
/* buffer was filled and unable to print that tuple */ if (LIST_FIND(list, ct_seq_real_show,
return 1; struct ip_conntrack_tuple_hash *, s))
} ret = -ENOSPC;
return 0; READ_UNLOCK(&ip_conntrack_lock);
return ret;
} }
static struct seq_operations ct_seq_ops = { static struct seq_operations ct_seq_ops = {
...@@ -255,7 +231,6 @@ static void exp_seq_stop(struct seq_file *s, void *v) ...@@ -255,7 +231,6 @@ static void exp_seq_stop(struct seq_file *s, void *v)
static int exp_seq_show(struct seq_file *s, void *v) static int exp_seq_show(struct seq_file *s, void *v)
{ {
struct ip_conntrack_expect *expect = v; struct ip_conntrack_expect *expect = v;
char buffer[IP_CT_PRINT_BUFLEN];
if (expect->expectant->helper->timeout) if (expect->expectant->helper->timeout)
seq_printf(s, "%lu ", timer_pending(&expect->timeout) seq_printf(s, "%lu ", timer_pending(&expect->timeout)
...@@ -266,9 +241,8 @@ static int exp_seq_show(struct seq_file *s, void *v) ...@@ -266,9 +241,8 @@ static int exp_seq_show(struct seq_file *s, void *v)
seq_printf(s, "use=%u proto=%u ", atomic_read(&expect->use), seq_printf(s, "use=%u proto=%u ", atomic_read(&expect->use),
expect->tuple.dst.protonum); expect->tuple.dst.protonum);
print_tuple(buffer, &expect->tuple, return print_tuple(s, &expect->tuple,
__ip_ct_find_proto(expect->tuple.dst.protonum)); __ip_ct_find_proto(expect->tuple.dst.protonum));
return seq_printf(s, "%s\n", buffer);
} }
static struct seq_operations exp_seq_ops = { static struct seq_operations exp_seq_ops = {
......
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