Linux 4.7 changed nf_connlabels_replace() to trigger conntrack event
for a label change only when the labels actually changed.  Without
this change an update event is triggered even if the labels already
have the values they are being set to.

There is no way we can detect this functional change from Linux
headers, so provide replacements that work the same for older Linux
releases regardless if a distribution provides backports or not.

VMware-BZ: #1837218
Signed-off-by: Jarno Rajahalme <[email protected]>
---
v2: New for v2.

 .../include/net/netfilter/nf_conntrack_labels.h    | 45 ++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/datapath/linux/compat/include/net/netfilter/nf_conntrack_labels.h 
b/datapath/linux/compat/include/net/netfilter/nf_conntrack_labels.h
index 5af5a9a..61cf19e 100644
--- a/datapath/linux/compat/include/net/netfilter/nf_conntrack_labels.h
+++ b/datapath/linux/compat/include/net/netfilter/nf_conntrack_labels.h
@@ -57,4 +57,49 @@ static inline int nf_connlabels_get(struct net *net, 
unsigned int bits)
 static inline void nf_connlabels_put(struct net *net) { }
 #endif /* CONFIG_NF_CONNTRACK_LABELS */
 #endif /* HAVE_NF_CONNLABELS_GET_TAKES_BIT */
+
+/* Linux 4.7 introduced a functional change to trigger conntrack event for a
+ * label change only when the labels actually changed.  There is no way we can
+ * detect this from the headers, so provide replacements that work the same for
+ * OVS (where labels size is 128 bits == 16 bytes == 4 4-byte words). */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+static int replace_u32(u32 *address, u32 mask, u32 new)
+{
+       u32 old, tmp;
+
+       do {
+               old = *address;
+               tmp = (old & mask) ^ new;
+               if (old == tmp)
+                       return 0;
+       } while (cmpxchg(address, old, tmp) != old);
+
+       return 1;
+}
+
+static int rpl_nf_connlabels_replace(struct nf_conn *ct,
+                                    const u32 *data,
+                                    const u32 *mask, unsigned int words32)
+{
+       struct nf_conn_labels *labels;
+       unsigned int i;
+       int changed = 0;
+       u32 *dst;
+
+       labels = nf_ct_labels_find(ct);
+       if (!labels)
+               return -ENOSPC;
+
+       dst = (u32 *) labels->bits;
+       for (i = 0; i < words32; i++)
+               changed |= replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]);
+
+       if (changed)
+               nf_conntrack_event_cache(IPCT_LABEL, ct);
+
+       return 0;
+}
+#define nf_connlabels_replace rpl_nf_connlabels_replace
+#endif
+
 #endif /* _NF_CONNTRACK_LABELS_WRAPPER_H */
-- 
2.1.4

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to