* 802.1q + device removal causing hang
@ 2002-09-11 22:32 Simon Kirby
2002-09-11 22:31 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Simon Kirby @ 2002-09-11 22:32 UTC (permalink / raw)
To: Ben Greear, David S. Miller, linux-kernel
Hi,
In 2.4.20-pre6, 2.4.20-pre4, 2.4.19, and 2.4.18 at least, I have been
having problems with vlan shutdown when removing tg3.o. rmmod hangs on
rtnl_sem, call trace:
rtnetlink_event
unregister_netdev
vlan_device_event
notifier_call_chain
unregister_netdevice
unregister_netdev
[tg3 module]
[tg3 module]
pci_unregister_driver
...
The hang is happening at rmmod time. All other operation appears to be
fine. If I manually make sure all vlans are removed first, everything
works, so it's probably something to do with the NETDEV_UNREGISTER case
in vlan_device_event() in vlan.c.
Steps to reproduce:
modprobe tg3
ifconfig eth0 up
vconfig add eth0 10
ifconfig eth0.10 1.2.3.4
ifconfig eth0.10 down
ifconfig eth0 down
rmmod tg3
Note that the IP address stays assigned even with eth0.10 set down.
Everything works if the IP assignment is not made. Tested on
2.4.20-pre6.
Does anybody see what would be causing this (perhaps unregister_netdev
reentering)?
(Also, why is the "ifconfig eth0 up" necessary for vconfig? It allows
"ifconfig eth0 down" _after_ the vlan is created, and vlans can still be
removed (but not created) at that point.)
Simon-
[ Stormix Technologies Inc. ][ NetNation Communications Inc. ]
[ sim@stormix.com ][ sim@netnation.com ]
[ Opinions expressed are not necessarily those of my employers. ]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: 802.1q + device removal causing hang
2002-09-11 22:32 802.1q + device removal causing hang Simon Kirby
@ 2002-09-11 22:31 ` David S. Miller
2002-09-12 6:36 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
2002-09-12 23:49 ` 802.1q + device removal causing hang Simon Kirby
0 siblings, 2 replies; 39+ messages in thread
From: David S. Miller @ 2002-09-11 22:31 UTC (permalink / raw)
To: sim; +Cc: greearb, linux-kernel
Try this:
--- net/8021q/vlan.c.~1~ Wed Sep 11 15:34:49 2002
+++ net/8021q/vlan.c Wed Sep 11 15:34:59 2002
@@ -626,7 +626,7 @@
ret = unregister_vlan_dev(dev,
VLAN_DEV_INFO(vlandev)->vlan_id);
- unregister_netdev(vlandev);
+ unregister_netdevice(vlandev);
/* Group was destroyed? */
if (ret == 1)
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-11 22:31 ` David S. Miller
@ 2002-09-12 6:36 ` Bart De Schuymer
2002-09-12 23:04 ` David S. Miller
` (2 more replies)
2002-09-12 23:49 ` 802.1q + device removal causing hang Simon Kirby
1 sibling, 3 replies; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-12 6:36 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel
Hello David, list,
Ebtables is a project similar to iptables, but working on the bridge netfilter
hooks. It allows for a basic transparent firewall, making a brouter and doing
MAC source address and destination address manipulation. The firewall part
has currently modules for basic IP filtering, 802.1q filtering, ARP
filtering, logging and a mark match/target.
Ebtables has been under development for over 1.5 year and has more than 100
users, I think.
The patch is 3662 lines long, so I won't list it in this mail. It is available
at:
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.34.diff
or, gzipped:
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.34.diff.gz
It is vs 2.5.34, I can make a patch vs 2.4.x when the time is right.
Comments/questions are appreciated.
For more information, see
http://users.pandora.be/bart.de.schuymer/ebtables/
There is an ebtables hacking howto, some basic examples and some real life
examples from users. And ofcourse the userspace program.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-12 6:36 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
@ 2002-09-12 23:04 ` David S. Miller
2002-09-13 3:20 ` Bart De Schuymer
2002-10-14 18:05 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42 Bart De Schuymer
[not found] ` <200210141953.38933.bart.de.schuymer@pandora.be>
2 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-09-12 23:04 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Thu, 12 Sep 2002 08:36:52 +0200
ARP filtering
People should use ARP tables for arp filtering, that is why I wrote
it. ARP filtering should not need to be bridge specific.
Next, has Lennert Buytenhek, the bridging maintainer, approved of your
changes to the bridging layer APIs?
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-12 23:04 ` David S. Miller
@ 2002-09-13 3:20 ` Bart De Schuymer
2002-09-13 4:29 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-13 3:20 UTC (permalink / raw)
To: David S. Miller, Lennert Buytenhek; +Cc: linux-kernel
Hello David, Lennert, list,
> ARP filtering
>
> People should use ARP tables for arp filtering, that is why I wrote
> it. ARP filtering should not need to be bridge specific.
Well, a bridge can also just _bridge_ ARP packets between two sides of the
bridge. The ARP module can filter out those packets. These packets will not
pass through the ARP code of the Linux kernel. Ofcourse, the ebtables ARP
module can be easily adjusted for arptables, I will do this later if nobody
beats me to it... For the same reason, basic ebtables IP filtering is not
redundant.
> Next, has Lennert Buytenhek, the bridging maintainer, approved of your
> changes to the bridging layer APIs?
OK. This is to Lennert:
Could you please have a look at the ebtables patch located at
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.34.diff
and approve the changes made to the bridging layer API? They are necessary to
make a brouter and to deal with bogus NETFILTER_DEBUG warnings if the option
is compiled in the kernel. Any questions will be gladly answered... Note that
the brouting facility has been working for atleast three months and it has
already been used in real-life situations, there's an example usage on the
ebtables homepage. Dealing with NETFILTER_DEBUG warnings consists of setting
nf_debug to zero when the netfilter hooks change from bridge hooks to some
other stack's hooks and vice versa. See the patch.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-13 3:20 ` Bart De Schuymer
@ 2002-09-13 4:29 ` David S. Miller
2002-09-13 6:12 ` Bart De Schuymer
0 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-09-13 4:29 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: buytenh, linux-kernel
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Fri, 13 Sep 2002 05:20:41 +0200
Well, a bridge can also just _bridge_ ARP packets between two sides of the
bridge. The ARP module can filter out those packets. These packets will not
pass through the ARP code of the Linux kernel. Ofcourse, the ebtables ARP
module can be easily adjusted for arptables
No, I think I understand the difference and why you're problem
space does not intersect what arptables handles.
It may not be nice that we can't immediately just reuse ipv4/netfilter
handlers for bridging, but I'm not going to require that you make that
work before I'll accept your patch.
Once you work things out with Lennert and he approves the changes,
I'll apply your patch.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-13 4:29 ` David S. Miller
@ 2002-09-13 6:12 ` Bart De Schuymer
2002-09-13 6:09 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-13 6:12 UTC (permalink / raw)
To: David S. Miller; +Cc: buytenh, linux-kernel
> It may not be nice that we can't immediately just reuse ipv4/netfilter
> handlers for bridging, but I'm not going to require that you make that
> work before I'll accept your patch.
If you mean using iptables on bridged packets, this is possible with the br-nf
patch. It is not trivial however, 2 new fields to the sk_buff need to be
added, a small change in the IP fragment code and a small change in
ip_tables.c, a change to netfilter.h and netfilter.c. The br-nf patch has
been under development for over 1.5 years, most work done by Lennert with me
helping now and then...
So, if you would accept br-nf, that would be great. I think Lennert and me
would need some time to agree on a few things before submitting anything,
however...
> Once you work things out with Lennert and he approves the changes,
> I'll apply your patch.
Cool, looking forward to his comments.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
2002-09-13 6:12 ` Bart De Schuymer
@ 2002-09-13 6:09 ` David S. Miller
2002-09-13 12:45 ` bridge-netfilter patch (was: Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34) Lennert Buytenhek
2002-09-16 6:50 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.35 Bart De Schuymer
0 siblings, 2 replies; 39+ messages in thread
From: David S. Miller @ 2002-09-13 6:09 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: buytenh, linux-kernel
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Fri, 13 Sep 2002 08:12:27 +0200
It is not trivial however, 2 new fields to the sk_buff need to be
added, a small change in the IP fragment code and a small change in
ip_tables.c, a change to netfilter.h and netfilter.c.
I've seen these changes, they are very buggy. The IPv4 copies added
are just ugly and are buggy too, they potentially copy past the end
of the packet buffer.
So, if you would accept br-nf, that would be great.
You need to remove the IPv4 bits, that copy of the MAC has to happen
at a different layer, it does not belong in IPv4. At best, everyone
shouldn't eat that header copy.
Franks a lot,
David S. Miller
davem@redhat.com
^ permalink raw reply [flat|nested] 39+ messages in thread
* bridge-netfilter patch (was: Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34)
2002-09-13 6:09 ` David S. Miller
@ 2002-09-13 12:45 ` Lennert Buytenhek
2002-09-13 18:22 ` bridge-netfilter patch David S. Miller
2002-09-16 6:50 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.35 Bart De Schuymer
1 sibling, 1 reply; 39+ messages in thread
From: Lennert Buytenhek @ 2002-09-13 12:45 UTC (permalink / raw)
To: David S. Miller; +Cc: bart.de.schuymer, linux-kernel
On Thu, Sep 12, 2002 at 11:09:16PM -0700, David S. Miller wrote:
> From: Bart De Schuymer <bart.de.schuymer@pandora.be>
> Date: Fri, 13 Sep 2002 08:12:27 +0200
>
> It is not trivial however, 2 new fields to the sk_buff need to be
> added, a small change in the IP fragment code and a small change in
> ip_tables.c, a change to netfilter.h and netfilter.c.
>
> I've seen these changes, they are very buggy. The IPv4 copies added
> are just ugly and are buggy too, they potentially copy past the end
> of the packet buffer.
You mean this part? This is the only copy added to generic code
that I can find.
--- linux-2.4.19/net/ipv4/ip_output.c 2002-08-03 02:39:46.000000000 +0200
+++ linux-2.4.19-brnf0.0.7/net/ipv4/ip_output.c 2002-09-11 17:40:25.000000000 +0200
@@ -883,6 +885,7 @@
iph->tot_len = htons(len + hlen);
ip_send_check(iph);
+ memcpy(skb2->data - 16, skb->data - 16, 16);
err = output(skb2);
if (err)
If this code is buggy, isn't the following bit from ip_output.c buggy
too? (around line 170)
if (hh) {
read_lock_bh(&hh->hh_lock);
>>> memcpy(skb->data - 16, hh->hh_data, 16);
read_unlock_bh(&hh->hh_lock);
skb_push(skb, hh->hh_len);
return hh->hh_output(skb);
} else if (dst->neighbour)
> So, if you would accept br-nf, that would be great.
>
> You need to remove the IPv4 bits, that copy of the MAC has to happen
> at a different layer, it does not belong in IPv4. At best, everyone
> shouldn't eat that header copy.
What if I make the memcpy conditional on "if (skb->physindev != NULL)"?
cheers,
Lennert
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: bridge-netfilter patch
2002-09-13 12:45 ` bridge-netfilter patch (was: Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34) Lennert Buytenhek
@ 2002-09-13 18:22 ` David S. Miller
2002-09-14 7:05 ` Bart De Schuymer
2002-09-15 21:27 ` Lennert Buytenhek
0 siblings, 2 replies; 39+ messages in thread
From: David S. Miller @ 2002-09-13 18:22 UTC (permalink / raw)
To: buytenh; +Cc: bart.de.schuymer, linux-kernel
From: Lennert Buytenhek <buytenh@math.leidenuniv.nl>
Date: Fri, 13 Sep 2002 14:45:18 +0200
> You need to remove the IPv4 bits, that copy of the MAC has to happen
> at a different layer, it does not belong in IPv4. At best, everyone
> shouldn't eat that header copy.
What if I make the memcpy conditional on "if (skb->physindev != NULL)"?
First explain to me why the copy is needed for.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-13 18:22 ` bridge-netfilter patch David S. Miller
@ 2002-09-14 7:05 ` Bart De Schuymer
2002-09-16 3:35 ` David S. Miller
2002-09-15 21:27 ` Lennert Buytenhek
1 sibling, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-14 7:05 UTC (permalink / raw)
To: David S. Miller, buytenh; +Cc: linux-kernel
On Friday 13 September 2002 20:22, David S. Miller wrote:
> From: Lennert Buytenhek <buytenh@math.leidenuniv.nl>
> Date: Fri, 13 Sep 2002 14:45:18 +0200
>
> > You need to remove the IPv4 bits, that copy of the MAC has to happen
> > at a different layer, it does not belong in IPv4. At best, everyone
> > shouldn't eat that header copy.
>
> What if I make the memcpy conditional on "if (skb->physindev != NULL)"?
>
> First explain to me why the copy is needed for.
memcpy(skb2->data - 16, skb->data - 16, 16);
This is for purely bridged packets.
IP connection tracking first gathers all fragments, before the bridged IP
packets are sent out the packet is fragmented. However, since this
fragmenting actually happens while the IP packet "is in the bridge code", no
existing code makes sure the fragments' Ethernet headers are correctly
filled. Now, AFAIK an Ethernet header has length 14 bytes, so don't ask me
why the magic number 16 is used (Lennert got IP fragmenting fixed).
A nice comment in front of that copy is certainly needed...
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-14 7:05 ` Bart De Schuymer
@ 2002-09-16 3:35 ` David S. Miller
2002-09-16 21:41 ` Bart De Schuymer
0 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-09-16 3:35 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: buytenh, linux-kernel
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Sat, 14 Sep 2002 09:05:40 +0200
On Friday 13 September 2002 20:22, David S. Miller wrote:
> First explain to me why the copy is needed for.
memcpy(skb2->data - 16, skb->data - 16, 16);
This is for purely bridged packets.
Why is it being added, therefore, to ip_queue_xmit() which is only
ever invoked by TCP output processing?
If the patch adds the call somewhere else, please correct me, but
I specifically remember it being added to ip_queue_xmit() which is
why I barfed when seeing it :-)
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-16 3:35 ` David S. Miller
@ 2002-09-16 21:41 ` Bart De Schuymer
2002-09-16 23:21 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-16 21:41 UTC (permalink / raw)
To: David S. Miller; +Cc: buytenh, linux-kernel
> This is for purely bridged packets.
>
> Why is it being added, therefore, to ip_queue_xmit() which is only
> ever invoked by TCP output processing?
>
> If the patch adds the call somewhere else, please correct me, but
> I specifically remember it being added to ip_queue_xmit() which is
> why I barfed when seeing it :-)
I've never seen this in the patch. It sure isn't in it now.
To be more precise:
net/ipv4/netfilter/ip_conntrack_standalone.c:ip_refrag() is (or can be)
attached to the NF_IP_POST_ROUTING hook. This function calls:
net/ipv4/ip_output.c:ip_fragment()
In this function the copy of the Ethernet frame is added for each fragment (by
the br-nf patch).
The bridge-netfilter patch lets IP packets/frames passing the
NF_BR_POST_ROUTING hook go through the NF_IP_POST_ROUTING hook, so the
ip_fragment() code is executed while the IP packet/frame is really in the
bridge code. After this, the fragments get queued:
net/bridge/br_forward.c:br_dev_queue_push_xmit() calls dev_queue_xmit()
Lennert's previous mail says in which cases and why this header copy has to be
explicitly done.
The following document might be useful to know what we are doing:
http://users.pandora.be/bart.de.schuymer/ebtables/br_fw_ia/br_fw_ia.html
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-16 21:41 ` Bart De Schuymer
@ 2002-09-16 23:21 ` David S. Miller
2002-09-17 19:10 ` Bart De Schuymer
0 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-09-16 23:21 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: buytenh, linux-kernel
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Mon, 16 Sep 2002 23:41:17 +0200
net/ipv4/ip_output.c:ip_fragment()
In this function the copy of the Ethernet frame is added for each fragment (by
the br-nf patch).
'output' callback arg to ip_fragment() must generate correct hardware
headers when necessary. This hack usage of it via netfilter, in this
weird bridging case, is violating this requirement.
Normally ip_finish_output2() is going to make this.
If it can't do the job properly, pass instead a routine that can do
what netfilter needs.
Lennert says:
So if we postpone FORWARD and POST_ROUTING until after br_dev_xmit,
we effectively reverse refragmentation and neighbor resolution.
But refragmentation messes up the hardware header.
The 16byte hardware header copy fixes this by copying to each
fragment the hardware header that was tacked onto or was already
present on the bigger packet. It's ugly, I admit. There's
currently no better way though.
I don't understand why you can't add on the hardware header some other
way.
If ip_finish_output doesn't put the right hardware header on there,
you have to use as 'okfn' (what netfilter sends down as 'output' to
ip_fragment) some routine which will do it correctly.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-16 23:21 ` David S. Miller
@ 2002-09-17 19:10 ` Bart De Schuymer
2002-09-17 19:35 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-17 19:10 UTC (permalink / raw)
To: David S. Miller; +Cc: buytenh, linux-kernel
> net/ipv4/ip_output.c:ip_fragment()
> In this function the copy of the Ethernet frame is added for each
> fragment (by the br-nf patch).
>
> 'output' callback arg to ip_fragment() must generate correct hardware
> headers when necessary. This hack usage of it via netfilter, in this
> weird bridging case, is violating this requirement.
>
> Normally ip_finish_output2() is going to make this.
>
> If it can't do the job properly, pass instead a routine that can do
> what netfilter needs.
Aha. In our case, the output function is
net/bridge/br_forward.c:__dev_queue_push_xmit(). This is because
__br_forward_finish() (same file) uses this as okfn. Remember the IP hooks
are "faked" on the bridge hooks, so functions attached to NF_IP_POST_ROUTING
are called when the IP packet/frame passes the NF_BR_POST_ROUTING hook. They
are not called earlier. All of this assuming that the destination device
according to the routing table is a (logical) bridge device. If not, the
packets go through the IP code and netfilter hooks normally.
So, what if we were to add the following code to the start of
__dev_queue_push_xmit():
if (skb->protocol == __constant_htons(ETH_P_IP)) {
struct dst_entry *dst = skb->dst;
if (hh) {
read_lock_bh(&hh->hh_lock);
memcpy(skb->data - 16, hh->hh_data, 16);
read_unlock_bh(&hh->hh_lock);
}
}
hh being NULL for an unfragmented IP packet and else non-NULL? Do realize that
I (I can't speak for Lennert ofcourse) am not very familiar to the workings
of the IP code.
Then we can remove the memcpy from ip_fragment(). Does that make sense?
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: bridge-netfilter patch
2002-09-13 18:22 ` bridge-netfilter patch David S. Miller
2002-09-14 7:05 ` Bart De Schuymer
@ 2002-09-15 21:27 ` Lennert Buytenhek
1 sibling, 0 replies; 39+ messages in thread
From: Lennert Buytenhek @ 2002-09-15 21:27 UTC (permalink / raw)
To: David S. Miller; +Cc: bart.de.schuymer, linux-kernel
On Fri, Sep 13, 2002 at 11:22:35AM -0700, David S. Miller wrote:
> > You need to remove the IPv4 bits, that copy of the MAC has to happen
> > at a different layer, it does not belong in IPv4. At best, everyone
> > shouldn't eat that header copy.
>
> What if I make the memcpy conditional on "if (skb->physindev != NULL)"?
>
> First explain to me why the copy is needed for.
This is just to elaborate upon what Bart said earlier.
In the "L2 switched frame" case, we have a bit of a nasty problem
with IP fragmentation. And in the "L3 'switched' frame" case
(brouted frame), we have an ordering problem with IP fragmentation
and neighbor resolution.
This is what the call stack looks like when we have a purely
bridged frame (that needs to be netfiltered):
net_rx_action
-> br_handle_frame
-> PF_BRIDGE/PRE_ROUTING
-> br_handle_frame_finish
-> br_forward
-> PF_BRIDGE/FORWARD
-> __br_forward_finish
-> PF_BRIDGE/POST_ROUTING
-> dev_queue_xmit
This case is easy to see. With ip_conntrack enabled, packets
are reassembled in PRE_ROUTING and refragmented in POST_ROUTING.
This refragmenting messes up the hardware header, so the fragments
will leave the box with incorrect HW headers.
The broute case is a bit harder to see. If L3 (routed) packets
are destined for a bridge device, we don't know what subdevice
(slave port) they will go to until the bridge layer's br_dev_xmit
has its way. However, we would like to be able to use the real
outgoing interface (physoutdev) in FORWARD and POST_ROUTING.
To be able to do this, we postpone calling IPv4/FORWARD and
IPv4/POST_ROUTING until after PF_BRIDGE/POST_ROUTING has happened,
because at that point we know physoutdev so we can feed it to
said IPv4 hooks.
But. Packet refragmentation normally happens in IPv4/POST_ROUTING.
We don't want to do it there though, because that would cause the
eventual call to IPv4/FORWARD and IPv4/POST_ROUTING to see all
fragments instead of one packet (which goes against the idea of
conntrack).
So if we postpone FORWARD and POST_ROUTING until after br_dev_xmit,
we effectively reverse refragmentation and neighbor resolution.
But refragmentation messes up the hardware header.
The 16byte hardware header copy fixes this by copying to each
fragment the hardware header that was tacked onto or was already
present on the bigger packet. It's ugly, I admit. There's
currently no better way though.
(And Bart, I chose 16 because 16-byte aligned 16-byte copies
should be cheaper than 2-byte aligned 14-byte copies, and there
should be at least 16 bytes before skb->data at this point
anyway. That is, if I understood the code correctly.)
cheers,
Lennert
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH] ebtables - Ethernet bridge tables, for 2.5.35
2002-09-13 6:09 ` David S. Miller
2002-09-13 12:45 ` bridge-netfilter patch (was: Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34) Lennert Buytenhek
@ 2002-09-16 6:50 ` Bart De Schuymer
2002-09-16 23:01 ` David S. Miller
1 sibling, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-16 6:50 UTC (permalink / raw)
To: David S. Miller; +Cc: buytenh, linux-kernel, ebtables-user, bridge
Hello David,
The following link points to the ebtables patch approved by Lennert:
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.35-try3.diff
Changes:
cleanup brouter interface in the bridge code + brouter bugfix.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.35
2002-09-16 6:50 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.35 Bart De Schuymer
@ 2002-09-16 23:01 ` David S. Miller
0 siblings, 0 replies; 39+ messages in thread
From: David S. Miller @ 2002-09-16 23:01 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: buytenh, linux-kernel, ebtables-user, bridge
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Mon, 16 Sep 2002 08:50:16 +0200
The following link points to the ebtables patch approved by Lennert:
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.35-try3.diff
I'm applying this, but please in the future do not put these
"*** deal with blah blah" seperators in one big patch file.
Give me one bug patch I can just do a clean "patch -p1 <your_patch"
with, thanks.
Sure it's just noise and patch perhaps knows how to skip over it, but
lots of other patch reading tools might not be able to. (for example
diffstat gives a lot of different paths as the patch base directory
because you've done the patch this way).
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-09-12 6:36 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
2002-09-12 23:04 ` David S. Miller
@ 2002-10-14 18:05 ` Bart De Schuymer
2002-10-14 18:01 ` David S. Miller
2002-10-20 22:20 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44 Bart De Schuymer
[not found] ` <200210141953.38933.bart.de.schuymer@pandora.be>
2 siblings, 2 replies; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-14 18:05 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, Lennert Buytenhek
Hello David, list,
David, you asked for a patch for mapping the IPv4 hooks onto the bridge hooks,
this is the patch.
Comments and suggestions for better solutions are welcome.
I don't know how to (easily) move the copy of the Ethernet header out of
ip_output.c::ip_fragment.c. The patch adds 3 members to the skbuff...
There is a little text file explaining the source code more in-depth here:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre1-against-2.5.39-comments.txt
A high-level explanation of what we're doing is here:
http://users.pandora.be/bart.de.schuymer/ebtables/br_fw_ia/br_fw_ia.html
The patch is also available at:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff
Note that Lennert has not yet responded to my private RFC to him, but
Halloween is coming, so we should at least try to get this into 2.5...
cheers,
Bart
Here's the patch:
--- linux-2.5.42/include/linux/netfilter.h Sat Oct 12 06:22:08 2002
+++ linux-2.5.42-brnf/include/linux/netfilter.h Sun Oct 13 11:45:19 2002
@@ -117,17 +117,23 @@
/* This is gross, but inline doesn't cut it for avoiding the function
call in fast path: gcc doesn't inline (needs value tracking?). --RR */
#ifdef CONFIG_NETFILTER_DEBUG
-#define NF_HOOK nf_hook_slow
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
+ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_THRESH nf_hook_slow
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
(list_empty(&nf_hooks[(pf)][(hook)]) \
? (okfn)(skb) \
- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
+#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \
+(list_empty(&nf_hooks[(pf)][(hook)]) \
+ ? (okfn)(skb) \
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
#endif
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *));
+ int (*okfn)(struct sk_buff *), int thresh);
/* Call setsockopt() */
int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt,
--- linux-2.5.42/include/linux/netfilter_ipv4.h Sat Oct 12 06:22:18 2002
+++ linux-2.5.42-brnf/include/linux/netfilter_ipv4.h Sun Oct 13 11:45:19 2002
@@ -52,8 +52,10 @@
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
--- linux-2.5.42/include/linux/netfilter_bridge.h Sat Oct 12 06:22:09 2002
+++ linux-2.5.42-brnf/include/linux/netfilter_bridge.h Sun Oct 13 11:45:19
2002
@@ -22,14 +22,27 @@
#define NF_BR_BROUTING 5
#define NF_BR_NUMHOOKS 6
+/* Masks for skb->brnfmask */
+#define BRNF_PKT_TYPE 0x01
+#define BRNF_BRIDGED_DNAT 0x02
+#define BRNF_COPY_HEADER 0x04
+#define BRNF_DONT_TAKE_PARENT 0x08
+
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
- NF_BR_PRI_FILTER_BRIDGED = -200,
- NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_FILTER_BRIDGED = -200,
+ NF_BR_PRI_BRNF = 0,
NF_BR_PRI_NAT_DST_OTHER = 100,
+ NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
};
+/* Used in br_netfilter.c */
+struct bridge_skb_cb {
+ union {
+ __u32 ipv4;
+ } daddr;
+};
#endif
--- linux-2.5.42/include/linux/skbuff.h Sat Oct 12 06:22:09 2002
+++ linux-2.5.42-brnf/include/linux/skbuff.h Sun Oct 13 11:45:19 2002
@@ -140,6 +140,8 @@
* @sk: Socket we are owned by
* @stamp: Time we arrived
* @dev: Device we arrived on/are leaving by
+ * @physindev: Physical device we arrived on - see br_netfilter.c
+ * @physoutdev: Phsical device we will leave by - see br_netfilter.c
* @h: Transport layer header
* @nh: Network layer header
* @mac: Link layer header
@@ -166,6 +168,7 @@
* @nfcache: Cache info
* @nfct: Associated connection, if any
* @nf_debug: Netfilter debugging
+ * @brnfmask: Info about a bridged frame - see br_netfilter.c
* @tc_index: Traffic control index
*/
@@ -178,6 +181,8 @@
struct sock *sk;
struct timeval stamp;
struct net_device *dev;
+ struct net_device *physindev;
+ struct net_device *physoutdev;
union {
struct tcphdr *th;
@@ -236,6 +241,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
+ unsigned int brnfmask;
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
--- linux-2.5.42/net/bridge/br.c Sat Oct 12 06:21:34 2002
+++ linux-2.5.42-brnf/net/bridge/br.c Sun Oct 13 11:45:19 2002
@@ -45,6 +45,8 @@
{
printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+ if (br_netfilter_init())
+ return 1;
br_handle_frame_hook = br_handle_frame;
br_ioctl_hook = br_ioctl_deviceless_stub;
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
@@ -63,6 +65,7 @@
static void __exit br_deinit(void)
{
+ br_netfilter_fini();
unregister_netdevice_notifier(&br_device_notifier);
br_call_ioctl_atomic(__br_clear_ioctl_hook);
--- linux-2.5.42/net/bridge/br_forward.c Sat Oct 12 06:21:37 2002
+++ linux-2.5.42-brnf/net/bridge/br_forward.c Sun Oct 13 11:45:19 2002
@@ -30,7 +30,7 @@
return 1;
}
-static int __dev_queue_push_xmit(struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct sk_buff *skb)
{
skb_push(skb, ETH_HLEN);
dev_queue_xmit(skb);
@@ -38,10 +38,10 @@
return 0;
}
-static int __br_forward_finish(struct sk_buff *skb)
+int br_forward_finish(struct sk_buff *skb)
{
NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
- __dev_queue_push_xmit);
+ br_dev_queue_push_xmit);
return 0;
}
@@ -53,7 +53,7 @@
skb->nf_debug = 0;
#endif
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
@@ -64,7 +64,7 @@
skb->dev = to->dev;
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
/* called under bridge lock */
--- linux-2.5.42/net/bridge/br_input.c Sat Oct 12 06:21:35 2002
+++ linux-2.5.42-brnf/net/bridge/br_input.c Sun Oct 13 11:45:19 2002
@@ -49,7 +49,7 @@
br_pass_frame_up_finish);
}
-static int br_handle_frame_finish(struct sk_buff *skb)
+int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
--- linux-2.5.42/net/bridge/br_private.h Sat Oct 12 06:21:35 2002
+++ linux-2.5.42-brnf/net/bridge/br_private.h Sun Oct 13 11:45:19 2002
@@ -144,8 +144,10 @@
/* br_forward.c */
extern void br_deliver(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br,
struct sk_buff *skb,
int clone);
@@ -166,6 +168,7 @@
int *ifindices);
/* br_input.c */
+extern int br_handle_frame_finish(struct sk_buff *skb);
extern int br_handle_frame(struct sk_buff *skb);
/* br_ioctl.c */
@@ -176,6 +179,10 @@
unsigned long arg1,
unsigned long arg2);
extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_netfilter.c */
+extern int br_netfilter_init(void);
+extern void br_netfilter_fini(void);
/* br_stp.c */
extern int br_is_root_bridge(struct net_bridge *br);
--- linux-2.5.42/net/bridge/Makefile Sat Oct 12 06:22:45 2002
+++ linux-2.5.42-brnf/net/bridge/Makefile Sun Oct 13 11:45:19 2002
@@ -9,6 +9,11 @@
bridge-objs := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
+
+ifeq ($(CONFIG_NETFILTER),y)
+bridge-objs += br_netfilter.o
+endif
+
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
include $(TOPDIR)/Rules.make
--- linux-2.5.42/net/core/netfilter.c Sat Oct 12 06:22:07 2002
+++ linux-2.5.42-brnf/net/core/netfilter.c Sun Oct 13 11:45:19 2002
@@ -342,10 +342,15 @@
const struct net_device *indev,
const struct net_device *outdev,
struct list_head **i,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
for (*i = (*i)->next; *i != head; *i = (*i)->next) {
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
+
+ if (hook_thresh > elem->priority)
+ continue;
+
switch (elem->hook(hook, skb, indev, outdev, okfn)) {
case NF_QUEUE:
return NF_QUEUE;
@@ -413,6 +418,8 @@
{
int status;
struct nf_info *info;
+ struct net_device *physindev;
+ struct net_device *physoutdev;
if (!queue_handler[pf].outfn) {
kfree_skb(skb);
@@ -435,11 +442,16 @@
if (indev) dev_hold(indev);
if (outdev) dev_hold(outdev);
+ if ((physindev = skb->physindev)) dev_hold(physindev);
+ if ((physoutdev = skb->physoutdev)) dev_hold(physoutdev);
+
status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
if (status < 0) {
/* James M doesn't say fuck enough. */
if (indev) dev_put(indev);
if (outdev) dev_put(outdev);
+ if (physindev) dev_put(physindev);
+ if (physoutdev) dev_put(physoutdev);
kfree(info);
kfree_skb(skb);
return;
@@ -449,7 +461,8 @@
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
struct list_head *elem;
unsigned int verdict;
@@ -481,7 +494,7 @@
elem = &nf_hooks[pf][hook];
verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
- outdev, &elem, okfn);
+ outdev, &elem, okfn, hook_thresh);
if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
@@ -530,7 +543,7 @@
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
&skb, info->hook,
info->indev, info->outdev, &elem,
- info->okfn);
+ info->okfn, INT_MIN);
}
switch (verdict) {
--- linux-2.5.42/net/core/skbuff.c Sat Oct 12 06:21:34 2002
+++ linux-2.5.42-brnf/net/core/skbuff.c Sun Oct 13 11:45:19 2002
@@ -234,6 +234,8 @@
skb->sk = NULL;
skb->stamp.tv_sec = 0; /* No idea about time */
skb->dev = NULL;
+ skb->physindev = NULL;
+ skb->physoutdev = NULL;
skb->dst = NULL;
memset(skb->cb, 0, sizeof(skb->cb));
skb->pkt_type = PACKET_HOST; /* Default type */
@@ -248,6 +250,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
#endif
+ skb->brnfmask = 0;
#endif
#ifdef CONFIG_NET_SCHED
skb->tc_index = 0;
@@ -363,6 +366,8 @@
n->sk = NULL;
C(stamp);
C(dev);
+ C(physindev);
+ C(physoutdev);
C(h);
C(nh);
C(mac);
@@ -392,6 +397,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
C(nf_debug);
#endif
+ C(brnfmask);
#endif /*CONFIG_NETFILTER*/
#if defined(CONFIG_HIPPI)
C(private);
@@ -418,6 +424,8 @@
new->list = NULL;
new->sk = NULL;
new->dev = old->dev;
+ new->physindev = old->physindev;
+ new->physoutdev = old->physoutdev;
new->priority = old->priority;
new->protocol = old->protocol;
new->dst = dst_clone(old->dst);
@@ -438,6 +446,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
new->nf_debug = old->nf_debug;
#endif
+ new->brnfmask = old->brnfmask;
#endif
#ifdef CONFIG_NET_SCHED
new->tc_index = old->tc_index;
--- linux-2.5.42/net/ipv4/ip_output.c Sat Oct 12 06:22:45 2002
+++ linux-2.5.42-brnf/net/ipv4/ip_output.c Sun Oct 13 11:45:19 2002
@@ -75,6 +75,7 @@
#include <net/inetpeer.h>
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_bridge.h>
#include <linux/mroute.h>
#include <linux/netlink.h>
@@ -908,6 +909,18 @@
iph->tot_len = htons(len + hlen);
ip_send_check(iph);
+
+ /*
+ * Fragments with a bridge device destination need
+ * to get the Ethernet header copied here, as
+ * br_dev_queue_push_xmit() can't do this.
+ * See net/bridge/br_netfilter.c
+ */
+
+#ifdef CONFIG_NETFILTER
+ if (skb->brnfmask & BRNF_COPY_HEADER)
+ memcpy(skb2->data - 16, skb->data - 16, 16);
+#endif
err = output(skb2);
if (err)
--- linux-2.5.42/net/ipv4/netfilter/ip_tables.c Sat Oct 12 06:21:35 2002
+++ linux-2.5.42-brnf/net/ipv4/netfilter/ip_tables.c Sun Oct 13 11:45:19 2002
@@ -121,12 +121,14 @@
static inline int
ip_packet_match(const struct iphdr *ip,
const char *indev,
+ const char *physindev,
const char *outdev,
+ const char *physoutdev,
const struct ipt_ip *ipinfo,
int isfrag)
{
size_t i;
- unsigned long ret;
+ unsigned long ret, ret2;
#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
@@ -156,7 +158,13 @@
& ((const unsigned long *)ipinfo->iniface_mask)[i];
}
- if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physindev)[i]
+ ^ ((const unsigned long *)ipinfo->iniface)[i])
+ & ((const unsigned long *)ipinfo->iniface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n",
indev, ipinfo->iniface,
ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
@@ -169,7 +177,13 @@
& ((const unsigned long *)ipinfo->outiface_mask)[i];
}
- if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physoutdev)[i]
+ ^ ((const unsigned long *)ipinfo->outiface)[i])
+ & ((const unsigned long *)ipinfo->outiface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n",
outdev, ipinfo->outiface,
ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
@@ -268,6 +282,7 @@
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
+ const char *physindev, *physoutdev;
void *table_base;
struct ipt_entry *e, *back;
@@ -277,6 +292,9 @@
datalen = (*pskb)->len - ip->ihl * 4;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
+ physindev = (*pskb)->physindev ? (*pskb)->physindev->name : nulldevname;
+ physoutdev = (*pskb)->physoutdev ? (*pskb)->physoutdev->name : nulldevname;
+
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
@@ -311,7 +329,8 @@
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
(*pskb)->nfcache |= e->nfcache;
- if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
+ if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
+ &e->ip, offset)) {
struct ipt_entry_target *t;
if (IPT_MATCH_ITERATE(e, do_match,
--- linux-2.5.42/net/ipv4/netfilter/ipt_LOG.c Sat Oct 12 06:21:38 2002
+++ linux-2.5.42-brnf/net/ipv4/netfilter/ipt_LOG.c Sun Oct 13 11:45:19 2002
@@ -285,10 +285,13 @@
level_string[1] = '0' + (loginfo->level % 8);
spin_lock_bh(&log_lock);
printk(level_string);
- printk("%sIN=%s OUT=%s ",
- loginfo->prefix,
- in ? in->name : "",
- out ? out->name : "");
+ printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
+ if ((*pskb)->physindev && in != (*pskb)->physindev)
+ printk("PHYSIN=%s ", (*pskb)->physindev->name);
+ printk("OUT=%s ", out ? out->name : "");
+ if ((*pskb)->physoutdev && out != (*pskb)->physoutdev)
+ printk("PHYSOUT=%s ", (*pskb)->physoutdev->name);
+
if (in && !out) {
/* MAC logging for input chain only. */
printk("MAC=");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.42-brnf/net/bridge/br_netfilter.c Sun Oct 13 11:45:19 2002
@@ -0,0 +1,602 @@
+/*
+ * Handle firewalling
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * $Id: br_netfilter.c,v 1.2 2002/09/11 19:35:44 bdschuym Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/in_route.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include "br_private.h"
+
+
+#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
+ (skb->cb))->daddr.ipv4)
+#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
+#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define clear_cb(skb) (memset(&skb_origaddr(skb), 0, \
+ sizeof(struct bridge_skb_cb)))
+
+#define has_bridge_parent(device) ((device)->br_port != NULL)
+#define bridge_parent(device) (&((device)->br_port->br->dev))
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it. Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static struct net_device __fake_net_device = {
+ hard_header_len: ETH_HLEN
+};
+
+static struct rtable __fake_rtable = {
+ u: {
+ dst: {
+ __refcnt: ATOMIC_INIT(1),
+ dev: &__fake_net_device,
+ pmtu: 1500
+ }
+ },
+
+ rt_flags: 0
+};
+
+
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static void __br_dnat_complain(void)
+{
+ static unsigned long last_complaint = 0;
+
+ if (jiffies - last_complaint >= 5 * HZ) {
+ printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
+ "forwarding to be enabled\n");
+ last_complaint = jiffies;
+ }
+}
+
+
+/* This requires some explaining. If DNAT has taken place,
+ * we will need to fix up the destination Ethernet address,
+ * and this is a tricky process.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ * port group as it was received on. We can still bridge
+ * the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ * a non-bridged device or another bridge port group.
+ * The packet will need to be routed.
+ *
+ * The correct way of distinguishing between these two cases is to
+ * call ip_route_input() and to look at skb->dst->dev, which is
+ * changed to the destination device if ip_route_input() succeeds.
+ *
+ * Let us first consider the case that ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet
+ * came in on, we can consider this bridging. We then call
+ * skb->dst->output() which will make the packet enter br_nf_local_out()
+ * not much later. In that function it is assured that the iptables
+ * FORWARD chain is traversed for the packet.
+ *
+ * Otherwise, the packet is considered to be routed and we just
+ * change the destination MAC address so that the packet will
+ * later be passed up to the IP stack to be routed.
+ *
+ * Let us now consider the case that ip_route_input() fails:
+ *
+ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
+ * will fail, while ip_route_output() will return success. The source
+ * address for ip_route_output() is set to zero, so ip_route_output()
+ * thinks we're handling a locally generated packet and won't care
+ * if IP forwarding is allowed. We send a warning message to the users's
+ * log telling her to put IP forwarding on.
+ *
+ * ip_route_input() will also fail if there is no route available.
+ * In that case we just drop the packet.
+ *
+ * --Lennert, 20020411
+ * --Bart, 20020416 (updated)
+ * --Bart, 20021007 (updated)
+ */
+
+static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
+#endif
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->brnfmask |= BRNF_PKT_TYPE;
+ }
+
+ skb->dev = bridge_parent(skb->dev);
+ skb->dst->output(skb);
+ return 0;
+}
+
+static int br_nf_pre_routing_finish(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct iphdr *iph = skb->nh.iph;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+#endif
+
+ if (skb->brnfmask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->brnfmask ^= BRNF_PKT_TYPE;
+ }
+
+ if (dnat_took_place(skb)) {
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
+ dev)) {
+ struct rtable *rt;
+
+ if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
+ /* Bridged-and-DNAT'ed traffic doesn't
+ * require ip_forwarding.
+ */
+ if (((struct dst_entry *)rt)->dev == dev) {
+ skb->dst = (struct dst_entry *)rt;
+ goto bridged_dnat;
+ }
+ __br_dnat_complain();
+ dst_release((struct dst_entry *)rt);
+ }
+ kfree_skb(skb);
+ return 0;
+ } else {
+ if (skb->dst->dev == dev) {
+bridged_dnat:
+ /* Tell br_nf_local_out this is a
+ * bridged frame
+ */
+ skb->brnfmask |= BRNF_BRIDGED_DNAT;
+ skb->dev = skb->physindev;
+ clear_cb(skb);
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
+ skb, skb->dev, NULL,
+ br_nf_pre_routing_finish_bridge,
+ 1);
+ return 0;
+ }
+ memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
+ ETH_ALEN);
+ }
+ } else {
+ skb->dst = (struct dst_entry *)&__fake_rtable;
+ dst_hold(skb->dst);
+ }
+
+ clear_cb(skb);
+ skb->dev = skb->physindev;
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish, 1);
+
+ return 0;
+}
+
+/* Replicate the checks that IPv4 does on packet reception.
+ * Set skb->dev to the bridge device (i.e. parent of the
+ * receiving device) to make netfilter happy, the REDIRECT
+ * target in particular. Save the original destination IP
+ * address to be able to detect DNAT afterwards.
+ */
+static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct iphdr *iph;
+ __u32 len;
+ struct sk_buff *skb;
+
+ if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+ goto out;
+
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (iph->ihl < 5 || iph->version != 4)
+ goto inhdr_error;
+
+ if (!pskb_may_pull(skb, 4*iph->ihl))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+ goto inhdr_error;
+
+ len = ntohs(iph->tot_len);
+ if (skb->len < len || len < 4*iph->ihl)
+ goto inhdr_error;
+
+ if (skb->len > len) {
+ __pskb_trim(skb, len);
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+#endif
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->brnfmask |= BRNF_PKT_TYPE;
+ }
+
+ skb->physindev = skb->dev;
+ skb->dev = bridge_parent(skb->dev);
+ store_orig_dstaddr(skb);
+
+ NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+ br_nf_pre_routing_finish);
+
+ return NF_STOLEN;
+
+inhdr_error:
+// IP_INC_STATS_BH(IpInHdrErrors);
+out:
+ return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one. On the way up, the
+ * packet would pass through PRE_ROUTING again (which already
+ * took place when the packet entered the bridge), but we
+ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
+ * prevent this from happening.
+ */
+static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+
+ return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf_forward_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ if (skb->brnfmask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->brnfmask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, skb->physindev,
+ skb->dev, br_forward_finish, 1);
+
+ return 0;
+}
+
+/* This is the 'purely bridged' case. We pass the packet to
+ * netfilter with indev and outdev set to the bridge device,
+ * but we are still able to filter on the 'real' indev/outdev
+ * because another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
+static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->brnfmask |= BRNF_PKT_TYPE;
+ }
+
+ skb->physoutdev = skb->dev;
+
+ NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(skb->physindev),
+ bridge_parent(skb->dev), br_nf_forward_finish);
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT ***********************************************/
+static int br_nf_local_out_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ br_forward_finish, NF_BR_PRI_FIRST + 1);
+
+ return 0;
+}
+
+
+/* This function sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device). It also sees bridged-and-DNAT'ed packets.
+ * For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
+ * functions, and give them back later, when we have determined the real
+ * output device. This is done in here.
+ *
+ * If (skb->brnfmask & BRNF_BRIDGED_DNAT) then the packet is bridged
+ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
+ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
+ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
+ * will be executed.
+ * Otherwise, if skb->physindev is NULL, the bridge-nf code never touched
+ * this packet before, and so the packet was locally originated. We fake
+ * the PF_INET/LOCAL_OUT hook.
+ * Finally, if skb->physindev isn't NULL, then the packet was IP routed,
+ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
+ * even routed packets that didn't arrive on a bridge interface have their
+ * skb->physindev set.
+ */
+
+static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*_okfn)(struct sk_buff *))
+{
+ int (*okfn)(struct sk_buff *skb);
+ struct net_device *realindev;
+ struct sk_buff *skb = *pskb;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+ skb->physoutdev = skb->dev;
+
+ realindev = skb->physindev;
+
+ /* Bridged, take PF_BRIDGE/FORWARD.
+ * (see big note in front of br_nf_pre_routing_finish)
+ */
+ if (skb->brnfmask & BRNF_BRIDGED_DNAT) {
+ okfn = br_forward_finish;
+
+ if (skb->brnfmask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->brnfmask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
+ skb->dev, okfn);
+ } else {
+ okfn = br_nf_local_out_finish;
+ /* IP forwarded traffic has a physindev, locally
+ * generated traffic hasn't.
+ */
+ if (realindev != NULL) {
+ if (((skb->brnfmask & BRNF_DONT_TAKE_PARENT) == 0) &&
+ has_bridge_parent(realindev))
+ realindev = bridge_parent(realindev);
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
+ } else {
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
+ }
+ }
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ /* Be very paranoid. */
+ if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+ printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
+ "bad mac.raw pointer.");
+ if (skb->dev != NULL) {
+ printk("[%s]", skb->dev->name);
+ if (has_bridge_parent(skb->dev))
+ printk("[%s]", bridge_parent(skb->dev)->name);
+ }
+ printk("\n");
+ return NF_ACCEPT;
+ }
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
+#endif
+
+ /* We assume any code from br_dev_queue_push_xmit onwards doesn't care
+ * about the value of skb->pkt_type.
+ */
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->brnfmask |= BRNF_PKT_TYPE;
+ }
+
+ /* Fragmented packets need a good Ethernet header, tell this to
+ * ip_output.c::ip_fragment().
+ */
+ skb->brnfmask |= BRNF_COPY_HEADER;
+
+ NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+ bridge_parent(skb->dev), br_dev_queue_push_xmit);
+
+ return NF_STOLEN;
+}
+
+
+/* IPv4/SABOTAGE *****************************************************/
+
+/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
+ * for the second time.
+ */
+static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (in->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_pre_routing_finish) {
+ okfn(*pskb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
+ * and PF_INET/POST_ROUTING until we have done the forwarding
+ * decision in the bridge code and have determined skb->physoutdev.
+ */
+static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (out->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_forward_finish &&
+ okfn != br_nf_local_out_finish &&
+ okfn != br_dev_queue_push_xmit) {
+ struct sk_buff *skb = *pskb;
+
+ /* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
+ * will need the indev then. For a brouter, the real indev
+ * can be a bridge port, so we make sure br_nf_local_out()
+ * doesn't use the bridge parent of the indev by using
+ * the BRNF_DONT_TAKE_PARENT mask.
+ */
+ if (hook == NF_IP_FORWARD && skb->physindev == NULL) {
+ skb->brnfmask &= BRNF_DONT_TAKE_PARENT;
+ skb->physindev = (struct net_device *)in;
+ }
+ okfn(skb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that
innocent
+ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
+ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
+ * ip_refrag() can return NF_STOLEN.
+ */
+static struct nf_hook_ops br_nf_ops[] = {
+ { { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING,
NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF
},
+ { { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT,
NF_BR_PRI_FIRST },
+ { { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING,
NF_BR_PRI_LAST },
+ { { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING,
NF_IP_PRI_FIRST },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD,
NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT,
NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING,
NF_IP_PRI_FIRST }
+};
+
+#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
+
+int br_netfilter_init(void)
+{
+ int i;
+
+ for (i = 0; i < NUMHOOKS; i++) {
+ int ret;
+
+ if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
+ continue;
+
+ while (i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+
+ return ret;
+ }
+
+ printk(KERN_NOTICE "Bridge firewalling registered\n");
+
+ return 0;
+}
+
+void br_netfilter_fini(void)
+{
+ int i;
+
+ for (i = NUMHOOKS - 1; i >= 0; i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+}
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 18:05 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42 Bart De Schuymer
@ 2002-10-14 18:01 ` David S. Miller
2002-10-14 18:32 ` bert hubert
2002-10-14 18:58 ` Bart De Schuymer
2002-10-20 22:20 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44 Bart De Schuymer
1 sibling, 2 replies; 39+ messages in thread
From: David S. Miller @ 2002-10-14 18:01 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel, buytenh
These changes cannot go in:
1) There is no reason the 'okfn' you use cannot be the
function doing the MAC header copy.
This is how this is supposed to work.
I explained in that long thread a few weeks ago how
this copy may not be placed in the generic IP code.
This is final, you must find a way to make this copy
without touching ipv4/*.c
2) The netfilter changes need to be approved by the netfilter
team.
I suspect, like myself, they will barf at the phys{in,out}dev
additions to sk_buff. We already have enough junk sitting
in sk_buff making it larger than it needs to be.
Perhaps you can hang this off the nf_conntrack pointer and
specify a destructor.
3) The bridging layer changes need to be approved by Lennert.
But I'd suggest working out #1 and #2 first.
Thanks.
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 18:01 ` David S. Miller
@ 2002-10-14 18:32 ` bert hubert
2002-10-14 18:58 ` Bart De Schuymer
1 sibling, 0 replies; 39+ messages in thread
From: bert hubert @ 2002-10-14 18:32 UTC (permalink / raw)
To: David S. Miller; +Cc: bart.de.schuymer, linux-kernel, buytenh
On Mon, Oct 14, 2002 at 11:01:59AM -0700, David S. Miller wrote:
> 3) The bridging layer changes need to be approved by Lennert.
> But I'd suggest working out #1 and #2 first.
Lennert appears to have dropped off the net.
--
http://www.PowerDNS.com Versatile DNS Software & Services
http://www.tk the dot in .tk
http://lartc.org Linux Advanced Routing & Traffic Control HOWTO
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 18:01 ` David S. Miller
2002-10-14 18:32 ` bert hubert
@ 2002-10-14 18:58 ` Bart De Schuymer
2002-10-14 19:02 ` David S. Miller
1 sibling, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-14 18:58 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, buytenh, Lennert Buytenhek, rusty
On Monday 14 October 2002 20:01, David S. Miller wrote:
Hello,
These are probably stupid questions to you, but here it goes.
> These changes cannot go in:
>
> 1) There is no reason the 'okfn' you use cannot be the
> function doing the MAC header copy.
>
> This is how this is supposed to work.
>
> I explained in that long thread a few weeks ago how
> this copy may not be placed in the generic IP code.
> This is final, you must find a way to make this copy
> without touching ipv4/*.c
I've checked the skb->dst->hh field and it (or skb->dst itself) was NULL for
purely bridged packets. So we'd have to fill this in ourselves. Can the
bridge code go fill in a skb->dst and skb->dst->hh? Is this considered clean?
> 2) The netfilter changes need to be approved by the netfilter
> team.
>
> I suspect, like myself, they will barf at the phys{in,out}dev
> additions to sk_buff. We already have enough junk sitting
> in sk_buff making it larger than it needs to be.
I added a third member as well... It's needed too, in my opinion.
There could ofcourse be added a pointer to a struct containing these three
values (and a copied Ethernet header). Then we go from 3 to 1 extra member...
Anyway, it's not like Lennert and me like adding new members, but we need to
save those things somewhere...
> Perhaps you can hang this off the nf_conntrack pointer and
> specify a destructor.
>
> 3) The bridging layer changes need to be approved by Lennert.
> But I'd suggest working out #1 and #2 first.
So if I change
struct nf_conntrack {
atomic_t use;
void (*destroy)(struct nf_conntrack *);
};
into this:
struct nf_conntrack {
atomic_t use;
void (*destroy)(struct nf_conntrack *);
struct brnf_data *brnf;
};
I can keep the copy of the Ethernet header in the struct brnf_data too (then I
don't have to touch skbuff->dst).
The skbuff->nfct field can already be in use by an IP connection tracker (or
something), so I can't use my own destroy function.
So I'd have to go do something in
net/ipv4/netfilter/ip_conntrack_core.c::destroy_conntrack() and I don't know
that stuff.
I sure don't like this solution more than the current situation.
Anyway, mapping the IPv4 hooks onto the bridge hooks is in my opinion by
definition a hack. But a very useful hack. So if you want this in the kernel
you'll have to be forgiving. Or present a nice solution, because I and
probably Lennert really don't see a nice(r) solution.
So, the best solution I can think of is adding a skbuff->brnf pointer to a
struct brnf_data. This will get rid of the copy in ip_output.c. Is that
enough? This will uglify the ip_tables.c patch however.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 18:58 ` Bart De Schuymer
@ 2002-10-14 19:02 ` David S. Miller
2002-10-14 19:29 ` Bart De Schuymer
0 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-10-14 19:02 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel, buytenh, rusty
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Mon, 14 Oct 2002 20:58:53 +0200
Can the bridge code go fill in a skb->dst and skb->dst->hh? Is this
considered clean?
If it is a properly formed 'dst' entry, it will get cleaned up
at SKB free and there will be no problems.
> 3) The bridging layer changes need to be approved by Lennert.
> But I'd suggest working out #1 and #2 first.
So if I change
struct nf_conntrack {
You shouldn't be touching nf_conntrack, you should perhaps
instead do something like:
struct nf_ct_info {
union {
struct nf_conntrack *master;
struct nf_bridge_info *brinfo;
} u;
};
But again, you need to get these sorts of extensions and core
changes approved by the netfilter team.
I'm the wrong person to ask about how they would prefer this
stuff be done.
So if you want this in the kernel you'll have to be forgiving. Or
present a nice solution, because I and probably Lennert really
don't see a nice(r) solution.
It is my job to show you why a piece of code isn't going
to go in. It is not my job to help you dream up a better
solution.
Because, frankly I don't care about bridge netfiltering.
I do care about keeping the code as clean as possible so I don't
run into road blocks when trying to rework input/output processing
just because I let some bogon hack into the tree I must continue to
support.
You do care about bridge netfiltering, so you are going to be the
one to find the clean solution that doesn't touch net/ipv4/*.c :-)
This is life in the kernel hacking community :-)
So, the best solution I can think of is adding a skbuff->brnf pointer to a
struct brnf_data. This will get rid of the copy in ip_output.c. Is that
enough? This will uglify the ip_tables.c patch however.
That could work too, I think you'll need to specify a seperate
destructor in that case, and all this stuff ifdef'd on whether
bridge netfiltering is enabled or not.
Again, talk to the netfilter folks. They may even have ideas
for you that you haven't dreamt of yet.
Franks a lot,
David S. Miller
davem@redhat.com
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 19:02 ` David S. Miller
@ 2002-10-14 19:29 ` Bart De Schuymer
2002-10-14 19:26 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-14 19:29 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, buytenh, rusty
> It is my job to show you why a piece of code isn't going
> to go in. It is not my job to help you dream up a better
> solution.
>
> Because, frankly I don't care about bridge netfiltering.
You were the one who asked for that patch.
> I do care about keeping the code as clean as possible so I don't
> run into road blocks when trying to rework input/output processing
> just because I let some bogon hack into the tree I must continue to
> support.
Ack.
> You do care about bridge netfiltering, so you are going to be the
> one to find the clean solution that doesn't touch net/ipv4/*.c :-)
I care about Linux. I absolutely don't need a bridging firewall for anything.
I just happen to know something about it.
> That could work too, I think you'll need to specify a seperate
> destructor in that case, and all this stuff ifdef'd on whether
> bridge netfiltering is enabled or not.
This brings me to another question: I've been told it is the general concensus
that this bridge firewall should be compiled in the kernel if
CONFIG_NETFILTER=y. Or should it be a user option? It is predicted that using
a user option will give alot of questions about the bridge firewall not
working.
> Again, talk to the netfilter folks. They may even have ideas
> for you that you haven't dreamt of yet.
Will do.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42
2002-10-14 19:29 ` Bart De Schuymer
@ 2002-10-14 19:26 ` David S. Miller
0 siblings, 0 replies; 39+ messages in thread
From: David S. Miller @ 2002-10-14 19:26 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel, buytenh, rusty
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Mon, 14 Oct 2002 21:29:56 +0200
You were the one who asked for that patch.
I asked for the patch to be cleaned up to eliminate
the net/ipv4/*.c hacking. :)
This brings me to another question: I've been told it is the
general concensus that this bridge firewall should be compiled in
the kernel if CONFIG_NETFILTER=y.
I don't have any strong opinion here.
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44
2002-10-14 18:05 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42 Bart De Schuymer
2002-10-14 18:01 ` David S. Miller
@ 2002-10-20 22:20 ` Bart De Schuymer
2002-10-20 22:19 ` David S. Miller
2002-10-22 23:40 ` Bart De Schuymer
1 sibling, 2 replies; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-20 22:20 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, Lennert Buytenhek, coreteam
Hello,
This is a follow-up from the previous RFC for the bridge-nf patch.
The new patch adds one member to the skbuff, a pointer to a struct
nf_bridge_info. There is still a need to change ip_output.c, but the change
is the analogue as is done for the skbuff->nfct pointer field. So, for me
this is a clean solution. The copy of the Ethernet header is no longer done
in ip_fragment().
The patch is available at:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre2-against-2.5.44.diff
An incremental diff, for 2.5.42, against the previous patch is here:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre2.001-against-2.5.42.diff
David, are you happy with this solution?
Other comments?
Here's the patch.
--- linux-2.5.44/include/linux/netfilter.h Sat Oct 19 06:01:54 2002
+++ linux-2.5.44-brnf/include/linux/netfilter.h Sun Oct 20 21:57:52 2002
@@ -117,17 +117,23 @@
/* This is gross, but inline doesn't cut it for avoiding the function
call in fast path: gcc doesn't inline (needs value tracking?). --RR */
#ifdef CONFIG_NETFILTER_DEBUG
-#define NF_HOOK nf_hook_slow
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
+ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_THRESH nf_hook_slow
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
(list_empty(&nf_hooks[(pf)][(hook)]) \
? (okfn)(skb) \
- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
+#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \
+(list_empty(&nf_hooks[(pf)][(hook)]) \
+ ? (okfn)(skb) \
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
#endif
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *));
+ int (*okfn)(struct sk_buff *), int thresh);
/* Call setsockopt() */
int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt,
--- linux-2.5.44/include/linux/netfilter_ipv4.h Sat Oct 19 06:02:28 2002
+++ linux-2.5.44-brnf/include/linux/netfilter_ipv4.h Sun Oct 20 21:57:52 2002
@@ -52,8 +52,10 @@
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
--- linux-2.5.44/include/linux/netfilter_bridge.h Sat Oct 19 06:01:57 2002
+++ linux-2.5.44-brnf/include/linux/netfilter_bridge.h Sun Oct 20 21:57:52
2002
@@ -6,6 +6,7 @@
#include <linux/config.h>
#include <linux/netfilter.h>
+#include <asm/atomic.h>
/* Bridge Hooks */
/* After promisc drops, checksum checks. */
@@ -22,14 +23,39 @@
#define NF_BR_BROUTING 5
#define NF_BR_NUMHOOKS 6
+#define BRNF_PKT_TYPE 0x01
+#define BRNF_BRIDGED_DNAT 0x02
+#define BRNF_DONT_TAKE_PARENT 0x04
+
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
- NF_BR_PRI_FILTER_BRIDGED = -200,
- NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_FILTER_BRIDGED = -200,
+ NF_BR_PRI_BRNF = 0,
NF_BR_PRI_NAT_DST_OTHER = 100,
+ NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
+};
+
+static inline
+struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
+{
+ struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
+
+ if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
+ atomic_set(&(*nf_bridge)->use, 1);
+ (*nf_bridge)->mask = 0;
+ (*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
+ }
+
+ return *nf_bridge;
+}
+
+struct bridge_skb_cb {
+ union {
+ __u32 ipv4;
+ } daddr;
};
#endif
--- linux-2.5.44/include/linux/skbuff.h Sat Oct 19 06:01:58 2002
+++ linux-2.5.44-brnf/include/linux/skbuff.h Sun Oct 20 21:57:52 2002
@@ -96,6 +96,14 @@
struct nf_ct_info {
struct nf_conntrack *master;
};
+
+struct nf_bridge_info {
+ atomic_t use;
+ struct net_device *physindev;
+ struct net_device *physoutdev;
+ unsigned int mask;
+ unsigned long hh[16 / sizeof(unsigned long)];
+};
#endif
struct sk_buff_head {
@@ -166,6 +174,7 @@
* @nfcache: Cache info
* @nfct: Associated connection, if any
* @nf_debug: Netfilter debugging
+ * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @tc_index: Traffic control index
*/
@@ -236,6 +245,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
+ struct nf_bridge_info *nf_bridge;
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
@@ -1145,6 +1155,17 @@
{
if (nfct)
atomic_inc(&nfct->master->use);
+}
+
+static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
+{
+ if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
+ kfree(nf_bridge);
+}
+static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
+{
+ if (nf_bridge)
+ atomic_inc(&nf_bridge->use);
}
#endif
--- linux-2.5.44/net/bridge/br.c Sat Oct 19 06:01:15 2002
+++ linux-2.5.44-brnf/net/bridge/br.c Sun Oct 20 21:57:52 2002
@@ -45,6 +45,10 @@
{
printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+#ifdef CONFIG_NETFILTER
+ if (br_netfilter_init())
+ return 1;
+#endif
br_handle_frame_hook = br_handle_frame;
br_ioctl_hook = br_ioctl_deviceless_stub;
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
@@ -63,6 +67,9 @@
static void __exit br_deinit(void)
{
+#ifdef CONFIG_NETFILTER
+ br_netfilter_fini();
+#endif
unregister_netdevice_notifier(&br_device_notifier);
br_call_ioctl_atomic(__br_clear_ioctl_hook);
--- linux-2.5.44/net/bridge/br_forward.c Sat Oct 19 06:01:20 2002
+++ linux-2.5.44-brnf/net/bridge/br_forward.c Sun Oct 20 21:57:52 2002
@@ -30,18 +30,23 @@
return 1;
}
-static int __dev_queue_push_xmit(struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct sk_buff *skb)
{
+#ifdef CONFIG_NETFILTER
+ if (skb->nf_bridge)
+ memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
+#endif
skb_push(skb, ETH_HLEN);
+
dev_queue_xmit(skb);
return 0;
}
-static int __br_forward_finish(struct sk_buff *skb)
+int br_forward_finish(struct sk_buff *skb)
{
NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
- __dev_queue_push_xmit);
+ br_dev_queue_push_xmit);
return 0;
}
@@ -53,7 +58,7 @@
skb->nf_debug = 0;
#endif
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
@@ -64,7 +69,7 @@
skb->dev = to->dev;
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
/* called under bridge lock */
--- linux-2.5.44/net/bridge/br_input.c Sat Oct 19 06:01:18 2002
+++ linux-2.5.44-brnf/net/bridge/br_input.c Sun Oct 20 21:57:52 2002
@@ -49,7 +49,7 @@
br_pass_frame_up_finish);
}
-static int br_handle_frame_finish(struct sk_buff *skb)
+int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
--- linux-2.5.44/net/bridge/br_private.h Sat Oct 19 06:01:18 2002
+++ linux-2.5.44-brnf/net/bridge/br_private.h Sun Oct 20 21:57:52 2002
@@ -144,8 +144,10 @@
/* br_forward.c */
extern void br_deliver(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br,
struct sk_buff *skb,
int clone);
@@ -166,6 +168,7 @@
int *ifindices);
/* br_input.c */
+extern int br_handle_frame_finish(struct sk_buff *skb);
extern int br_handle_frame(struct sk_buff *skb);
/* br_ioctl.c */
@@ -176,6 +179,10 @@
unsigned long arg1,
unsigned long arg2);
extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_netfilter.c */
+extern int br_netfilter_init(void);
+extern void br_netfilter_fini(void);
/* br_stp.c */
extern int br_is_root_bridge(struct net_bridge *br);
--- linux-2.5.44/net/bridge/Makefile Sat Oct 19 06:02:32 2002
+++ linux-2.5.44-brnf/net/bridge/Makefile Sun Oct 20 21:57:52 2002
@@ -9,6 +9,11 @@
bridge-objs := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
+
+ifeq ($(CONFIG_NETFILTER),y)
+bridge-objs += br_netfilter.o
+endif
+
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
include $(TOPDIR)/Rules.make
--- linux-2.5.44/net/core/netfilter.c Sat Oct 19 06:01:53 2002
+++ linux-2.5.44-brnf/net/core/netfilter.c Sun Oct 20 21:57:52 2002
@@ -342,10 +342,15 @@
const struct net_device *indev,
const struct net_device *outdev,
struct list_head **i,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
for (*i = (*i)->next; *i != head; *i = (*i)->next) {
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
+
+ if (hook_thresh > elem->priority)
+ continue;
+
switch (elem->hook(hook, skb, indev, outdev, okfn)) {
case NF_QUEUE:
return NF_QUEUE;
@@ -413,6 +418,8 @@
{
int status;
struct nf_info *info;
+ struct net_device *physindev = NULL;
+ struct net_device *physoutdev = NULL;
if (!queue_handler[pf].outfn) {
kfree_skb(skb);
@@ -435,11 +442,20 @@
if (indev) dev_hold(indev);
if (outdev) dev_hold(outdev);
+ if (skb->nf_bridge) {
+ physindev = skb->nf_bridge->physindev;
+ if (physindev) dev_hold(physindev);
+ physoutdev = skb->nf_bridge->physoutdev;
+ if (physoutdev) dev_hold(physoutdev);
+ }
+
status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
if (status < 0) {
/* James M doesn't say fuck enough. */
if (indev) dev_put(indev);
if (outdev) dev_put(outdev);
+ if (physindev) dev_put(physindev);
+ if (physoutdev) dev_put(physoutdev);
kfree(info);
kfree_skb(skb);
return;
@@ -449,7 +465,8 @@
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
struct list_head *elem;
unsigned int verdict;
@@ -481,7 +498,7 @@
elem = &nf_hooks[pf][hook];
verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
- outdev, &elem, okfn);
+ outdev, &elem, okfn, hook_thresh);
if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
@@ -530,7 +547,7 @@
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
&skb, info->hook,
info->indev, info->outdev, &elem,
- info->okfn);
+ info->okfn, INT_MIN);
}
switch (verdict) {
--- linux-2.5.44/net/core/skbuff.c Sat Oct 19 06:01:17 2002
+++ linux-2.5.44-brnf/net/core/skbuff.c Sun Oct 20 21:57:52 2002
@@ -248,6 +248,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
#endif
+ skb->nf_bridge = NULL;
#endif
#ifdef CONFIG_NET_SCHED
skb->tc_index = 0;
@@ -327,6 +328,7 @@
}
#ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
+ nf_bridge_put(skb->nf_bridge);
#endif
skb_headerinit(skb, NULL, 0); /* clean state */
kfree_skbmem(skb);
@@ -392,6 +394,7 @@
#ifdef CONFIG_NETFILTER_DEBUG
C(nf_debug);
#endif
+ C(nf_bridge);
#endif /*CONFIG_NETFILTER*/
#if defined(CONFIG_HIPPI)
C(private);
@@ -404,6 +407,7 @@
skb->cloned = 1;
#ifdef CONFIG_NETFILTER
nf_conntrack_get(skb->nfct);
+ nf_bridge_get(skb->nf_bridge);
#endif
return n;
}
@@ -438,6 +442,8 @@
#ifdef CONFIG_NETFILTER_DEBUG
new->nf_debug = old->nf_debug;
#endif
+ new->nf_bridge = old->nf_bridge;
+ nf_bridge_get(new->nf_bridge);
#endif
#ifdef CONFIG_NET_SCHED
new->tc_index = old->tc_index;
--- linux-2.5.44/net/ipv4/ip_output.c Sat Oct 19 06:02:34 2002
+++ linux-2.5.44-brnf/net/ipv4/ip_output.c Sun Oct 20 22:00:22 2002
@@ -396,6 +396,8 @@
/* Connection association is same as pre-frag packet */
to->nfct = from->nfct;
nf_conntrack_get(to->nfct);
+ to->nf_bridge = from->nf_bridge;
+ nf_bridge_get(to->nf_bridge);
#ifdef CONFIG_NETFILTER_DEBUG
to->nf_debug = from->nf_debug;
#endif
--- linux-2.5.44/net/ipv4/netfilter/ip_tables.c Sat Oct 19 06:01:18 2002
+++ linux-2.5.44-brnf/net/ipv4/netfilter/ip_tables.c Sun Oct 20 21:57:52 2002
@@ -121,12 +121,14 @@
static inline int
ip_packet_match(const struct iphdr *ip,
const char *indev,
+ const char *physindev,
const char *outdev,
+ const char *physoutdev,
const struct ipt_ip *ipinfo,
int isfrag)
{
size_t i;
- unsigned long ret;
+ unsigned long ret, ret2;
#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
@@ -156,7 +158,13 @@
& ((const unsigned long *)ipinfo->iniface_mask)[i];
}
- if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physindev)[i]
+ ^ ((const unsigned long *)ipinfo->iniface)[i])
+ & ((const unsigned long *)ipinfo->iniface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n",
indev, ipinfo->iniface,
ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
@@ -169,7 +177,13 @@
& ((const unsigned long *)ipinfo->outiface_mask)[i];
}
- if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
+ for (i = 0, ret2 = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret2 |= (((const unsigned long *)physoutdev)[i]
+ ^ ((const unsigned long *)ipinfo->outiface)[i])
+ & ((const unsigned long *)ipinfo->outiface_mask)[i];
+ }
+
+ if (FWINV(ret != 0 && ret2 != 0, IPT_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n",
outdev, ipinfo->outiface,
ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
@@ -268,6 +282,7 @@
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
+ const char *physindev, *physoutdev;
void *table_base;
struct ipt_entry *e, *back;
@@ -277,6 +292,16 @@
datalen = (*pskb)->len - ip->ihl * 4;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
+ if ((*pskb)->nf_bridge) {
+ physindev = (*pskb)->nf_bridge->physindev ?
+ (*pskb)->nf_bridge->physindev->name : nulldevname;
+ physoutdev = (*pskb)->nf_bridge->physoutdev ?
+ (*pskb)->nf_bridge->physoutdev->name : nulldevname;
+ } else {
+ physindev = nulldevname;
+ physoutdev = nulldevname;
+ }
+
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
@@ -311,7 +336,8 @@
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
(*pskb)->nfcache |= e->nfcache;
- if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
+ if (ip_packet_match(ip, indev, physindev, outdev, physoutdev,
+ &e->ip, offset)) {
struct ipt_entry_target *t;
if (IPT_MATCH_ITERATE(e, do_match,
--- linux-2.5.44/net/ipv4/netfilter/ipt_LOG.c Sat Oct 19 06:01:21 2002
+++ linux-2.5.44-brnf/net/ipv4/netfilter/ipt_LOG.c Sun Oct 20 21:57:52 2002
@@ -285,10 +285,18 @@
level_string[1] = '0' + (loginfo->level % 8);
spin_lock_bh(&log_lock);
printk(level_string);
- printk("%sIN=%s OUT=%s ",
- loginfo->prefix,
- in ? in->name : "",
- out ? out->name : "");
+ printk("%sIN=%s ", loginfo->prefix, in ? in->name : "");
+ if ((*pskb)->nf_bridge) {
+ struct net_device *physindev = (*pskb)->nf_bridge->physindev;
+ struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
+
+ if (physindev && in != physindev)
+ printk("PHYSIN=%s ", physindev->name);
+ printk("OUT=%s ", out ? out->name : "");
+ if (physoutdev && out != physoutdev)
+ printk("PHYSOUT=%s ", physoutdev->name);
+ }
+
if (in && !out) {
/* MAC logging for input chain only. */
printk("MAC=");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.44-brnf/net/bridge/br_netfilter.c Sun Oct 20 21:57:52 2002
@@ -0,0 +1,616 @@
+/*
+ * Handle firewalling
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * $Id: bridge-nf-0.0.10-dev-pre2-against-2.5.42.diff,v 1.1 2002/10/19
10:46:51 bdschuym Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/in_route.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include "br_private.h"
+
+
+#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
+ (skb->cb))->daddr.ipv4)
+#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
+#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define clear_cb(skb) (memset(&skb_origaddr(skb), 0, \
+ sizeof(struct bridge_skb_cb)))
+
+#define has_bridge_parent(device) ((device)->br_port != NULL)
+#define bridge_parent(device) (&((device)->br_port->br->dev))
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it. Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static struct net_device __fake_net_device = {
+ hard_header_len: ETH_HLEN
+};
+
+static struct rtable __fake_rtable = {
+ u: {
+ dst: {
+ __refcnt: ATOMIC_INIT(1),
+ dev: &__fake_net_device,
+ pmtu: 1500
+ }
+ },
+
+ rt_flags: 0
+};
+
+
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static void __br_dnat_complain(void)
+{
+ static unsigned long last_complaint = 0;
+
+ if (jiffies - last_complaint >= 5 * HZ) {
+ printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
+ "forwarding to be enabled\n");
+ last_complaint = jiffies;
+ }
+}
+
+
+/* This requires some explaining. If DNAT has taken place,
+ * we will need to fix up the destination Ethernet address,
+ * and this is a tricky process.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ * port group as it was received on. We can still bridge
+ * the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ * a non-bridged device or another bridge port group.
+ * The packet will need to be routed.
+ *
+ * The correct way of distinguishing between these two cases is to
+ * call ip_route_input() and to look at skb->dst->dev, which is
+ * changed to the destination device if ip_route_input() succeeds.
+ *
+ * Let us first consider the case that ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet
+ * came in on, we can consider this bridging. We then call
+ * skb->dst->output() which will make the packet enter br_nf_local_out()
+ * not much later. In that function it is assured that the iptables
+ * FORWARD chain is traversed for the packet.
+ *
+ * Otherwise, the packet is considered to be routed and we just
+ * change the destination MAC address so that the packet will
+ * later be passed up to the IP stack to be routed.
+ *
+ * Let us now consider the case that ip_route_input() fails:
+ *
+ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
+ * will fail, while ip_route_output() will return success. The source
+ * address for ip_route_output() is set to zero, so ip_route_output()
+ * thinks we're handling a locally generated packet and won't care
+ * if IP forwarding is allowed. We send a warning message to the users's
+ * log telling her to put IP forwarding on.
+ *
+ * ip_route_input() will also fail if there is no route available.
+ * In that case we just drop the packet.
+ *
+ * --Lennert, 20020411
+ * --Bart, 20020416 (updated)
+ * --Bart, 20021007 (updated)
+ */
+
+static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
+#endif
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ skb->dev = bridge_parent(skb->dev);
+ skb->dst->output(skb);
+ return 0;
+}
+
+static int br_nf_pre_routing_finish(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct iphdr *iph = skb->nh.iph;
+ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+#endif
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ if (dnat_took_place(skb)) {
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
+ dev)) {
+ struct rtable *rt;
+
+ if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
+ /* Bridged-and-DNAT'ed traffic doesn't
+ * require ip_forwarding.
+ */
+ if (((struct dst_entry *)rt)->dev == dev) {
+ skb->dst = (struct dst_entry *)rt;
+ goto bridged_dnat;
+ }
+ __br_dnat_complain();
+ dst_release((struct dst_entry *)rt);
+ }
+ kfree_skb(skb);
+ return 0;
+ } else {
+ if (skb->dst->dev == dev) {
+bridged_dnat:
+ /* Tell br_nf_local_out this is a
+ * bridged frame
+ */
+ nf_bridge->mask |= BRNF_BRIDGED_DNAT;
+ skb->dev = nf_bridge->physindev;
+ clear_cb(skb);
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
+ skb, skb->dev, NULL,
+ br_nf_pre_routing_finish_bridge,
+ 1);
+ return 0;
+ }
+ memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
+ ETH_ALEN);
+ }
+ } else {
+ skb->dst = (struct dst_entry *)&__fake_rtable;
+ dst_hold(skb->dst);
+ }
+
+ clear_cb(skb);
+ skb->dev = nf_bridge->physindev;
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish, 1);
+
+ return 0;
+}
+
+/* Replicate the checks that IPv4 does on packet reception.
+ * Set skb->dev to the bridge device (i.e. parent of the
+ * receiving device) to make netfilter happy, the REDIRECT
+ * target in particular. Save the original destination IP
+ * address to be able to detect DNAT afterwards.
+ */
+static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct iphdr *iph;
+ __u32 len;
+ struct sk_buff *skb;
+ struct nf_bridge_info *nf_bridge;
+
+ if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+ goto out;
+
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (iph->ihl < 5 || iph->version != 4)
+ goto inhdr_error;
+
+ if (!pskb_may_pull(skb, 4*iph->ihl))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+ goto inhdr_error;
+
+ len = ntohs(iph->tot_len);
+ if (skb->len < len || len < 4*iph->ihl)
+ goto inhdr_error;
+
+ if (skb->len > len) {
+ __pskb_trim(skb, len);
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+#endif
+ if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+ return NF_DROP;
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ nf_bridge->physindev = skb->dev;
+ skb->dev = bridge_parent(skb->dev);
+ store_orig_dstaddr(skb);
+
+ NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+ br_nf_pre_routing_finish);
+
+ return NF_STOLEN;
+
+inhdr_error:
+// IP_INC_STATS_BH(IpInHdrErrors);
+out:
+ return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one. On the way up, the
+ * packet would pass through PRE_ROUTING again (which already
+ * took place when the packet entered the bridge), but we
+ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
+ * prevent this from happening.
+ */
+static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+
+ return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf_forward_finish(struct sk_buff *skb)
+{
+ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
+ skb->dev, br_forward_finish, 1);
+
+ return 0;
+}
+
+/* This is the 'purely bridged' case. We pass the packet to
+ * netfilter with indev and outdev set to the bridge device,
+ * but we are still able to filter on the 'real' indev/outdev
+ * because another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
+static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ nf_bridge = skb->nf_bridge;
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ nf_bridge->physoutdev = skb->dev;
+
+ NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
+ bridge_parent(skb->dev), br_nf_forward_finish);
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT ***********************************************/
+static int br_nf_local_out_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ br_forward_finish, NF_BR_PRI_FIRST + 1);
+
+ return 0;
+}
+
+
+/* This function sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device). It also sees bridged-and-DNAT'ed packets.
+ * For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
+ * functions, and give them back later, when we have determined the real
+ * output device. This is done in here.
+ *
+ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
+ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
+ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
+ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
+ * will be executed.
+ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never
touched
+ * this packet before, and so the packet was locally originated. We fake
+ * the PF_INET/LOCAL_OUT hook.
+ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP
routed,
+ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
+ * even routed packets that didn't arrive on a bridge interface have their
+ * nf_bridge->physindev set.
+ */
+
+static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*_okfn)(struct sk_buff *))
+{
+ int (*okfn)(struct sk_buff *skb);
+ struct net_device *realindev;
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+ nf_bridge = skb->nf_bridge;
+ nf_bridge->physoutdev = skb->dev;
+
+ realindev = nf_bridge->physindev;
+
+ /* Bridged, take PF_BRIDGE/FORWARD.
+ * (see big note in front of br_nf_pre_routing_finish)
+ */
+ if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
+ okfn = br_forward_finish;
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
+ skb->dev, okfn);
+ } else {
+ okfn = br_nf_local_out_finish;
+ /* IP forwarded traffic has a physindev, locally
+ * generated traffic hasn't.
+ */
+ if (realindev != NULL) {
+ if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
+ has_bridge_parent(realindev))
+ realindev = bridge_parent(realindev);
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
+ } else {
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
+ }
+ }
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
+
+ /* Be very paranoid. */
+ if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+ printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
+ "bad mac.raw pointer.");
+ if (skb->dev != NULL) {
+ printk("[%s]", skb->dev->name);
+ if (has_bridge_parent(skb->dev))
+ printk("[%s]", bridge_parent(skb->dev)->name);
+ }
+ printk("\n");
+ return NF_ACCEPT;
+ }
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
+#endif
+
+ /* We assume any code from br_dev_queue_push_xmit onwards doesn't care
+ * about the value of skb->pkt_type.
+ */
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ memcpy(nf_bridge->hh, skb->data - 16, 16);
+
+ NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+ bridge_parent(skb->dev), br_dev_queue_push_xmit);
+
+ return NF_STOLEN;
+}
+
+
+/* IPv4/SABOTAGE *****************************************************/
+
+/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
+ * for the second time.
+ */
+static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (in->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_pre_routing_finish) {
+ okfn(*pskb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
+ * and PF_INET/POST_ROUTING until we have done the forwarding
+ * decision in the bridge code and have determined skb->physoutdev.
+ */
+static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (out->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_forward_finish &&
+ okfn != br_nf_local_out_finish &&
+ okfn != br_dev_queue_push_xmit) {
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (!skb->nf_bridge && !nf_bridge_alloc(skb))
+ return NF_DROP;
+
+ nf_bridge = skb->nf_bridge;
+
+ /* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
+ * will need the indev then. For a brouter, the real indev
+ * can be a bridge port, so we make sure br_nf_local_out()
+ * doesn't use the bridge parent of the indev by using
+ * the BRNF_DONT_TAKE_PARENT mask.
+ */
+ if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
+ nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
+ nf_bridge->physindev = (struct net_device *)in;
+ }
+ okfn(skb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that
innocent
+ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
+ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
+ * ip_refrag() can return NF_STOLEN.
+ */
+static struct nf_hook_ops br_nf_ops[] = {
+ { { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING,
NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF
},
+ { { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT,
NF_BR_PRI_FIRST },
+ { { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING,
NF_BR_PRI_LAST },
+ { { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING,
NF_IP_PRI_FIRST },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD,
NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT,
NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING,
NF_IP_PRI_FIRST }
+};
+
+#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
+
+int br_netfilter_init(void)
+{
+ int i;
+
+ for (i = 0; i < NUMHOOKS; i++) {
+ int ret;
+
+ if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
+ continue;
+
+ while (i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+
+ return ret;
+ }
+
+ printk(KERN_NOTICE "Bridge firewalling registered\n");
+
+ return 0;
+}
+
+void br_netfilter_fini(void)
+{
+ int i;
+
+ for (i = NUMHOOKS - 1; i >= 0; i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+}
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44
2002-10-20 22:20 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44 Bart De Schuymer
@ 2002-10-20 22:19 ` David S. Miller
2002-10-22 23:40 ` Bart De Schuymer
1 sibling, 0 replies; 39+ messages in thread
From: David S. Miller @ 2002-10-20 22:19 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel, buytenh, coreteam
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Mon, 21 Oct 2002 00:20:37 +0200
This is a follow-up from the previous RFC for the bridge-nf patch.
The new patch adds one member to the skbuff, a pointer to a struct
nf_bridge_info. There is still a need to change ip_output.c, but the change
is the analogue as is done for the skbuff->nfct pointer field. So, for me
this is a clean solution. The copy of the Ethernet header is no longer done
in ip_fragment().
This definitely looks a lot better.
I still want the netfilter team to 'ACK' the core/ipv4 netfilter
changes before I apply this. :-)
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44
2002-10-20 22:20 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44 Bart De Schuymer
2002-10-20 22:19 ` David S. Miller
@ 2002-10-22 23:40 ` Bart De Schuymer
2002-10-25 6:01 ` [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, " Bart De Schuymer
1 sibling, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-22 23:40 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, Lennert Buytenhek, coreteam
Is coreteam@netfilter.org dead, or? Anyway, if you think the patch just
totally sucks, don't worry, I can take it. Just say so.
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, vs 2.5.44
2002-10-22 23:40 ` Bart De Schuymer
@ 2002-10-25 6:01 ` Bart De Schuymer
2002-10-25 6:22 ` [netfilter-core] " Harald Welte
2002-10-28 13:02 ` David S. Miller
0 siblings, 2 replies; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-25 6:01 UTC (permalink / raw)
To: David S. Miller; +Cc: linux-kernel, Lennert Buytenhek, coreteam
Hello David, Harald, others,
The following patch deals with the problems you still had with the earlier one.
Changes:
1. add #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) everywhere
2. don't touch ip_tables.c
3. no ipt_physdev.c file yet. I'll try to make it this weekend.
As this ipt_physdev.c module is not essential I propose to already apply this patch.
Harald, should I make this module for patch-o-magic or can I post it directly to David?
The patch is also here:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre3-against-2.5.44.diff
An incremental patch vs the previous one is here:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre3.001-against-2.5.44.diff
cheers,
Bart
--- linux-2.5.44/include/linux/netfilter.h Sat Oct 19 06:01:54 2002
+++ linux-2.5.44-brnfpre3/include/linux/netfilter.h Thu Oct 24 18:32:34 2002
@@ -117,17 +117,23 @@
/* This is gross, but inline doesn't cut it for avoiding the function
call in fast path: gcc doesn't inline (needs value tracking?). --RR */
#ifdef CONFIG_NETFILTER_DEBUG
-#define NF_HOOK nf_hook_slow
+#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
+ nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN)
+#define NF_HOOK_THRESH nf_hook_slow
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
(list_empty(&nf_hooks[(pf)][(hook)]) \
? (okfn)(skb) \
- : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), INT_MIN))
+#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh) \
+(list_empty(&nf_hooks[(pf)][(hook)]) \
+ ? (okfn)(skb) \
+ : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn), (thresh)))
#endif
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *));
+ int (*okfn)(struct sk_buff *), int thresh);
/* Call setsockopt() */
int nf_setsockopt(struct sock *sk, int pf, int optval, char *opt,
--- linux-2.5.44/include/linux/netfilter_ipv4.h Sat Oct 19 06:02:28 2002
+++ linux-2.5.44-brnfpre3/include/linux/netfilter_ipv4.h Thu Oct 24 18:32:34 2002
@@ -52,8 +52,10 @@
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK = -200,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD = -175,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_LAST = INT_MAX,
--- linux-2.5.44/include/linux/netfilter_bridge.h Sat Oct 19 06:01:57 2002
+++ linux-2.5.44-brnfpre3/include/linux/netfilter_bridge.h Thu Oct 24 18:32:34 2002
@@ -6,6 +6,7 @@
#include <linux/config.h>
#include <linux/netfilter.h>
+#include <asm/atomic.h>
/* Bridge Hooks */
/* After promisc drops, checksum checks. */
@@ -22,14 +23,39 @@
#define NF_BR_BROUTING 5
#define NF_BR_NUMHOOKS 6
+#define BRNF_PKT_TYPE 0x01
+#define BRNF_BRIDGED_DNAT 0x02
+#define BRNF_DONT_TAKE_PARENT 0x04
+
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
- NF_BR_PRI_FILTER_BRIDGED = -200,
- NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_FILTER_BRIDGED = -200,
+ NF_BR_PRI_BRNF = 0,
NF_BR_PRI_NAT_DST_OTHER = 100,
+ NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
+};
+
+static inline
+struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
+{
+ struct nf_bridge_info **nf_bridge = &(skb->nf_bridge);
+
+ if ((*nf_bridge = kmalloc(sizeof(**nf_bridge), GFP_ATOMIC)) != NULL) {
+ atomic_set(&(*nf_bridge)->use, 1);
+ (*nf_bridge)->mask = 0;
+ (*nf_bridge)->physindev = (*nf_bridge)->physoutdev = NULL;
+ }
+
+ return *nf_bridge;
+}
+
+struct bridge_skb_cb {
+ union {
+ __u32 ipv4;
+ } daddr;
};
#endif
--- linux-2.5.44/include/linux/skbuff.h Sat Oct 19 06:01:58 2002
+++ linux-2.5.44-brnfpre3/include/linux/skbuff.h Thu Oct 24 18:32:34 2002
@@ -96,6 +96,17 @@
struct nf_ct_info {
struct nf_conntrack *master;
};
+
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+struct nf_bridge_info {
+ atomic_t use;
+ struct net_device *physindev;
+ struct net_device *physoutdev;
+ unsigned int mask;
+ unsigned long hh[16 / sizeof(unsigned long)];
+};
+#endif
+
#endif
struct sk_buff_head {
@@ -166,6 +177,7 @@
* @nfcache: Cache info
* @nfct: Associated connection, if any
* @nf_debug: Netfilter debugging
+ * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @tc_index: Traffic control index
*/
@@ -236,6 +248,9 @@
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ struct nf_bridge_info *nf_bridge;
+#endif
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
@@ -1146,6 +1161,20 @@
if (nfct)
atomic_inc(&nfct->master->use);
}
+
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
+{
+ if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
+ kfree(nf_bridge);
+}
+static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge)
+{
+ if (nf_bridge)
+ atomic_inc(&nf_bridge->use);
+}
+#endif
+
#endif
#endif /* __KERNEL__ */
--- linux-2.5.44/net/bridge/br.c Sat Oct 19 06:01:15 2002
+++ linux-2.5.44-brnfpre3/net/bridge/br.c Thu Oct 24 18:32:34 2002
@@ -45,6 +45,10 @@
{
printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
+#ifdef CONFIG_NETFILTER
+ if (br_netfilter_init())
+ return 1;
+#endif
br_handle_frame_hook = br_handle_frame;
br_ioctl_hook = br_ioctl_deviceless_stub;
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
@@ -63,6 +67,9 @@
static void __exit br_deinit(void)
{
+#ifdef CONFIG_NETFILTER
+ br_netfilter_fini();
+#endif
unregister_netdevice_notifier(&br_device_notifier);
br_call_ioctl_atomic(__br_clear_ioctl_hook);
--- linux-2.5.44/net/bridge/br_forward.c Sat Oct 19 06:01:20 2002
+++ linux-2.5.44-brnfpre3/net/bridge/br_forward.c Thu Oct 24 18:32:34 2002
@@ -30,18 +30,23 @@
return 1;
}
-static int __dev_queue_push_xmit(struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct sk_buff *skb)
{
+#ifdef CONFIG_NETFILTER
+ if (skb->nf_bridge)
+ memcpy(skb->data - 16, skb->nf_bridge->hh, 16);
+#endif
skb_push(skb, ETH_HLEN);
+
dev_queue_xmit(skb);
return 0;
}
-static int __br_forward_finish(struct sk_buff *skb)
+int br_forward_finish(struct sk_buff *skb)
{
NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
- __dev_queue_push_xmit);
+ br_dev_queue_push_xmit);
return 0;
}
@@ -53,7 +58,7 @@
skb->nf_debug = 0;
#endif
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb)
@@ -64,7 +69,7 @@
skb->dev = to->dev;
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
- __br_forward_finish);
+ br_forward_finish);
}
/* called under bridge lock */
--- linux-2.5.44/net/bridge/br_input.c Sat Oct 19 06:01:18 2002
+++ linux-2.5.44-brnfpre3/net/bridge/br_input.c Thu Oct 24 18:32:34 2002
@@ -49,7 +49,7 @@
br_pass_frame_up_finish);
}
-static int br_handle_frame_finish(struct sk_buff *skb)
+int br_handle_frame_finish(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
--- linux-2.5.44/net/bridge/br_private.h Sat Oct 19 06:01:18 2002
+++ linux-2.5.44-brnfpre3/net/bridge/br_private.h Thu Oct 24 18:32:34 2002
@@ -144,8 +144,10 @@
/* br_forward.c */
extern void br_deliver(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_dev_queue_push_xmit(struct sk_buff *skb);
extern void br_forward(struct net_bridge_port *to,
struct sk_buff *skb);
+extern int br_forward_finish(struct sk_buff *skb);
extern void br_flood_deliver(struct net_bridge *br,
struct sk_buff *skb,
int clone);
@@ -166,6 +168,7 @@
int *ifindices);
/* br_input.c */
+extern int br_handle_frame_finish(struct sk_buff *skb);
extern int br_handle_frame(struct sk_buff *skb);
/* br_ioctl.c */
@@ -176,6 +179,10 @@
unsigned long arg1,
unsigned long arg2);
extern int br_ioctl_deviceless_stub(unsigned long arg);
+
+/* br_netfilter.c */
+extern int br_netfilter_init(void);
+extern void br_netfilter_fini(void);
/* br_stp.c */
extern int br_is_root_bridge(struct net_bridge *br);
--- linux-2.5.44/net/bridge/Makefile Sat Oct 19 06:02:32 2002
+++ linux-2.5.44-brnfpre3/net/bridge/Makefile Thu Oct 24 18:32:34 2002
@@ -9,6 +9,11 @@
bridge-objs := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
+
+ifeq ($(CONFIG_NETFILTER),y)
+bridge-objs += br_netfilter.o
+endif
+
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
include $(TOPDIR)/Rules.make
--- linux-2.5.44/net/core/netfilter.c Sat Oct 19 06:01:53 2002
+++ linux-2.5.44-brnfpre3/net/core/netfilter.c Thu Oct 24 18:32:34 2002
@@ -342,10 +342,15 @@
const struct net_device *indev,
const struct net_device *outdev,
struct list_head **i,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
for (*i = (*i)->next; *i != head; *i = (*i)->next) {
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
+
+ if (hook_thresh > elem->priority)
+ continue;
+
switch (elem->hook(hook, skb, indev, outdev, okfn)) {
case NF_QUEUE:
return NF_QUEUE;
@@ -413,6 +418,10 @@
{
int status;
struct nf_info *info;
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ struct net_device *physindev = NULL;
+ struct net_device *physoutdev = NULL;
+#endif
if (!queue_handler[pf].outfn) {
kfree_skb(skb);
@@ -435,11 +444,24 @@
if (indev) dev_hold(indev);
if (outdev) dev_hold(outdev);
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->nf_bridge) {
+ physindev = skb->nf_bridge->physindev;
+ if (physindev) dev_hold(physindev);
+ physoutdev = skb->nf_bridge->physoutdev;
+ if (physoutdev) dev_hold(physoutdev);
+ }
+#endif
+
status = queue_handler[pf].outfn(skb, info, queue_handler[pf].data);
if (status < 0) {
/* James M doesn't say fuck enough. */
if (indev) dev_put(indev);
if (outdev) dev_put(outdev);
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (physindev) dev_put(physindev);
+ if (physoutdev) dev_put(physoutdev);
+#endif
kfree(info);
kfree_skb(skb);
return;
@@ -449,7 +471,8 @@
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sk_buff *),
+ int hook_thresh)
{
struct list_head *elem;
unsigned int verdict;
@@ -481,7 +504,7 @@
elem = &nf_hooks[pf][hook];
verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
- outdev, &elem, okfn);
+ outdev, &elem, okfn, hook_thresh);
if (verdict == NF_QUEUE) {
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
nf_queue(skb, elem, pf, hook, indev, outdev, okfn);
@@ -530,7 +553,7 @@
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
&skb, info->hook,
info->indev, info->outdev, &elem,
- info->okfn);
+ info->okfn, INT_MIN);
}
switch (verdict) {
--- linux-2.5.44/net/core/skbuff.c Sat Oct 19 06:01:17 2002
+++ linux-2.5.44-brnfpre3/net/core/skbuff.c Thu Oct 24 18:32:34 2002
@@ -248,6 +248,9 @@
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
#endif
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ skb->nf_bridge = NULL;
+#endif
#endif
#ifdef CONFIG_NET_SCHED
skb->tc_index = 0;
@@ -327,6 +330,9 @@
}
#ifdef CONFIG_NETFILTER
nf_conntrack_put(skb->nfct);
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ nf_bridge_put(skb->nf_bridge);
+#endif
#endif
skb_headerinit(skb, NULL, 0); /* clean state */
kfree_skbmem(skb);
@@ -392,6 +398,9 @@
#ifdef CONFIG_NETFILTER_DEBUG
C(nf_debug);
#endif
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ C(nf_bridge);
+#endif
#endif /*CONFIG_NETFILTER*/
#if defined(CONFIG_HIPPI)
C(private);
@@ -404,6 +413,9 @@
skb->cloned = 1;
#ifdef CONFIG_NETFILTER
nf_conntrack_get(skb->nfct);
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ nf_bridge_get(skb->nf_bridge);
+#endif
#endif
return n;
}
@@ -437,6 +449,10 @@
nf_conntrack_get(new->nfct);
#ifdef CONFIG_NETFILTER_DEBUG
new->nf_debug = old->nf_debug;
+#endif
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ new->nf_bridge = old->nf_bridge;
+ nf_bridge_get(new->nf_bridge);
#endif
#endif
#ifdef CONFIG_NET_SCHED
--- linux-2.5.44/net/ipv4/ip_output.c Sat Oct 19 06:02:34 2002
+++ linux-2.5.44-brnfpre3/net/ipv4/ip_output.c Thu Oct 24 18:32:34 2002
@@ -396,6 +396,10 @@
/* Connection association is same as pre-frag packet */
to->nfct = from->nfct;
nf_conntrack_get(to->nfct);
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ to->nf_bridge = from->nf_bridge;
+ nf_bridge_get(to->nf_bridge);
+#endif
#ifdef CONFIG_NETFILTER_DEBUG
to->nf_debug = from->nf_debug;
#endif
--- linux-2.5.44/net/ipv4/netfilter/ipt_LOG.c Sat Oct 19 06:01:21 2002
+++ linux-2.5.44-brnfpre3/net/ipv4/netfilter/ipt_LOG.c Thu Oct 24 18:58:08 2002
@@ -289,6 +289,18 @@
loginfo->prefix,
in ? in->name : "",
out ? out->name : "");
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if ((*pskb)->nf_bridge) {
+ struct net_device *physindev = (*pskb)->nf_bridge->physindev;
+ struct net_device *physoutdev = (*pskb)->nf_bridge->physoutdev;
+
+ if (physindev && in != physindev)
+ printk("PHYSIN=%s ", physindev->name);
+ if (physoutdev && out != physoutdev)
+ printk("PHYSOUT=%s ", physoutdev->name);
+ }
+#endif
+
if (in && !out) {
/* MAC logging for input chain only. */
printk("MAC=");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.44-brnfpre3/net/bridge/br_netfilter.c Thu Oct 24 18:32:34 2002
@@ -0,0 +1,614 @@
+/*
+ * Handle firewalling
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/in_route.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include "br_private.h"
+
+
+#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
+ (skb->cb))->daddr.ipv4)
+#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
+#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define clear_cb(skb) (memset(&skb_origaddr(skb), 0, \
+ sizeof(struct bridge_skb_cb)))
+
+#define has_bridge_parent(device) ((device)->br_port != NULL)
+#define bridge_parent(device) (&((device)->br_port->br->dev))
+
+/* We need these fake structures to make netfilter happy --
+ * lots of places assume that skb->dst != NULL, which isn't
+ * all that unreasonable.
+ *
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it. Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static struct net_device __fake_net_device = {
+ hard_header_len: ETH_HLEN
+};
+
+static struct rtable __fake_rtable = {
+ u: {
+ dst: {
+ __refcnt: ATOMIC_INIT(1),
+ dev: &__fake_net_device,
+ pmtu: 1500
+ }
+ },
+
+ rt_flags: 0
+};
+
+
+/* PF_BRIDGE/PRE_ROUTING *********************************************/
+static void __br_dnat_complain(void)
+{
+ static unsigned long last_complaint = 0;
+
+ if (jiffies - last_complaint >= 5 * HZ) {
+ printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
+ "forwarding to be enabled\n");
+ last_complaint = jiffies;
+ }
+}
+
+
+/* This requires some explaining. If DNAT has taken place,
+ * we will need to fix up the destination Ethernet address,
+ * and this is a tricky process.
+ *
+ * There are two cases to consider:
+ * 1. The packet was DNAT'ed to a device in the same bridge
+ * port group as it was received on. We can still bridge
+ * the packet.
+ * 2. The packet was DNAT'ed to a different device, either
+ * a non-bridged device or another bridge port group.
+ * The packet will need to be routed.
+ *
+ * The correct way of distinguishing between these two cases is to
+ * call ip_route_input() and to look at skb->dst->dev, which is
+ * changed to the destination device if ip_route_input() succeeds.
+ *
+ * Let us first consider the case that ip_route_input() succeeds:
+ *
+ * If skb->dst->dev equals the logical bridge device the packet
+ * came in on, we can consider this bridging. We then call
+ * skb->dst->output() which will make the packet enter br_nf_local_out()
+ * not much later. In that function it is assured that the iptables
+ * FORWARD chain is traversed for the packet.
+ *
+ * Otherwise, the packet is considered to be routed and we just
+ * change the destination MAC address so that the packet will
+ * later be passed up to the IP stack to be routed.
+ *
+ * Let us now consider the case that ip_route_input() fails:
+ *
+ * After a "echo '0' > /proc/sys/net/ipv4/ip_forward" ip_route_input()
+ * will fail, while ip_route_output() will return success. The source
+ * address for ip_route_output() is set to zero, so ip_route_output()
+ * thinks we're handling a locally generated packet and won't care
+ * if IP forwarding is allowed. We send a warning message to the users's
+ * log telling her to put IP forwarding on.
+ *
+ * ip_route_input() will also fail if there is no route available.
+ * In that case we just drop the packet.
+ *
+ * --Lennert, 20020411
+ * --Bart, 20020416 (updated)
+ * --Bart, 20021007 (updated)
+ */
+
+static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug |= (1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_FORWARD);
+#endif
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ skb->nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ skb->dev = bridge_parent(skb->dev);
+ skb->dst->output(skb);
+ return 0;
+}
+
+static int br_nf_pre_routing_finish(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct iphdr *iph = skb->nh.iph;
+ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING);
+#endif
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ if (dnat_took_place(skb)) {
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
+ dev)) {
+ struct rtable *rt;
+
+ if (!ip_route_output(&rt, iph->daddr, 0, iph->tos, 0)) {
+ /* Bridged-and-DNAT'ed traffic doesn't
+ * require ip_forwarding.
+ */
+ if (((struct dst_entry *)rt)->dev == dev) {
+ skb->dst = (struct dst_entry *)rt;
+ goto bridged_dnat;
+ }
+ __br_dnat_complain();
+ dst_release((struct dst_entry *)rt);
+ }
+ kfree_skb(skb);
+ return 0;
+ } else {
+ if (skb->dst->dev == dev) {
+bridged_dnat:
+ /* Tell br_nf_local_out this is a
+ * bridged frame
+ */
+ nf_bridge->mask |= BRNF_BRIDGED_DNAT;
+ skb->dev = nf_bridge->physindev;
+ clear_cb(skb);
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
+ skb, skb->dev, NULL,
+ br_nf_pre_routing_finish_bridge,
+ 1);
+ return 0;
+ }
+ memcpy(skb->mac.ethernet->h_dest, dev->dev_addr,
+ ETH_ALEN);
+ }
+ } else {
+ skb->dst = (struct dst_entry *)&__fake_rtable;
+ dst_hold(skb->dst);
+ }
+
+ clear_cb(skb);
+ skb->dev = nf_bridge->physindev;
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish, 1);
+
+ return 0;
+}
+
+/* Replicate the checks that IPv4 does on packet reception.
+ * Set skb->dev to the bridge device (i.e. parent of the
+ * receiving device) to make netfilter happy, the REDIRECT
+ * target in particular. Save the original destination IP
+ * address to be able to detect DNAT afterwards.
+ */
+static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct iphdr *iph;
+ __u32 len;
+ struct sk_buff *skb;
+ struct nf_bridge_info *nf_bridge;
+
+ if ((*pskb)->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
+ goto out;
+
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (iph->ihl < 5 || iph->version != 4)
+ goto inhdr_error;
+
+ if (!pskb_may_pull(skb, 4*iph->ihl))
+ goto inhdr_error;
+
+ iph = skb->nh.iph;
+ if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+ goto inhdr_error;
+
+ len = ntohs(iph->tot_len);
+ if (skb->len < len || len < 4*iph->ihl)
+ goto inhdr_error;
+
+ if (skb->len > len) {
+ __pskb_trim(skb, len);
+ if (skb->ip_summed == CHECKSUM_HW)
+ skb->ip_summed = CHECKSUM_NONE;
+ }
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING);
+#endif
+ if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+ return NF_DROP;
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ nf_bridge->physindev = skb->dev;
+ skb->dev = bridge_parent(skb->dev);
+ store_orig_dstaddr(skb);
+
+ NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+ br_nf_pre_routing_finish);
+
+ return NF_STOLEN;
+
+inhdr_error:
+// IP_INC_STATS_BH(IpInHdrErrors);
+out:
+ return NF_DROP;
+}
+
+
+/* PF_BRIDGE/LOCAL_IN ************************************************/
+/* The packet is locally destined, which requires a real
+ * dst_entry, so detach the fake one. On the way up, the
+ * packet would pass through PRE_ROUTING again (which already
+ * took place when the packet entered the bridge), but we
+ * register an IPv4 PRE_ROUTING 'sabotage' hook that will
+ * prevent this from happening.
+ */
+static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ if (skb->dst == (struct dst_entry *)&__fake_rtable) {
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ }
+
+ return NF_ACCEPT;
+}
+
+
+/* PF_BRIDGE/FORWARD *************************************************/
+static int br_nf_forward_finish(struct sk_buff *skb)
+{
+ struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, nf_bridge->physindev,
+ skb->dev, br_forward_finish, 1);
+
+ return 0;
+}
+
+/* This is the 'purely bridged' case. We pass the packet to
+ * netfilter with indev and outdev set to the bridge device,
+ * but we are still able to filter on the 'real' indev/outdev
+ * because another bit of the bridge-nf patch overloads the
+ * '-i' and '-o' iptables interface checks to take
+ * skb->phys{in,out}dev into account as well (so both the real
+ * device and the bridge device will match).
+ */
+static unsigned int br_nf_forward(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_BR_FORWARD);
+#endif
+
+ nf_bridge = skb->nf_bridge;
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ nf_bridge->physoutdev = skb->dev;
+
+ NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(nf_bridge->physindev),
+ bridge_parent(skb->dev), br_nf_forward_finish);
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/LOCAL_OUT ***********************************************/
+static int br_nf_local_out_finish(struct sk_buff *skb)
+{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug &= ~(1 << NF_BR_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ br_forward_finish, NF_BR_PRI_FIRST + 1);
+
+ return 0;
+}
+
+
+/* This function sees both locally originated IP packets and forwarded
+ * IP packets (in both cases the destination device is a bridge
+ * device). It also sees bridged-and-DNAT'ed packets.
+ * For the sake of interface transparency (i.e. properly
+ * overloading the '-o' option), we steal packets destined to
+ * a bridge device away from the PF_INET/FORWARD and PF_INET/OUTPUT hook
+ * functions, and give them back later, when we have determined the real
+ * output device. This is done in here.
+ *
+ * If (nf_bridge->mask & BRNF_BRIDGED_DNAT) then the packet is bridged
+ * and we fake the PF_BRIDGE/FORWARD hook. The function br_nf_forward()
+ * will then fake the PF_INET/FORWARD hook. br_nf_local_out() has priority
+ * NF_BR_PRI_FIRST, so no relevant PF_BRIDGE/INPUT functions have been nor
+ * will be executed.
+ * Otherwise, if nf_bridge->physindev is NULL, the bridge-nf code never touched
+ * this packet before, and so the packet was locally originated. We fake
+ * the PF_INET/LOCAL_OUT hook.
+ * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed,
+ * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure
+ * even routed packets that didn't arrive on a bridge interface have their
+ * nf_bridge->physindev set.
+ */
+
+static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*_okfn)(struct sk_buff *))
+{
+ int (*okfn)(struct sk_buff *skb);
+ struct net_device *realindev;
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+ nf_bridge = skb->nf_bridge;
+ nf_bridge->physoutdev = skb->dev;
+
+ realindev = nf_bridge->physindev;
+
+ /* Bridged, take PF_BRIDGE/FORWARD.
+ * (see big note in front of br_nf_pre_routing_finish)
+ */
+ if (nf_bridge->mask & BRNF_BRIDGED_DNAT) {
+ okfn = br_forward_finish;
+
+ if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ skb->pkt_type = PACKET_OTHERHOST;
+ nf_bridge->mask ^= BRNF_PKT_TYPE;
+ }
+
+ NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev,
+ skb->dev, okfn);
+ } else {
+ okfn = br_nf_local_out_finish;
+ /* IP forwarded traffic has a physindev, locally
+ * generated traffic hasn't.
+ */
+ if (realindev != NULL) {
+ if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) &&
+ has_bridge_parent(realindev))
+ realindev = bridge_parent(realindev);
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1);
+ } else {
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT);
+#endif
+
+ NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev,
+ bridge_parent(skb->dev), okfn,
+ NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1);
+ }
+ }
+
+ return NF_STOLEN;
+}
+
+
+/* PF_BRIDGE/POST_ROUTING ********************************************/
+static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
+
+ /* Be very paranoid. */
+ if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+ printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
+ "bad mac.raw pointer.");
+ if (skb->dev != NULL) {
+ printk("[%s]", skb->dev->name);
+ if (has_bridge_parent(skb->dev))
+ printk("[%s]", bridge_parent(skb->dev)->name);
+ }
+ printk("\n");
+ return NF_ACCEPT;
+ }
+
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return NF_ACCEPT;
+
+ /* Sometimes we get packets with NULL ->dst here (for example,
+ * running a dhcp client daemon triggers this).
+ */
+ if (skb->dst == NULL)
+ return NF_ACCEPT;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug ^= (1 << NF_IP_POST_ROUTING);
+#endif
+
+ /* We assume any code from br_dev_queue_push_xmit onwards doesn't care
+ * about the value of skb->pkt_type.
+ */
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ skb->pkt_type = PACKET_HOST;
+ nf_bridge->mask |= BRNF_PKT_TYPE;
+ }
+
+ memcpy(nf_bridge->hh, skb->data - 16, 16);
+
+ NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL,
+ bridge_parent(skb->dev), br_dev_queue_push_xmit);
+
+ return NF_STOLEN;
+}
+
+
+/* IPv4/SABOTAGE *****************************************************/
+
+/* Don't hand locally destined packets to PF_INET/PRE_ROUTING
+ * for the second time.
+ */
+static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (in->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_pre_routing_finish) {
+ okfn(*pskb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT
+ * and PF_INET/POST_ROUTING until we have done the forwarding
+ * decision in the bridge code and have determined skb->physoutdev.
+ */
+static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ if (out->hard_start_xmit == br_dev_xmit &&
+ okfn != br_nf_forward_finish &&
+ okfn != br_nf_local_out_finish &&
+ okfn != br_dev_queue_push_xmit) {
+ struct sk_buff *skb = *pskb;
+ struct nf_bridge_info *nf_bridge;
+
+ if (!skb->nf_bridge && !nf_bridge_alloc(skb))
+ return NF_DROP;
+
+ nf_bridge = skb->nf_bridge;
+
+ /* This frame will arrive on PF_BRIDGE/LOCAL_OUT and we
+ * will need the indev then. For a brouter, the real indev
+ * can be a bridge port, so we make sure br_nf_local_out()
+ * doesn't use the bridge parent of the indev by using
+ * the BRNF_DONT_TAKE_PARENT mask.
+ */
+ if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) {
+ nf_bridge->mask &= BRNF_DONT_TAKE_PARENT;
+ nf_bridge->physindev = (struct net_device *)in;
+ }
+ okfn(skb);
+ return NF_STOLEN;
+ }
+
+ return NF_ACCEPT;
+}
+
+/* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent
+ * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input.
+ * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
+ * ip_refrag() can return NF_STOLEN.
+ */
+static struct nf_hook_ops br_nf_ops[] = {
+ { { NULL, NULL }, br_nf_pre_routing, PF_BRIDGE, NF_BR_PRE_ROUTING, NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_in, PF_BRIDGE, NF_BR_LOCAL_IN, NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_forward, PF_BRIDGE, NF_BR_FORWARD, NF_BR_PRI_BRNF },
+ { { NULL, NULL }, br_nf_local_out, PF_BRIDGE, NF_BR_LOCAL_OUT, NF_BR_PRI_FIRST },
+ { { NULL, NULL }, br_nf_post_routing, PF_BRIDGE, NF_BR_POST_ROUTING, NF_BR_PRI_LAST },
+ { { NULL, NULL }, ipv4_sabotage_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FIRST },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_FORWARD, NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT },
+ { { NULL, NULL }, ipv4_sabotage_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FIRST }
+};
+
+#define NUMHOOKS (sizeof(br_nf_ops)/sizeof(br_nf_ops[0]))
+
+int br_netfilter_init(void)
+{
+ int i;
+
+ for (i = 0; i < NUMHOOKS; i++) {
+ int ret;
+
+ if ((ret = nf_register_hook(&br_nf_ops[i])) >= 0)
+ continue;
+
+ while (i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+
+ return ret;
+ }
+
+ printk(KERN_NOTICE "Bridge firewalling registered\n");
+
+ return 0;
+}
+
+void br_netfilter_fini(void)
+{
+ int i;
+
+ for (i = NUMHOOKS - 1; i >= 0; i--)
+ nf_unregister_hook(&br_nf_ops[i]);
+}
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [netfilter-core] [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, vs 2.5.44
2002-10-25 6:01 ` [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, " Bart De Schuymer
@ 2002-10-25 6:22 ` Harald Welte
2002-10-28 13:02 ` David S. Miller
1 sibling, 0 replies; 39+ messages in thread
From: Harald Welte @ 2002-10-25 6:22 UTC (permalink / raw)
To: Bart De Schuymer
Cc: David S. Miller, linux-kernel, Lennert Buytenhek, coreteam,
Netfilter Development Mailinglist
[-- Attachment #1: Type: text/plain, Size: 721 bytes --]
On Fri, Oct 25, 2002 at 08:01:16AM +0200, Bart De Schuymer wrote:
> As this ipt_physdev.c module is not essential I propose to already apply this
> patch.
I'm fine with this patch
> Harald, should I make this module for patch-o-magic or can I post it directly
> to David?
Please send the patch to me for review. I will pass it on very quickly once
I have received it.
> cheers,
> Bart
--
Live long and prosper
- Harald Welte / laforge@gnumonks.org http://www.gnumonks.org/
============================================================================
"If this were a dictatorship, it'd be a heck of a lot easier, just so long
as I'm the dictator." -- George W. Bush Dec 18, 2000
[-- Attachment #2: Type: application/pgp-signature, Size: 232 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, vs 2.5.44
2002-10-25 6:01 ` [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, " Bart De Schuymer
2002-10-25 6:22 ` [netfilter-core] " Harald Welte
@ 2002-10-28 13:02 ` David S. Miller
1 sibling, 0 replies; 39+ messages in thread
From: David S. Miller @ 2002-10-28 13:02 UTC (permalink / raw)
To: bart.de.schuymer; +Cc: linux-kernel, buytenh, coreteam
From: Bart De Schuymer <bart.de.schuymer@pandora.be>
Date: Fri, 25 Oct 2002 08:01:16 +0200
The following patch deals with the problems you still had with the earlier one.
Changes:
1. add #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) everywhere
2. don't touch ip_tables.c
3. no ipt_physdev.c file yet. I'll try to make it this weekend.
I've applied this to my tree, thanks.
^ permalink raw reply [flat|nested] 39+ messages in thread
[parent not found: <200210141953.38933.bart.de.schuymer@pandora.be>]
* [RFC] place to put bridge-netfilter specific data in the skbuff
[not found] ` <200210141953.38933.bart.de.schuymer@pandora.be>
@ 2002-10-14 19:59 ` Bart De Schuymer
2002-10-24 8:16 ` [netfilter-core] " Harald Welte
0 siblings, 1 reply; 39+ messages in thread
From: Bart De Schuymer @ 2002-10-14 19:59 UTC (permalink / raw)
To: coreteam; +Cc: linux-kernel, Lennert Buytenhek, David S. Miller
Hello netfilter team and others,
DaveM suggested I talk to you (netfilter team) about this.
What's bridge-netfilter: the mapping of the IPv4 onto the bridge hooks, to
make a powerful bridging firewall.
The problem: in the current br-nf patch we add 3 fields to the skbuff, which
is (as expected) not acceptable. So we need to find a way to solve this.
We cannot use the control buffer to save this data because tcp uses it while
we still need the brnf data.
The solution I like best (and David seems to not mind) is adding one pointer
to a struct nf_bridge_info in the skbuff. So, adding one new member.
Another suggestion by David is this:
struct nf_ct_info {
union {
struct nf_conntrack *master;
struct nf_bridge_info *brinfo;
} u;
};
But I don't think this will not work because master will be in use while we
need brinfo.
So another solution could be this:
struct nf_ct_info {
struct nf_conntrack *master;
struct nf_bridge_info *brinfo;
};
But I don't know anything about the intricacies of adding this.
Do you have any other suggestions? Comments? Help?
The current patch was already posted on lkml so I won't repeat it.
Also, could you have a look at the current patch, to spot any other
obstacles/things you don't like?
The patch is available at:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff
There is a little text file explaining the source code more in-depth here:
http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre1-against-2.5.39-comments.txt
A high-level explanation of what we're doing is here:
http://users.pandora.be/bart.de.schuymer/ebtables/br_fw_ia/br_fw_ia.html
Another question:
I've been told it is the general concensus that this bridge firewall should be
compiled in the kernel if CONFIG_NETFILTER=y. Or should it be a user option?
It is predicted that using a user option will give alot of questions about
the bridge firewall not working.
Do you have any strong opinion about this?
--
cheers,
Bart
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [netfilter-core] [RFC] place to put bridge-netfilter specific data in the skbuff
2002-10-14 19:59 ` [RFC] place to put bridge-netfilter specific data in the skbuff Bart De Schuymer
@ 2002-10-24 8:16 ` Harald Welte
2002-10-24 8:15 ` David S. Miller
0 siblings, 1 reply; 39+ messages in thread
From: Harald Welte @ 2002-10-24 8:16 UTC (permalink / raw)
To: Bart De Schuymer
Cc: coreteam, linux-kernel, Lennert Buytenhek, David S. Miller
[-- Attachment #1: Type: text/plain, Size: 3082 bytes --]
On Mon, Oct 14, 2002 at 09:59:49PM +0200, Bart De Schuymer wrote:
> Hello netfilter team and others,
Hi Bart.
> DaveM suggested I talk to you (netfilter team) about this.
Thanks, unfortunately there was this huge delay before your email was answered. Sorry for this inconvenience.
> What's bridge-netfilter: the mapping of the IPv4 onto the bridge hooks, to
> make a powerful bridging firewall.
Yes, I am aware of this project. In fact, Lennert did a presentation about
this on the netfilter developer workshop in Nov 2001
> The solution I like best (and David seems to not mind) is adding one pointer
> to a struct nf_bridge_info in the skbuff. So, adding one new member.
That would be acceptable.
> Another suggestion by David is this:
>
> struct nf_ct_info {
> union {
> struct nf_conntrack *master;
> struct nf_bridge_info *brinfo;
> } u;
> };
>
> But I don't think this will not work because master will be in use while we
> need brinfo.
no, it will not work. It clashes as soon as you want to use connection
tracking on a bridging firewall.
> So another solution could be this:
>
> struct nf_ct_info {
> struct nf_conntrack *master;
> struct nf_bridge_info *brinfo;
> };
This is, of course, possible.
> But I don't know anything about the intricacies of adding this.
I don't see any big problems. If master stays the first member in struct
nf_ct_info, it should even work if there is a mis-use somewhere in the code,
referencing directly to nf_ct_info instead of nf_ct_info->master.
> Do you have any other suggestions? Comments? Help?
It's great seeing that the bridging stuff finally gets included.
> Also, could you have a look at the current patch, to spot any other
> obstacles/things you don't like?
> The patch is available at:
> http://users.pandora.be/bart.de.schuymer/ebtables/br-nf/bridge-nf-0.0.10-dev-pre1-against-2.5.42.diff
I have read the new (2.5.44) patch.
The only issue that comes to my mind is:
ip_packet_match is getting a way too long argument list. Can't you write the
matching against physical in/out devices as iptables match extension? (like
ipt_physdev.c?)
> Another question:
> I've been told it is the general concensus that this bridge firewall should
> be compiled in the kernel if CONFIG_NETFILTER=y. Or should it be a user
> option?
> It is predicted that using a user option will give alot of questions about
> the bridge firewall not working.
> Do you have any strong opinion about this?
Mh. Since bridging firewall is cool, but not something everybody will
use by default [and it adds code as well as enlarges the skb], I think it
should be a compiletime kernel config option.
> cheers,
> Bart
--
Live long and prosper
- Harald Welte / laforge@gnumonks.org http://www.gnumonks.org/
============================================================================
"If this were a dictatorship, it'd be a heck of a lot easier, just so long
as I'm the dictator." -- George W. Bush Dec 18, 2000
[-- Attachment #2: Type: application/pgp-signature, Size: 232 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [netfilter-core] [RFC] place to put bridge-netfilter specific data in the skbuff
2002-10-24 8:16 ` [netfilter-core] " Harald Welte
@ 2002-10-24 8:15 ` David S. Miller
2002-10-24 12:22 ` Harald Welte
0 siblings, 1 reply; 39+ messages in thread
From: David S. Miller @ 2002-10-24 8:15 UTC (permalink / raw)
To: laforge; +Cc: bart.de.schuymer, coreteam, linux-kernel, buytenh
From: Harald Welte <laforge@gnumonks.org>
Date: Thu, 24 Oct 2002 10:16:56 +0200
Mh. Since bridging firewall is cool, but not something everybody will
use by default [and it adds code as well as enlarges the skb], I think it
should be a compiletime kernel config option.
This was my initial reaction, but both of us misunderstand what
is going on I think.
If you use bridging, using netfilter on the bridged traffic "is not
possible" without these bridge-netfilter changes.
So he's saying, if we have bridging enable and netfilter, should
bridge-netfilter be on, and right now I say yes.
Bart, correct me if I'm wrong.
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [netfilter-core] [RFC] place to put bridge-netfilter specific data in the skbuff
2002-10-24 8:15 ` David S. Miller
@ 2002-10-24 12:22 ` Harald Welte
0 siblings, 0 replies; 39+ messages in thread
From: Harald Welte @ 2002-10-24 12:22 UTC (permalink / raw)
To: David S. Miller; +Cc: bart.de.schuymer, coreteam, linux-kernel, buytenh
[-- Attachment #1: Type: text/plain, Size: 1107 bytes --]
On Thu, Oct 24, 2002 at 01:15:12AM -0700, David Miller wrote:
> From: Harald Welte <laforge@gnumonks.org>
> Date: Thu, 24 Oct 2002 10:16:56 +0200
>
> Mh. Since bridging firewall is cool, but not something everybody will
> use by default [and it adds code as well as enlarges the skb], I think it
> should be a compiletime kernel config option.
>
> This was my initial reaction, but both of us misunderstand what
> is going on I think.
Ok ;)
> If you use bridging, using netfilter on the bridged traffic "is not
> possible" without these bridge-netfilter changes.
>
> So he's saying, if we have bridging enable and netfilter, should
> bridge-netfilter be on, and right now I say yes.
Yes, this does perfectly make sense. This is the way to go.
--
Live long and prosper
- Harald Welte / laforge@gnumonks.org http://www.gnumonks.org/
============================================================================
"If this were a dictatorship, it'd be a heck of a lot easier, just so long
as I'm the dictator." -- George W. Bush Dec 18, 2000
[-- Attachment #2: Type: application/pgp-signature, Size: 232 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: 802.1q + device removal causing hang
2002-09-11 22:31 ` David S. Miller
2002-09-12 6:36 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
@ 2002-09-12 23:49 ` Simon Kirby
2002-09-12 23:53 ` David S. Miller
1 sibling, 1 reply; 39+ messages in thread
From: Simon Kirby @ 2002-09-12 23:49 UTC (permalink / raw)
To: David S. Miller; +Cc: greearb, linux-kernel
On Wed, Sep 11, 2002 at 03:31:32PM -0700, David S. Miller wrote:
> Try this:
>
> --- net/8021q/vlan.c.~1~ Wed Sep 11 15:34:49 2002
> +++ net/8021q/vlan.c Wed Sep 11 15:34:59 2002
> @@ -626,7 +626,7 @@
> ret = unregister_vlan_dev(dev,
> VLAN_DEV_INFO(vlandev)->vlan_id);
>
> - unregister_netdev(vlandev);
> + unregister_netdevice(vlandev);
>
> /* Group was destroyed? */
> if (ret == 1)
Woops, sorry about the erik.ca domain.
Yup, this fixed it!
Simon-
[ Stormix Technologies Inc. ][ NetNation Communications Inc. ]
[ sim@stormix.com ][ sim@netnation.com ]
[ Opinions expressed are not necessarily those of my employers. ]
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH] ebtables - Ethernet bridge tables, for 2.5.34
@ 2002-09-12 17:07 Bart De Schuymer
0 siblings, 0 replies; 39+ messages in thread
From: Bart De Schuymer @ 2002-09-12 17:07 UTC (permalink / raw)
To: netdev, linux-net
Hello list,
I sent this message to lkml too, but without the actual patch. I hope I'm
allowed to send the full patch here. It's quite large.
Ebtables is a project similar to iptables, but working on the bridge netfilter
hooks. It allows for a basic transparent firewall, making a brouter and doing
MAC source address and destination address manipulation. The firewall part
has currently modules for basic IP filtering, 802.1q filtering, ARP
filtering, logging and a mark match/target.
Ebtables has been under development for over 1.5 year and has more than 100
users, I think.
You can also download the patch here:
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.34.diff
http://users.pandora.be/bart.de.schuymer/ebtables/v2.0/ebtables-v2.0_vs_2.5.34.diff.gz
For more information, see
http://users.pandora.be/bart.de.schuymer/ebtables/
There is an ebtables hacking howto, some basic examples and some real life
examples from users. And ofcourse the userspace program.
This is the first time I'm submitting a large patch, so feel free to rant if
I'm doing something wrong... Here it goes:
ebtables-v2.0 - 11 September 2002
Bart De Schuymer
****** Add ebtables project info
--- linux-2.5.34/MAINTAINERS Mon Sep 9 19:35:13 2002
+++ linux-2.5.34-ebtables/MAINTAINERS Wed Sep 11 23:06:55 2002
@@ -531,6 +531,14 @@ M: mike@i-Connect.Net
L: linux-eata@i-connect.net, linux-scsi@vger.kernel.org
S: Maintained
+EBTABLES
+P: Bart De Schuymer
+M: bart.de.schuymer@pandora.be
+L: ebtables-user@lists.sourceforge.net
+L: ebtables-devel@lists.sourceforge.net
+W: http://ebtables.sourceforge.net/
+S: Maintained
+
EEPRO100 NETWORK DRIVER
P: Andrey V. Savochkin
M: saw@saw.sw.com.sg
****** Clear nf_debug, we will be dealing with different netfilter hooks:
the bridge hooks.
--- linux-2.5.34/net/bridge/br_forward.c Mon Sep 9 19:35:07 2002
+++ linux-2.5.34-ebtables/net/bridge/br_forward.c Wed Sep 11 23:06:55 2002
@@ -49,6 +49,9 @@ static int __br_forward_finish(struct sk
static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb)
{
skb->dev = to->dev;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
__br_forward_finish);
}
****** modification for brouter support
--- linux-2.5.34/net/bridge/br_private.h Mon Sep 9 19:35:05 2002
+++ linux-2.5.34-ebtables/net/bridge/br_private.h Wed Sep 11 23:06:55 2002
@@ -166,7 +166,7 @@ extern void br_get_port_ifindices(struct
int *ifindices);
/* br_input.c */
-extern void br_handle_frame(struct sk_buff *skb);
+extern int br_handle_frame(struct sk_buff *skb);
/* br_ioctl.c */
extern void br_call_ioctl_atomic(void (*fn)(void));
****** modification for brouter support
--- linux-2.5.34/include/linux/if_bridge.h Mon Sep 9 19:35:11 2002
+++ linux-2.5.34-ebtables/include/linux/if_bridge.h Wed Sep 11 23:06:55 2002
@@ -102,8 +102,13 @@ struct net_bridge;
struct net_bridge_port;
extern int (*br_ioctl_hook)(unsigned long arg);
-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
-
+extern int (*br_handle_frame_hook)(struct sk_buff *skb);
+#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff
**pskb,
+ const struct net_device *in, const struct net_device *out,
+ int (*okfn)(struct sk_buff *));
+#endif
#endif
#endif
****** modification for brouter support
br_handle_frame_hook return non-zero if the frame has to be routed,
else the frame has been bridged.
--- linux-2.5.34/net/core/dev.c Mon Sep 9 19:35:12 2002
+++ linux-2.5.34-ebtables/net/core/dev.c Wed Sep 11 23:06:55 2002
@@ -1395,7 +1395,7 @@ void net_call_rx_atomic(void (*fn)(void)
}
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
#endif
static __inline__ int handle_bridge(struct sk_buff *skb,
@@ -1412,7 +1412,6 @@ static __inline__ int handle_bridge(stru
}
}
- br_handle_frame_hook(skb);
return ret;
}
@@ -1473,7 +1472,11 @@ int netif_receive_skb(struct sk_buff *sk
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if (skb->dev->br_port && br_handle_frame_hook) {
- return handle_bridge(skb, pt_prev);
+ int ret;
+
+ ret = handle_bridge(skb, pt_prev);
+ if (br_handle_frame_hook(skb) == 0)
+ return ret;
}
#endif
****** modification for brouter support + clear nf_debug
br_handle_frame_hook calls broute_decision(), which traverses
the ebtables brouting table to get the decision to bridge or route
the frame. Return value -1 means route, 0 means the frame has been
bridged.
--- linux-2.5.34/net/bridge/br_input.c Mon Sep 9 19:35:06 2002
+++ linux-2.5.34-ebtables/net/bridge/br_input.c Wed Sep 11 23:06:55 2002
@@ -19,11 +19,18 @@
#include <linux/if_bridge.h>
#include <linux/netfilter_bridge.h>
#include "br_private.h"
+#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+#include <linux/netfilter.h>
+#endif
unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static int br_pass_frame_up_finish(struct sk_buff *skb)
{
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
netif_rx(skb);
return 0;
@@ -112,7 +119,7 @@ err_nolock:
return 0;
}
-void br_handle_frame(struct sk_buff *skb)
+int br_handle_frame(struct sk_buff *skb)
{
struct net_bridge *br;
unsigned char *dest;
@@ -146,25 +153,32 @@ void br_handle_frame(struct sk_buff *skb
goto handle_special_frame;
if (p->state == BR_STATE_FORWARDING) {
+#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
+ skb->dev, NULL, NULL) == NF_DROP)
+ return -1;
+#endif
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
read_unlock(&br->lock);
- return;
+ return 0;
}
err:
read_unlock(&br->lock);
err_nolock:
kfree_skb(skb);
- return;
+ return 0;
handle_special_frame:
if (!dest[5]) {
br_stp_handle_bpdu(skb);
read_unlock(&br->lock);
- return;
+ return 0;
}
kfree_skb(skb);
read_unlock(&br->lock);
+ return 0;
}
****** broute_decision is defined here.
--- linux-2.5.34/net/bridge/br.c Mon Sep 9 19:35:04 2002
+++ linux-2.5.34-ebtables/net/bridge/br.c Wed Sep 11 23:06:55 2002
@@ -28,6 +28,14 @@
#include "../atm/lec.h"
#endif
+#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *)) = NULL;
+#endif
+
void br_dec_use_count()
{
MOD_DEC_USE_COUNT;
@@ -73,6 +81,11 @@ static void __exit br_deinit(void)
br_fdb_put_hook = NULL;
#endif
}
+
+#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+EXPORT_SYMBOL(broute_decision);
+#endif
module_init(br_init)
module_exit(br_deinit)
****** br.o may have to export broute_decision.
Add the bridge/netfilter directory.
--- linux-2.5.34/net/bridge/Makefile Mon Sep 9 19:35:21 2002
+++ linux-2.5.34-ebtables/net/bridge/Makefile Wed Sep 11 23:06:55 2002
@@ -2,10 +2,17 @@
# Makefile for the IEEE 802.1d ethernet bridging layer.
#
+ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
+ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
+export-objs := br.o
+endif
+endif
+
obj-$(CONFIG_BRIDGE) += bridge.o
bridge-objs := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
+obj-$(CONFIG_BRIDGE_EBT) += netfilter/
include $(TOPDIR)/Rules.make
****** Add the NF_BR_BROUTING "hook"
--- linux-2.5.34/include/linux/netfilter_bridge.h Mon Sep 9 19:35:12 2002
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge.h Wed Sep 11 23:06:55
2002
@@ -18,7 +18,18 @@
#define NF_BR_LOCAL_OUT 3
/* Packets about to hit the wire. */
#define NF_BR_POST_ROUTING 4
-#define NF_BR_NUMHOOKS 5
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING 5
+#define NF_BR_NUMHOOKS 6
+enum nf_br_hook_priorities {
+ NF_BR_PRI_FIRST = INT_MIN,
+ NF_BR_PRI_FILTER_BRIDGED = -200,
+ NF_BR_PRI_FILTER_OTHER = 200,
+ NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_NAT_DST_OTHER = 100,
+ NF_BR_PRI_NAT_SRC = 300,
+ NF_BR_PRI_LAST = INT_MAX,
+};
#endif
****** Include the ebtables Config.in
--- linux-2.5.34/net/Config.in Mon Sep 9 19:35:05 2002
+++ linux-2.5.34-ebtables/net/Config.in Wed Sep 11 23:06:55 2002
@@ -65,6 +65,9 @@ if [ "$CONFIG_DECNET" != "n" ]; then
source net/decnet/Config.in
fi
dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
+if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ source net/bridge/netfilter/Config.in
+fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
****** Now follow all the new files, without much comment.
ebtables is modular like iptables, currently there are 3 tables:
broute, nat and filter. There are some mark modules and target
modules too. Also one watcher module, the log module.
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/Makefile Wed Sep 11 23:06:55
2002
@@ -0,0 +1,20 @@
+#
+# Makefile for the netfilter modules for Link Layer filtering on a bridge.
+#
+
+export-objs := ebtables.o
+
+obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
+obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
+obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
+obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
+obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
+obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
+obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
+obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+include $(TOPDIR)/Rules.make
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/Config.in Wed Sep 11 23:06:55
2002
@@ -0,0 +1,17 @@
+#
+# Bridge netfilter configuration
+#
+dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
+dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: log support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)'
CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT
$CONFIG_BRIDGE_EBT
+dep_tristate ' ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T
$CONFIG_BRIDGE_EBT
+
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/Config.help Wed Sep 11 23:06:55
2002
@@ -0,0 +1,105 @@
+CONFIG_BRIDGE_EBT
+ ebtables is an extendable frame filtering system for the Linux
+ Ethernet bridge. Its usage and implementation is very similar to that
+ of iptables.
+ The difference is that ebtables works on the Link Layer, while iptables
+ works on the Network Layer. ebtables can filter all frames that come
+ into contact with a logical bridge device.
+ Apart from filtering, ebtables also allows MAC source and destination
+ alterations (we call it MAC SNAT and MAC DNAT) and also provides
+ functionality for making Linux a brouter.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_T_FILTER
+ The ebtables filter table is used to define frame filtering rules at
+ local input, forwarding and local output. See the man page for
+ ebtables(8).
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_T_NAT
+ The ebtables nat table is used to define rules that alter the MAC
+ source address (MAC SNAT) or the MAC destination address (MAC DNAT).
+ See the man page for ebtables(8).
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_BROUTE
+ The ebtables broute table is used to define rules that decide between
+ bridging and routing frames, giving Linux the functionality of a
+ brouter. See the man page for ebtables(8) and examples on the ebtables
+ website.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_LOG
+ This option adds the log target, that you can use in any rule in
+ any ebtables table. It records the frame header to the syslog.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_IPF
+ This option adds the IP match, which allows basic IP header field
+ filtering.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_ARPF
+ This option adds the ARP match, which allows ARP and RARP header field
+ filtering.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_VLANF
+ This option adds the 802.1Q vlan match, which allows the filtering of
+ 802.1Q vlan fields.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_MARKF
+ This option adds the mark match, which allows matching frames based on
+ the 'nfmark' value in the frame. This can be set by the mark target.
+ This value is the same as the one used in the iptables mark match and
+ target.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_SNAT
+ This option adds the MAC SNAT target, which allows altering the MAC
+ source address of frames.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_DNAT
+ This option adds the MAC DNAT target, which allows altering the MAC
+ destination address of frames.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_REDIRECT
+ This option adds the MAC redirect target, which allows altering the MAC
+ destination address of a frame to that of the device it arrived on.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
+
+CONFIG_BRIDGE_EBT_MARK_T
+ This option adds the mark target, which allows marking frames by
+ setting the 'nfmark' value in the frame.
+ This value is the same as the one used in the iptables mark match and
+ target.
+
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. If unsure, say `N'.
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebtable_filter.c Wed Sep 11
23:06:55 2002
@@ -0,0 +1,90 @@
+/*
+ * ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+ (1 << NF_BR_LOCAL_OUT))
+
+static struct ebt_entries initial_chains[] =
+{
+ {0, "INPUT", 0, EBT_ACCEPT, 0},
+ {0, "FORWARD", 0, EBT_ACCEPT, 0},
+ {0, "OUTPUT", 0, EBT_ACCEPT, 0}
+};
+
+static struct ebt_replace initial_table =
+{
+ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
+ [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~FILTER_VALID_HOOKS)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table frame_filter =
+{
+ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
+ RW_LOCK_UNLOCKED, check, NULL
+};
+
+static unsigned int
+ebt_hook (unsigned int hook, struct sk_buff **pskb, const struct net_device
*in,
+ const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_filter);
+}
+
+static struct nf_hook_ops ebt_ops_filter[] = {
+ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
+ NF_BR_PRI_FILTER_BRIDGED},
+ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
+ NF_BR_PRI_FILTER_BRIDGED},
+ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
+ NF_BR_PRI_FILTER_OTHER}
+};
+
+static int __init init(void)
+{
+ int i, j, ret;
+
+ ret = ebt_register_table(&frame_filter);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
+ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
+ goto cleanup;
+ return ret;
+cleanup:
+ for (j = 0; j < i; j++)
+ nf_unregister_hook(&ebt_ops_filter[j]);
+ ebt_unregister_table(&frame_filter);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
+ nf_unregister_hook(&ebt_ops_filter[i]);
+ ebt_unregister_table(&frame_filter);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebtable_nat.c Wed Sep 11
23:06:55 2002
@@ -0,0 +1,96 @@
+/*
+ * ebtable_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) |
\
+ (1 << NF_BR_POST_ROUTING))
+
+static struct ebt_entries initial_chains[] =
+{
+ {0, "PREROUTING", 0, EBT_ACCEPT, 0},
+ {0, "OUTPUT", 0, EBT_ACCEPT, 0},
+ {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
+};
+
+static struct ebt_replace initial_table =
+{
+ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+ { [NF_BR_PRE_ROUTING]&initial_chains[0],
[NF_BR_LOCAL_OUT]&initial_chains[1],
+ [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~NAT_VALID_HOOKS)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table frame_nat =
+{
+ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
+ RW_LOCK_UNLOCKED, check, NULL
+};
+
+static unsigned int
+ebt_nat_dst(unsigned int hook, struct sk_buff **pskb, const struct net_device
*in
+ , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static unsigned int
+ebt_nat_src(unsigned int hook, struct sk_buff **pskb, const struct net_device
*in
+ , const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+}
+
+static struct nf_hook_ops ebt_ops_nat[] = {
+ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
+ NF_BR_PRI_NAT_DST_OTHER},
+ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
+ NF_BR_PRI_NAT_SRC},
+ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
+ NF_BR_PRI_NAT_DST_BRIDGED},
+};
+
+static int __init init(void)
+{
+ int i, ret, j;
+
+ ret = ebt_register_table(&frame_nat);
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
+ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
+ goto cleanup;
+ return ret;
+cleanup:
+ for (j = 0; j < i; j++)
+ nf_unregister_hook(&ebt_ops_nat[j]);
+ ebt_unregister_table(&frame_nat);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
+ nf_unregister_hook(&ebt_ops_nat[i]);
+ ebt_unregister_table(&frame_nat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebtable_broute.c Wed Sep 11
23:06:55 2002
@@ -0,0 +1,75 @@
+/*
+ * ebtable_broute
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ * This table lets you choose between routing and bridging for frames
+ * entering on a bridge enslaved nic. This table is traversed before any
+ * other ebtables table. See net/bridge/br_input.c.
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/module.h>
+#include <linux/if_bridge.h>
+#include <linux/brlock.h>
+
+// EBT_ACCEPT means the frame will be bridged
+// EBT_DROP means the frame will be routed
+static struct ebt_entries initial_chain =
+ {0, "BROUTING", 0, EBT_ACCEPT, 0};
+
+static struct ebt_replace initial_table =
+{
+ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
+ { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
+};
+
+static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+{
+ if (valid_hooks & ~(1 << NF_BR_BROUTING))
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_table broute_table =
+{
+ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
+ RW_LOCK_UNLOCKED, check, NULL
+};
+
+static unsigned int
+ebt_broute(unsigned int hook, struct sk_buff **pskb, const struct net_device
*in,
+ const struct net_device *out, int (*okfn)(struct sk_buff *))
+{
+ return ebt_do_table(hook, pskb, in, out, &broute_table);
+}
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = ebt_register_table(&broute_table);
+ if (ret < 0)
+ return ret;
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ // in br_input.c, br_handle_frame() wants to call broute_decision()
+ broute_decision = ebt_broute;
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ broute_decision = NULL;
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+ ebt_unregister_table(&broute_table);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_mark.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,66 @@
+/*
+ * ebt_mark
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * July, 2002
+ *
+ */
+
+// The mark target can be used in any chain
+// I believe adding a mangle table just for marking is total overkill
+// Marking a frame doesn't really change anything in the frame anyway
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+#include <linux/module.h>
+
+static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+ if ((*pskb)->nfmark != info->mark) {
+ (*pskb)->nfmark = info->mark;
+ (*pskb)->nfcache |= NFC_ALTERED;
+ }
+ return info->target;
+}
+
+static int ebt_target_mark_check(const char *tablename, unsigned int
hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_mark_t_info *info = (struct ebt_mark_t_info *)data;
+
+ if (datalen != sizeof(struct ebt_mark_t_info))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target mark_target =
+{
+ {NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
+ ebt_target_mark_check, NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&mark_target);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&mark_target);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_mark_m.c Wed Sep 11
23:06:55 2002
@@ -0,0 +1,61 @@
+/*
+ * ebt_mark_m
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * July, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+#include <linux/module.h>
+
+static int ebt_filter_mark(const struct sk_buff *skb,
+ const struct net_device *in, const struct net_device *out, const void
*data,
+ unsigned int datalen)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+ if (info->bitmask & EBT_MARK_OR)
+ return !(!!(skb->nfmark & info->mask) ^ info->invert);
+ return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
+}
+
+static int ebt_mark_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
+
+ if (datalen != sizeof(struct ebt_mark_m_info))
+ return -EINVAL;
+ if (info->bitmask & ~EBT_MARK_MASK)
+ return -EINVAL;
+ if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
+ return -EINVAL;
+ if (!info->bitmask)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_mark =
+{
+ {NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
+ THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_mark);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_mark);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_redirect.c Wed Sep 11
23:06:55 2002
@@ -0,0 +1,71 @@
+/*
+ * ebt_redirect
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_redirect.h>
+#include <linux/module.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+ if (hooknr != NF_BR_BROUTING)
+ memcpy((**pskb).mac.ethernet->h_dest,
+ in->br_port->br->dev.dev_addr, ETH_ALEN);
+ else {
+ memcpy((**pskb).mac.ethernet->h_dest,
+ in->dev_addr, ETH_ALEN);
+ (*pskb)->pkt_type = PACKET_HOST;
+ }
+ return info->target;
+}
+
+static int ebt_target_redirect_check(const char *tablename, unsigned int
hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_redirect_info *info = (struct ebt_redirect_info *)data;
+
+ if (datalen != sizeof(struct ebt_redirect_info))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target redirect_target =
+{
+ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
+ ebt_target_redirect_check, NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&redirect_target);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&redirect_target);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_arp.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,102 @@
+/*
+ * ebt_arp
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ * Tim Gardner <timg@tpi.com>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+
+static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device
*in,
+ const struct net_device *out, const void *data, unsigned int datalen)
+{
+ struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+ if (info->bitmask & EBT_ARP_OPCODE && FWINV(info->opcode !=
+ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_HTYPE && FWINV(info->htype !=
+ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_PTYPE && FWINV(info->ptype !=
+ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
+ return EBT_NOMATCH;
+
+ if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
+ {
+ uint32_t arp_len = sizeof(struct arphdr) +
+ (2 * (((*skb).nh.arph)->ar_hln)) +
+ (2 * (((*skb).nh.arph)->ar_pln));
+ uint32_t dst;
+ uint32_t src;
+
+ // Make sure the packet is long enough.
+ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
+ return EBT_NOMATCH;
+ // IPv4 addresses are always 4 bytes.
+ if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t))
+ return EBT_NOMATCH;
+
+ if (info->bitmask & EBT_ARP_SRC_IP) {
+ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
+ ((*skb).nh.arph)->ar_hln, sizeof(uint32_t));
+ if (FWINV(info->saddr != (src & info->smsk),
+ EBT_ARP_SRC_IP))
+ return EBT_NOMATCH;
+ }
+
+ if (info->bitmask & EBT_ARP_DST_IP) {
+ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
+ (2*(((*skb).nh.arph)->ar_hln)) +
+ (((*skb).nh.arph)->ar_pln), sizeof(uint32_t));
+ if (FWINV(info->daddr != (dst & info->dmsk),
+ EBT_ARP_DST_IP))
+ return EBT_NOMATCH;
+ }
+ }
+ return EBT_MATCH;
+}
+
+static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_arp_info *info = (struct ebt_arp_info *)data;
+
+ if (datalen != sizeof(struct ebt_arp_info))
+ return -EINVAL;
+ if ((e->ethproto != __constant_htons(ETH_P_ARP) &&
+ e->ethproto != __constant_htons(ETH_P_RARP)) ||
+ e->invflags & EBT_IPROTO)
+ return -EINVAL;
+ if (info->bitmask & ~EBT_ARP_MASK || info->invflags & ~EBT_ARP_MASK)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_arp =
+{
+ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
+ THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_arp);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_arp);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_ip.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,73 @@
+/*
+ * ebt_ip
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ip.h>
+#include <linux/ip.h>
+#include <linux/module.h>
+
+static int ebt_filter_ip(const struct sk_buff *skb, const struct net_device
*in,
+ const struct net_device *out, const void *data,
+ unsigned int datalen)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+ if (info->bitmask & EBT_IP_TOS &&
+ FWINV(info->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_PROTO && FWINV(info->protocol !=
+ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_IP_SOURCE &&
+ FWINV((((*skb).nh.iph)->saddr & info->smsk) !=
+ info->saddr, EBT_IP_SOURCE))
+ return EBT_NOMATCH;
+ if ((info->bitmask & EBT_IP_DEST) &&
+ FWINV((((*skb).nh.iph)->daddr & info->dmsk) !=
+ info->daddr, EBT_IP_DEST))
+ return EBT_NOMATCH;
+ return EBT_MATCH;
+}
+
+static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_ip_info *info = (struct ebt_ip_info *)data;
+
+ if (datalen != sizeof(struct ebt_ip_info))
+ return -EINVAL;
+ if (e->ethproto != __constant_htons(ETH_P_IP) ||
+ e->invflags & EBT_IPROTO)
+ return -EINVAL;
+ if (info->bitmask & ~EBT_IP_MASK || info->invflags & ~EBT_IP_MASK)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_match filter_ip =
+{
+ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
+ THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_match(&filter_ip);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_match(&filter_ip);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_vlan.c Wed Sep 11 23:56:37
2002
@@ -0,0 +1,318 @@
+/*
+ * Description: EBTables 802.1Q match extension kernelspace module.
+ * Authors: Nick Fedchik <nick@fedchik.org.ua>
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_vlan.h>
+
+static unsigned char debug;
+#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
+
+MODULE_PARM (debug, "0-1b");
+MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
+MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
+MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
+ MODULE_VERSION);
+MODULE_LICENSE ("GPL");
+
+
+#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":"
__VA_ARGS__)
+#define INV_FLAG(_inv_flag_) (info->invflags & _inv_flag_) ? "!" : ""
+#define GET_BITMASK(_BIT_MASK_) info->bitmask & _BIT_MASK_
+#define SET_BITMASK(_BIT_MASK_) info->bitmask |= _BIT_MASK_
+#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((info->_MATCH_ ==
_MATCH_)^!!(info->invflags & _MASK_))) return 1;
+
+/*
+ * Function description: ebt_filter_vlan() is main engine for
+ * checking passed 802.1Q frame according to
+ * the passed extension parameters (in the *data buffer)
+ * ebt_filter_vlan() is called after successfull check the rule params
+ * by ebt_check_vlan() function.
+ * Parameters:
+ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
+ * const void *data - pointer to passed extension parameters
+ * unsigned int datalen - length of passed *data buffer
+ * const struct net_device *in -
+ * const struct net_device *out -
+ * const struct ebt_counter *c -
+ * Returned values:
+ * 0 - ok (all rule params matched)
+ * 1 - miss (rule params not acceptable to the parsed frame)
+ */
+static int
+ebt_filter_vlan (const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *data,
+ unsigned int datalen)
+{
+ struct ebt_vlan_info *info = (struct ebt_vlan_info *) data; /* userspace
data */
+ struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw; /* Passed
tagged frame */
+
+ unsigned short TCI; /* Whole TCI, given from parsed frame */
+ unsigned short id; /* VLAN ID, given from frame TCI */
+ unsigned char prio; /* user_priority, given from frame TCI */
+ unsigned short encap; /* VLAN encapsulated Type/Length field, given from
orig frame */
+
+ /*
+ * Tag Control Information (TCI) consists of the following elements:
+ * - User_priority. This field allows the tagged frame to carry
user_priority
+ * information across Bridged LANs in which individual LAN segments may be
unable to signal
+ * priority information (e.g., 802.3/Ethernet segments).
+ * The user_priority field is three bits in length,
+ * interpreted as a binary number. The user_priority is therefore
+ * capable of representing eight priority levels, 0 through 7.
+ * The use and interpretation of this field is defined in ISO/IEC 15802-3.
+ * - Canonical Format Indicator (CFI). This field is used,
+ * in 802.3/Ethernet, to signal the presence or absence
+ * of a RIF field, and, in combination with the Non-canonical Format
Indicator (NCFI) carried
+ * in the RIF, to signal the bit order of address information carried in the
encapsulated
+ * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
+ * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
+ * which the frame belongs. The twelve-bit VLAN Identifier (VID) field
+ * uniquely identify the VLAN to which the frame belongs.
+ * The VID is encoded as an unsigned binary number.
+ */
+ TCI = ntohs (frame->h_vlan_TCI);
+ id = TCI & 0xFFF;
+ prio = TCI >> 13;
+ encap = frame->h_vlan_encapsulated_proto;
+
+ /*
+ * First step is to check is null VLAN ID present
+ * in the parsed frame
+ */
+ if (!(id)) {
+ /*
+ * Checking VLAN Identifier (VID)
+ */
+ if (GET_BITMASK (EBT_VLAN_ID)) { /* Is VLAN ID parsed? */
+ EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
+ DEBUG_MSG
+ ("matched rule id=%s%d for frame id=%d\n",
+ INV_FLAG (EBT_VLAN_ID), info->id, id);
+ }
+ } else {
+ /*
+ * Checking user_priority
+ */
+ if (GET_BITMASK (EBT_VLAN_PRIO)) { /* Is VLAN user_priority parsed? */
+ EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
+ DEBUG_MSG
+ ("matched rule prio=%s%d for frame prio=%d\n",
+ INV_FLAG (EBT_VLAN_PRIO), info->prio,
+ prio);
+ }
+ }
+ /*
+ * Checking Encapsulated Proto (Length/Type) field
+ */
+ if (GET_BITMASK (EBT_VLAN_ENCAP)) { /* Is VLAN Encap parsed? */
+ EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
+ DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
+ INV_FLAG (EBT_VLAN_ENCAP),
+ ntohs (info->encap), ntohs (encap));
+ }
+ /*
+ * All possible extension parameters was parsed.
+ * If rule never returned by missmatch, then all ok.
+ */
+ return 0;
+}
+
+/*
+ * Function description: ebt_vlan_check() is called when userspace
+ * delivers the table to the kernel,
+ * and to check that userspace doesn't give a bad table.
+ * Parameters:
+ * const char *tablename - table name string
+ * unsigned int hooknr - hook number
+ * const struct ebt_entry *e - ebtables entry basic set
+ * const void *data - pointer to passed extension parameters
+ * unsigned int datalen - length of passed *data buffer
+ * Returned values:
+ * 0 - ok (all delivered rule params are correct)
+ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
+ */
+static int
+ebt_check_vlan (const char *tablename,
+ unsigned int hooknr,
+ const struct ebt_entry *e, void *data,
+ unsigned int datalen)
+{
+ struct ebt_vlan_info *info = (struct ebt_vlan_info *) data;
+
+ /*
+ * Parameters buffer overflow check
+ */
+ if (datalen != sizeof (struct ebt_vlan_info)) {
+ DEBUG_MSG
+ ("params size %d is not eq to ebt_vlan_info (%d)\n",
+ datalen, sizeof (struct ebt_vlan_info));
+ return -EINVAL;
+ }
+
+ /*
+ * Is it 802.1Q frame checked?
+ */
+ if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
+ DEBUG_MSG ("passed entry proto %2.4X is not 802.1Q (8100)\n",
+ (unsigned short) ntohs (e->ethproto));
+ return -EINVAL;
+ }
+
+ /*
+ * Check for bitmask range
+ * True if even one bit is out of mask
+ */
+ if (info->bitmask & ~EBT_VLAN_MASK) {
+ DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
+ info->bitmask, EBT_VLAN_MASK);
+ return -EINVAL;
+ }
+
+ /*
+ * Check for inversion flags range
+ */
+ if (info->invflags & ~EBT_VLAN_MASK) {
+ DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
+ info->invflags, EBT_VLAN_MASK);
+ return -EINVAL;
+ }
+
+ /*
+ * Reserved VLAN ID (VID) values
+ * -----------------------------
+ * 0 - The null VLAN ID. Indicates that the tag header contains only
user_priority information;
+ * no VLAN identifier is present in the frame. This VID value shall not be
+ * configured as a PVID, configured in any Filtering Database entry, or used
in any
+ * Management operation.
+ *
+ * 1 - The default Port VID (PVID) value used for classifying frames on
ingress through a Bridge
+ * Port. The PVID value can be changed by management on a per-Port basis.
+ *
+ * 0x0FFF - Reserved for implementation use. This VID value shall not be
configured as a
+ * PVID or transmitted in a tag header.
+ *
+ * The remaining values of VID are available for general use as VLAN
identifiers.
+ * A Bridge may implement the ability to support less than the full range of
VID values;
+ * i.e., for a given implementation,
+ * an upper limit, N, is defined for the VID values supported, where N is
less than or equal to 4094.
+ * All implementations shall support the use of all VID values in the range
0 through their defined maximum
+ * VID, N.
+ *
+ * For Linux, N = 4094.
+ */
+ if (GET_BITMASK (EBT_VLAN_ID)) { /* when vlan-id param was spec-ed */
+ if (!!info->id) { /* if id!=0 => check vid range */
+ if (info->id > 4094) { /* check if id > than (0x0FFE) */
+ DEBUG_MSG
+ ("vlan id %d is out of range (1-4094)\n",
+ info->id);
+ return -EINVAL;
+ }
+ /*
+ * Note: This is valid VLAN-tagged frame point.
+ * Any value of user_priority are acceptable, but could be ignored
+ * according to 802.1Q Std.
+ */
+ } else {
+ /*
+ * if id=0 (null VLAN ID) => Check for user_priority range
+ */
+ if (GET_BITMASK (EBT_VLAN_PRIO)) {
+ if ((unsigned char) info->prio > 7) {
+ DEBUG_MSG
+ ("prio %d is out of range (0-7)\n",
+ info->prio);
+ return -EINVAL;
+ }
+ }
+ /*
+ * Note2: This is valid priority-tagged frame point
+ * with null VID field.
+ */
+ }
+ } else { /* VLAN Id not set */
+ if (GET_BITMASK (EBT_VLAN_PRIO)) { /* But user_priority is set - abnormal!
*/
+ info->id = 0; /* Set null VID (case for Priority-tagged frames) */
+ SET_BITMASK (EBT_VLAN_ID); /* and set id flag */
+ }
+ }
+ /*
+ * Check for encapsulated proto range - it is possible to be any value for
u_short range.
+ * When relaying a tagged frame between 802.3/Ethernet MACs,
+ * a Bridge may adjust the padding field such that
+ * the minimum size of a transmitted tagged frame is 68 octets (7.2).
+ * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS
+ */
+ if (GET_BITMASK (EBT_VLAN_ENCAP)) {
+ if ((unsigned short) ntohs (info->encap) < ETH_ZLEN) {
+ DEBUG_MSG
+ ("encap packet length %d is less than minimal %d\n",
+ ntohs (info->encap), ETH_ZLEN);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Otherwise is all correct
+ */
+ DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
+ tablename, hooknr);
+ return 0;
+}
+
+static struct ebt_match filter_vlan = {
+ {NULL, NULL},
+ EBT_VLAN_MATCH,
+ ebt_filter_vlan,
+ ebt_check_vlan,
+ NULL,
+ THIS_MODULE
+};
+
+/*
+ * Module initialization function.
+ * Called when module is loaded to kernelspace
+ */
+static int __init init (void)
+{
+ DEBUG_MSG ("ebtables 802.1Q extension module v"
+ MODULE_VERSION "\n");
+ DEBUG_MSG ("module debug=%d\n", !!debug);
+ return ebt_register_match (&filter_vlan);
+}
+
+/*
+ * Module "finalization" function
+ * Called when download module from kernelspace
+ */
+static void __exit fini (void)
+{
+ ebt_unregister_match (&filter_vlan);
+}
+
+module_init (init);
+module_exit (fini);
+
+EXPORT_NO_SYMBOLS;
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_log.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,100 @@
+/*
+ * ebt_log
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * April, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_log.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/spinlock.h>
+
+static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
+
+static int ebt_log_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_log_info *info = (struct ebt_log_info *)data;
+
+ if (datalen != sizeof(struct ebt_log_info))
+ return -EINVAL;
+ if (info->bitmask & ~EBT_LOG_MASK)
+ return -EINVAL;
+ if (info->loglevel >= 8)
+ return -EINVAL;
+ info->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
+ return 0;
+}
+
+static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *data, unsigned int datalen)
+{
+ struct ebt_log_info *info = (struct ebt_log_info *)data;
+ char level_string[4] = "< >";
+ level_string[1] = '0' + info->loglevel;
+
+ spin_lock_bh(&ebt_log_lock);
+ printk(level_string);
+ printk("%s IN=%s OUT=%s ", info->prefix, in ? in->name : "",
+ out ? out->name : "");
+
+ if (skb->dev->hard_header_len) {
+ int i;
+ unsigned char *p = (skb->mac.ethernet)->h_source;
+
+ printk("MAC source = ");
+ for (i = 0; i < ETH_ALEN; i++,p++)
+ printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ printk("MAC dest = ");
+ p = (skb->mac.ethernet)->h_dest;
+ for (i = 0; i < ETH_ALEN; i++,p++)
+ printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
+ }
+ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
+
+ if ((info->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
+ htons(ETH_P_IP)){
+ struct iphdr *iph = skb->nh.iph;
+ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
+ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
+ }
+
+ if ((info->bitmask & EBT_LOG_ARP) &&
+ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
+ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
+ struct arphdr * arph = skb->nh.arph;
+ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
+ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
+ ntohs(arph->ar_op));
+ }
+ printk("\n");
+ spin_unlock_bh(&ebt_log_lock);
+}
+
+struct ebt_watcher log =
+{
+ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
+ THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_watcher(&log);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_watcher(&log);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_snat.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,64 @@
+/*
+ * ebt_snat
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+
+static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+ memcpy(((**pskb).mac.ethernet)->h_source, info->mac,
+ ETH_ALEN * sizeof(unsigned char));
+ return info->target;
+}
+
+static int ebt_target_snat_check(const char *tablename, unsigned int
hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *) data;
+
+ if (datalen != sizeof(struct ebt_nat_info))
+ return -EINVAL;
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if (strcmp(tablename, "nat"))
+ return -EINVAL;
+ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target snat =
+{
+ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
+ NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&snat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&snat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebt_dnat.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,65 @@
+/*
+ * ebt_dnat
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * June, 2002
+ *
+ */
+
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+#include <linux/module.h>
+#include <net/sock.h>
+
+static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+ memcpy(((**pskb).mac.ethernet)->h_dest, info->mac,
+ ETH_ALEN * sizeof(unsigned char));
+ return info->target;
+}
+
+static int ebt_target_dnat_check(const char *tablename, unsigned int
hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+ struct ebt_nat_info *info = (struct ebt_nat_info *)data;
+
+ if (BASE_CHAIN && info->target == EBT_RETURN)
+ return -EINVAL;
+ CLEAR_BASE_CHAIN_BIT;
+ if ( (strcmp(tablename, "nat") ||
+ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (datalen != sizeof(struct ebt_nat_info))
+ return -EINVAL;
+ if (INVALID_TARGET)
+ return -EINVAL;
+ return 0;
+}
+
+static struct ebt_target dnat =
+{
+ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
+ NULL, THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ebt_register_target(&dnat);
+}
+
+static void __exit fini(void)
+{
+ ebt_unregister_target(&dnat);
+}
+
+module_init(init);
+module_exit(fini);
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/net/bridge/netfilter/ebtables.c Wed Sep 11 23:06:55
2002
@@ -0,0 +1,1484 @@
+/*
+ * ebtables
+ *
+ * Author:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * ebtables.c,v 2.0, July, 2002
+ *
+ * This code is stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+// used for print_string
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <net/sock.h>
+// needed for logical [in,out]-dev filtering
+#include "../br_private.h"
+
+// list_named_find
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0 // use this for remote debugging
+// Copyright (C) 1998 by Ori Pomerantz
+// Print the string to the appropriate tty, the one
+// the current task uses
+static void print_string(char *str)
+{
+ struct tty_struct *my_tty;
+
+ /* The tty for the current task */
+ my_tty = current->tty;
+ if (my_tty != NULL) {
+ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
+ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
+ }
+}
+
+#define BUGPRINT(args) print_string(args);
+#else
+#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
+ "report to author: "format, ## args)
+// #define BUGPRINT(format, args...)
+#endif
+#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
+ ": out of memory: "format, ## args)
+// #define MEMPRINT(format, args...)
+
+
+
+// Each cpu has its own set of counters, so there is no need for write_lock
in
+// the softirq
+// For reading or updating the counters, the user context needs to
+// get a write_lock
+
+// The size of each set of counters is altered to get cache alignment
+#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
+#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
+#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
+ COUNTER_OFFSET(n) * cpu))
+
+
+
+static DECLARE_MUTEX(ebt_mutex);
+static LIST_HEAD(ebt_tables);
+static LIST_HEAD(ebt_targets);
+static LIST_HEAD(ebt_matches);
+static LIST_HEAD(ebt_watchers);
+
+static struct ebt_target ebt_standard_target =
+{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+
+static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+ const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out)
+{
+ w->u.watcher->watcher(skb, in, out, w->data,
+ w->watcher_size);
+ // watchers don't give a verdict
+ return 0;
+}
+
+static inline int ebt_do_match (struct ebt_entry_match *m,
+ const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out)
+{
+ return m->u.match->match(skb, in, out, m->data,
+ m->match_size);
+}
+
+static inline int ebt_dev_check(char *entry, const struct net_device *device)
+{
+ if (*entry == '\0')
+ return 0;
+ if (!device)
+ return 1;
+ return !!strcmp(entry, device->name);
+}
+
+#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+// process standard matches
+static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
+ const struct net_device *in, const struct net_device *out)
+{
+ int verdict, i;
+
+ if (e->bitmask & EBT_802_3) {
+ if (FWINV2(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
+ return 1;
+ } else if (!(e->bitmask & EBT_NOPROTO) &&
+ FWINV2(e->ethproto != h->h_proto, EBT_IPROTO))
+ return 1;
+
+ if (FWINV2(ebt_dev_check(e->in, in), EBT_IIN))
+ return 1;
+ if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
+ return 1;
+ if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
+ e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+ return 1;
+ if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
+ e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+ return 1;
+
+ if (e->bitmask & EBT_SOURCEMAC) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
+ e->sourcemsk[i];
+ if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ return 1;
+ }
+ if (e->bitmask & EBT_DESTMAC) {
+ verdict = 0;
+ for (i = 0; i < 6; i++)
+ verdict |= (h->h_dest[i] ^ e->destmac[i]) &
+ e->destmsk[i];
+ if (FWINV2(verdict != 0, EBT_IDEST) )
+ return 1;
+ }
+ return 0;
+}
+
+// Do some firewalling
+unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ struct ebt_table *table)
+{
+ int i, nentries;
+ struct ebt_entry *point;
+ struct ebt_counter *counter_base, *cb_base;
+ struct ebt_entry_target *t;
+ int verdict, sp = 0;
+ struct ebt_chainstack *cs;
+ struct ebt_entries *chaininfo;
+ char *base;
+ struct ebt_table_info *private = table->private;
+
+ read_lock_bh(&table->lock);
+ cb_base = COUNTER_BASE(private->counters, private->nentries,
+ smp_processor_id());
+ if (private->chainstack)
+ cs = private->chainstack[smp_processor_id()];
+ else
+ cs = NULL;
+ chaininfo = private->hook_entry[hook];
+ nentries = private->hook_entry[hook]->nentries;
+ point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+ counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+ // base for chain jumps
+ base = (char *)chaininfo;
+ i = 0;
+ while (i < nentries) {
+ if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
+ goto letscontinue;
+
+ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out) != 0)
+ goto letscontinue;
+
+ // increase counter
+ (*(counter_base + i)).pcnt++;
+
+ // these should only watch: not modify, nor tell us
+ // what to do with the packet
+ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+ out);
+
+ t = (struct ebt_entry_target *)
+ (((char *)point) + point->target_offset);
+ // standard target
+ if (!t->u.target->target)
+ verdict = ((struct ebt_standard_target *)t)->verdict;
+ else
+ verdict = t->u.target->target(pskb, hook,
+ in, out, t->data, t->target_size);
+ if (verdict == EBT_ACCEPT) {
+ read_unlock_bh(&table->lock);
+ return NF_ACCEPT;
+ }
+ if (verdict == EBT_DROP) {
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+ if (verdict == EBT_RETURN) {
+letsreturn:
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (sp == 0) {
+ BUGPRINT("RETURN on base chain");
+ // act like this is EBT_CONTINUE
+ goto letscontinue;
+ }
+#endif
+ sp--;
+ // put all the local variables right
+ i = cs[sp].n;
+ chaininfo = cs[sp].chaininfo;
+ nentries = chaininfo->nentries;
+ point = cs[sp].e;
+ counter_base = cb_base +
+ chaininfo->counter_offset;
+ continue;
+ }
+ if (verdict == EBT_CONTINUE)
+ goto letscontinue;
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (verdict < 0) {
+ BUGPRINT("bogus standard verdict\n");
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+#endif
+ // jump to a udc
+ cs[sp].n = i + 1;
+ cs[sp].chaininfo = chaininfo;
+ cs[sp].e = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
+ i = 0;
+ chaininfo = (struct ebt_entries *) (base + verdict);
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (chaininfo->distinguisher) {
+ BUGPRINT("jump to non-chain\n");
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+#endif
+ nentries = chaininfo->nentries;
+ point = (struct ebt_entry *)chaininfo->data;
+ counter_base = cb_base + chaininfo->counter_offset;
+ sp++;
+ continue;
+letscontinue:
+ point = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
+ i++;
+ }
+
+ // I actually like this :)
+ if (chaininfo->policy == EBT_RETURN)
+ goto letsreturn;
+ if (chaininfo->policy == EBT_ACCEPT) {
+ read_unlock_bh(&table->lock);
+ return NF_ACCEPT;
+ }
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+}
+
+// If it succeeds, returns element and locks mutex
+static inline void *
+find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
+ struct semaphore *mutex)
+{
+ void *ret;
+
+ *error = down_interruptible(mutex);
+ if (*error != 0)
+ return NULL;
+
+ ret = list_named_find(head, name);
+ if (!ret) {
+ *error = -ENOENT;
+ up(mutex);
+ }
+ return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head, const char *name, const char
*prefix,
+ int *error, struct semaphore *mutex)
+{
+ void *ret;
+
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ if (!ret) {
+ char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
+ strcpy(modulename, prefix);
+ strcat(modulename, name);
+ request_module(modulename);
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ }
+ return ret;
+}
+#endif
+
+static inline struct ebt_table *
+find_table_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
+}
+
+static inline struct ebt_match *
+find_match_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_watcher *
+find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
+}
+
+static inline struct ebt_target *
+find_target_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
+}
+
+static inline int
+ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+ const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+ struct ebt_match *match;
+ int ret;
+
+ if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
+ ((char *)e) + e->watchers_offset)
+ return -EINVAL;
+ match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+ if (!match)
+ return ret;
+ m->u.match = match;
+ if (match->me)
+ __MOD_INC_USE_COUNT(match->me);
+ up(&ebt_mutex);
+ if (match->check &&
+ match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+ BUGPRINT("match->check failed\n");
+ if (match->me)
+ __MOD_DEC_USE_COUNT(match->me);
+ return -EINVAL;
+ }
+ (*cnt)++;
+ return 0;
+}
+
+static inline int
+ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+ const char *name, unsigned int hookmask, unsigned int *cnt)
+{
+ struct ebt_watcher *watcher;
+ int ret;
+
+ if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
+ ((char *)e) + e->target_offset)
+ return -EINVAL;
+ watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+ if (!watcher)
+ return ret;
+ w->u.watcher = watcher;
+ if (watcher->me)
+ __MOD_INC_USE_COUNT(watcher->me);
+ up(&ebt_mutex);
+ if (watcher->check &&
+ watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+ BUGPRINT("watcher->check failed\n");
+ if (watcher->me)
+ __MOD_DEC_USE_COUNT(watcher->me);
+ return -EINVAL;
+ }
+ (*cnt)++;
+ return 0;
+}
+
+// this one is very careful, as it is the first function
+// to parse the userspace data
+static inline int
+ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+ struct ebt_table_info *newinfo, char *base, char *limit,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+{
+ int i;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if ( (char *)hook_entries[i] - base ==
+ (char *)e - newinfo->entries)
+ break;
+ }
+ // beginning of a new chain
+ // if i == NF_BR_NUMHOOKS it must be a user defined chain
+ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+ // we make userspace set this right,
+ // so there is no misunderstanding
+ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+ "in distinguisher\n");
+ return -EINVAL;
+ }
+ // this checks if the previous chain has as many entries
+ // as it said it has
+ if (*n != *cnt) {
+ BUGPRINT("nentries does not equal the nr of entries "
+ "in the chain\n");
+ return -EINVAL;
+ }
+ // before we look at the struct, be sure it is not too big
+ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
+ > limit) {
+ BUGPRINT("entries_size too small\n");
+ return -EINVAL;
+ }
+ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+ // only RETURN from udc
+ if (i != NF_BR_NUMHOOKS ||
+ ((struct ebt_entries *)e)->policy != EBT_RETURN) {
+ BUGPRINT("bad policy\n");
+ return -EINVAL;
+ }
+ }
+ if (i == NF_BR_NUMHOOKS) // it's a user defined chain
+ (*udc_cnt)++;
+ else
+ newinfo->hook_entry[i] = (struct ebt_entries *)e;
+ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
+ BUGPRINT("counter_offset != totalcnt");
+ return -EINVAL;
+ }
+ *n = ((struct ebt_entries *)e)->nentries;
+ *cnt = 0;
+ return 0;
+ }
+ // a plain old entry, heh
+ if (sizeof(struct ebt_entry) > e->watchers_offset ||
+ e->watchers_offset > e->target_offset ||
+ e->target_offset >= e->next_offset) {
+ BUGPRINT("entry offsets not in right order\n");
+ return -EINVAL;
+ }
+ // this is not checked anywhere else
+ if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
+ BUGPRINT("target size too small\n");
+ return -EINVAL;
+ }
+
+ (*cnt)++;
+ (*totalcnt)++;
+ return 0;
+}
+
+struct ebt_cl_stack
+{
+ struct ebt_chainstack cs;
+ int from;
+ unsigned int hookmask;
+};
+
+// we need these positions to check that the jumps to a different part of the
+// entries is a jump to the beginning of a new chain.
+static inline int
+ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int
valid_hooks,
+ struct ebt_cl_stack *udc)
+{
+ int i;
+
+ // we're only interested in chain starts
+ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+ return 0;
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
+ break;
+ }
+ // only care about udc
+ if (i != NF_BR_NUMHOOKS)
+ return 0;
+
+ udc[*n].cs.chaininfo = (struct ebt_entries *)e;
+ // these initialisations are depended on later in check_chainloops()
+ udc[*n].cs.n = 0;
+ udc[*n].hookmask = 0;
+
+ (*n)++;
+ return 0;
+}
+
+static inline int
+ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+{
+ if (i && (*i)-- == 0)
+ return 1;
+ if (m->u.match->destroy)
+ m->u.match->destroy(m->data, m->match_size);
+ if (m->u.match->me)
+ __MOD_DEC_USE_COUNT(m->u.match->me);
+
+ return 0;
+}
+
+static inline int
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+{
+ if (i && (*i)-- == 0)
+ return 1;
+ if (w->u.watcher->destroy)
+ w->u.watcher->destroy(w->data, w->watcher_size);
+ if (w->u.watcher->me)
+ __MOD_DEC_USE_COUNT(w->u.watcher->me);
+
+ return 0;
+}
+
+static inline int
+ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+{
+ struct ebt_entry_target *t;
+
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+ // we're done
+ if (cnt && (*cnt)-- == 0)
+ return 1;
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ if (t->u.target->destroy)
+ t->u.target->destroy(t->data, t->target_size);
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+
+ return 0;
+}
+
+static inline int
+ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ const char *name, unsigned int *cnt, unsigned int valid_hooks,
+ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+{
+ struct ebt_entry_target *t;
+ struct ebt_target *target;
+ unsigned int i, j, hook = 0, hookmask = 0;
+ int ret;
+
+ // Don't mess with the struct ebt_entries
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+
+ if (e->bitmask & ~EBT_F_MASK) {
+ BUGPRINT("Unknown flag for bitmask\n");
+ return -EINVAL;
+ }
+ if (e->invflags & ~EBT_INV_MASK) {
+ BUGPRINT("Unknown flag for inv bitmask\n");
+ return -EINVAL;
+ }
+ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+ BUGPRINT("NOPROTO & 802_3 not allowed\n");
+ return -EINVAL;
+ }
+ // what hook do we belong to?
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if ((valid_hooks & (1 << i)) == 0)
+ continue;
+ if ((char *)newinfo->hook_entry[i] < (char *)e)
+ hook = i;
+ else
+ break;
+ }
+ // (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
+ // a base chain
+ if (i < NF_BR_NUMHOOKS)
+ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else {
+ for (i = 0; i < udc_cnt; i++)
+ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+ break;
+ if (i == 0)
+ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else
+ hookmask = cl_s[i - 1].hookmask;
+ }
+ i = 0;
+ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+ if (ret != 0)
+ goto cleanup_matches;
+ j = 0;
+ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+ if (ret != 0)
+ goto cleanup_watchers;
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+ if (!target)
+ goto cleanup_watchers;
+ if (target->me)
+ __MOD_INC_USE_COUNT(target->me);
+ up(&ebt_mutex);
+
+ t->u.target = target;
+ if (t->u.target == &ebt_standard_target) {
+ if (e->target_offset + sizeof(struct ebt_standard_target) >
+ e->next_offset) {
+ BUGPRINT("Standard target size too big\n");
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ if (((struct ebt_standard_target *)t)->verdict <
+ -NUM_STANDARD_TARGETS) {
+ BUGPRINT("Invalid standard target\n");
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ } else if ((e->target_offset + t->target_size +
+ sizeof(struct ebt_entry_target) > e->next_offset) ||
+ (t->u.target->check &&
+ t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ (*cnt)++;
+ return 0;
+cleanup_watchers:
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+cleanup_matches:
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+ return ret;
+}
+
+// checks for loops and sets the hook mask for udc
+// the hook mask for udc tells us from which base chains the udc can be
+// accessed. This mask is a parameter to the check() functions of the
extensions
+int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
+ unsigned int udc_cnt, unsigned int hooknr, char *base)
+{
+ int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
+ struct ebt_entry *e = (struct ebt_entry *)chain->data;
+ struct ebt_entry_target *t;
+
+ while (pos < nentries || chain_nr != -1) {
+ // end of udc, go back one 'recursion' step
+ if (pos == nentries) {
+ // put back values of the time when this chain was called
+ e = cl_s[chain_nr].cs.e;
+ if (cl_s[chain_nr].from != -1)
+ nentries =
+ cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+ else
+ nentries = chain->nentries;
+ pos = cl_s[chain_nr].cs.n;
+ // make sure we won't see a loop that isn't one
+ cl_s[chain_nr].cs.n = 0;
+ chain_nr = cl_s[chain_nr].from;
+ if (pos == nentries)
+ continue;
+ }
+ t = (struct ebt_entry_target *)
+ (((char *)e) + e->target_offset);
+ if (strcmp(t->u.name, EBT_STANDARD_TARGET))
+ goto letscontinue;
+ if (e->target_offset + sizeof(struct ebt_standard_target) >
+ e->next_offset) {
+ BUGPRINT("Standard target size too big\n");
+ return -1;
+ }
+ verdict = ((struct ebt_standard_target *)t)->verdict;
+ if (verdict >= 0) { // jump to another chain
+ struct ebt_entries *hlp2 =
+ (struct ebt_entries *)(base + verdict);
+ for (i = 0; i < udc_cnt; i++)
+ if (hlp2 == cl_s[i].cs.chaininfo)
+ break;
+ // bad destination or loop
+ if (i == udc_cnt) {
+ BUGPRINT("bad destination\n");
+ return -1;
+ }
+ if (cl_s[i].cs.n) {
+ BUGPRINT("loop\n");
+ return -1;
+ }
+ // this can't be 0, so the above test is correct
+ cl_s[i].cs.n = pos + 1;
+ pos = 0;
+ cl_s[i].cs.e = ((void *)e + e->next_offset);
+ e = (struct ebt_entry *)(hlp2->data);
+ nentries = hlp2->nentries;
+ cl_s[i].from = chain_nr;
+ chain_nr = i;
+ // this udc is accessible from the base chain for hooknr
+ cl_s[i].hookmask |= (1 << hooknr);
+ continue;
+ }
+letscontinue:
+ e = (void *)e + e->next_offset;
+ pos++;
+ }
+ return 0;
+}
+
+// do the parsing of the table/chains/entries/matches/watchers/targets, heh
+static int translate_table(struct ebt_replace *repl,
+ struct ebt_table_info *newinfo)
+{
+ unsigned int i, j, k, udc_cnt;
+ int ret;
+ struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
+
+ i = 0;
+ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+ i++;
+ if (i == NF_BR_NUMHOOKS) {
+ BUGPRINT("No valid hooks specified\n");
+ return -EINVAL;
+ }
+ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+ BUGPRINT("Chains don't start at beginning\n");
+ return -EINVAL;
+ }
+ // make sure chains are ordered after each other in same order
+ // as their corresponding hooks
+ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
+ if (!(repl->valid_hooks & (1 << j)))
+ continue;
+ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+ BUGPRINT("Hook order must be followed\n");
+ return -EINVAL;
+ }
+ i = j;
+ }
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ newinfo->hook_entry[i] = NULL;
+
+ newinfo->entries_size = repl->entries_size;
+ newinfo->nentries = repl->nentries;
+
+ // do some early checkings and initialize some things
+ i = 0; // holds the expected nr. of entries for the chain
+ j = 0; // holds the up to now counted entries for the chain
+ k = 0; // holds the total nr. of entries, should equal
+ // newinfo->nentries afterwards
+ udc_cnt = 0; // will hold the nr. of user defined chains (udc)
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+ &udc_cnt, repl->valid_hooks);
+
+ if (ret != 0)
+ return ret;
+
+ if (i != j) {
+ BUGPRINT("nentries does not equal the nr of entries in the "
+ "(last) chain\n");
+ return -EINVAL;
+ }
+ if (k != newinfo->nentries) {
+ BUGPRINT("Total nentries is wrong\n");
+ return -EINVAL;
+ }
+
+ // check if all valid hooks have a chain
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (newinfo->hook_entry[i] == NULL &&
+ (repl->valid_hooks & (1 << i))) {
+ BUGPRINT("Valid hook without chain\n");
+ return -EINVAL;
+ }
+ }
+
+ // Get the location of the udc, put them in an array
+ // While we're at it, allocate the chainstack
+ if (udc_cnt) {
+ // this will get free'd in do_replace()/ebt_register_table()
+ // if an error occurs
+ newinfo->chainstack = (struct ebt_chainstack **)
+ vmalloc(NR_CPUS * sizeof(struct ebt_chainstack));
+ if (!newinfo->chainstack)
+ return -ENOMEM;
+ for (i = 0; i < NR_CPUS; i++) {
+ newinfo->chainstack[i] =
+ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
+ if (!newinfo->chainstack[i]) {
+ while (i)
+ vfree(newinfo->chainstack[--i]);
+ vfree(newinfo->chainstack);
+ newinfo->chainstack = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ cl_s = (struct ebt_cl_stack *)
+ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
+ if (!cl_s)
+ return -ENOMEM;
+ i = 0; // the i'th udc
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
+ repl->valid_hooks, cl_s);
+ // sanity check
+ if (i != udc_cnt) {
+ BUGPRINT("i != udc_cnt\n");
+ vfree(cl_s);
+ return -EFAULT;
+ }
+ }
+
+ // Check for loops
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ if (repl->valid_hooks & (1 << i))
+ if (check_chainloops(newinfo->hook_entry[i],
+ cl_s, udc_cnt, i, newinfo->entries)) {
+ if (cl_s)
+ vfree(cl_s);
+ return -EINVAL;
+ }
+
+ // we now know the following (along with E=mc²):
+ // - the nr of entries in each chain is right
+ // - the size of the allocated space is right
+ // - all valid hooks have a corresponding chain
+ // - there are no loops
+ // - wrong data can still be on the level of a single entry
+ // - could be there are jumps to places that are not the
+ // beginning of a chain. This can only occur in chains that
+ // are not accessible from any base chains, so we don't care.
+
+ // used to know what we need to clean up if something goes wrong
+ i = 0;
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
+ cl_s, udc_cnt);
+ if (ret != 0) {
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, &i);
+ }
+ if (cl_s)
+ vfree(cl_s);
+ return ret;
+}
+
+// called under write_lock
+static void get_counters(struct ebt_counter *oldcounters,
+ struct ebt_counter *counters, unsigned int nentries)
+{
+ int i, cpu;
+ struct ebt_counter *counter_base;
+
+ // counters of cpu 0
+ memcpy(counters, oldcounters,
+ sizeof(struct ebt_counter) * nentries);
+ // add other counters to those of cpu 0
+ for (cpu = 1; cpu < NR_CPUS; cpu++) {
+ counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+ for (i = 0; i < nentries; i++)
+ counters[i].pcnt += counter_base[i].pcnt;
+ }
+}
+
+// replace the table
+static int do_replace(void *user, unsigned int len)
+{
+ int ret, i, countersize;
+ struct ebt_table_info *newinfo;
+ struct ebt_replace tmp;
+ struct ebt_table *t;
+ struct ebt_counter *counterstmp = NULL;
+ // used to be able to unlock earlier
+ struct ebt_table_info *table;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ if (len != sizeof(tmp) + tmp.entries_size) {
+ BUGPRINT("Wrong len argument\n");
+ return -EINVAL;
+ }
+
+ if (tmp.entries_size == 0) {
+ BUGPRINT("Entries_size never zero\n");
+ return -EINVAL;
+ }
+ countersize = COUNTER_OFFSET(tmp.nentries) * NR_CPUS;
+ newinfo = (struct ebt_table_info *)
+ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ if (!newinfo)
+ return -ENOMEM;
+
+ if (countersize)
+ memset(newinfo->counters, 0, countersize);
+
+ newinfo->entries = (char *)vmalloc(tmp.entries_size);
+ if (!newinfo->entries) {
+ ret = -ENOMEM;
+ goto free_newinfo;
+ }
+ if (copy_from_user(
+ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+ BUGPRINT("Couldn't copy entries from userspace\n");
+ ret = -EFAULT;
+ goto free_entries;
+ }
+
+ // the user wants counters back
+ // the check on the size is done later, when we have the lock
+ if (tmp.num_counters) {
+ counterstmp = (struct ebt_counter *)
+ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
+ if (!counterstmp) {
+ ret = -ENOMEM;
+ goto free_entries;
+ }
+ }
+ else
+ counterstmp = NULL;
+
+ // this can get initialized by translate_table()
+ newinfo->chainstack = NULL;
+ ret = translate_table(&tmp, newinfo);
+
+ if (ret != 0)
+ goto free_counterstmp;
+
+ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ if (!t)
+ goto free_iterate;
+
+ // the table doesn't like it
+ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+ goto free_unlock;
+
+ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+ BUGPRINT("Wrong nr. of counters requested\n");
+ ret = -EINVAL;
+ goto free_unlock;
+ }
+
+ // we have the mutex lock, so no danger in reading this pointer
+ table = t->private;
+ // we need an atomic snapshot of the counters
+ write_lock_bh(&t->lock);
+ if (tmp.num_counters)
+ get_counters(t->private->counters, counterstmp,
+ t->private->nentries);
+
+ t->private = newinfo;
+ write_unlock_bh(&t->lock);
+ up(&ebt_mutex);
+ // So, a user can change the chains while having messed up her counter
+ // allocation. Only reason why this is done is because this way the lock
+ // is held only once, while this doesn't bring the kernel into a
+ // dangerous state.
+ if (tmp.num_counters &&
+ copy_to_user(tmp.counters, counterstmp,
+ tmp.num_counters * sizeof(struct ebt_counter))) {
+ BUGPRINT("Couldn't copy counters to userspace\n");
+ ret = -EFAULT;
+ }
+ else
+ ret = 0;
+
+ // decrease module count and free resources
+ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
+ ebt_cleanup_entry, NULL);
+
+ vfree(table->entries);
+ if (table->chainstack) {
+ for (i = 0; i < NR_CPUS; i++)
+ vfree(table->chainstack[i]);
+ vfree(table->chainstack);
+ }
+ vfree(table);
+
+ if (counterstmp)
+ vfree(counterstmp);
+ return ret;
+
+free_unlock:
+ up(&ebt_mutex);
+free_iterate:
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, NULL);
+free_counterstmp:
+ if (counterstmp)
+ vfree(counterstmp);
+ // can be initialized in translate_table()
+ if (newinfo->chainstack) {
+ for (i = 0; i < NR_CPUS; i++)
+ vfree(newinfo->chainstack[i]);
+ vfree(newinfo->chainstack);
+ }
+free_entries:
+ if (newinfo->entries)
+ vfree(newinfo->entries);
+free_newinfo:
+ if (newinfo)
+ vfree(newinfo);
+ return ret;
+}
+
+int ebt_register_target(struct ebt_target *target)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_targets, target)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+void ebt_unregister_target(struct ebt_target *target)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_targets, target);
+ up(&ebt_mutex);
+ MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_match(struct ebt_match *match)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_matches, match)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+void ebt_unregister_match(struct ebt_match *match)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_matches, match);
+ up(&ebt_mutex);
+ MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_watcher(struct ebt_watcher *watcher)
+{
+ int ret;
+
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ return ret;
+ if (!list_named_insert(&ebt_watchers, watcher)) {
+ up(&ebt_mutex);
+ return -EEXIST;
+ }
+ up(&ebt_mutex);
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+void ebt_unregister_watcher(struct ebt_watcher *watcher)
+{
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_watchers, watcher);
+ up(&ebt_mutex);
+ MOD_DEC_USE_COUNT;
+}
+
+int ebt_register_table(struct ebt_table *table)
+{
+ struct ebt_table_info *newinfo;
+ int ret, i, countersize;
+
+ if (!table || !table->table ||!table->table->entries ||
+ table->table->entries_size == 0 ||
+ table->table->counters || table->private) {
+ BUGPRINT("Bad table data for ebt_register_table!!!\n");
+ return -EINVAL;
+ }
+
+ countersize = COUNTER_OFFSET(table->table->nentries) * NR_CPUS;
+ newinfo = (struct ebt_table_info *)
+ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ ret = -ENOMEM;
+ if (!newinfo)
+ return -ENOMEM;
+
+ newinfo->entries = (char *)vmalloc(table->table->entries_size);
+ if (!(newinfo->entries))
+ goto free_newinfo;
+
+ memcpy(newinfo->entries, table->table->entries,
+ table->table->entries_size);
+
+ if (countersize)
+ memset(newinfo->counters, 0, countersize);
+
+ // fill in newinfo and parse the entries
+ newinfo->chainstack = NULL;
+ ret = translate_table(table->table, newinfo);
+ if (ret != 0) {
+ BUGPRINT("Translate_table failed\n");
+ goto free_chainstack;
+ }
+
+ if (table->check && table->check(newinfo, table->valid_hooks)) {
+ BUGPRINT("The table doesn't like its own initial data, lol\n");
+ return -EINVAL;
+ }
+
+ table->private = newinfo;
+ table->lock = RW_LOCK_UNLOCKED;
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+ goto free_chainstack;
+
+ if (list_named_find(&ebt_tables, table->name)) {
+ ret = -EEXIST;
+ BUGPRINT("Table name already exists\n");
+ goto free_unlock;
+ }
+
+ list_prepend(&ebt_tables, table);
+ up(&ebt_mutex);
+ MOD_INC_USE_COUNT;
+ return 0;
+free_unlock:
+ up(&ebt_mutex);
+free_chainstack:
+ if (newinfo->chainstack) {
+ for (i = 0; i < NR_CPUS; i++)
+ vfree(newinfo->chainstack[i]);
+ vfree(newinfo->chainstack);
+ }
+ vfree(newinfo->entries);
+free_newinfo:
+ vfree(newinfo);
+ return ret;
+}
+
+void ebt_unregister_table(struct ebt_table *table)
+{
+ int i;
+
+ if (!table) {
+ BUGPRINT("Request to unregister NULL table!!!\n");
+ return;
+ }
+ down(&ebt_mutex);
+ LIST_DELETE(&ebt_tables, table);
+ up(&ebt_mutex);
+ EBT_ENTRY_ITERATE(table->private->entries,
+ table->private->entries_size, ebt_cleanup_entry, NULL);
+ if (table->private->entries)
+ vfree(table->private->entries);
+ if (table->private->chainstack) {
+ for (i = 0; i < NR_CPUS; i++)
+ vfree(table->private->chainstack[i]);
+ vfree(table->private->chainstack);
+ }
+ vfree(table->private);
+ MOD_DEC_USE_COUNT;
+}
+
+// userspace just supplied us with counters
+static int update_counters(void *user, unsigned int len)
+{
+ int i, ret;
+ struct ebt_counter *tmp;
+ struct ebt_replace hlp;
+ struct ebt_table *t;
+
+ if (copy_from_user(&hlp, user, sizeof(hlp)))
+ return -EFAULT;
+
+ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
+ return -EINVAL;
+ if (hlp.num_counters == 0)
+ return -EINVAL;
+
+ if ( !(tmp = (struct ebt_counter *)
+ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
+ MEMPRINT("Update_counters && nomemory\n");
+ return -ENOMEM;
+ }
+
+ t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ if (!t)
+ goto free_tmp;
+
+ if (hlp.num_counters != t->private->nentries) {
+ BUGPRINT("Wrong nr of counters\n");
+ ret = -EINVAL;
+ goto unlock_mutex;
+ }
+
+ if ( copy_from_user(tmp, hlp.counters,
+ hlp.num_counters * sizeof(struct ebt_counter)) ) {
+ BUGPRINT("Updata_counters && !cfu\n");
+ ret = -EFAULT;
+ goto unlock_mutex;
+ }
+
+ // we want an atomic add of the counters
+ write_lock_bh(&t->lock);
+
+ // we add to the counters of the first cpu
+ for (i = 0; i < hlp.num_counters; i++)
+ t->private->counters[i].pcnt += tmp[i].pcnt;
+
+ write_unlock_bh(&t->lock);
+ ret = 0;
+unlock_mutex:
+ up(&ebt_mutex);
+free_tmp:
+ vfree(tmp);
+ return ret;
+}
+
+static inline int ebt_make_matchname(struct ebt_entry_match *m,
+ char *base, char *ubase)
+{
+ char *hlp = ubase - base + (char *)m;
+ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
+ char *base, char *ubase)
+{
+ char *hlp = ubase - base + (char *)w;
+ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char
*ubase)
+{
+ int ret;
+ char *hlp;
+ struct ebt_entry_target *t;
+
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+
+ hlp = ubase - base + (char *)e + e->target_offset;
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+
+ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
+ if (ret != 0)
+ return ret;
+ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
+ if (ret != 0)
+ return ret;
+ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+ return -EFAULT;
+ return 0;
+}
+
+// called with ebt_mutex down
+static int copy_everything_to_user(struct ebt_table *t, void *user,
+ int *len, int cmd)
+{
+ struct ebt_replace tmp;
+ struct ebt_counter *counterstmp, *oldcounters;
+ unsigned int entries_size, nentries;
+ char *entries;
+
+ if (cmd == EBT_SO_GET_ENTRIES) {
+ entries_size = t->private->entries_size;
+ nentries = t->private->nentries;
+ entries = t->private->entries;
+ oldcounters = t->private->counters;
+ } else {
+ entries_size = t->table->entries_size;
+ nentries = t->table->nentries;
+ entries = t->table->entries;
+ oldcounters = t->table->counters;
+ }
+
+ if (copy_from_user(&tmp, user, sizeof(tmp))) {
+ BUGPRINT("Cfu didn't work\n");
+ return -EFAULT;
+ }
+
+ if (*len != sizeof(struct ebt_replace) + entries_size +
+ (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
+ BUGPRINT("Wrong size\n");
+ return -EINVAL;
+ }
+
+ if (tmp.nentries != nentries) {
+ BUGPRINT("Nentries wrong\n");
+ return -EINVAL;
+ }
+
+ if (tmp.entries_size != entries_size) {
+ BUGPRINT("Wrong size\n");
+ return -EINVAL;
+ }
+
+ // userspace might not need the counters
+ if (tmp.num_counters) {
+ if (tmp.num_counters != nentries) {
+ BUGPRINT("Num_counters wrong\n");
+ return -EINVAL;
+ }
+ counterstmp = (struct ebt_counter *)
+ vmalloc(nentries * sizeof(struct ebt_counter));
+ if (!counterstmp) {
+ MEMPRINT("Couldn't copy counters, out of memory\n");
+ return -ENOMEM;
+ }
+ write_lock_bh(&t->lock);
+ get_counters(oldcounters, counterstmp, nentries);
+ write_unlock_bh(&t->lock);
+
+ if (copy_to_user(tmp.counters, counterstmp,
+ nentries * sizeof(struct ebt_counter))) {
+ BUGPRINT("Couldn't copy counters to userspace\n");
+ vfree(counterstmp);
+ return -EFAULT;
+ }
+ vfree(counterstmp);
+ }
+
+ if (copy_to_user(tmp.entries, entries, entries_size)) {
+ BUGPRINT("Couldn't copy entries to userspace\n");
+ return -EFAULT;
+ }
+ // set the match/watcher/target names right
+ return EBT_ENTRY_ITERATE(entries, entries_size,
+ ebt_make_names, entries, tmp.entries);
+}
+
+static int do_ebt_set_ctl(struct sock *sk,
+ int cmd, void *user, unsigned int len)
+{
+ int ret;
+
+ switch(cmd) {
+ case EBT_SO_SET_ENTRIES:
+ ret = do_replace(user, len);
+ break;
+ case EBT_SO_SET_COUNTERS:
+ ret = update_counters(user, len);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+ int ret;
+ struct ebt_replace tmp;
+ struct ebt_table *t;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)))
+ return -EFAULT;
+
+ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ if (!t)
+ return ret;
+
+ switch(cmd) {
+ case EBT_SO_GET_INFO:
+ case EBT_SO_GET_INIT_INFO:
+ if (*len != sizeof(struct ebt_replace)){
+ ret = -EINVAL;
+ up(&ebt_mutex);
+ break;
+ }
+ if (cmd == EBT_SO_GET_INFO) {
+ tmp.nentries = t->private->nentries;
+ tmp.entries_size = t->private->entries_size;
+ tmp.valid_hooks = t->valid_hooks;
+ } else {
+ tmp.nentries = t->table->nentries;
+ tmp.entries_size = t->table->entries_size;
+ tmp.valid_hooks = t->table->valid_hooks;
+ }
+ up(&ebt_mutex);
+ if (copy_to_user(user, &tmp, *len) != 0){
+ BUGPRINT("c2u Didn't work\n");
+ ret = -EFAULT;
+ break;
+ }
+ ret = 0;
+ break;
+
+ case EBT_SO_GET_ENTRIES:
+ case EBT_SO_GET_INIT_ENTRIES:
+ ret = copy_everything_to_user(t, user, len, cmd);
+ up(&ebt_mutex);
+ break;
+
+ default:
+ up(&ebt_mutex);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct nf_sockopt_ops ebt_sockopts =
+{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
+ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+};
+
+static int __init init(void)
+{
+ int ret;
+
+ down(&ebt_mutex);
+ list_named_insert(&ebt_targets, &ebt_standard_target);
+ up(&ebt_mutex);
+ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
+ return ret;
+
+ printk("Ebtables v2.0 registered");
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ nf_unregister_sockopt(&ebt_sockopts);
+ printk("Ebtables v2.0 unregistered");
+}
+
+EXPORT_SYMBOL(ebt_register_table);
+EXPORT_SYMBOL(ebt_unregister_table);
+EXPORT_SYMBOL(ebt_register_match);
+EXPORT_SYMBOL(ebt_unregister_match);
+EXPORT_SYMBOL(ebt_register_watcher);
+EXPORT_SYMBOL(ebt_unregister_watcher);
+EXPORT_SYMBOL(ebt_register_target);
+EXPORT_SYMBOL(ebt_unregister_target);
+EXPORT_SYMBOL(ebt_do_table);
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebtables.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,358 @@
+/*
+ * ebtables
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ * ebtables.c,v 2.0, April, 2002
+ *
+ * This code is stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+// [gs]etsockopt numbers
+#define EBT_BASE_CTL 128
+
+#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX (EBT_SO_GET_INIT_ENTRIES+1)
+
+// verdicts >0 are "branches"
+#define EBT_ACCEPT -1
+#define EBT_DROP -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN -4
+#define NUM_STANDARD_TARGETS 4
+
+// return values for match() functions
+#define EBT_MATCH 0
+#define EBT_NOMATCH 1
+
+struct ebt_counter
+{
+ uint64_t pcnt;
+};
+
+struct ebt_entries {
+ // this field is always set to zero
+ // See EBT_ENTRY_OR_ENTRIES.
+ // Must be same size as ebt_entry.bitmask
+ unsigned int distinguisher;
+ // the chain name
+ char name[EBT_CHAIN_MAXNAMELEN];
+ // counter offset for this chain
+ unsigned int counter_offset;
+ // one standard (accept, drop, return) per hook
+ int policy;
+ // nr. of entries
+ unsigned int nentries;
+ // entry list
+ char data[0];
+};
+
+// used for the bitmask of struct ebt_entry
+
+// This is a hack to make a difference between an ebt_entry struct and an
+// ebt_entries struct when traversing the entries from start to end.
+// Using this simplifies the code alot, while still being able to use
+// ebt_entries.
+// Contrary, iptables doesn't use something like ebt_entries and therefore
uses
+// different techniques for naming the policy and such. So, iptables doesn't
+// need a hack like this.
+#define EBT_ENTRY_OR_ENTRIES 0x01
+// these are the normal masks
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+ | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match
+{
+ union {
+ char name[EBT_FUNCTION_MAXNAMELEN];
+ struct ebt_match *match;
+ } u;
+ // size of data
+ unsigned int match_size;
+ unsigned char data[0];
+};
+
+struct ebt_entry_watcher
+{
+ union {
+ char name[EBT_FUNCTION_MAXNAMELEN];
+ struct ebt_watcher *watcher;
+ } u;
+ // size of data
+ unsigned int watcher_size;
+ unsigned char data[0];
+};
+
+struct ebt_entry_target
+{
+ union {
+ char name[EBT_FUNCTION_MAXNAMELEN];
+ struct ebt_target *target;
+ } u;
+ // size of data
+ unsigned int target_size;
+ unsigned char data[0];
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+ struct ebt_entry_target target;
+ int verdict;
+};
+
+// one entry
+struct ebt_entry {
+ // this needs to be the first field
+ unsigned int bitmask;
+ unsigned int invflags;
+ uint16_t ethproto;
+ // the physical in-dev
+ char in[IFNAMSIZ];
+ // the logical in-dev
+ char logical_in[IFNAMSIZ];
+ // the physical out-dev
+ char out[IFNAMSIZ];
+ // the logical out-dev
+ char logical_out[IFNAMSIZ];
+ unsigned char sourcemac[ETH_ALEN];
+ unsigned char sourcemsk[ETH_ALEN];
+ unsigned char destmac[ETH_ALEN];
+ unsigned char destmsk[ETH_ALEN];
+ // sizeof ebt_entry + matches
+ unsigned int watchers_offset;
+ // sizeof ebt_entry + matches + watchers
+ unsigned int target_offset;
+ // sizeof ebt_entry + matches + watchers + target
+ unsigned int next_offset;
+ unsigned char elems[0];
+};
+
+struct ebt_replace
+{
+ char name[EBT_TABLE_MAXNAMELEN];
+ unsigned int valid_hooks;
+ // nr of rules in the table
+ unsigned int nentries;
+ // total size of the entries
+ unsigned int entries_size;
+ // start of the chains
+ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+ // nr of counters userspace expects back
+ unsigned int num_counters;
+ // where the kernel will put the old counters
+ struct ebt_counter *counters;
+ char *entries;
+};
+
+#ifdef __KERNEL__
+
+struct ebt_match
+{
+ struct list_head list;
+ const char name[EBT_FUNCTION_MAXNAMELEN];
+ // 0 == it matches
+ int (*match)(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *matchdata,
+ unsigned int datalen);
+ // 0 == let it in
+ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+ void (*destroy)(void *matchdata, unsigned int datalen);
+ struct module *me;
+};
+
+struct ebt_watcher
+{
+ struct list_head list;
+ const char name[EBT_FUNCTION_MAXNAMELEN];
+ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *watcherdata,
+ unsigned int datalen);
+ // 0 == let it in
+ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+ void (*destroy)(void *watcherdata, unsigned int datalen);
+ struct module *me;
+};
+
+struct ebt_target
+{
+ struct list_head list;
+ const char name[EBT_FUNCTION_MAXNAMELEN];
+ // returns one of the standard verdicts
+ int (*target)(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *targetdata, unsigned int datalen);
+ // 0 == let it in
+ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+ void (*destroy)(void *targetdata, unsigned int datalen);
+ struct module *me;
+};
+
+// used for jumping from and into user defined chains (udc)
+struct ebt_chainstack
+{
+ struct ebt_entries *chaininfo; // pointer to chain data
+ struct ebt_entry *e; // pointer to entry data
+ unsigned int n; // n'th entry
+};
+
+struct ebt_table_info
+{
+ // total size of the entries
+ unsigned int entries_size;
+ unsigned int nentries;
+ // pointers to the start of the chains
+ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+ // room to maintain the stack used for jumping from and into udc
+ struct ebt_chainstack **chainstack;
+ char *entries;
+ struct ebt_counter counters[0] ____cacheline_aligned;
+};
+
+struct ebt_table
+{
+ struct list_head list;
+ char name[EBT_TABLE_MAXNAMELEN];
+ struct ebt_replace *table;
+ unsigned int valid_hooks;
+ rwlock_t lock;
+ // e.g. could be the table explicitly only allows certain
+ // matches, targets, ... 0 == let it in
+ int (*check)(const struct ebt_table_info *info,
+ unsigned int valid_hooks);
+ // the data used by the kernel
+ struct ebt_table_info *private;
+};
+
+extern int ebt_register_table(struct ebt_table *table);
+extern void ebt_unregister_table(struct ebt_table *table);
+extern int ebt_register_match(struct ebt_match *match);
+extern void ebt_unregister_match(struct ebt_match *match);
+extern int ebt_register_watcher(struct ebt_watcher *watcher);
+extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
+extern int ebt_register_target(struct ebt_target *target);
+extern void ebt_unregister_target(struct ebt_target *target);
+extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ struct ebt_table *table);
+
+ // Used in the kernel match() functions
+#define FWINV(bool,invflg) ((bool) ^ !!(info->invflags & invflg))
+// True if the hook mask denotes that the rule is in a base chain,
+// used in the check() functions
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+// Clear the bit in the hook mask that tells if the rule is on a base chain
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+// True if the target is not a standard target
+#define INVALID_TARGET (info->target < -NUM_STANDARD_TARGETS || info->target
>= 0)
+
+#endif /* __KERNEL__ */
+
+// blatently stolen from ip_tables.h
+// fn returns 0 to continue iteration
+#define EBT_MATCH_ITERATE(e, fn, args...) \
+({ \
+ unsigned int __i; \
+ int __ret = 0; \
+ struct ebt_entry_match *__match; \
+ \
+ for (__i = sizeof(struct ebt_entry); \
+ __i < (e)->watchers_offset; \
+ __i += __match->match_size + \
+ sizeof(struct ebt_entry_match)) { \
+ __match = (void *)(e) + __i; \
+ \
+ __ret = fn(__match , ## args); \
+ if (__ret != 0) \
+ break; \
+ } \
+ if (__ret == 0) { \
+ if (__i != (e)->watchers_offset) \
+ __ret = -EINVAL; \
+ } \
+ __ret; \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...) \
+({ \
+ unsigned int __i; \
+ int __ret = 0; \
+ struct ebt_entry_watcher *__watcher; \
+ \
+ for (__i = e->watchers_offset; \
+ __i < (e)->target_offset; \
+ __i += __watcher->watcher_size + \
+ sizeof(struct ebt_entry_watcher)) { \
+ __watcher = (void *)(e) + __i; \
+ \
+ __ret = fn(__watcher , ## args); \
+ if (__ret != 0) \
+ break; \
+ } \
+ if (__ret == 0) { \
+ if (__i != (e)->target_offset) \
+ __ret = -EINVAL; \
+ } \
+ __ret; \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
+({ \
+ unsigned int __i; \
+ int __ret = 0; \
+ struct ebt_entry *__entry; \
+ \
+ for (__i = 0; __i < (size);) { \
+ __entry = (void *)(entries) + __i; \
+ __ret = fn(__entry , ## args); \
+ if (__ret != 0) \
+ break; \
+ if (__entry->bitmask != 0) \
+ __i += __entry->next_offset; \
+ else \
+ __i += sizeof(struct ebt_entries); \
+ } \
+ if (__ret == 0) { \
+ if (__i != (size)) \
+ __ret = -EINVAL; \
+ } \
+ __ret; \
+})
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_arp.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,26 @@
+#ifndef __LINUX_BRIDGE_EBT_ARP_H
+#define __LINUX_BRIDGE_EBT_ARP_H
+
+#define EBT_ARP_OPCODE 0x01
+#define EBT_ARP_HTYPE 0x02
+#define EBT_ARP_PTYPE 0x04
+#define EBT_ARP_SRC_IP 0x08
+#define EBT_ARP_DST_IP 0x10
+#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
+ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
+#define EBT_ARP_MATCH "arp"
+
+struct ebt_arp_info
+{
+ uint16_t htype;
+ uint16_t ptype;
+ uint16_t opcode;
+ uint32_t saddr;
+ uint32_t smsk;
+ uint32_t daddr;
+ uint32_t dmsk;
+ uint8_t bitmask;
+ uint8_t invflags;
+};
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_ip.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,24 @@
+#ifndef __LINUX_BRIDGE_EBT_IP_H
+#define __LINUX_BRIDGE_EBT_IP_H
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#define EBT_IP_PROTO 0x08
+#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
+#define EBT_IP_MATCH "ip"
+
+// the same values are used for the invflags
+struct ebt_ip_info
+{
+ uint32_t saddr;
+ uint32_t daddr;
+ uint32_t smsk;
+ uint32_t dmsk;
+ uint8_t tos;
+ uint8_t protocol;
+ uint8_t bitmask;
+ uint8_t invflags;
+};
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_vlan.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,20 @@
+#ifndef __LINUX_BRIDGE_EBT_VLAN_H
+#define __LINUX_BRIDGE_EBT_VLAN_H
+
+#define EBT_VLAN_ID 0x01
+#define EBT_VLAN_PRIO 0x02
+#define EBT_VLAN_ENCAP 0x04
+#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+#define EBT_VLAN_MATCH "vlan"
+
+struct ebt_vlan_info {
+ uint16_t id; /* VLAN ID {1-4095} */
+ uint8_t prio; /* VLAN User Priority {0-7} */
+ uint16_t encap; /* VLAN Encapsulated frame code {0-65535} */
+ uint8_t bitmask; /* Args bitmask bit 1=1 - ID arg,
+ bit 2=1 User-Priority arg, bit 3=1 encap*/
+ uint8_t invflags; /* Inverse bitmask bit 1=1 - inversed ID arg,
+ bit 2=1 - inversed Pirority arg */
+};
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_log.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,17 @@
+#ifndef __LINUX_BRIDGE_EBT_LOG_H
+#define __LINUX_BRIDGE_EBT_LOG_H
+
+#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
+#define EBT_LOG_ARP 0x02
+#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
+#define EBT_LOG_PREFIX_SIZE 30
+#define EBT_LOG_WATCHER "log"
+
+struct ebt_log_info
+{
+ uint8_t loglevel;
+ uint8_t prefix[EBT_LOG_PREFIX_SIZE];
+ uint32_t bitmask;
+};
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_nat.h Wed Sep 11
23:06:55 2002
@@ -0,0 +1,13 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+struct ebt_nat_info
+{
+ unsigned char mac[ETH_ALEN];
+ // EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
+ int target;
+};
+#define EBT_SNAT_TARGET "snat"
+#define EBT_DNAT_TARGET "dnat"
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_redirect.h Wed
Sep 11 23:06:55 2002
@@ -0,0 +1,11 @@
+#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
+#define __LINUX_BRIDGE_EBT_REDIRECT_H
+
+struct ebt_redirect_info
+{
+ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+ int target;
+};
+#define EBT_REDIRECT_TARGET "redirect"
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_mark_m.h Wed Sep
11 23:06:55 2002
@@ -0,0 +1,15 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
+#define __LINUX_BRIDGE_EBT_MARK_M_H
+
+#define EBT_MARK_AND 0x01
+#define EBT_MARK_OR 0x02
+#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
+struct ebt_mark_m_info
+{
+ unsigned long mark, mask;
+ uint8_t invert;
+ uint8_t bitmask;
+};
+#define EBT_MARK_MATCH "mark_m"
+
+#endif
--- /dev/null Thu Aug 24 11:00:32 2000
+++ linux-2.5.34-ebtables/include/linux/netfilter_bridge/ebt_mark_t.h Wed Sep
11 23:06:55 2002
@@ -0,0 +1,12 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
+#define __LINUX_BRIDGE_EBT_MARK_T_H
+
+struct ebt_mark_t_info
+{
+ unsigned long mark;
+ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
+ int target;
+};
+#define EBT_MARK_TARGET "mark"
+
+#endif
^ permalink raw reply [flat|nested] 39+ messages in thread
end of thread, other threads:[~2002-10-28 13:04 UTC | newest]
Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-09-11 22:32 802.1q + device removal causing hang Simon Kirby
2002-09-11 22:31 ` David S. Miller
2002-09-12 6:36 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
2002-09-12 23:04 ` David S. Miller
2002-09-13 3:20 ` Bart De Schuymer
2002-09-13 4:29 ` David S. Miller
2002-09-13 6:12 ` Bart De Schuymer
2002-09-13 6:09 ` David S. Miller
2002-09-13 12:45 ` bridge-netfilter patch (was: Re: [PATCH] ebtables - Ethernet bridge tables, for 2.5.34) Lennert Buytenhek
2002-09-13 18:22 ` bridge-netfilter patch David S. Miller
2002-09-14 7:05 ` Bart De Schuymer
2002-09-16 3:35 ` David S. Miller
2002-09-16 21:41 ` Bart De Schuymer
2002-09-16 23:21 ` David S. Miller
2002-09-17 19:10 ` Bart De Schuymer
2002-09-17 19:35 ` David S. Miller
2002-09-15 21:27 ` Lennert Buytenhek
2002-09-16 6:50 ` [PATCH] ebtables - Ethernet bridge tables, for 2.5.35 Bart De Schuymer
2002-09-16 23:01 ` David S. Miller
2002-10-14 18:05 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.42 Bart De Schuymer
2002-10-14 18:01 ` David S. Miller
2002-10-14 18:32 ` bert hubert
2002-10-14 18:58 ` Bart De Schuymer
2002-10-14 19:02 ` David S. Miller
2002-10-14 19:29 ` Bart De Schuymer
2002-10-14 19:26 ` David S. Miller
2002-10-20 22:20 ` [RFC] bridge-nf -- map IPv4 hooks onto bridge hooks, vs 2.5.44 Bart De Schuymer
2002-10-20 22:19 ` David S. Miller
2002-10-22 23:40 ` Bart De Schuymer
2002-10-25 6:01 ` [PATCH][RFC] bridge-nf -- map IPv4 hooks onto bridge hooks - try 3, " Bart De Schuymer
2002-10-25 6:22 ` [netfilter-core] " Harald Welte
2002-10-28 13:02 ` David S. Miller
[not found] ` <200210141953.38933.bart.de.schuymer@pandora.be>
2002-10-14 19:59 ` [RFC] place to put bridge-netfilter specific data in the skbuff Bart De Schuymer
2002-10-24 8:16 ` [netfilter-core] " Harald Welte
2002-10-24 8:15 ` David S. Miller
2002-10-24 12:22 ` Harald Welte
2002-09-12 23:49 ` 802.1q + device removal causing hang Simon Kirby
2002-09-12 23:53 ` David S. Miller
-- strict thread matches above, loose matches on Subject: below --
2002-09-12 17:07 [PATCH] ebtables - Ethernet bridge tables, for 2.5.34 Bart De Schuymer
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.