aboutsummaryrefslogtreecommitdiff
path: root/net/netfilter
diff options
context:
space:
mode:
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/x_tables.c199
1 files changed, 142 insertions, 57 deletions
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index bfbf521f6ea..5baccfa5a0d 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -827,59 +827,143 @@ static const struct file_operations xt_table_ops = {
.release = seq_release_net,
};
-static void *xt_match_seq_start(struct seq_file *seq, loff_t *pos)
+/*
+ * Traverse state for ip{,6}_{tables,matches} for helping crossing
+ * the multi-AF mutexes.
+ */
+struct nf_mttg_trav {
+ struct list_head *head, *curr;
+ uint8_t class, nfproto;
+};
+
+enum {
+ MTTG_TRAV_INIT,
+ MTTG_TRAV_NFP_UNSPEC,
+ MTTG_TRAV_NFP_SPEC,
+ MTTG_TRAV_DONE,
+};
+
+static void *xt_mttg_seq_next(struct seq_file *seq, void *v, loff_t *ppos,
+ bool is_target)
{
- struct proc_dir_entry *pde = (struct proc_dir_entry *)seq->private;
- u_int16_t af = (unsigned long)pde->data;
+ static const uint8_t next_class[] = {
+ [MTTG_TRAV_NFP_UNSPEC] = MTTG_TRAV_NFP_SPEC,
+ [MTTG_TRAV_NFP_SPEC] = MTTG_TRAV_DONE,
+ };
+ struct nf_mttg_trav *trav = seq->private;
+
+ switch (trav->class) {
+ case MTTG_TRAV_INIT:
+ trav->class = MTTG_TRAV_NFP_UNSPEC;
+ mutex_lock(&xt[NFPROTO_UNSPEC].mutex);
+ trav->head = trav->curr = is_target ?
+ &xt[NFPROTO_UNSPEC].target : &xt[NFPROTO_UNSPEC].match;
+ break;
+ case MTTG_TRAV_NFP_UNSPEC:
+ trav->curr = trav->curr->next;
+ if (trav->curr != trav->head)
+ break;
+ mutex_unlock(&xt[NFPROTO_UNSPEC].mutex);
+ mutex_lock(&xt[trav->nfproto].mutex);
+ trav->head = trav->curr = is_target ?
+ &xt[trav->nfproto].target : &xt[trav->nfproto].match;
+ trav->class = next_class[trav->class];
+ break;
+ case MTTG_TRAV_NFP_SPEC:
+ trav->curr = trav->curr->next;
+ if (trav->curr != trav->head)
+ break;
+ /* fallthru, _stop will unlock */
+ default:
+ return NULL;
+ }
- mutex_lock(&xt[af].mutex);
- return seq_list_start(&xt[af].match, *pos);
+ if (ppos != NULL)
+ ++*ppos;
+ return trav;
}
-static void *xt_match_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+static void *xt_mttg_seq_start(struct seq_file *seq, loff_t *pos,
+ bool is_target)
{
- struct proc_dir_entry *pde = (struct proc_dir_entry *)seq->private;
- u_int16_t af = (unsigned long)pde->data;
+ struct nf_mttg_trav *trav = seq->private;
+ unsigned int j;
- return seq_list_next(v, &xt[af].match, pos);
+ trav->class = MTTG_TRAV_INIT;
+ for (j = 0; j < *pos; ++j)
+ if (xt_mttg_seq_next(seq, NULL, NULL, is_target) == NULL)
+ return NULL;
+ return trav;
}
-static void xt_match_seq_stop(struct seq_file *seq, void *v)
+static void xt_mttg_seq_stop(struct seq_file *seq, void *v)
{
- struct proc_dir_entry *pde = seq->private;
- u_int16_t af = (unsigned long)pde->data;
+ struct nf_mttg_trav *trav = seq->private;
+
+ switch (trav->class) {
+ case MTTG_TRAV_NFP_UNSPEC:
+ mutex_unlock(&xt[NFPROTO_UNSPEC].mutex);
+ break;
+ case MTTG_TRAV_NFP_SPEC:
+ mutex_unlock(&xt[trav->nfproto].mutex);
+ break;
+ }
+}
- mutex_unlock(&xt[af].mutex);
+static void *xt_match_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ return xt_mttg_seq_start(seq, pos, false);
}
-static int xt_match_seq_show(struct seq_file *seq, void *v)
+static void *xt_match_seq_next(struct seq_file *seq, void *v, loff_t *ppos)
{
- struct xt_match *match = list_entry(v, struct xt_match, list);
+ return xt_mttg_seq_next(seq, v, ppos, false);
+}
- if (strlen(match->name))
- return seq_printf(seq, "%s\n", match->name);
- else
- return 0;
+static int xt_match_seq_show(struct seq_file *seq, void *v)
+{
+ const struct nf_mttg_trav *trav = seq->private;
+ const struct xt_match *match;
+
+ switch (trav->class) {
+ case MTTG_TRAV_NFP_UNSPEC:
+ case MTTG_TRAV_NFP_SPEC:
+ if (trav->curr == trav->head)
+ return 0;
+ match = list_entry(trav->curr, struct xt_match, list);
+ return (*match->name == '\0') ? 0 :
+ seq_printf(seq, "%s\n", match->name);
+ }
+ return 0;
}
static const struct seq_operations xt_match_seq_ops = {
.start = xt_match_seq_start,
.next = xt_match_seq_next,
- .stop = xt_match_seq_stop,
+ .stop = xt_mttg_seq_stop,
.show = xt_match_seq_show,
};
static int xt_match_open(struct inode *inode, struct file *file)
{
+ struct seq_file *seq;
+ struct nf_mttg_trav *trav;
int ret;
- ret = seq_open(file, &xt_match_seq_ops);
- if (!ret) {
- struct seq_file *seq = file->private_data;
+ trav = kmalloc(sizeof(*trav), GFP_KERNEL);
+ if (trav == NULL)
+ return -ENOMEM;
- seq->private = PDE(inode);
+ ret = seq_open(file, &xt_match_seq_ops);
+ if (ret < 0) {
+ kfree(trav);
+ return ret;
}
- return ret;
+
+ seq = file->private_data;
+ seq->private = trav;
+ trav->nfproto = (unsigned long)PDE(inode)->data;
+ return 0;
}
static const struct file_operations xt_match_ops = {
@@ -887,62 +971,63 @@ static const struct file_operations xt_match_ops = {
.open = xt_match_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_private,
};
static void *xt_target_seq_start(struct seq_file *seq, loff_t *pos)
{
- struct proc_dir_entry *pde = (struct proc_dir_entry *)seq->private;
- u_int16_t af = (unsigned long)pde->data;
-
- mutex_lock(&xt[af].mutex);
- return seq_list_start(&xt[af].target, *pos);
+ return xt_mttg_seq_start(seq, pos, true);
}
-static void *xt_target_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+static void *xt_target_seq_next(struct seq_file *seq, void *v, loff_t *ppos)
{
- struct proc_dir_entry *pde = (struct proc_dir_entry *)seq->private;
- u_int16_t af = (unsigned long)pde->data;
-
- return seq_list_next(v, &xt[af].target, pos);
-}
-
-static void xt_target_seq_stop(struct seq_file *seq, void *v)
-{
- struct proc_dir_entry *pde = seq->private;
- u_int16_t af = (unsigned long)pde->data;
-
- mutex_unlock(&xt[af].mutex);
+ return xt_mttg_seq_next(seq, v, ppos, true);
}
static int xt_target_seq_show(struct seq_file *seq, void *v)
{
- struct xt_target *target = list_entry(v, struct xt_target, list);
-
- if (strlen(target->name))
- return seq_printf(seq, "%s\n", target->name);
- else
- return 0;
+ const struct nf_mttg_trav *trav = seq->private;
+ const struct xt_target *target;
+
+ switch (trav->class) {
+ case MTTG_TRAV_NFP_UNSPEC:
+ case MTTG_TRAV_NFP_SPEC:
+ if (trav->curr == trav->head)
+ return 0;
+ target = list_entry(trav->curr, struct xt_target, list);
+ return (*target->name == '\0') ? 0 :
+ seq_printf(seq, "%s\n", target->name);
+ }
+ return 0;
}
static const struct seq_operations xt_target_seq_ops = {
.start = xt_target_seq_start,
.next = xt_target_seq_next,
- .stop = xt_target_seq_stop,
+ .stop = xt_mttg_seq_stop,
.show = xt_target_seq_show,
};
static int xt_target_open(struct inode *inode, struct file *file)
{
+ struct seq_file *seq;
+ struct nf_mttg_trav *trav;
int ret;
- ret = seq_open(file, &xt_target_seq_ops);
- if (!ret) {
- struct seq_file *seq = file->private_data;
+ trav = kmalloc(sizeof(*trav), GFP_KERNEL);
+ if (trav == NULL)
+ return -ENOMEM;
- seq->private = PDE(inode);
+ ret = seq_open(file, &xt_target_seq_ops);
+ if (ret < 0) {
+ kfree(trav);
+ return ret;
}
- return ret;
+
+ seq = file->private_data;
+ seq->private = trav;
+ trav->nfproto = (unsigned long)PDE(inode)->data;
+ return 0;
}
static const struct file_operations xt_target_ops = {
@@ -950,7 +1035,7 @@ static const struct file_operations xt_target_ops = {
.open = xt_target_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_private,
};
#define FORMAT_TABLES "_tables_names"