1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * test/set flag bits stored in conntrack extension area. 4 * 5 * (C) 2013 Astaro GmbH & Co KG 6 */ 7 8#include <linux/export.h> 9#include <linux/types.h> 10 11#include <net/netfilter/nf_conntrack_ecache.h> 12#include <net/netfilter/nf_conntrack_labels.h> 13 14static int replace_u32(u32 *address, u32 mask, u32 new) 15{ 16 u32 old, tmp; 17 18 do { 19 old = *address; 20 tmp = (old & mask) ^ new; 21 if (old == tmp) 22 return 0; 23 } while (cmpxchg(address, old, tmp) != old); 24 25 return 1; 26} 27 28int nf_connlabels_replace(struct nf_conn *ct, 29 const u32 *data, 30 const u32 *mask, unsigned int words32) 31{ 32 struct nf_conn_labels *labels; 33 unsigned int size, i; 34 int changed = 0; 35 u32 *dst; 36 37 labels = nf_ct_labels_find(ct); 38 if (!labels) 39 return -ENOSPC; 40 41 size = sizeof(labels->bits); 42 if (size < (words32 * sizeof(u32))) 43 words32 = size / sizeof(u32); 44 45 dst = (u32 *) labels->bits; 46 for (i = 0; i < words32; i++) 47 changed |= replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]); 48 49 size /= sizeof(u32); 50 for (i = words32; i < size; i++) /* pad */ 51 replace_u32(&dst[i], 0, 0); 52 53 if (changed) 54 nf_conntrack_event_cache(IPCT_LABEL, ct); 55 return 0; 56} 57EXPORT_SYMBOL_GPL(nf_connlabels_replace); 58 59int nf_connlabels_get(struct net *net, unsigned int bits) 60{ 61 int v; 62 63 if (BIT_WORD(bits) >= NF_CT_LABELS_MAX_SIZE / sizeof(long)) 64 return -ERANGE; 65 66 BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE / sizeof(long) >= U8_MAX); 67 68 v = atomic_inc_return_relaxed(&net->ct.labels_used); 69 WARN_ON_ONCE(v <= 0); 70 71 return 0; 72} 73EXPORT_SYMBOL_GPL(nf_connlabels_get); 74 75void nf_connlabels_put(struct net *net) 76{ 77 int v = atomic_dec_return_relaxed(&net->ct.labels_used); 78 79 WARN_ON_ONCE(v < 0); 80} 81EXPORT_SYMBOL_GPL(nf_connlabels_put); 82