1/* 2 * test/set flag bits stored in conntrack extension area. 3 * 4 * (C) 2013 Astaro GmbH & Co KG 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/export.h> 12#include <linux/types.h> 13 14#include <net/netfilter/nf_conntrack_ecache.h> 15#include <net/netfilter/nf_conntrack_labels.h> 16 17static spinlock_t nf_connlabels_lock; 18 19static unsigned int label_bits(const struct nf_conn_labels *l) 20{ 21 unsigned int longs = l->words; 22 return longs * BITS_PER_LONG; 23} 24 25bool nf_connlabel_match(const struct nf_conn *ct, u16 bit) 26{ 27 struct nf_conn_labels *labels = nf_ct_labels_find(ct); 28 29 if (!labels) 30 return false; 31 32 return bit < label_bits(labels) && test_bit(bit, labels->bits); 33} 34EXPORT_SYMBOL_GPL(nf_connlabel_match); 35 36int nf_connlabel_set(struct nf_conn *ct, u16 bit) 37{ 38 struct nf_conn_labels *labels = nf_ct_labels_find(ct); 39 40 if (!labels || bit >= label_bits(labels)) 41 return -ENOSPC; 42 43 if (test_bit(bit, labels->bits)) 44 return 0; 45 46 if (!test_and_set_bit(bit, labels->bits)) 47 nf_conntrack_event_cache(IPCT_LABEL, ct); 48 49 return 0; 50} 51EXPORT_SYMBOL_GPL(nf_connlabel_set); 52 53static void replace_u32(u32 *address, u32 mask, u32 new) 54{ 55 u32 old, tmp; 56 57 do { 58 old = *address; 59 tmp = (old & mask) ^ new; 60 } while (cmpxchg(address, old, tmp) != old); 61} 62 63int nf_connlabels_replace(struct nf_conn *ct, 64 const u32 *data, 65 const u32 *mask, unsigned int words32) 66{ 67 struct nf_conn_labels *labels; 68 unsigned int size, i; 69 u32 *dst; 70 71 labels = nf_ct_labels_find(ct); 72 if (!labels) 73 return -ENOSPC; 74 75 size = labels->words * sizeof(long); 76 if (size < (words32 * sizeof(u32))) 77 words32 = size / sizeof(u32); 78 79 dst = (u32 *) labels->bits; 80 if (words32) { 81 for (i = 0; i < words32; i++) 82 replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]); 83 } 84 85 size /= sizeof(u32); 86 for (i = words32; i < size; i++) /* pad */ 87 replace_u32(&dst[i], 0, 0); 88 89 nf_conntrack_event_cache(IPCT_LABEL, ct); 90 return 0; 91} 92EXPORT_SYMBOL_GPL(nf_connlabels_replace); 93 94int nf_connlabels_get(struct net *net, unsigned int n_bits) 95{ 96 size_t words; 97 98 if (n_bits > (NF_CT_LABELS_MAX_SIZE * BITS_PER_BYTE)) 99 return -ERANGE; 100 101 words = BITS_TO_LONGS(n_bits); 102 103 spin_lock(&nf_connlabels_lock); 104 net->ct.labels_used++; 105 if (words > net->ct.label_words) 106 net->ct.label_words = words; 107 spin_unlock(&nf_connlabels_lock); 108 109 return 0; 110} 111EXPORT_SYMBOL_GPL(nf_connlabels_get); 112 113void nf_connlabels_put(struct net *net) 114{ 115 spin_lock(&nf_connlabels_lock); 116 net->ct.labels_used--; 117 if (net->ct.labels_used == 0) 118 net->ct.label_words = 0; 119 spin_unlock(&nf_connlabels_lock); 120} 121EXPORT_SYMBOL_GPL(nf_connlabels_put); 122 123static struct nf_ct_ext_type labels_extend __read_mostly = { 124 .len = sizeof(struct nf_conn_labels), 125 .align = __alignof__(struct nf_conn_labels), 126 .id = NF_CT_EXT_LABELS, 127}; 128 129int nf_conntrack_labels_init(void) 130{ 131 spin_lock_init(&nf_connlabels_lock); 132 return nf_ct_extend_register(&labels_extend); 133} 134 135void nf_conntrack_labels_fini(void) 136{ 137 nf_ct_extend_unregister(&labels_extend); 138} 139