Commit a26aea20 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Don't flag SCTP heartbeat as invalid for re-used connections,
   from Florian Westphal.

2) Bogus overlap report due to rbtree tree rotations, from Stefano Brivio.

3) Detect partial overlap with start end point match, also from Stefano.

4) Skip netlink dump of NFTA_SET_USERDATA is unset.

5) Incorrect nft_list_attributes enumeration definition.

6) Missing zeroing before memcpy to destination register, also
   from Florian.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7ef1fc57 1e105e6a
...@@ -9,6 +9,8 @@ struct ip_ct_sctp { ...@@ -9,6 +9,8 @@ struct ip_ct_sctp {
enum sctp_conntrack state; enum sctp_conntrack state;
__be32 vtag[IP_CT_DIR_MAX]; __be32 vtag[IP_CT_DIR_MAX];
u8 last_dir;
u8 flags;
}; };
#endif /* _NF_CONNTRACK_SCTP_H */ #endif /* _NF_CONNTRACK_SCTP_H */
...@@ -143,6 +143,8 @@ static inline u64 nft_reg_load64(const u32 *sreg) ...@@ -143,6 +143,8 @@ static inline u64 nft_reg_load64(const u32 *sreg)
static inline void nft_data_copy(u32 *dst, const struct nft_data *src, static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
unsigned int len) unsigned int len)
{ {
if (len % NFT_REG32_SIZE)
dst[len / NFT_REG32_SIZE] = 0;
memcpy(dst, src, len); memcpy(dst, src, len);
} }
......
...@@ -133,7 +133,7 @@ enum nf_tables_msg_types { ...@@ -133,7 +133,7 @@ enum nf_tables_msg_types {
* @NFTA_LIST_ELEM: list element (NLA_NESTED) * @NFTA_LIST_ELEM: list element (NLA_NESTED)
*/ */
enum nft_list_attributes { enum nft_list_attributes {
NFTA_LIST_UNPEC, NFTA_LIST_UNSPEC,
NFTA_LIST_ELEM, NFTA_LIST_ELEM,
__NFTA_LIST_MAX __NFTA_LIST_MAX
}; };
......
...@@ -62,6 +62,8 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = { ...@@ -62,6 +62,8 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
[SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS, [SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS,
}; };
#define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
#define sNO SCTP_CONNTRACK_NONE #define sNO SCTP_CONNTRACK_NONE
#define sCL SCTP_CONNTRACK_CLOSED #define sCL SCTP_CONNTRACK_CLOSED
#define sCW SCTP_CONNTRACK_COOKIE_WAIT #define sCW SCTP_CONNTRACK_COOKIE_WAIT
...@@ -369,6 +371,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -369,6 +371,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
u_int32_t offset, count; u_int32_t offset, count;
unsigned int *timeouts; unsigned int *timeouts;
unsigned long map[256 / sizeof(unsigned long)] = { 0 }; unsigned long map[256 / sizeof(unsigned long)] = { 0 };
bool ignore = false;
if (sctp_error(skb, dataoff, state)) if (sctp_error(skb, dataoff, state))
return -NF_ACCEPT; return -NF_ACCEPT;
...@@ -427,15 +430,39 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -427,15 +430,39 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
/* Sec 8.5.1 (D) */ /* Sec 8.5.1 (D) */
if (sh->vtag != ct->proto.sctp.vtag[dir]) if (sh->vtag != ct->proto.sctp.vtag[dir])
goto out_unlock; goto out_unlock;
} else if (sch->type == SCTP_CID_HEARTBEAT || } else if (sch->type == SCTP_CID_HEARTBEAT) {
sch->type == SCTP_CID_HEARTBEAT_ACK) { if (ct->proto.sctp.vtag[dir] == 0) {
pr_debug("Setting %d vtag %x for dir %d\n", sch->type, sh->vtag, dir);
ct->proto.sctp.vtag[dir] = sh->vtag;
} else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
if (test_bit(SCTP_CID_DATA, map) || ignore)
goto out_unlock;
ct->proto.sctp.flags |= SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
ct->proto.sctp.last_dir = dir;
ignore = true;
continue;
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
}
} else if (sch->type == SCTP_CID_HEARTBEAT_ACK) {
if (ct->proto.sctp.vtag[dir] == 0) { if (ct->proto.sctp.vtag[dir] == 0) {
pr_debug("Setting vtag %x for dir %d\n", pr_debug("Setting vtag %x for dir %d\n",
sh->vtag, dir); sh->vtag, dir);
ct->proto.sctp.vtag[dir] = sh->vtag; ct->proto.sctp.vtag[dir] = sh->vtag;
} else if (sh->vtag != ct->proto.sctp.vtag[dir]) { } else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
pr_debug("Verification tag check failed\n"); if (test_bit(SCTP_CID_DATA, map) || ignore)
goto out_unlock; goto out_unlock;
if ((ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) == 0 ||
ct->proto.sctp.last_dir == dir)
goto out_unlock;
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
ct->proto.sctp.vtag[dir] = sh->vtag;
ct->proto.sctp.vtag[!dir] = 0;
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
} }
} }
...@@ -470,6 +497,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -470,6 +497,10 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
} }
spin_unlock_bh(&ct->lock); spin_unlock_bh(&ct->lock);
/* allow but do not refresh timeout */
if (ignore)
return NF_ACCEPT;
timeouts = nf_ct_timeout_lookup(ct); timeouts = nf_ct_timeout_lookup(ct);
if (!timeouts) if (!timeouts)
timeouts = nf_sctp_pernet(nf_ct_net(ct))->timeouts; timeouts = nf_sctp_pernet(nf_ct_net(ct))->timeouts;
......
...@@ -3770,7 +3770,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, ...@@ -3770,7 +3770,8 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
goto nla_put_failure; goto nla_put_failure;
} }
if (nla_put(skb, NFTA_SET_USERDATA, set->udlen, set->udata)) if (set->udata &&
nla_put(skb, NFTA_SET_USERDATA, set->udlen, set->udata))
goto nla_put_failure; goto nla_put_failure;
nest = nla_nest_start_noflag(skb, NFTA_SET_DESC); nest = nla_nest_start_noflag(skb, NFTA_SET_DESC);
......
...@@ -87,7 +87,9 @@ void nft_payload_eval(const struct nft_expr *expr, ...@@ -87,7 +87,9 @@ void nft_payload_eval(const struct nft_expr *expr,
u32 *dest = &regs->data[priv->dreg]; u32 *dest = &regs->data[priv->dreg];
int offset; int offset;
dest[priv->len / NFT_REG32_SIZE] = 0; if (priv->len % NFT_REG32_SIZE)
dest[priv->len / NFT_REG32_SIZE] = 0;
switch (priv->base) { switch (priv->base) {
case NFT_PAYLOAD_LL_HEADER: case NFT_PAYLOAD_LL_HEADER:
if (!skb_mac_header_was_set(skb)) if (!skb_mac_header_was_set(skb))
......
...@@ -218,11 +218,11 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, ...@@ -218,11 +218,11 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
struct nft_rbtree_elem *new, struct nft_rbtree_elem *new,
struct nft_set_ext **ext) struct nft_set_ext **ext)
{ {
bool overlap = false, dup_end_left = false, dup_end_right = false;
struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree *priv = nft_set_priv(set);
u8 genmask = nft_genmask_next(net); u8 genmask = nft_genmask_next(net);
struct nft_rbtree_elem *rbe; struct nft_rbtree_elem *rbe;
struct rb_node *parent, **p; struct rb_node *parent, **p;
bool overlap = false;
int d; int d;
/* Detect overlaps as we descend the tree. Set the flag in these cases: /* Detect overlaps as we descend the tree. Set the flag in these cases:
...@@ -238,24 +238,44 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, ...@@ -238,24 +238,44 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
* *
* b1. _ _ __>| !_ _ __| (insert end before existing start) * b1. _ _ __>| !_ _ __| (insert end before existing start)
* b2. _ _ ___| !_ _ _>| (insert end after existing start) * b2. _ _ ___| !_ _ _>| (insert end after existing start)
* b3. _ _ ___! >|_ _ __| (insert start after existing end) * b3. _ _ ___! >|_ _ __| (insert start after existing end, as a leaf)
* '--' no nodes falling in this range
* b4. >|_ _ ! (insert start before existing start)
* *
* Case a3. resolves to b3.: * Case a3. resolves to b3.:
* - if the inserted start element is the leftmost, because the '0' * - if the inserted start element is the leftmost, because the '0'
* element in the tree serves as end element * element in the tree serves as end element
* - otherwise, if an existing end is found. Note that end elements are * - otherwise, if an existing end is found immediately to the left. If
* always inserted after corresponding start elements. * there are existing nodes in between, we need to further descend the
* tree before we can conclude the new start isn't causing an overlap
*
* or to b4., which, preceded by a3., means we already traversed one or
* more existing intervals entirely, from the right.
* *
* For a new, rightmost pair of elements, we'll hit cases b3. and b2., * For a new, rightmost pair of elements, we'll hit cases b3. and b2.,
* in that order. * in that order.
* *
* The flag is also cleared in two special cases: * The flag is also cleared in two special cases:
* *
* b4. |__ _ _!|<_ _ _ (insert start right before existing end) * b5. |__ _ _!|<_ _ _ (insert start right before existing end)
* b5. |__ _ >|!__ _ _ (insert end right after existing start) * b6. |__ _ >|!__ _ _ (insert end right after existing start)
* *
* which always happen as last step and imply that no further * which always happen as last step and imply that no further
* overlapping is possible. * overlapping is possible.
*
* Another special case comes from the fact that start elements matching
* an already existing start element are allowed: insertion is not
* performed but we return -EEXIST in that case, and the error will be
* cleared by the caller if NLM_F_EXCL is not present in the request.
* This way, request for insertion of an exact overlap isn't reported as
* error to userspace if not desired.
*
* However, if the existing start matches a pre-existing start, but the
* end element doesn't match the corresponding pre-existing end element,
* we need to report a partial overlap. This is a local condition that
* can be noticed without need for a tracking flag, by checking for a
* local duplicated end for a corresponding start, from left and right,
* separately.
*/ */
parent = NULL; parent = NULL;
...@@ -272,26 +292,41 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, ...@@ -272,26 +292,41 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
if (nft_rbtree_interval_start(new)) { if (nft_rbtree_interval_start(new)) {
if (nft_rbtree_interval_end(rbe) && if (nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext, genmask) && nft_set_elem_active(&rbe->ext, genmask) &&
!nft_set_elem_expired(&rbe->ext)) !nft_set_elem_expired(&rbe->ext) && !*p)
overlap = false; overlap = false;
} else { } else {
if (dup_end_left && !*p)
return -ENOTEMPTY;
overlap = nft_rbtree_interval_end(rbe) && overlap = nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext, nft_set_elem_active(&rbe->ext,
genmask) && genmask) &&
!nft_set_elem_expired(&rbe->ext); !nft_set_elem_expired(&rbe->ext);
if (overlap) {
dup_end_right = true;
continue;
}
} }
} else if (d > 0) { } else if (d > 0) {
p = &parent->rb_right; p = &parent->rb_right;
if (nft_rbtree_interval_end(new)) { if (nft_rbtree_interval_end(new)) {
if (dup_end_right && !*p)
return -ENOTEMPTY;
overlap = nft_rbtree_interval_end(rbe) && overlap = nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext, nft_set_elem_active(&rbe->ext,
genmask) && genmask) &&
!nft_set_elem_expired(&rbe->ext); !nft_set_elem_expired(&rbe->ext);
} else if (nft_rbtree_interval_end(rbe) &&
nft_set_elem_active(&rbe->ext, genmask) && if (overlap) {
dup_end_left = true;
continue;
}
} else if (nft_set_elem_active(&rbe->ext, genmask) &&
!nft_set_elem_expired(&rbe->ext)) { !nft_set_elem_expired(&rbe->ext)) {
overlap = true; overlap = nft_rbtree_interval_end(rbe);
} }
} else { } else {
if (nft_rbtree_interval_end(rbe) && if (nft_rbtree_interval_end(rbe) &&
...@@ -316,6 +351,8 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, ...@@ -316,6 +351,8 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
p = &parent->rb_left; p = &parent->rb_left;
} }
} }
dup_end_left = dup_end_right = false;
} }
if (overlap) if (overlap)
......
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