summaryrefslogtreecommitdiffstats
path: root/libiptc
diff options
context:
space:
mode:
Diffstat (limited to 'libiptc')
-rw-r--r--libiptc/libiptc.c36
1 files changed, 24 insertions, 12 deletions
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
index 2ddbc5d..0085a06 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -295,6 +295,21 @@ static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
return NULL;
}
+/* Get a specific rule within a chain backwards */
+static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
+ unsigned int rulenum)
+{
+ struct rule_head *r;
+ unsigned int num = 0;
+
+ list_for_each_entry_reverse(r, &c->rules, list) {
+ num++;
+ if (num == rulenum)
+ return r;
+ }
+ return NULL;
+}
+
/* Returns chain head if found, otherwise NULL. */
static struct chain_head *
iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
@@ -1267,13 +1282,11 @@ TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
prev points to. */
if (rulenum == c->num_rules) {
prev = &c->rules;
- } else {
+ } else if (rulenum + 1 <= c->num_rules/2) {
r = iptcc_get_rule_num(c, rulenum + 1);
- if (!r) {
- /* Shouldn't happen */
- errno = ENOENT;
- return 0;
- }
+ prev = &r->list;
+ } else {
+ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
prev = &r->list;
}
@@ -1504,17 +1517,16 @@ TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
return 0;
}
- if (c->num_rules == 0) {
+ if (rulenum >= c->num_rules) {
errno = E2BIG;
return 0;
}
/* Take advantage of the double linked list if possible. */
- if (rulenum == c->num_rules - 1)
- r = list_entry(c->rules.prev, struct rule_head, list);
- else if (!(r = iptcc_get_rule_num(c, rulenum + 1))) {
- errno = E2BIG;
- return 0;
+ if (rulenum + 1 <= c->num_rules/2) {
+ r = iptcc_get_rule_num(c, rulenum + 1);
+ } else {
+ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
}
/* If we are about to delete the rule that is the current