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 @@
#define _IP_CONNTRACK_PROTOCOL_H
#include <linux/netfilter_ipv4/ip_conntrack.h>
/* length of buffer to which print_tuple/print_conntrack members are
* writing */
#define IP_CT_PRINT_BUFLEN 100
struct seq_file;
struct ip_conntrack_protocol
{
......@@ -31,13 +28,12 @@ struct ip_conntrack_protocol
int (*invert_tuple)(struct ip_conntrack_tuple *inverse,
const struct ip_conntrack_tuple *orig);
/* Print out the per-protocol part of the tuple. */
unsigned int (*print_tuple)(char *buffer,
const struct ip_conntrack_tuple *);
/* Print out the per-protocol part of the tuple. Return like seq_* */
int (*print_tuple)(struct seq_file *,
const struct ip_conntrack_tuple *);
/* Print out the private part of the conntrack. */
unsigned int (*print_conntrack)(char *buffer,
const struct ip_conntrack *);
int (*print_conntrack)(struct seq_file *, const struct ip_conntrack *);
/* Returns verdict for packet, or -1 for invalid. */
int (*packet)(struct ip_conntrack *conntrack,
......
......@@ -34,15 +34,15 @@ static int generic_invert_tuple(struct ip_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
static unsigned int generic_print_tuple(char *buffer,
const struct ip_conntrack_tuple *tuple)
static int generic_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple)
{
return 0;
}
/* Print out the private part of the conntrack. */
static unsigned int generic_print_conntrack(char *buffer,
const struct ip_conntrack *state)
static int generic_print_conntrack(struct seq_file *s,
const struct ip_conntrack *state)
{
return 0;
}
......
......@@ -12,6 +12,7 @@
#include <linux/netfilter.h>
#include <linux/in.h>
#include <linux/icmp.h>
#include <linux/seq_file.h>
#include <net/ip.h>
#include <net/checksum.h>
#include <linux/netfilter.h>
......@@ -70,18 +71,18 @@ static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
static unsigned int icmp_print_tuple(char *buffer,
const struct ip_conntrack_tuple *tuple)
static int icmp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple)
{
return sprintf(buffer, "type=%u code=%u id=%u ",
tuple->dst.u.icmp.type,
tuple->dst.u.icmp.code,
ntohs(tuple->src.u.icmp.id));
return seq_printf(s, "type=%u code=%u id=%u ",
tuple->dst.u.icmp.type,
tuple->dst.u.icmp.code,
ntohs(tuple->src.u.icmp.id));
}
/* Print out the private part of the conntrack. */
static unsigned int icmp_print_conntrack(char *buffer,
const struct ip_conntrack *conntrack)
static int icmp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack)
{
return 0;
}
......
......@@ -22,6 +22,7 @@
#include <linux/ip.h>
#include <linux/sctp.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
......@@ -178,20 +179,20 @@ static int sctp_invert_tuple(struct ip_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
static unsigned int sctp_print_tuple(char *buffer,
const struct ip_conntrack_tuple *tuple)
static int sctp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple)
{
DEBUGP(__FUNCTION__);
DEBUGP("\n");
return sprintf(buffer, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.sctp.port),
ntohs(tuple->dst.u.sctp.port));
return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.sctp.port),
ntohs(tuple->dst.u.sctp.port));
}
/* Print out the private part of the conntrack. */
static unsigned int sctp_print_conntrack(char *buffer,
const struct ip_conntrack *conntrack)
static int sctp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack)
{
enum sctp_conntrack state;
......@@ -202,7 +203,7 @@ static unsigned int sctp_print_conntrack(char *buffer,
state = conntrack->proto.sctp.state;
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) \
......
......@@ -316,17 +316,17 @@ static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
static unsigned int tcp_print_tuple(char *buffer,
const struct ip_conntrack_tuple *tuple)
static int tcp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple)
{
return sprintf(buffer, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.tcp.port),
ntohs(tuple->dst.u.tcp.port));
return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.tcp.port),
ntohs(tuple->dst.u.tcp.port));
}
/* Print out the private part of the conntrack. */
static unsigned int tcp_print_conntrack(char *buffer,
const struct ip_conntrack *conntrack)
static int tcp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack)
{
enum tcp_conntrack state;
......@@ -334,7 +334,7 @@ static unsigned int tcp_print_conntrack(char *buffer,
state = conntrack->proto.tcp.state;
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)
......
......@@ -12,6 +12,7 @@
#include <linux/netfilter.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/seq_file.h>
#include <net/checksum.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
......@@ -46,17 +47,17 @@ static int udp_invert_tuple(struct ip_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
static unsigned int udp_print_tuple(char *buffer,
const struct ip_conntrack_tuple *tuple)
static int udp_print_tuple(struct seq_file *s,
const struct ip_conntrack_tuple *tuple)
{
return sprintf(buffer, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.udp.port),
ntohs(tuple->dst.u.udp.port));
return seq_printf(s, "sport=%hu dport=%hu ",
ntohs(tuple->src.u.udp.port),
ntohs(tuple->dst.u.udp.port));
}
/* Print out the private part of the conntrack. */
static unsigned int udp_print_conntrack(char *buffer,
const struct ip_conntrack *conntrack)
static int udp_print_conntrack(struct seq_file *s,
const struct ip_conntrack *conntrack)
{
return 0;
}
......
......@@ -57,18 +57,13 @@ static int kill_proto(const struct ip_conntrack *i, void *data)
}
#ifdef CONFIG_PROC_FS
static unsigned int
print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple,
static int
print_tuple(struct seq_file *s, const struct ip_conntrack_tuple *tuple,
struct ip_conntrack_protocol *proto)
{
int len;
len = sprintf(buffer, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
len += proto->print_tuple(buffer + len, tuple);
return len;
seq_printf(s, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
return proto->print_tuple(s, tuple);
}
#ifdef CONFIG_IP_NF_CT_ACCT
......@@ -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)
{
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)
return NULL;
bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
if (!bucket) {
return ERR_PTR(-ENOMEM);
}
*bucket = *pos;
return bucket;
return &ip_conntrack_hash[*pos];
}
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);
if (*pos >= ip_conntrack_htable_size) {
kfree(v);
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= ip_conntrack_htable_size)
return NULL;
}
return bucket;
return &ip_conntrack_hash[*pos];
}
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 */
static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
struct seq_file *s)
{
struct ip_conntrack *conntrack = hash->ctrack;
struct ip_conntrack_protocol *proto;
char buffer[IP_CT_PRINT_BUFLEN];
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
......@@ -147,12 +123,12 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
? (conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
return 1;
proto->print_conntrack(buffer, conntrack);
if (seq_puts(s, buffer))
if (proto->print_conntrack(s, conntrack))
return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto);
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
proto))
return 1;
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
return 1;
......@@ -161,9 +137,8 @@ static int ct_seq_real_show(const struct ip_conntrack_tuple_hash *hash,
if (seq_printf(s, "[UNREPLIED] "))
return 1;
print_tuple(buffer, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto);
if (seq_puts(s, buffer))
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
proto))
return 1;
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,
return 0;
}
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,
struct ip_conntrack_tuple_hash *, s)) {
/* buffer was filled and unable to print that tuple */
return 1;
}
return 0;
/* FIXME: Simply truncates if hash chain too long. */
READ_LOCK(&ip_conntrack_lock);
if (LIST_FIND(list, ct_seq_real_show,
struct ip_conntrack_tuple_hash *, s))
ret = -ENOSPC;
READ_UNLOCK(&ip_conntrack_lock);
return ret;
}
static struct seq_operations ct_seq_ops = {
......@@ -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)
{
struct ip_conntrack_expect *expect = v;
char buffer[IP_CT_PRINT_BUFLEN];
if (expect->expectant->helper->timeout)
seq_printf(s, "%lu ", timer_pending(&expect->timeout)
......@@ -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),
expect->tuple.dst.protonum);
print_tuple(buffer, &expect->tuple,
__ip_ct_find_proto(expect->tuple.dst.protonum));
return seq_printf(s, "%s\n", buffer);
return print_tuple(s, &expect->tuple,
__ip_ct_find_proto(expect->tuple.dst.protonum));
}
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