Commit f5caadbb authored by David S. Miller's avatar David S. Miller
parents 0ca87f05 0f598f0b
...@@ -613,6 +613,12 @@ extern void audit_log_d_path(struct audit_buffer *ab, ...@@ -613,6 +613,12 @@ extern void audit_log_d_path(struct audit_buffer *ab,
extern void audit_log_key(struct audit_buffer *ab, extern void audit_log_key(struct audit_buffer *ab,
char *key); char *key);
extern void audit_log_lost(const char *message); extern void audit_log_lost(const char *message);
#ifdef CONFIG_SECURITY
extern void audit_log_secctx(struct audit_buffer *ab, u32 secid);
#else
#define audit_log_secctx(b,s) do { ; } while (0)
#endif
extern int audit_update_lsm_rules(void); extern int audit_update_lsm_rules(void);
/* Private API (for audit.c only) */ /* Private API (for audit.c only) */
...@@ -635,6 +641,7 @@ extern int audit_enabled; ...@@ -635,6 +641,7 @@ extern int audit_enabled;
#define audit_log_untrustedstring(a,s) do { ; } while (0) #define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b, p, d) do { ; } while (0) #define audit_log_d_path(b, p, d) do { ; } while (0)
#define audit_log_key(b, k) do { ; } while (0) #define audit_log_key(b, k) do { ; } while (0)
#define audit_log_secctx(b,s) do { ; } while (0)
#define audit_enabled 0 #define audit_enabled 0
#endif #endif
#endif #endif
......
...@@ -28,7 +28,32 @@ ...@@ -28,7 +28,32 @@
/* Number of elements to store in an initial array block */ /* Number of elements to store in an initial array block */
#define AHASH_INIT_SIZE 4 #define AHASH_INIT_SIZE 4
/* Max number of elements to store in an array block */ /* Max number of elements to store in an array block */
#define AHASH_MAX_SIZE (3*4) #define AHASH_MAX_SIZE (3*AHASH_INIT_SIZE)
/* Max number of elements can be tuned */
#ifdef IP_SET_HASH_WITH_MULTI
#define AHASH_MAX(h) ((h)->ahash_max)
static inline u8
tune_ahash_max(u8 curr, u32 multi)
{
u32 n;
if (multi < curr)
return curr;
n = curr + AHASH_INIT_SIZE;
/* Currently, at listing one hash bucket must fit into a message.
* Therefore we have a hard limit here.
*/
return n > curr && n <= 64 ? n : curr;
}
#define TUNE_AHASH_MAX(h, multi) \
((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi))
#else
#define AHASH_MAX(h) AHASH_MAX_SIZE
#define TUNE_AHASH_MAX(h, multi)
#endif
/* A hash bucket */ /* A hash bucket */
struct hbucket { struct hbucket {
...@@ -60,6 +85,9 @@ struct ip_set_hash { ...@@ -60,6 +85,9 @@ struct ip_set_hash {
u32 timeout; /* timeout value, if enabled */ u32 timeout; /* timeout value, if enabled */
struct timer_list gc; /* garbage collection when timeout enabled */ struct timer_list gc; /* garbage collection when timeout enabled */
struct type_pf_next next; /* temporary storage for uadd */ struct type_pf_next next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_MULTI
u8 ahash_max; /* max elements in an array block */
#endif
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */ u8 netmask; /* netmask value for subnets to store */
#endif #endif
...@@ -211,12 +239,16 @@ ip_set_hash_destroy(struct ip_set *set) ...@@ -211,12 +239,16 @@ ip_set_hash_destroy(struct ip_set *set)
set->data = NULL; set->data = NULL;
} }
#endif /* _IP_SET_AHASH_H */
#ifndef HKEY_DATALEN
#define HKEY_DATALEN sizeof(struct type_pf_elem)
#endif
#define HKEY(data, initval, htable_bits) \ #define HKEY(data, initval, htable_bits) \
(jhash2((u32 *)(data), sizeof(struct type_pf_elem)/sizeof(u32), initval) \ (jhash2((u32 *)(data), HKEY_DATALEN/sizeof(u32), initval) \
& jhash_mask(htable_bits)) & jhash_mask(htable_bits))
#endif /* _IP_SET_AHASH_H */
#define CONCAT(a, b, c) a##b##c #define CONCAT(a, b, c) a##b##c
#define TOKEN(a, b, c) CONCAT(a, b, c) #define TOKEN(a, b, c) CONCAT(a, b, c)
...@@ -275,12 +307,13 @@ ip_set_hash_destroy(struct ip_set *set) ...@@ -275,12 +307,13 @@ ip_set_hash_destroy(struct ip_set *set)
/* Add an element to the hash table when resizing the set: /* Add an element to the hash table when resizing the set:
* we spare the maintenance of the internal counters. */ * we spare the maintenance of the internal counters. */
static int static int
type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value) type_pf_elem_add(struct hbucket *n, const struct type_pf_elem *value,
u8 ahash_max)
{ {
if (n->pos >= n->size) { if (n->pos >= n->size) {
void *tmp; void *tmp;
if (n->size >= AHASH_MAX_SIZE) if (n->size >= ahash_max)
/* Trigger rehashing */ /* Trigger rehashing */
return -EAGAIN; return -EAGAIN;
...@@ -335,7 +368,7 @@ type_pf_resize(struct ip_set *set, bool retried) ...@@ -335,7 +368,7 @@ type_pf_resize(struct ip_set *set, bool retried)
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_data(n, j); data = ahash_data(n, j);
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_add(m, data); ret = type_pf_elem_add(m, data, AHASH_MAX(h));
if (ret < 0) { if (ret < 0) {
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
ahash_destroy(t); ahash_destroy(t);
...@@ -359,7 +392,7 @@ type_pf_resize(struct ip_set *set, bool retried) ...@@ -359,7 +392,7 @@ type_pf_resize(struct ip_set *set, bool retried)
return 0; return 0;
} }
static void static inline void
type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d); type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
/* Add an element to a hash and update the internal counters when succeeded, /* Add an element to a hash and update the internal counters when succeeded,
...@@ -372,7 +405,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -372,7 +405,7 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
const struct type_pf_elem *d = value; const struct type_pf_elem *d = value;
struct hbucket *n; struct hbucket *n;
int i, ret = 0; int i, ret = 0;
u32 key; u32 key, multi = 0;
if (h->elements >= h->maxelem) if (h->elements >= h->maxelem)
return -IPSET_ERR_HASH_FULL; return -IPSET_ERR_HASH_FULL;
...@@ -382,12 +415,12 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -382,12 +415,12 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) for (i = 0; i < n->pos; i++)
if (type_pf_data_equal(ahash_data(n, i), d)) { if (type_pf_data_equal(ahash_data(n, i), d, &multi)) {
ret = -IPSET_ERR_EXIST; ret = -IPSET_ERR_EXIST;
goto out; goto out;
} }
TUNE_AHASH_MAX(h, multi);
ret = type_pf_elem_add(n, value); ret = type_pf_elem_add(n, value, AHASH_MAX(h));
if (ret != 0) { if (ret != 0) {
if (ret == -EAGAIN) if (ret == -EAGAIN)
type_pf_data_next(h, d); type_pf_data_next(h, d);
...@@ -415,13 +448,13 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -415,13 +448,13 @@ type_pf_del(struct ip_set *set, void *value, u32 timeout, u32 flags)
struct hbucket *n; struct hbucket *n;
int i; int i;
struct type_pf_elem *data; struct type_pf_elem *data;
u32 key; u32 key, multi = 0;
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (!type_pf_data_equal(data, d)) if (!type_pf_data_equal(data, d, &multi))
continue; continue;
if (i != n->pos - 1) if (i != n->pos - 1)
/* Not last one */ /* Not last one */
...@@ -462,17 +495,17 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) ...@@ -462,17 +495,17 @@ type_pf_test_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
struct hbucket *n; struct hbucket *n;
const struct type_pf_elem *data; const struct type_pf_elem *data;
int i, j = 0; int i, j = 0;
u32 key; u32 key, multi = 0;
u8 host_mask = SET_HOST_MASK(set->family); u8 host_mask = SET_HOST_MASK(set->family);
pr_debug("test by nets\n"); pr_debug("test by nets\n");
for (; j < host_mask && h->nets[j].cidr; j++) { for (; j < host_mask && h->nets[j].cidr && !multi; j++) {
type_pf_data_netmask(d, h->nets[j].cidr); type_pf_data_netmask(d, h->nets[j].cidr);
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return 1; return 1;
} }
} }
...@@ -490,7 +523,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -490,7 +523,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
struct hbucket *n; struct hbucket *n;
const struct type_pf_elem *data; const struct type_pf_elem *data;
int i; int i;
u32 key; u32 key, multi = 0;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
/* If we test an IP address and not a network address, /* If we test an IP address and not a network address,
...@@ -503,7 +536,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -503,7 +536,7 @@ type_pf_test(struct ip_set *set, void *value, u32 timeout, u32 flags)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_data(n, i); data = ahash_data(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return 1; return 1;
} }
return 0; return 0;
...@@ -660,14 +693,14 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout) ...@@ -660,14 +693,14 @@ type_pf_data_timeout_set(struct type_pf_elem *data, u32 timeout)
static int static int
type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value, type_pf_elem_tadd(struct hbucket *n, const struct type_pf_elem *value,
u32 timeout) u8 ahash_max, u32 timeout)
{ {
struct type_pf_elem *data; struct type_pf_elem *data;
if (n->pos >= n->size) { if (n->pos >= n->size) {
void *tmp; void *tmp;
if (n->size >= AHASH_MAX_SIZE) if (n->size >= ahash_max)
/* Trigger rehashing */ /* Trigger rehashing */
return -EAGAIN; return -EAGAIN;
...@@ -772,7 +805,7 @@ type_pf_tresize(struct ip_set *set, bool retried) ...@@ -772,7 +805,7 @@ type_pf_tresize(struct ip_set *set, bool retried)
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_tdata(n, j); data = ahash_tdata(n, j);
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_tadd(m, data, ret = type_pf_elem_tadd(m, data, AHASH_MAX(h),
type_pf_data_timeout(data)); type_pf_data_timeout(data));
if (ret < 0) { if (ret < 0) {
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
...@@ -803,9 +836,9 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -803,9 +836,9 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
const struct type_pf_elem *d = value; const struct type_pf_elem *d = value;
struct hbucket *n; struct hbucket *n;
struct type_pf_elem *data; struct type_pf_elem *data;
int ret = 0, i, j = AHASH_MAX_SIZE + 1; int ret = 0, i, j = AHASH_MAX(h) + 1;
bool flag_exist = flags & IPSET_FLAG_EXIST; bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 key; u32 key, multi = 0;
if (h->elements >= h->maxelem) if (h->elements >= h->maxelem)
/* FIXME: when set is full, we slow down here */ /* FIXME: when set is full, we slow down here */
...@@ -819,18 +852,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -819,18 +852,18 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) { if (type_pf_data_equal(data, d, &multi)) {
if (type_pf_data_expired(data) || flag_exist) if (type_pf_data_expired(data) || flag_exist)
j = i; j = i;
else { else {
ret = -IPSET_ERR_EXIST; ret = -IPSET_ERR_EXIST;
goto out; goto out;
} }
} else if (j == AHASH_MAX_SIZE + 1 && } else if (j == AHASH_MAX(h) + 1 &&
type_pf_data_expired(data)) type_pf_data_expired(data))
j = i; j = i;
} }
if (j != AHASH_MAX_SIZE + 1) { if (j != AHASH_MAX(h) + 1) {
data = ahash_tdata(n, j); data = ahash_tdata(n, j);
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
del_cidr(h, data->cidr, HOST_MASK); del_cidr(h, data->cidr, HOST_MASK);
...@@ -840,7 +873,8 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -840,7 +873,8 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
type_pf_data_timeout_set(data, timeout); type_pf_data_timeout_set(data, timeout);
goto out; goto out;
} }
ret = type_pf_elem_tadd(n, d, timeout); TUNE_AHASH_MAX(h, multi);
ret = type_pf_elem_tadd(n, d, AHASH_MAX(h), timeout);
if (ret != 0) { if (ret != 0) {
if (ret == -EAGAIN) if (ret == -EAGAIN)
type_pf_data_next(h, d); type_pf_data_next(h, d);
...@@ -865,13 +899,13 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -865,13 +899,13 @@ type_pf_tdel(struct ip_set *set, void *value, u32 timeout, u32 flags)
struct hbucket *n; struct hbucket *n;
int i; int i;
struct type_pf_elem *data; struct type_pf_elem *data;
u32 key; u32 key, multi = 0;
key = HKEY(value, h->initval, t->htable_bits); key = HKEY(value, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (!type_pf_data_equal(data, d)) if (!type_pf_data_equal(data, d, &multi))
continue; continue;
if (type_pf_data_expired(data)) if (type_pf_data_expired(data))
return -IPSET_ERR_EXIST; return -IPSET_ERR_EXIST;
...@@ -911,16 +945,16 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout) ...@@ -911,16 +945,16 @@ type_pf_ttest_cidrs(struct ip_set *set, struct type_pf_elem *d, u32 timeout)
struct type_pf_elem *data; struct type_pf_elem *data;
struct hbucket *n; struct hbucket *n;
int i, j = 0; int i, j = 0;
u32 key; u32 key, multi = 0;
u8 host_mask = SET_HOST_MASK(set->family); u8 host_mask = SET_HOST_MASK(set->family);
for (; j < host_mask && h->nets[j].cidr; j++) { for (; j < host_mask && h->nets[j].cidr && !multi; j++) {
type_pf_data_netmask(d, h->nets[j].cidr); type_pf_data_netmask(d, h->nets[j].cidr);
key = HKEY(d, h->initval, t->htable_bits); key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return !type_pf_data_expired(data); return !type_pf_data_expired(data);
} }
} }
...@@ -936,7 +970,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -936,7 +970,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
struct type_pf_elem *data, *d = value; struct type_pf_elem *data, *d = value;
struct hbucket *n; struct hbucket *n;
int i; int i;
u32 key; u32 key, multi = 0;
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
if (d->cidr == SET_HOST_MASK(set->family)) if (d->cidr == SET_HOST_MASK(set->family))
...@@ -946,7 +980,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags) ...@@ -946,7 +980,7 @@ type_pf_ttest(struct ip_set *set, void *value, u32 timeout, u32 flags)
n = hbucket(t, key); n = hbucket(t, key);
for (i = 0; i < n->pos; i++) { for (i = 0; i < n->pos; i++) {
data = ahash_tdata(n, i); data = ahash_tdata(n, i);
if (type_pf_data_equal(data, d)) if (type_pf_data_equal(data, d, &multi))
return !type_pf_data_expired(data); return !type_pf_data_expired(data);
} }
return 0; return 0;
...@@ -1054,6 +1088,8 @@ type_pf_gc_init(struct ip_set *set) ...@@ -1054,6 +1088,8 @@ type_pf_gc_init(struct ip_set *set)
IPSET_GC_PERIOD(h->timeout)); IPSET_GC_PERIOD(h->timeout));
} }
#undef HKEY_DATALEN
#undef HKEY
#undef type_pf_data_equal #undef type_pf_data_equal
#undef type_pf_data_isnull #undef type_pf_data_isnull
#undef type_pf_data_copy #undef type_pf_data_copy
......
...@@ -60,6 +60,9 @@ struct nfnl_callback { ...@@ -60,6 +60,9 @@ struct nfnl_callback {
int (*call)(struct sock *nl, struct sk_buff *skb, int (*call)(struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const cda[]); const struct nlattr * const cda[]);
int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
const struct nla_policy *policy; /* netlink attribute policy */ const struct nla_policy *policy; /* netlink attribute policy */
const u_int16_t attr_count; /* number of nlattr's */ const u_int16_t attr_count; /* number of nlattr's */
}; };
......
...@@ -8,6 +8,7 @@ enum nfqnl_msg_types { ...@@ -8,6 +8,7 @@ enum nfqnl_msg_types {
NFQNL_MSG_PACKET, /* packet from kernel to userspace */ NFQNL_MSG_PACKET, /* packet from kernel to userspace */
NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */
NFQNL_MSG_CONFIG, /* connect to a particular queue */ NFQNL_MSG_CONFIG, /* connect to a particular queue */
NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */
NFQNL_MSG_MAX NFQNL_MSG_MAX
}; };
......
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
#include <net/sock.h> #include <net/sock.h>
#include <net/netlink.h> #include <net/netlink.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#ifdef CONFIG_SECURITY
#include <linux/security.h>
#endif
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/tty.h> #include <linux/tty.h>
...@@ -1502,6 +1505,32 @@ void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, ...@@ -1502,6 +1505,32 @@ void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type,
} }
} }
#ifdef CONFIG_SECURITY
/**
* audit_log_secctx - Converts and logs SELinux context
* @ab: audit_buffer
* @secid: security number
*
* This is a helper function that calls security_secid_to_secctx to convert
* secid to secctx and then adds the (converted) SELinux context to the audit
* log by calling audit_log_format, thus also preventing leak of internal secid
* to userspace. If secid cannot be converted audit_panic is called.
*/
void audit_log_secctx(struct audit_buffer *ab, u32 secid)
{
u32 len;
char *secctx;
if (security_secid_to_secctx(secid, &secctx, &len)) {
audit_panic("Cannot convert secid to context");
} else {
audit_log_format(ab, " obj=%s", secctx);
security_release_secctx(secctx, len);
}
}
EXPORT_SYMBOL(audit_log_secctx);
#endif
EXPORT_SYMBOL(audit_log_start); EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end); EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format); EXPORT_SYMBOL(audit_log_format);
......
...@@ -53,7 +53,8 @@ struct hash_ip4_telem { ...@@ -53,7 +53,8 @@ struct hash_ip4_telem {
static inline bool static inline bool
hash_ip4_data_equal(const struct hash_ip4_elem *ip1, hash_ip4_data_equal(const struct hash_ip4_elem *ip1,
const struct hash_ip4_elem *ip2) const struct hash_ip4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip; return ip1->ip == ip2->ip;
} }
...@@ -225,7 +226,8 @@ struct hash_ip6_telem { ...@@ -225,7 +226,8 @@ struct hash_ip6_telem {
static inline bool static inline bool
hash_ip6_data_equal(const struct hash_ip6_elem *ip1, hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
const struct hash_ip6_elem *ip2) const struct hash_ip6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0; return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0;
} }
......
...@@ -60,7 +60,8 @@ struct hash_ipport4_telem { ...@@ -60,7 +60,8 @@ struct hash_ipport4_telem {
static inline bool static inline bool
hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1, hash_ipport4_data_equal(const struct hash_ipport4_elem *ip1,
const struct hash_ipport4_elem *ip2) const struct hash_ipport4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->port == ip2->port && ip1->port == ip2->port &&
...@@ -276,7 +277,8 @@ struct hash_ipport6_telem { ...@@ -276,7 +277,8 @@ struct hash_ipport6_telem {
static inline bool static inline bool
hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1, hash_ipport6_data_equal(const struct hash_ipport6_elem *ip1,
const struct hash_ipport6_elem *ip2) const struct hash_ipport6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port && ip1->port == ip2->port &&
......
...@@ -62,7 +62,8 @@ struct hash_ipportip4_telem { ...@@ -62,7 +62,8 @@ struct hash_ipportip4_telem {
static inline bool static inline bool
hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1,
const struct hash_ipportip4_elem *ip2) const struct hash_ipportip4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 && ip1->ip2 == ip2->ip2 &&
...@@ -286,7 +287,8 @@ struct hash_ipportip6_telem { ...@@ -286,7 +287,8 @@ struct hash_ipportip6_telem {
static inline bool static inline bool
hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1,
const struct hash_ipportip6_elem *ip2) const struct hash_ipportip6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
......
...@@ -62,7 +62,8 @@ struct hash_ipportnet4_telem { ...@@ -62,7 +62,8 @@ struct hash_ipportnet4_telem {
static inline bool static inline bool
hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1,
const struct hash_ipportnet4_elem *ip2) const struct hash_ipportnet4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->ip2 == ip2->ip2 && ip1->ip2 == ip2->ip2 &&
...@@ -335,7 +336,8 @@ struct hash_ipportnet6_telem { ...@@ -335,7 +336,8 @@ struct hash_ipportnet6_telem {
static inline bool static inline bool
hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1,
const struct hash_ipportnet6_elem *ip2) const struct hash_ipportnet6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 &&
......
...@@ -58,7 +58,8 @@ struct hash_net4_telem { ...@@ -58,7 +58,8 @@ struct hash_net4_telem {
static inline bool static inline bool
hash_net4_data_equal(const struct hash_net4_elem *ip1, hash_net4_data_equal(const struct hash_net4_elem *ip1,
const struct hash_net4_elem *ip2) const struct hash_net4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr; return ip1->ip == ip2->ip && ip1->cidr == ip2->cidr;
} }
...@@ -249,7 +250,8 @@ struct hash_net6_telem { ...@@ -249,7 +250,8 @@ struct hash_net6_telem {
static inline bool static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1, hash_net6_data_equal(const struct hash_net6_elem *ip1,
const struct hash_net6_elem *ip2) const struct hash_net6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr; ip1->cidr == ip2->cidr;
......
...@@ -99,7 +99,7 @@ iface_test(struct rb_root *root, const char **iface) ...@@ -99,7 +99,7 @@ iface_test(struct rb_root *root, const char **iface)
while (n) { while (n) {
const char *d = iface_data(n); const char *d = iface_data(n);
int res = ifname_compare(*iface, d); long res = ifname_compare(*iface, d);
if (res < 0) if (res < 0)
n = n->rb_left; n = n->rb_left;
...@@ -121,7 +121,7 @@ iface_add(struct rb_root *root, const char **iface) ...@@ -121,7 +121,7 @@ iface_add(struct rb_root *root, const char **iface)
while (*n) { while (*n) {
char *ifname = iface_data(*n); char *ifname = iface_data(*n);
int res = ifname_compare(*iface, ifname); long res = ifname_compare(*iface, ifname);
p = *n; p = *n;
if (res < 0) if (res < 0)
...@@ -159,31 +159,42 @@ hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b); ...@@ -159,31 +159,42 @@ hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b);
/* The type variant functions: IPv4 */ /* The type variant functions: IPv4 */
struct hash_netiface4_elem_hashed {
__be32 ip;
u8 physdev;
u8 cidr;
u16 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
/* Member elements without timeout */ /* Member elements without timeout */
struct hash_netiface4_elem { struct hash_netiface4_elem {
__be32 ip; __be32 ip;
const char *iface;
u8 physdev; u8 physdev;
u8 cidr; u8 cidr;
u16 padding; u16 padding;
const char *iface;
}; };
/* Member elements with timeout support */ /* Member elements with timeout support */
struct hash_netiface4_telem { struct hash_netiface4_telem {
__be32 ip; __be32 ip;
const char *iface;
u8 physdev; u8 physdev;
u8 cidr; u8 cidr;
u16 padding; u16 padding;
const char *iface;
unsigned long timeout; unsigned long timeout;
}; };
static inline bool static inline bool
hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1, hash_netiface4_data_equal(const struct hash_netiface4_elem *ip1,
const struct hash_netiface4_elem *ip2) const struct hash_netiface4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->cidr == ip2->cidr && ip1->cidr == ip2->cidr &&
(++*multi) &&
ip1->physdev == ip2->physdev && ip1->physdev == ip2->physdev &&
ip1->iface == ip2->iface; ip1->iface == ip2->iface;
} }
...@@ -257,6 +268,7 @@ hash_netiface4_data_tlist(struct sk_buff *skb, ...@@ -257,6 +268,7 @@ hash_netiface4_data_tlist(struct sk_buff *skb,
#define IP_SET_HASH_WITH_NETS #define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_RBTREE #define IP_SET_HASH_WITH_RBTREE
#define IP_SET_HASH_WITH_MULTI
#define PF 4 #define PF 4
#define HOST_MASK 32 #define HOST_MASK 32
...@@ -424,29 +436,40 @@ hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b) ...@@ -424,29 +436,40 @@ hash_netiface_same_set(const struct ip_set *a, const struct ip_set *b)
/* The type variant functions: IPv6 */ /* The type variant functions: IPv6 */
struct hash_netiface6_elem_hashed {
union nf_inet_addr ip;
u8 physdev;
u8 cidr;
u16 padding;
};
#define HKEY_DATALEN sizeof(struct hash_netiface6_elem_hashed)
struct hash_netiface6_elem { struct hash_netiface6_elem {
union nf_inet_addr ip; union nf_inet_addr ip;
const char *iface;
u8 physdev; u8 physdev;
u8 cidr; u8 cidr;
u16 padding; u16 padding;
const char *iface;
}; };
struct hash_netiface6_telem { struct hash_netiface6_telem {
union nf_inet_addr ip; union nf_inet_addr ip;
const char *iface;
u8 physdev; u8 physdev;
u8 cidr; u8 cidr;
u16 padding; u16 padding;
const char *iface;
unsigned long timeout; unsigned long timeout;
}; };
static inline bool static inline bool
hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1, hash_netiface6_data_equal(const struct hash_netiface6_elem *ip1,
const struct hash_netiface6_elem *ip2) const struct hash_netiface6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->cidr == ip2->cidr && ip1->cidr == ip2->cidr &&
(++*multi) &&
ip1->physdev == ip2->physdev && ip1->physdev == ip2->physdev &&
ip1->iface == ip2->iface; ip1->iface == ip2->iface;
} }
...@@ -681,6 +704,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags) ...@@ -681,6 +704,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
h->maxelem = maxelem; h->maxelem = maxelem;
get_random_bytes(&h->initval, sizeof(h->initval)); get_random_bytes(&h->initval, sizeof(h->initval));
h->timeout = IPSET_NO_TIMEOUT; h->timeout = IPSET_NO_TIMEOUT;
h->ahash_max = AHASH_MAX_SIZE;
hbits = htable_bits(hashsize); hbits = htable_bits(hashsize);
h->table = ip_set_alloc( h->table = ip_set_alloc(
......
...@@ -59,7 +59,8 @@ struct hash_netport4_telem { ...@@ -59,7 +59,8 @@ struct hash_netport4_telem {
static inline bool static inline bool
hash_netport4_data_equal(const struct hash_netport4_elem *ip1, hash_netport4_data_equal(const struct hash_netport4_elem *ip1,
const struct hash_netport4_elem *ip2) const struct hash_netport4_elem *ip2,
u32 *multi)
{ {
return ip1->ip == ip2->ip && return ip1->ip == ip2->ip &&
ip1->port == ip2->port && ip1->port == ip2->port &&
...@@ -300,7 +301,8 @@ struct hash_netport6_telem { ...@@ -300,7 +301,8 @@ struct hash_netport6_telem {
static inline bool static inline bool
hash_netport6_data_equal(const struct hash_netport6_elem *ip1, hash_netport6_data_equal(const struct hash_netport6_elem *ip1,
const struct hash_netport6_elem *ip2) const struct hash_netport6_elem *ip2,
u32 *multi)
{ {
return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
ip1->port == ip2->port && ip1->port == ip2->port &&
......
...@@ -37,7 +37,7 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER); ...@@ -37,7 +37,7 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
static char __initdata nfversion[] = "0.30"; static char __initdata nfversion[] = "0.30";
static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]; static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
static DEFINE_MUTEX(nfnl_mutex); static DEFINE_MUTEX(nfnl_mutex);
void nfnl_lock(void) void nfnl_lock(void)
...@@ -59,7 +59,7 @@ int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) ...@@ -59,7 +59,7 @@ int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
nfnl_unlock(); nfnl_unlock();
return -EBUSY; return -EBUSY;
} }
subsys_table[n->subsys_id] = n; rcu_assign_pointer(subsys_table[n->subsys_id], n);
nfnl_unlock(); nfnl_unlock();
return 0; return 0;
...@@ -71,7 +71,7 @@ int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) ...@@ -71,7 +71,7 @@ int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
nfnl_lock(); nfnl_lock();
subsys_table[n->subsys_id] = NULL; subsys_table[n->subsys_id] = NULL;
nfnl_unlock(); nfnl_unlock();
synchronize_rcu();
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
...@@ -83,7 +83,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t ...@@ -83,7 +83,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
if (subsys_id >= NFNL_SUBSYS_COUNT) if (subsys_id >= NFNL_SUBSYS_COUNT)
return NULL; return NULL;
return subsys_table[subsys_id]; return rcu_dereference(subsys_table[subsys_id]);
} }
static inline const struct nfnl_callback * static inline const struct nfnl_callback *
...@@ -139,21 +139,27 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -139,21 +139,27 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
type = nlh->nlmsg_type; type = nlh->nlmsg_type;
replay: replay:
rcu_read_lock();
ss = nfnetlink_get_subsys(type); ss = nfnetlink_get_subsys(type);
if (!ss) { if (!ss) {
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
nfnl_unlock(); rcu_read_unlock();
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
nfnl_lock(); rcu_read_lock();
ss = nfnetlink_get_subsys(type); ss = nfnetlink_get_subsys(type);
if (!ss) if (!ss)
#endif #endif
{
rcu_read_unlock();
return -EINVAL; return -EINVAL;
} }
}
nc = nfnetlink_find_client(type, ss); nc = nfnetlink_find_client(type, ss);
if (!nc) if (!nc) {
rcu_read_unlock();
return -EINVAL; return -EINVAL;
}
{ {
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
...@@ -167,7 +173,23 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -167,7 +173,23 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (err < 0) if (err < 0)
return err; return err;
err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda); if (nc->call_rcu) {
err = nc->call_rcu(net->nfnl, skb, nlh,
(const struct nlattr **)cda);
rcu_read_unlock();
} else {
rcu_read_unlock();
nfnl_lock();
if (rcu_dereference_protected(
subsys_table[NFNL_SUBSYS_ID(type)],
lockdep_is_held(&nfnl_mutex)) != ss ||
nfnetlink_find_client(type, ss) != nc)
err = -EAGAIN;
else
err = nc->call(net->nfnl, skb, nlh,
(const struct nlattr **)cda);
nfnl_unlock();
}
if (err == -EAGAIN) if (err == -EAGAIN)
goto replay; goto replay;
return err; return err;
...@@ -176,9 +198,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -176,9 +198,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static void nfnetlink_rcv(struct sk_buff *skb) static void nfnetlink_rcv(struct sk_buff *skb)
{ {
nfnl_lock();
netlink_rcv_skb(skb, &nfnetlink_rcv_msg); netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
nfnl_unlock();
} }
static int __net_init nfnetlink_net_init(struct net *net) static int __net_init nfnetlink_net_init(struct net *net)
......
...@@ -58,7 +58,7 @@ struct nfqnl_instance { ...@@ -58,7 +58,7 @@ struct nfqnl_instance {
*/ */
spinlock_t lock; spinlock_t lock;
unsigned int queue_total; unsigned int queue_total;
atomic_t id_sequence; /* 'sequence' of pkt ids */ unsigned int id_sequence; /* 'sequence' of pkt ids */
struct list_head queue_list; /* packets in queue */ struct list_head queue_list; /* packets in queue */
}; };
...@@ -171,6 +171,13 @@ __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) ...@@ -171,6 +171,13 @@ __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry)
queue->queue_total++; queue->queue_total++;
} }
static void
__dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry)
{
list_del(&entry->list);
queue->queue_total--;
}
static struct nf_queue_entry * static struct nf_queue_entry *
find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id)
{ {
...@@ -185,10 +192,8 @@ find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) ...@@ -185,10 +192,8 @@ find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id)
} }
} }
if (entry) { if (entry)
list_del(&entry->list); __dequeue_entry(queue, entry);
queue->queue_total--;
}
spin_unlock_bh(&queue->lock); spin_unlock_bh(&queue->lock);
...@@ -213,13 +218,15 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) ...@@ -213,13 +218,15 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
static struct sk_buff * static struct sk_buff *
nfqnl_build_packet_message(struct nfqnl_instance *queue, nfqnl_build_packet_message(struct nfqnl_instance *queue,
struct nf_queue_entry *entry) struct nf_queue_entry *entry,
__be32 **packet_id_ptr)
{ {
sk_buff_data_t old_tail; sk_buff_data_t old_tail;
size_t size; size_t size;
size_t data_len = 0; size_t data_len = 0;
struct sk_buff *skb; struct sk_buff *skb;
struct nfqnl_msg_packet_hdr pmsg; struct nlattr *nla;
struct nfqnl_msg_packet_hdr *pmsg;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg; struct nfgenmsg *nfmsg;
struct sk_buff *entskb = entry->skb; struct sk_buff *entskb = entry->skb;
...@@ -272,12 +279,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, ...@@ -272,12 +279,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue,
nfmsg->version = NFNETLINK_V0; nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = htons(queue->queue_num); nfmsg->res_id = htons(queue->queue_num);
entry->id = atomic_inc_return(&queue->id_sequence); nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg));
pmsg.packet_id = htonl(entry->id); pmsg = nla_data(nla);
pmsg.hw_protocol = entskb->protocol; pmsg->hw_protocol = entskb->protocol;
pmsg.hook = entry->hook; pmsg->hook = entry->hook;
*packet_id_ptr = &pmsg->packet_id;
NLA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg);
indev = entry->indev; indev = entry->indev;
if (indev) { if (indev) {
...@@ -389,6 +395,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) ...@@ -389,6 +395,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
struct sk_buff *nskb; struct sk_buff *nskb;
struct nfqnl_instance *queue; struct nfqnl_instance *queue;
int err = -ENOBUFS; int err = -ENOBUFS;
__be32 *packet_id_ptr;
/* rcu_read_lock()ed by nf_hook_slow() */ /* rcu_read_lock()ed by nf_hook_slow() */
queue = instance_lookup(queuenum); queue = instance_lookup(queuenum);
...@@ -402,7 +409,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) ...@@ -402,7 +409,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
goto err_out; goto err_out;
} }
nskb = nfqnl_build_packet_message(queue, entry); nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr);
if (nskb == NULL) { if (nskb == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;
...@@ -421,6 +428,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) ...@@ -421,6 +428,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
queue->queue_total); queue->queue_total);
goto err_out_free_nskb; goto err_out_free_nskb;
} }
entry->id = ++queue->id_sequence;
*packet_id_ptr = htonl(entry->id);
/* nfnetlink_unicast will either free the nskb or add it to a socket */ /* nfnetlink_unicast will either free the nskb or add it to a socket */
err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT);
...@@ -608,6 +617,92 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { ...@@ -608,6 +617,92 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = {
[NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC },
}; };
static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = {
[NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) },
[NFQA_MARK] = { .type = NLA_U32 },
};
static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid)
{
struct nfqnl_instance *queue;
queue = instance_lookup(queue_num);
if (!queue)
return ERR_PTR(-ENODEV);
if (queue->peer_pid != nlpid)
return ERR_PTR(-EPERM);
return queue;
}
static struct nfqnl_msg_verdict_hdr*
verdicthdr_get(const struct nlattr * const nfqa[])
{
struct nfqnl_msg_verdict_hdr *vhdr;
unsigned int verdict;
if (!nfqa[NFQA_VERDICT_HDR])
return NULL;
vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]);
verdict = ntohl(vhdr->verdict);
if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)
return NULL;
return vhdr;
}
static int nfq_id_after(unsigned int id, unsigned int max)
{
return (int)(id - max) > 0;
}
static int
nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
struct nf_queue_entry *entry, *tmp;
unsigned int verdict, maxid;
struct nfqnl_msg_verdict_hdr *vhdr;
struct nfqnl_instance *queue;
LIST_HEAD(batch_list);
u16 queue_num = ntohs(nfmsg->res_id);
queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid);
if (IS_ERR(queue))
return PTR_ERR(queue);
vhdr = verdicthdr_get(nfqa);
if (!vhdr)
return -EINVAL;
verdict = ntohl(vhdr->verdict);
maxid = ntohl(vhdr->id);
spin_lock_bh(&queue->lock);
list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) {
if (nfq_id_after(entry->id, maxid))
break;
__dequeue_entry(queue, entry);
list_add_tail(&entry->list, &batch_list);
}
spin_unlock_bh(&queue->lock);
if (list_empty(&batch_list))
return -ENOENT;
list_for_each_entry_safe(entry, tmp, &batch_list, list) {
if (nfqa[NFQA_MARK])
entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK]));
nf_reinject(entry, verdict);
}
return 0;
}
static int static int
nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
...@@ -620,39 +715,23 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, ...@@ -620,39 +715,23 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
struct nfqnl_instance *queue; struct nfqnl_instance *queue;
unsigned int verdict; unsigned int verdict;
struct nf_queue_entry *entry; struct nf_queue_entry *entry;
int err;
rcu_read_lock();
queue = instance_lookup(queue_num); queue = instance_lookup(queue_num);
if (!queue) { if (!queue)
err = -ENODEV;
goto err_out_unlock;
}
if (queue->peer_pid != NETLINK_CB(skb).pid) { queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid);
err = -EPERM; if (IS_ERR(queue))
goto err_out_unlock; return PTR_ERR(queue);
}
if (!nfqa[NFQA_VERDICT_HDR]) { vhdr = verdicthdr_get(nfqa);
err = -EINVAL; if (!vhdr)
goto err_out_unlock; return -EINVAL;
}
vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]);
verdict = ntohl(vhdr->verdict); verdict = ntohl(vhdr->verdict);
if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) {
err = -EINVAL;
goto err_out_unlock;
}
entry = find_dequeue_entry(queue, ntohl(vhdr->id)); entry = find_dequeue_entry(queue, ntohl(vhdr->id));
if (entry == NULL) { if (entry == NULL)
err = -ENOENT; return -ENOENT;
goto err_out_unlock;
}
rcu_read_unlock();
if (nfqa[NFQA_PAYLOAD]) { if (nfqa[NFQA_PAYLOAD]) {
if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]),
...@@ -665,10 +744,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, ...@@ -665,10 +744,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
nf_reinject(entry, verdict); nf_reinject(entry, verdict);
return 0; return 0;
err_out_unlock:
rcu_read_unlock();
return err;
} }
static int static int
...@@ -781,14 +856,17 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ...@@ -781,14 +856,17 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
} }
static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = {
[NFQNL_MSG_PACKET] = { .call = nfqnl_recv_unsupp, [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp,
.attr_count = NFQA_MAX, }, .attr_count = NFQA_MAX, },
[NFQNL_MSG_VERDICT] = { .call = nfqnl_recv_verdict, [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict,
.attr_count = NFQA_MAX, .attr_count = NFQA_MAX,
.policy = nfqa_verdict_policy }, .policy = nfqa_verdict_policy },
[NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config,
.attr_count = NFQA_CFG_MAX, .attr_count = NFQA_CFG_MAX,
.policy = nfqa_cfg_policy }, .policy = nfqa_cfg_policy },
[NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch,
.attr_count = NFQA_MAX,
.policy = nfqa_verdict_batch_policy },
}; };
static const struct nfnetlink_subsystem nfqnl_subsys = { static const struct nfnetlink_subsystem nfqnl_subsys = {
...@@ -870,7 +948,7 @@ static int seq_show(struct seq_file *s, void *v) ...@@ -870,7 +948,7 @@ static int seq_show(struct seq_file *s, void *v)
inst->peer_pid, inst->queue_total, inst->peer_pid, inst->queue_total,
inst->copy_mode, inst->copy_range, inst->copy_mode, inst->copy_range,
inst->queue_dropped, inst->queue_user_dropped, inst->queue_dropped, inst->queue_user_dropped,
atomic_read(&inst->id_sequence), 1); inst->id_sequence, 1);
} }
static const struct seq_operations nfqnl_seq_ops = { static const struct seq_operations nfqnl_seq_ops = {
......
...@@ -163,6 +163,11 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -163,6 +163,11 @@ audit_tg(struct sk_buff *skb, const struct xt_action_param *par)
break; break;
} }
#ifdef CONFIG_NETWORK_SECMARK
if (skb->secmark)
audit_log_secctx(ab, skb->secmark);
#endif
audit_log_end(ab); audit_log_end(ab);
errout: errout:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment