Commit 894d0844 authored by David S. Miller's avatar David S. Miller

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

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next:

1) Use array_size() in ebtables, from Gustavo A. R. Silva.

2) Attach IPS_ASSURED to internal UDP stream state, reported by
   Maciej Zenczykowski.

3) Add NFT_META_IFTYPE to match on the interface type either
   from ingress or egress.

4) Generalize pktinfo->tprot_set to flags field.

5) Allow to match on inner headers / payload data.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2aec919f c46b38dc
...@@ -21,13 +21,19 @@ struct module; ...@@ -21,13 +21,19 @@ struct module;
#define NFT_JUMP_STACK_SIZE 16 #define NFT_JUMP_STACK_SIZE 16
enum {
NFT_PKTINFO_L4PROTO = (1 << 0),
NFT_PKTINFO_INNER = (1 << 1),
};
struct nft_pktinfo { struct nft_pktinfo {
struct sk_buff *skb; struct sk_buff *skb;
const struct nf_hook_state *state; const struct nf_hook_state *state;
bool tprot_set; u8 flags;
u8 tprot; u8 tprot;
u16 fragoff; u16 fragoff;
unsigned int thoff; unsigned int thoff;
unsigned int inneroff;
}; };
static inline struct sock *nft_sk(const struct nft_pktinfo *pkt) static inline struct sock *nft_sk(const struct nft_pktinfo *pkt)
...@@ -75,7 +81,7 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, ...@@ -75,7 +81,7 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt) static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt)
{ {
pkt->tprot_set = false; pkt->flags = 0;
pkt->tprot = 0; pkt->tprot = 0;
pkt->thoff = 0; pkt->thoff = 0;
pkt->fragoff = 0; pkt->fragoff = 0;
......
...@@ -10,7 +10,7 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt) ...@@ -10,7 +10,7 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt)
struct iphdr *ip; struct iphdr *ip;
ip = ip_hdr(pkt->skb); ip = ip_hdr(pkt->skb);
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = ip->protocol; pkt->tprot = ip->protocol;
pkt->thoff = ip_hdrlen(pkt->skb); pkt->thoff = ip_hdrlen(pkt->skb);
pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET; pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET;
...@@ -36,7 +36,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) ...@@ -36,7 +36,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
else if (len < thoff) else if (len < thoff)
return -1; return -1;
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = iph->protocol; pkt->tprot = iph->protocol;
pkt->thoff = thoff; pkt->thoff = thoff;
pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET; pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
...@@ -71,7 +71,7 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) ...@@ -71,7 +71,7 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
goto inhdr_error; goto inhdr_error;
} }
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = iph->protocol; pkt->tprot = iph->protocol;
pkt->thoff = thoff; pkt->thoff = thoff;
pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET; pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
...@@ -82,4 +82,5 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) ...@@ -82,4 +82,5 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INHDRERRORS); __IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INHDRERRORS);
return -1; return -1;
} }
#endif #endif
...@@ -18,7 +18,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt) ...@@ -18,7 +18,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)
return; return;
} }
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = protohdr; pkt->tprot = protohdr;
pkt->thoff = thoff; pkt->thoff = thoff;
pkt->fragoff = frag_off; pkt->fragoff = frag_off;
...@@ -50,7 +50,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) ...@@ -50,7 +50,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
if (protohdr < 0) if (protohdr < 0)
return -1; return -1;
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = protohdr; pkt->tprot = protohdr;
pkt->thoff = thoff; pkt->thoff = thoff;
pkt->fragoff = frag_off; pkt->fragoff = frag_off;
...@@ -96,7 +96,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt) ...@@ -96,7 +96,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)
if (protohdr < 0) if (protohdr < 0)
goto inhdr_error; goto inhdr_error;
pkt->tprot_set = true; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = protohdr; pkt->tprot = protohdr;
pkt->thoff = thoff; pkt->thoff = thoff;
pkt->fragoff = frag_off; pkt->fragoff = frag_off;
......
...@@ -753,11 +753,13 @@ enum nft_dynset_attributes { ...@@ -753,11 +753,13 @@ enum nft_dynset_attributes {
* @NFT_PAYLOAD_LL_HEADER: link layer header * @NFT_PAYLOAD_LL_HEADER: link layer header
* @NFT_PAYLOAD_NETWORK_HEADER: network header * @NFT_PAYLOAD_NETWORK_HEADER: network header
* @NFT_PAYLOAD_TRANSPORT_HEADER: transport header * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
* @NFT_PAYLOAD_INNER_HEADER: inner header / payload
*/ */
enum nft_payload_bases { enum nft_payload_bases {
NFT_PAYLOAD_LL_HEADER, NFT_PAYLOAD_LL_HEADER,
NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_NETWORK_HEADER,
NFT_PAYLOAD_TRANSPORT_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER,
NFT_PAYLOAD_INNER_HEADER,
}; };
/** /**
...@@ -896,7 +898,8 @@ enum nft_meta_keys { ...@@ -896,7 +898,8 @@ enum nft_meta_keys {
NFT_META_OIF, NFT_META_OIF,
NFT_META_IIFNAME, NFT_META_IIFNAME,
NFT_META_OIFNAME, NFT_META_OIFNAME,
NFT_META_IIFTYPE, NFT_META_IFTYPE,
#define NFT_META_IIFTYPE NFT_META_IFTYPE
NFT_META_OIFTYPE, NFT_META_OIFTYPE,
NFT_META_SKUID, NFT_META_SKUID,
NFT_META_SKGID, NFT_META_SKGID,
...@@ -923,6 +926,7 @@ enum nft_meta_keys { ...@@ -923,6 +926,7 @@ enum nft_meta_keys {
NFT_META_TIME_HOUR, NFT_META_TIME_HOUR,
NFT_META_SDIF, NFT_META_SDIF,
NFT_META_SDIFNAME, NFT_META_SDIFNAME,
__NFT_META_IIFTYPE,
}; };
/** /**
......
...@@ -1073,7 +1073,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl, ...@@ -1073,7 +1073,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
*/ */
if (repl->num_counters && if (repl->num_counters &&
copy_to_user(repl->counters, counterstmp, copy_to_user(repl->counters, counterstmp,
repl->num_counters * sizeof(struct ebt_counter))) { array_size(repl->num_counters, sizeof(struct ebt_counter)))) {
/* Silent error, can't fail, new table is already in place */ /* Silent error, can't fail, new table is already in place */
net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n"); net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");
} }
...@@ -1401,7 +1401,8 @@ static int do_update_counters(struct net *net, const char *name, ...@@ -1401,7 +1401,8 @@ static int do_update_counters(struct net *net, const char *name,
goto unlock_mutex; goto unlock_mutex;
} }
if (copy_from_user(tmp, counters, num_counters * sizeof(*counters))) { if (copy_from_user(tmp, counters,
array_size(num_counters, sizeof(*counters)))) {
ret = -EFAULT; ret = -EFAULT;
goto unlock_mutex; goto unlock_mutex;
} }
...@@ -1534,7 +1535,7 @@ static int copy_counters_to_user(struct ebt_table *t, ...@@ -1534,7 +1535,7 @@ static int copy_counters_to_user(struct ebt_table *t,
write_unlock_bh(&t->lock); write_unlock_bh(&t->lock);
if (copy_to_user(user, counterstmp, if (copy_to_user(user, counterstmp,
nentries * sizeof(struct ebt_counter))) array_size(nentries, sizeof(struct ebt_counter))))
ret = -EFAULT; ret = -EFAULT;
vfree(counterstmp); vfree(counterstmp);
return ret; return ret;
......
...@@ -104,10 +104,13 @@ int nf_conntrack_udp_packet(struct nf_conn *ct, ...@@ -104,10 +104,13 @@ int nf_conntrack_udp_packet(struct nf_conn *ct,
*/ */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
unsigned long extra = timeouts[UDP_CT_UNREPLIED]; unsigned long extra = timeouts[UDP_CT_UNREPLIED];
bool stream = false;
/* Still active after two seconds? Extend timeout. */ /* Still active after two seconds? Extend timeout. */
if (time_after(jiffies, ct->proto.udp.stream_ts)) if (time_after(jiffies, ct->proto.udp.stream_ts)) {
extra = timeouts[UDP_CT_REPLIED]; extra = timeouts[UDP_CT_REPLIED];
stream = true;
}
nf_ct_refresh_acct(ct, ctinfo, skb, extra); nf_ct_refresh_acct(ct, ctinfo, skb, extra);
...@@ -116,7 +119,7 @@ int nf_conntrack_udp_packet(struct nf_conn *ct, ...@@ -116,7 +119,7 @@ int nf_conntrack_udp_packet(struct nf_conn *ct,
return NF_ACCEPT; return NF_ACCEPT;
/* Also, more likely to be important, and not a probe */ /* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status)) if (stream && !test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_ASSURED, ct); nf_conntrack_event_cache(IPCT_ASSURED, ct);
} else { } else {
nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[UDP_CT_UNREPLIED]); nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[UDP_CT_UNREPLIED]);
......
...@@ -79,7 +79,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, ...@@ -79,7 +79,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
ptr = skb_network_header(skb); ptr = skb_network_header(skb);
else { else {
if (!pkt->tprot_set) if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
return false; return false;
ptr = skb_network_header(skb) + nft_thoff(pkt); ptr = skb_network_header(skb) + nft_thoff(pkt);
} }
......
...@@ -113,13 +113,13 @@ static int nf_trace_fill_pkt_info(struct sk_buff *nlskb, ...@@ -113,13 +113,13 @@ static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
int off = skb_network_offset(skb); int off = skb_network_offset(skb);
unsigned int len, nh_end; unsigned int len, nh_end;
nh_end = pkt->tprot_set ? nft_thoff(pkt) : skb->len; nh_end = pkt->flags & NFT_PKTINFO_L4PROTO ? nft_thoff(pkt) : skb->len;
len = min_t(unsigned int, nh_end - skb_network_offset(skb), len = min_t(unsigned int, nh_end - skb_network_offset(skb),
NFT_TRACETYPE_NETWORK_HSIZE); NFT_TRACETYPE_NETWORK_HSIZE);
if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len)) if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
return -1; return -1;
if (pkt->tprot_set) { if (pkt->flags & NFT_PKTINFO_L4PROTO) {
len = min_t(unsigned int, skb->len - nft_thoff(pkt), len = min_t(unsigned int, skb->len - nft_thoff(pkt),
NFT_TRACETYPE_TRANSPORT_HSIZE); NFT_TRACETYPE_TRANSPORT_HSIZE);
if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb, if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
......
...@@ -244,7 +244,11 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, ...@@ -244,7 +244,11 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
case NFT_META_OIF: case NFT_META_OIF:
nft_meta_store_ifindex(dest, nft_out(pkt)); nft_meta_store_ifindex(dest, nft_out(pkt));
break; break;
case NFT_META_IIFTYPE: case NFT_META_IFTYPE:
if (!nft_meta_store_iftype(dest, pkt->skb->dev))
return false;
break;
case __NFT_META_IIFTYPE:
if (!nft_meta_store_iftype(dest, nft_in(pkt))) if (!nft_meta_store_iftype(dest, nft_in(pkt)))
return false; return false;
break; break;
...@@ -329,7 +333,7 @@ void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -329,7 +333,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
nft_reg_store8(dest, nft_pf(pkt)); nft_reg_store8(dest, nft_pf(pkt));
break; break;
case NFT_META_L4PROTO: case NFT_META_L4PROTO:
if (!pkt->tprot_set) if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
goto err; goto err;
nft_reg_store8(dest, pkt->tprot); nft_reg_store8(dest, pkt->tprot);
break; break;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/ip.h>
#include <net/sctp/checksum.h> #include <net/sctp/checksum.h>
static bool nft_payload_rebuild_vlan_hdr(const struct sk_buff *skb, int mac_off, static bool nft_payload_rebuild_vlan_hdr(const struct sk_buff *skb, int mac_off,
...@@ -79,6 +80,45 @@ nft_payload_copy_vlan(u32 *d, const struct sk_buff *skb, u8 offset, u8 len) ...@@ -79,6 +80,45 @@ nft_payload_copy_vlan(u32 *d, const struct sk_buff *skb, u8 offset, u8 len)
return skb_copy_bits(skb, offset + mac_off, dst_u8, len) == 0; return skb_copy_bits(skb, offset + mac_off, dst_u8, len) == 0;
} }
static int __nft_payload_inner_offset(struct nft_pktinfo *pkt)
{
unsigned int thoff = nft_thoff(pkt);
if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
return -1;
switch (pkt->tprot) {
case IPPROTO_UDP:
pkt->inneroff = thoff + sizeof(struct udphdr);
break;
case IPPROTO_TCP: {
struct tcphdr *th, _tcph;
th = skb_header_pointer(pkt->skb, thoff, sizeof(_tcph), &_tcph);
if (!th)
return -1;
pkt->inneroff = thoff + __tcp_hdrlen(th);
}
break;
default:
return -1;
}
pkt->flags |= NFT_PKTINFO_INNER;
return 0;
}
static int nft_payload_inner_offset(const struct nft_pktinfo *pkt)
{
if (!(pkt->flags & NFT_PKTINFO_INNER) &&
__nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0)
return -1;
return pkt->inneroff;
}
void nft_payload_eval(const struct nft_expr *expr, void nft_payload_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
...@@ -108,10 +148,15 @@ void nft_payload_eval(const struct nft_expr *expr, ...@@ -108,10 +148,15 @@ void nft_payload_eval(const struct nft_expr *expr,
offset = skb_network_offset(skb); offset = skb_network_offset(skb);
break; break;
case NFT_PAYLOAD_TRANSPORT_HEADER: case NFT_PAYLOAD_TRANSPORT_HEADER:
if (!pkt->tprot_set) if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
goto err; goto err;
offset = nft_thoff(pkt); offset = nft_thoff(pkt);
break; break;
case NFT_PAYLOAD_INNER_HEADER:
offset = nft_payload_inner_offset(pkt);
if (offset < 0)
goto err;
break;
default: default:
BUG(); BUG();
} }
...@@ -610,10 +655,15 @@ static void nft_payload_set_eval(const struct nft_expr *expr, ...@@ -610,10 +655,15 @@ static void nft_payload_set_eval(const struct nft_expr *expr,
offset = skb_network_offset(skb); offset = skb_network_offset(skb);
break; break;
case NFT_PAYLOAD_TRANSPORT_HEADER: case NFT_PAYLOAD_TRANSPORT_HEADER:
if (!pkt->tprot_set) if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
goto err; goto err;
offset = nft_thoff(pkt); offset = nft_thoff(pkt);
break; break;
case NFT_PAYLOAD_INNER_HEADER:
offset = nft_payload_inner_offset(pkt);
if (offset < 0)
goto err;
break;
default: default:
BUG(); BUG();
} }
...@@ -622,7 +672,8 @@ static void nft_payload_set_eval(const struct nft_expr *expr, ...@@ -622,7 +672,8 @@ static void nft_payload_set_eval(const struct nft_expr *expr,
offset += priv->offset; offset += priv->offset;
if ((priv->csum_type == NFT_PAYLOAD_CSUM_INET || priv->csum_flags) && if ((priv->csum_type == NFT_PAYLOAD_CSUM_INET || priv->csum_flags) &&
(priv->base != NFT_PAYLOAD_TRANSPORT_HEADER || ((priv->base != NFT_PAYLOAD_TRANSPORT_HEADER &&
priv->base != NFT_PAYLOAD_INNER_HEADER) ||
skb->ip_summed != CHECKSUM_PARTIAL)) { skb->ip_summed != CHECKSUM_PARTIAL)) {
fsum = skb_checksum(skb, offset, priv->len, 0); fsum = skb_checksum(skb, offset, priv->len, 0);
tsum = csum_partial(src, priv->len, 0); tsum = csum_partial(src, priv->len, 0);
...@@ -741,6 +792,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx, ...@@ -741,6 +792,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
case NFT_PAYLOAD_LL_HEADER: case NFT_PAYLOAD_LL_HEADER:
case NFT_PAYLOAD_NETWORK_HEADER: case NFT_PAYLOAD_NETWORK_HEADER:
case NFT_PAYLOAD_TRANSPORT_HEADER: case NFT_PAYLOAD_TRANSPORT_HEADER:
case NFT_PAYLOAD_INNER_HEADER:
break; break;
default: default:
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
...@@ -759,7 +811,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx, ...@@ -759,7 +811,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
if (len <= 4 && is_power_of_2(len) && IS_ALIGNED(offset, len) && if (len <= 4 && is_power_of_2(len) && IS_ALIGNED(offset, len) &&
base != NFT_PAYLOAD_LL_HEADER) base != NFT_PAYLOAD_LL_HEADER && base != NFT_PAYLOAD_INNER_HEADER)
return &nft_payload_fast_ops; return &nft_payload_fast_ops;
else else
return &nft_payload_ops; return &nft_payload_ops;
......
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