return NULL;
}
+static struct obj_update *obj_update_by_rule(struct nft_handle *h,
+ struct nftnl_rule *r)
+{
+ struct obj_update *n;
+
+ list_for_each_entry(n, &h->obj_list, head) {
+ if (n->rule == r)
+ return n;
+ }
+ return NULL;
+}
+
+static void copy_nftnl_rule_attr(struct nftnl_rule *to,
+ const struct nftnl_rule *from,
+ uint16_t attr)
+{
+ const void *data;
+ uint32_t len;
+
+ if (nftnl_rule_is_set(from, attr)) {
+ data = nftnl_rule_get_data(from, attr, &len);
+ nftnl_rule_set_data(to, attr, data, len);
+ }
+}
+
int
nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
struct nftnl_rule *r, struct nftnl_rule *ref, bool verbose)
nft_fn = nft_rule_append;
- if (ref) {
- nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE,
- nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE));
+ if (ref && !nftnl_rule_is_set(ref, NFTNL_RULE_HANDLE)) {
+ /* replacing a new rule, hijack its obj_update */
+ struct obj_update *n = obj_update_by_rule(h, ref);
+
+ if (!n) {
+ errno = ENOENT;
+ return 0;
+ }
+ if (n->type != NFT_COMPAT_RULE_APPEND &&
+ n->type != NFT_COMPAT_RULE_INSERT) {
+ errno = EINVAL;
+ return 0;
+ }
+ copy_nftnl_rule_attr(r, ref, NFTNL_RULE_POSITION);
+ copy_nftnl_rule_attr(r, ref, NFTNL_RULE_ID);
+ nftnl_chain_rule_del(ref);
+ nftnl_rule_free(ref);
+ n->rule = r;
+ return 1;
+ } else if (ref) {
+ copy_nftnl_rule_attr(r, ref, NFTNL_RULE_HANDLE);
type = NFT_COMPAT_RULE_REPLACE;
- } else
+ } else {
type = NFT_COMPAT_RULE_APPEND;
+ }
if (batch_rule_add(h, type, r) == NULL)
return 0;
--- /dev/null
+#!/bin/bash
+
+set -e
+
+RS='*filter
+-A FORWARD -m comment --comment "new rule being replaced"
+-R FORWARD 1 -m comment --comment "new replacing rule"
+COMMIT'
+EXP='*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A FORWARD -m comment --comment "new replacing rule"
+COMMIT'
+$XT_MULTI iptables-restore <<< "$RS"
+diff -u -Z <(echo -e "$EXP") <($XT_MULTI iptables-save | grep -v '^#')
+
+RS='*filter
+-A FORWARD -m comment --comment "rule to insert before"
+-I FORWARD 1 -m comment --comment "new rule being replaced"
+-R FORWARD 1 -m comment --comment "new replacing rule"
+COMMIT'
+EXP='*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A FORWARD -m comment --comment "new replacing rule"
+-A FORWARD -m comment --comment "rule to insert before"
+COMMIT'
+$XT_MULTI iptables-restore <<< "$RS"
+diff -u -Z <(echo -e "$EXP") <($XT_MULTI iptables-save | grep -v '^#')