2014-06-27 05:36:11 -06:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/netlink.h>
|
|
|
|
#include <linux/netfilter.h>
|
|
|
|
#include <linux/netfilter/nf_tables.h>
|
|
|
|
#include <net/netfilter/nf_tables.h>
|
|
|
|
#include <net/netfilter/nft_reject.h>
|
2014-09-26 06:35:14 -06:00
|
|
|
#include <net/netfilter/ipv4/nf_reject.h>
|
|
|
|
#include <net/netfilter/ipv6/nf_reject.h>
|
2014-10-25 10:40:26 -06:00
|
|
|
#include <linux/ip.h>
|
|
|
|
#include <net/ip.h>
|
2014-11-02 19:19:15 -07:00
|
|
|
#include <net/ip6_checksum.h>
|
2014-10-27 07:08:17 -06:00
|
|
|
#include <linux/netfilter_bridge.h>
|
2015-02-16 10:57:53 -07:00
|
|
|
#include <linux/netfilter_ipv6.h>
|
2014-10-25 10:40:26 -06:00
|
|
|
#include "../br_private.h"
|
|
|
|
|
|
|
|
static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
|
|
|
|
struct sk_buff *nskb)
|
|
|
|
{
|
|
|
|
struct ethhdr *eth;
|
|
|
|
|
networking: make skb_push & __skb_push return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions return void * and remove all the casts across
the tree, adding a (u8 *) cast only where the unsigned char pointer
was used directly, all done with the following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
@@
expression SKB, LEN;
identifier fn = { skb_push, __skb_push, skb_push_rcsum };
@@
- fn(SKB, LEN)[0]
+ *(u8 *)fn(SKB, LEN)
Note that the last part there converts from push(...)[0] to the
more idiomatic *(u8 *)push(...).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:23 -06:00
|
|
|
eth = skb_push(nskb, ETH_HLEN);
|
2014-10-25 10:40:26 -06:00
|
|
|
skb_reset_mac_header(nskb);
|
|
|
|
ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest);
|
|
|
|
ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source);
|
|
|
|
eth->h_proto = eth_hdr(oldskb)->h_proto;
|
|
|
|
skb_pull(nskb, ETH_HLEN);
|
|
|
|
}
|
|
|
|
|
2016-09-09 04:42:52 -06:00
|
|
|
static int nft_bridge_iphdr_validate(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct iphdr *iph;
|
|
|
|
u32 len;
|
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iph = ip_hdr(skb);
|
|
|
|
if (iph->ihl < 5 || iph->version != 4)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = ntohs(iph->tot_len);
|
|
|
|
if (skb->len < len)
|
|
|
|
return 0;
|
|
|
|
else if (len < (iph->ihl*4))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, iph->ihl*4))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-02-16 10:57:53 -07:00
|
|
|
/* We cannot use oldskb->dev, it can be either bridge device (NF_BRIDGE INPUT)
|
|
|
|
* or the bridge port (NF_BRIDGE PREROUTING).
|
|
|
|
*/
|
2016-03-26 02:32:57 -06:00
|
|
|
static void nft_reject_br_send_v4_tcp_reset(struct net *net,
|
|
|
|
struct sk_buff *oldskb,
|
2015-02-16 10:57:53 -07:00
|
|
|
const struct net_device *dev,
|
|
|
|
int hook)
|
2014-10-25 10:40:26 -06:00
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
struct iphdr *niph;
|
|
|
|
const struct tcphdr *oth;
|
|
|
|
struct tcphdr _oth;
|
|
|
|
|
2014-11-26 02:21:36 -07:00
|
|
|
if (!nft_bridge_iphdr_validate(oldskb))
|
2014-10-25 10:40:26 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook);
|
|
|
|
if (!oth)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
|
|
|
|
LL_MAX_HEADER, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_reserve(nskb, LL_MAX_HEADER);
|
|
|
|
niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
|
2016-02-15 03:11:27 -07:00
|
|
|
net->ipv4.sysctl_ip_default_ttl);
|
2014-10-25 10:40:26 -06:00
|
|
|
nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
|
2016-02-15 03:11:27 -07:00
|
|
|
niph->ttl = net->ipv4.sysctl_ip_default_ttl;
|
2014-10-25 10:40:26 -06:00
|
|
|
niph->tot_len = htons(nskb->len);
|
|
|
|
ip_send_check(niph);
|
|
|
|
|
|
|
|
nft_reject_br_push_etherhdr(oldskb, nskb);
|
|
|
|
|
2016-07-13 21:10:02 -06:00
|
|
|
br_forward(br_port_get_rcu(dev), nskb, false, true);
|
2014-10-25 10:40:26 -06:00
|
|
|
}
|
|
|
|
|
2016-03-26 02:32:57 -06:00
|
|
|
static void nft_reject_br_send_v4_unreach(struct net *net,
|
|
|
|
struct sk_buff *oldskb,
|
2015-02-16 10:57:53 -07:00
|
|
|
const struct net_device *dev,
|
|
|
|
int hook, u8 code)
|
2014-10-25 10:40:26 -06:00
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
struct iphdr *niph;
|
|
|
|
struct icmphdr *icmph;
|
|
|
|
unsigned int len;
|
|
|
|
void *payload;
|
|
|
|
__wsum csum;
|
2015-02-16 10:57:53 -07:00
|
|
|
u8 proto;
|
2014-10-25 10:40:26 -06:00
|
|
|
|
2017-05-18 07:44:39 -06:00
|
|
|
if (!nft_bridge_iphdr_validate(oldskb))
|
2014-10-25 10:40:26 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* IP header checks: fragment. */
|
|
|
|
if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* RFC says return as much as we can without exceeding 576 bytes. */
|
|
|
|
len = min_t(unsigned int, 536, oldskb->len);
|
|
|
|
|
|
|
|
if (!pskb_may_pull(oldskb, len))
|
|
|
|
return;
|
|
|
|
|
2015-03-09 16:04:15 -06:00
|
|
|
if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len)))
|
2015-02-16 10:57:53 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (ip_hdr(oldskb)->protocol == IPPROTO_TCP ||
|
|
|
|
ip_hdr(oldskb)->protocol == IPPROTO_UDP)
|
|
|
|
proto = ip_hdr(oldskb)->protocol;
|
|
|
|
else
|
|
|
|
proto = 0;
|
|
|
|
|
|
|
|
if (!skb_csum_unnecessary(oldskb) &&
|
|
|
|
nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto))
|
2014-10-25 10:40:26 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) +
|
|
|
|
LL_MAX_HEADER + len, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_reserve(nskb, LL_MAX_HEADER);
|
|
|
|
niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP,
|
2016-02-15 03:11:27 -07:00
|
|
|
net->ipv4.sysctl_ip_default_ttl);
|
2014-10-25 10:40:26 -06:00
|
|
|
|
|
|
|
skb_reset_transport_header(nskb);
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:19 -06:00
|
|
|
icmph = skb_put_zero(nskb, sizeof(struct icmphdr));
|
2014-10-25 10:40:26 -06:00
|
|
|
icmph->type = ICMP_DEST_UNREACH;
|
|
|
|
icmph->code = code;
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:20 -06:00
|
|
|
payload = skb_put_data(nskb, skb_network_header(oldskb), len);
|
2014-10-25 10:40:26 -06:00
|
|
|
|
|
|
|
csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0);
|
|
|
|
icmph->checksum = csum_fold(csum);
|
|
|
|
|
|
|
|
niph->tot_len = htons(nskb->len);
|
|
|
|
ip_send_check(niph);
|
|
|
|
|
|
|
|
nft_reject_br_push_etherhdr(oldskb, nskb);
|
|
|
|
|
2016-07-13 21:10:02 -06:00
|
|
|
br_forward(br_port_get_rcu(dev), nskb, false, true);
|
2014-10-25 10:40:26 -06:00
|
|
|
}
|
|
|
|
|
2016-09-09 04:42:52 -06:00
|
|
|
static int nft_bridge_ip6hdr_validate(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ipv6hdr *hdr;
|
|
|
|
u32 pkt_len;
|
|
|
|
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
hdr = ipv6_hdr(skb);
|
|
|
|
if (hdr->version != 6)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pkt_len = ntohs(hdr->payload_len);
|
|
|
|
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-10-25 10:40:26 -06:00
|
|
|
static void nft_reject_br_send_v6_tcp_reset(struct net *net,
|
2015-02-16 10:57:53 -07:00
|
|
|
struct sk_buff *oldskb,
|
|
|
|
const struct net_device *dev,
|
|
|
|
int hook)
|
2014-10-25 10:40:26 -06:00
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
const struct tcphdr *oth;
|
|
|
|
struct tcphdr _oth;
|
|
|
|
unsigned int otcplen;
|
|
|
|
struct ipv6hdr *nip6h;
|
|
|
|
|
2014-11-26 02:21:36 -07:00
|
|
|
if (!nft_bridge_ip6hdr_validate(oldskb))
|
2014-10-25 10:40:26 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook);
|
|
|
|
if (!oth)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) +
|
|
|
|
LL_MAX_HEADER, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_reserve(nskb, LL_MAX_HEADER);
|
|
|
|
nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP,
|
|
|
|
net->ipv6.devconf_all->hop_limit);
|
|
|
|
nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen);
|
|
|
|
nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
|
|
|
|
|
|
|
|
nft_reject_br_push_etherhdr(oldskb, nskb);
|
|
|
|
|
2016-07-13 21:10:02 -06:00
|
|
|
br_forward(br_port_get_rcu(dev), nskb, false, true);
|
2015-02-16 10:57:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool reject6_br_csum_ok(struct sk_buff *skb, int hook)
|
|
|
|
{
|
|
|
|
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
|
|
|
int thoff;
|
|
|
|
__be16 fo;
|
|
|
|
u8 proto = ip6h->nexthdr;
|
|
|
|
|
|
|
|
if (skb_csum_unnecessary(skb))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (ip6h->payload_len &&
|
|
|
|
pskb_trim_rcsum(skb, ntohs(ip6h->payload_len) + sizeof(*ip6h)))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo);
|
|
|
|
if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
|
2014-10-25 10:40:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nft_reject_br_send_v6_unreach(struct net *net,
|
2015-02-16 10:57:53 -07:00
|
|
|
struct sk_buff *oldskb,
|
|
|
|
const struct net_device *dev,
|
|
|
|
int hook, u8 code)
|
2014-10-25 10:40:26 -06:00
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
struct ipv6hdr *nip6h;
|
|
|
|
struct icmp6hdr *icmp6h;
|
|
|
|
unsigned int len;
|
|
|
|
void *payload;
|
|
|
|
|
2014-11-26 02:21:36 -07:00
|
|
|
if (!nft_bridge_ip6hdr_validate(oldskb))
|
2014-10-25 10:40:26 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Include "As much of invoking packet as possible without the ICMPv6
|
|
|
|
* packet exceeding the minimum IPv6 MTU" in the ICMP payload.
|
|
|
|
*/
|
|
|
|
len = min_t(unsigned int, 1220, oldskb->len);
|
|
|
|
|
|
|
|
if (!pskb_may_pull(oldskb, len))
|
|
|
|
return;
|
|
|
|
|
2015-02-16 10:57:53 -07:00
|
|
|
if (!reject6_br_csum_ok(oldskb, hook))
|
|
|
|
return;
|
|
|
|
|
2014-10-25 10:40:26 -06:00
|
|
|
nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) +
|
|
|
|
LL_MAX_HEADER + len, GFP_ATOMIC);
|
|
|
|
if (!nskb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_reserve(nskb, LL_MAX_HEADER);
|
|
|
|
nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6,
|
|
|
|
net->ipv6.devconf_all->hop_limit);
|
|
|
|
|
|
|
|
skb_reset_transport_header(nskb);
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:19 -06:00
|
|
|
icmp6h = skb_put_zero(nskb, sizeof(struct icmp6hdr));
|
2014-10-25 10:40:26 -06:00
|
|
|
icmp6h->icmp6_type = ICMPV6_DEST_UNREACH;
|
|
|
|
icmp6h->icmp6_code = code;
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 06:29:20 -06:00
|
|
|
payload = skb_put_data(nskb, skb_network_header(oldskb), len);
|
2014-10-25 10:40:26 -06:00
|
|
|
nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
|
|
|
|
|
|
|
|
icmp6h->icmp6_cksum =
|
|
|
|
csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr,
|
|
|
|
nskb->len - sizeof(struct ipv6hdr),
|
|
|
|
IPPROTO_ICMPV6,
|
|
|
|
csum_partial(icmp6h,
|
|
|
|
nskb->len - sizeof(struct ipv6hdr),
|
|
|
|
0));
|
|
|
|
|
|
|
|
nft_reject_br_push_etherhdr(oldskb, nskb);
|
|
|
|
|
2016-07-13 21:10:02 -06:00
|
|
|
br_forward(br_port_get_rcu(dev), nskb, false, true);
|
2014-10-25 10:40:26 -06:00
|
|
|
}
|
2014-06-27 05:36:11 -06:00
|
|
|
|
|
|
|
static void nft_reject_bridge_eval(const struct nft_expr *expr,
|
2015-04-10 19:27:31 -06:00
|
|
|
struct nft_regs *regs,
|
|
|
|
const struct nft_pktinfo *pkt)
|
2014-06-27 05:36:11 -06:00
|
|
|
{
|
2014-09-26 06:35:14 -06:00
|
|
|
struct nft_reject *priv = nft_expr_priv(expr);
|
2014-10-25 10:40:26 -06:00
|
|
|
const unsigned char *dest = eth_hdr(pkt->skb)->h_dest;
|
|
|
|
|
|
|
|
if (is_broadcast_ether_addr(dest) ||
|
|
|
|
is_multicast_ether_addr(dest))
|
|
|
|
goto out;
|
2014-09-26 06:35:14 -06:00
|
|
|
|
2014-06-27 05:36:11 -06:00
|
|
|
switch (eth_hdr(pkt->skb)->h_proto) {
|
|
|
|
case htons(ETH_P_IP):
|
2014-09-26 06:35:14 -06:00
|
|
|
switch (priv->type) {
|
|
|
|
case NFT_REJECT_ICMP_UNREACH:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt),
|
2014-10-25 10:40:26 -06:00
|
|
|
priv->icmp_code);
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
case NFT_REJECT_TCP_RST:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v4_tcp_reset(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt));
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
case NFT_REJECT_ICMPX_UNREACH:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt),
|
2014-10-25 10:40:26 -06:00
|
|
|
nft_reject_icmp_code(priv->icmp_code));
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2014-06-27 05:36:11 -06:00
|
|
|
case htons(ETH_P_IPV6):
|
2014-09-26 06:35:14 -06:00
|
|
|
switch (priv->type) {
|
|
|
|
case NFT_REJECT_ICMP_UNREACH:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt),
|
2014-10-25 10:40:26 -06:00
|
|
|
priv->icmp_code);
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
case NFT_REJECT_TCP_RST:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v6_tcp_reset(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt));
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
case NFT_REJECT_ICMPX_UNREACH:
|
2016-11-03 03:56:26 -06:00
|
|
|
nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb,
|
|
|
|
nft_in(pkt),
|
|
|
|
nft_hook(pkt),
|
2014-10-25 10:40:26 -06:00
|
|
|
nft_reject_icmpv6_code(priv->icmp_code));
|
2014-09-26 06:35:14 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2014-06-27 05:36:11 -06:00
|
|
|
default:
|
|
|
|
/* No explicit way to reject this protocol, drop it. */
|
|
|
|
break;
|
|
|
|
}
|
2014-10-25 10:40:26 -06:00
|
|
|
out:
|
2015-04-10 19:27:31 -06:00
|
|
|
regs->verdict.code = NF_DROP;
|
2014-09-26 06:35:14 -06:00
|
|
|
}
|
|
|
|
|
2015-01-14 07:33:57 -07:00
|
|
|
static int nft_reject_bridge_validate(const struct nft_ctx *ctx,
|
|
|
|
const struct nft_expr *expr,
|
|
|
|
const struct nft_data **data)
|
2014-10-27 07:08:17 -06:00
|
|
|
{
|
2015-01-14 07:33:57 -07:00
|
|
|
return nft_chain_validate_hooks(ctx->chain, (1 << NF_BR_PRE_ROUTING) |
|
|
|
|
(1 << NF_BR_LOCAL_IN));
|
2014-10-27 07:08:17 -06:00
|
|
|
}
|
|
|
|
|
2014-09-26 06:35:14 -06:00
|
|
|
static int nft_reject_bridge_init(const struct nft_ctx *ctx,
|
|
|
|
const struct nft_expr *expr,
|
|
|
|
const struct nlattr * const tb[])
|
|
|
|
{
|
|
|
|
struct nft_reject *priv = nft_expr_priv(expr);
|
netfilter: nf_tables: validate the expr explicitly after init successfully
When we want to validate the expr's dependency or hooks, we must do two
things to accomplish it. First, write a X_validate callback function
and point ->validate to it. Second, call X_validate in init routine.
This is very common, such as fib, nat, reject expr and so on ...
It is a little ugly, since we will call X_validate in the expr's init
routine, it's better to do it in nf_tables_newexpr. So we can avoid to
do this again and again. After doing this, the second step listed above
is not useful anymore, remove them now.
Patch was tested by nftables/tests/py/nft-test.py and
nftables/tests/shell/run-tests.sh.
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
2017-03-05 06:02:23 -07:00
|
|
|
int icmp_code;
|
2014-09-26 06:35:14 -06:00
|
|
|
|
|
|
|
if (tb[NFTA_REJECT_TYPE] == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
|
|
|
|
switch (priv->type) {
|
|
|
|
case NFT_REJECT_ICMP_UNREACH:
|
|
|
|
case NFT_REJECT_ICMPX_UNREACH:
|
|
|
|
if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
|
|
|
|
if (priv->type == NFT_REJECT_ICMPX_UNREACH &&
|
|
|
|
icmp_code > NFT_REJECT_ICMPX_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
priv->icmp_code = icmp_code;
|
|
|
|
break;
|
|
|
|
case NFT_REJECT_TCP_RST:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nft_reject_bridge_dump(struct sk_buff *skb,
|
|
|
|
const struct nft_expr *expr)
|
|
|
|
{
|
|
|
|
const struct nft_reject *priv = nft_expr_priv(expr);
|
|
|
|
|
|
|
|
if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
switch (priv->type) {
|
|
|
|
case NFT_REJECT_ICMP_UNREACH:
|
|
|
|
case NFT_REJECT_ICMPX_UNREACH:
|
|
|
|
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
|
|
|
|
goto nla_put_failure;
|
|
|
|
break;
|
2015-04-07 21:05:42 -06:00
|
|
|
default:
|
|
|
|
break;
|
2014-09-26 06:35:14 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
return -1;
|
2014-06-27 05:36:11 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct nft_expr_type nft_reject_bridge_type;
|
|
|
|
static const struct nft_expr_ops nft_reject_bridge_ops = {
|
|
|
|
.type = &nft_reject_bridge_type,
|
|
|
|
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
|
|
|
|
.eval = nft_reject_bridge_eval,
|
2014-09-26 06:35:14 -06:00
|
|
|
.init = nft_reject_bridge_init,
|
|
|
|
.dump = nft_reject_bridge_dump,
|
2014-10-27 07:08:17 -06:00
|
|
|
.validate = nft_reject_bridge_validate,
|
2014-06-27 05:36:11 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
|
|
|
|
.family = NFPROTO_BRIDGE,
|
|
|
|
.name = "reject",
|
|
|
|
.ops = &nft_reject_bridge_ops,
|
|
|
|
.policy = nft_reject_policy,
|
|
|
|
.maxattr = NFTA_REJECT_MAX,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init nft_reject_bridge_module_init(void)
|
|
|
|
{
|
|
|
|
return nft_register_expr(&nft_reject_bridge_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit nft_reject_bridge_module_exit(void)
|
|
|
|
{
|
|
|
|
nft_unregister_expr(&nft_reject_bridge_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(nft_reject_bridge_module_init);
|
|
|
|
module_exit(nft_reject_bridge_module_exit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
|
|
|
|
MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject");
|