Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
690
net/netfilter/Kconfig
Normal file
690
net/netfilter/Kconfig
Normal file
@@ -0,0 +1,690 @@
|
||||
menu "Core Netfilter Configuration"
|
||||
depends on NET && INET && NETFILTER
|
||||
|
||||
config NETFILTER_NETLINK
|
||||
tristate "Netfilter netlink interface"
|
||||
help
|
||||
If this option is enabled, the kernel will include support
|
||||
for the new netfilter netlink interface.
|
||||
|
||||
config NETFILTER_NETLINK_QUEUE
|
||||
tristate "Netfilter NFQUEUE over NFNETLINK interface"
|
||||
depends on NETFILTER_NETLINK
|
||||
help
|
||||
If this option is enabled, the kernel will include support
|
||||
for queueing packets via NFNETLINK.
|
||||
|
||||
config NETFILTER_NETLINK_LOG
|
||||
tristate "Netfilter LOG over NFNETLINK interface"
|
||||
depends on NETFILTER_NETLINK
|
||||
help
|
||||
If this option is enabled, the kernel will include support
|
||||
for logging packets via NFNETLINK.
|
||||
|
||||
This obsoletes the existing ipt_ULOG and ebg_ulog mechanisms,
|
||||
and is also scheduled to replace the old syslog-based ipt_LOG
|
||||
and ip6t_LOG modules.
|
||||
|
||||
config NF_CONNTRACK_ENABLED
|
||||
tristate "Netfilter connection tracking support"
|
||||
help
|
||||
Connection tracking keeps a record of what packets have passed
|
||||
through your machine, in order to figure out how they are related
|
||||
into connections.
|
||||
|
||||
This is required to do Masquerading or other kinds of Network
|
||||
Address Translation (except for Fast NAT). It can also be used to
|
||||
enhance packet filtering (see `Connection state match support'
|
||||
below).
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
choice
|
||||
prompt "Netfilter connection tracking support"
|
||||
depends on NF_CONNTRACK_ENABLED
|
||||
|
||||
config NF_CONNTRACK_SUPPORT
|
||||
bool "Layer 3 Independent Connection tracking"
|
||||
help
|
||||
Layer 3 independent connection tracking is experimental scheme
|
||||
which generalize ip_conntrack to support other layer 3 protocols.
|
||||
|
||||
This is required to do Masquerading or other kinds of Network
|
||||
Address Translation (except for Fast NAT). It can also be used to
|
||||
enhance packet filtering (see `Connection state match support'
|
||||
below).
|
||||
|
||||
config IP_NF_CONNTRACK_SUPPORT
|
||||
bool "Layer 3 Dependent Connection tracking (OBSOLETE)"
|
||||
help
|
||||
The old, Layer 3 dependent ip_conntrack subsystem of netfilter.
|
||||
|
||||
This is required to do Masquerading or other kinds of Network
|
||||
Address Translation (except for Fast NAT). It can also be used to
|
||||
enhance packet filtering (see `Connection state match support'
|
||||
below).
|
||||
|
||||
endchoice
|
||||
|
||||
config NF_CONNTRACK
|
||||
tristate
|
||||
default m if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m
|
||||
default y if NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y
|
||||
|
||||
config IP_NF_CONNTRACK
|
||||
tristate
|
||||
default m if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=m
|
||||
default y if IP_NF_CONNTRACK_SUPPORT && NF_CONNTRACK_ENABLED=y
|
||||
|
||||
config NF_CT_ACCT
|
||||
bool "Connection tracking flow accounting"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
If this option is enabled, the connection tracking code will
|
||||
keep per-flow packet and byte counters.
|
||||
|
||||
Those counters can be used for flow-based accounting or the
|
||||
`connbytes' match.
|
||||
|
||||
If unsure, say `N'.
|
||||
|
||||
config NF_CONNTRACK_MARK
|
||||
bool 'Connection mark tracking support'
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
This option enables support for connection marks, used by the
|
||||
`CONNMARK' target and `connmark' match. Similar to the mark value
|
||||
of packets, but this mark value is kept in the conntrack session
|
||||
instead of the individual packets.
|
||||
|
||||
config NF_CONNTRACK_SECMARK
|
||||
bool 'Connection tracking security mark support'
|
||||
depends on NF_CONNTRACK && NETWORK_SECMARK
|
||||
help
|
||||
This option enables security markings to be applied to
|
||||
connections. Typically they are copied to connections from
|
||||
packets using the CONNSECMARK target and copied back from
|
||||
connections to packets with the same target, with the packets
|
||||
being originally labeled via SECMARK.
|
||||
|
||||
If unsure, say 'N'.
|
||||
|
||||
config NF_CONNTRACK_EVENTS
|
||||
bool "Connection tracking events (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
help
|
||||
If this option is enabled, the connection tracking code will
|
||||
provide a notifier chain that can be used by other kernel code
|
||||
to get notified about changes in the connection tracking state.
|
||||
|
||||
If unsure, say `N'.
|
||||
|
||||
config NF_CT_PROTO_GRE
|
||||
tristate
|
||||
depends on NF_CONNTRACK
|
||||
|
||||
config NF_CT_PROTO_SCTP
|
||||
tristate 'SCTP protocol connection tracking support (EXPERIMENTAL)'
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
default n
|
||||
help
|
||||
With this option enabled, the layer 3 independent connection
|
||||
tracking code will be able to do state tracking on SCTP connections.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
Documentation/modules.txt. If unsure, say `N'.
|
||||
|
||||
config NF_CONNTRACK_AMANDA
|
||||
tristate "Amanda backup protocol support"
|
||||
depends on NF_CONNTRACK
|
||||
select TEXTSEARCH
|
||||
select TEXTSEARCH_KMP
|
||||
help
|
||||
If you are running the Amanda backup package <http://www.amanda.org/>
|
||||
on this machine or machines that will be MASQUERADED through this
|
||||
machine, then you may want to enable this feature. This allows the
|
||||
connection tracking and natting code to allow the sub-channels that
|
||||
Amanda requires for communication of the backup data, messages and
|
||||
index.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_FTP
|
||||
tristate "FTP protocol support"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
Tracking FTP connections is problematic: special helpers are
|
||||
required for tracking them, and doing masquerading and other forms
|
||||
of Network Address Translation on them.
|
||||
|
||||
This is FTP support on Layer 3 independent connection tracking.
|
||||
Layer 3 independent connection tracking is experimental scheme
|
||||
which generalize ip_conntrack to support other layer 3 protocols.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_H323
|
||||
tristate "H.323 protocol support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK && (IPV6 || IPV6=n)
|
||||
help
|
||||
H.323 is a VoIP signalling protocol from ITU-T. As one of the most
|
||||
important VoIP protocols, it is widely used by voice hardware and
|
||||
software including voice gateways, IP phones, Netmeeting, OpenPhone,
|
||||
Gnomemeeting, etc.
|
||||
|
||||
With this module you can support H.323 on a connection tracking/NAT
|
||||
firewall.
|
||||
|
||||
This module supports RAS, Fast Start, H.245 Tunnelling, Call
|
||||
Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat,
|
||||
whiteboard, file transfer, etc. For more information, please
|
||||
visit http://nath323.sourceforge.net/.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_IRC
|
||||
tristate "IRC protocol support"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
There is a commonly-used extension to IRC called
|
||||
Direct Client-to-Client Protocol (DCC). This enables users to send
|
||||
files to each other, and also chat to each other without the need
|
||||
of a server. DCC Sending is used anywhere you send files over IRC,
|
||||
and DCC Chat is most commonly used by Eggdrop bots. If you are
|
||||
using NAT, this extension will enable you to send files and initiate
|
||||
chats. Note that you do NOT need this extension to get files or
|
||||
have others initiate chats, or everything else in IRC.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_NETBIOS_NS
|
||||
tristate "NetBIOS name service protocol support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
help
|
||||
NetBIOS name service requests are sent as broadcast messages from an
|
||||
unprivileged port and responded to with unicast messages to the
|
||||
same port. This make them hard to firewall properly because connection
|
||||
tracking doesn't deal with broadcasts. This helper tracks locally
|
||||
originating NetBIOS name service requests and the corresponding
|
||||
responses. It relies on correct IP address configuration, specifically
|
||||
netmask and broadcast address. When properly configured, the output
|
||||
of "ip address show" should look similar to this:
|
||||
|
||||
$ ip -4 address show eth0
|
||||
4: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 1000
|
||||
inet 172.16.2.252/24 brd 172.16.2.255 scope global eth0
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_PPTP
|
||||
tristate "PPtP protocol support"
|
||||
depends on NF_CONNTRACK
|
||||
select NF_CT_PROTO_GRE
|
||||
help
|
||||
This module adds support for PPTP (Point to Point Tunnelling
|
||||
Protocol, RFC2637) connection tracking and NAT.
|
||||
|
||||
If you are running PPTP sessions over a stateful firewall or NAT
|
||||
box, you may want to enable this feature.
|
||||
|
||||
Please note that not all PPTP modes of operation are supported yet.
|
||||
Specifically these limitations exist:
|
||||
- Blindy assumes that control connections are always established
|
||||
in PNS->PAC direction. This is a violation of RFC2637.
|
||||
- Only supports a single call within each session
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_SANE
|
||||
tristate "SANE protocol support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
help
|
||||
SANE is a protocol for remote access to scanners as implemented
|
||||
by the 'saned' daemon. Like FTP, it uses separate control and
|
||||
data connections.
|
||||
|
||||
With this module you can support SANE on a connection tracking
|
||||
firewall.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_SIP
|
||||
tristate "SIP protocol support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
help
|
||||
SIP is an application-layer control protocol that can establish,
|
||||
modify, and terminate multimedia sessions (conferences) such as
|
||||
Internet telephony calls. With the ip_conntrack_sip and
|
||||
the nf_nat_sip modules you can support the protocol on a connection
|
||||
tracking/NATing firewall.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CONNTRACK_TFTP
|
||||
tristate "TFTP protocol support"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
TFTP connection tracking helper, this is required depending
|
||||
on how restrictive your ruleset is.
|
||||
If you are using a tftp client behind -j SNAT or -j MASQUERADING
|
||||
you will need this.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CT_NETLINK
|
||||
tristate 'Connection tracking netlink interface (EXPERIMENTAL)'
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK
|
||||
depends on NF_CONNTRACK!=y || NETFILTER_NETLINK!=m
|
||||
depends on NF_NAT=n || NF_NAT
|
||||
help
|
||||
This option enables support for a netlink-based userspace interface
|
||||
|
||||
config NETFILTER_XTABLES
|
||||
tristate "Netfilter Xtables support (required for ip_tables)"
|
||||
help
|
||||
This is required if you intend to use any of ip_tables,
|
||||
ip6_tables or arp_tables.
|
||||
|
||||
# alphabetically ordered list of targets
|
||||
|
||||
config NETFILTER_XT_TARGET_CLASSIFY
|
||||
tristate '"CLASSIFY" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `CLASSIFY' target, which enables the user to set
|
||||
the priority of a packet. Some qdiscs can use this value for
|
||||
classification, among these are:
|
||||
|
||||
atm, cbq, dsmark, pfifo_fast, htb, prio
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_CONNMARK
|
||||
tristate '"CONNMARK" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_MANGLE || IP6_NF_MANGLE
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK
|
||||
select NF_CONNTRACK_MARK if NF_CONNTRACK
|
||||
help
|
||||
This option adds a `CONNMARK' target, which allows one to manipulate
|
||||
the connection mark value. Similar to the MARK target, but
|
||||
affects the connection mark value rather than the packet mark value.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. The module will be called
|
||||
ipt_CONNMARK.o. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_TARGET_DSCP
|
||||
tristate '"DSCP" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_MANGLE || IP6_NF_MANGLE
|
||||
help
|
||||
This option adds a `DSCP' target, which allows you to manipulate
|
||||
the IPv4/IPv6 header DSCP field (differentiated services codepoint).
|
||||
|
||||
The DSCP field can have any value between 0x0 and 0x3f inclusive.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_MARK
|
||||
tristate '"MARK" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `MARK' target, which allows you to create rules
|
||||
in the `mangle' table which alter the netfilter mark (nfmark) field
|
||||
associated with the packet prior to routing. This can change
|
||||
the routing method (see `Use netfilter MARK value as routing
|
||||
key') and can also be used by other subsystems to change their
|
||||
behavior.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_NFQUEUE
|
||||
tristate '"NFQUEUE" target Support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This target replaced the old obsolete QUEUE target.
|
||||
|
||||
As opposed to QUEUE, it supports 65535 different queues,
|
||||
not just one.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_NFLOG
|
||||
tristate '"NFLOG" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option enables the NFLOG target, which allows to LOG
|
||||
messages through the netfilter logging API, which can use
|
||||
either the old LOG target, the old ULOG target or nfnetlink_log
|
||||
as backend.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_NOTRACK
|
||||
tristate '"NOTRACK" target support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_RAW || IP6_NF_RAW
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
help
|
||||
The NOTRACK target allows a select rule to specify
|
||||
which packets *not* to enter the conntrack/NAT
|
||||
subsystem with all the consequences (no ICMP error tracking,
|
||||
no protocol helpers for the selected packets).
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_TARGET_SECMARK
|
||||
tristate '"SECMARK" target support'
|
||||
depends on NETFILTER_XTABLES && NETWORK_SECMARK
|
||||
help
|
||||
The SECMARK target allows security marking of network
|
||||
packets, for use with security subsystems.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_CONNSECMARK
|
||||
tristate '"CONNSECMARK" target support'
|
||||
depends on NETFILTER_XTABLES && \
|
||||
((NF_CONNTRACK && NF_CONNTRACK_SECMARK) || \
|
||||
(IP_NF_CONNTRACK && IP_NF_CONNTRACK_SECMARK))
|
||||
help
|
||||
The CONNSECMARK target copies security markings from packets
|
||||
to connections, and restores security markings from connections
|
||||
to packets (if the packets are not already marked). This would
|
||||
normally be used in conjunction with the SECMARK target.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_TARGET_TCPMSS
|
||||
tristate '"TCPMSS" target support'
|
||||
depends on NETFILTER_XTABLES && (IPV6 || IPV6=n)
|
||||
---help---
|
||||
This option adds a `TCPMSS' target, which allows you to alter the
|
||||
MSS value of TCP SYN packets, to control the maximum size for that
|
||||
connection (usually limiting it to your outgoing interface's MTU
|
||||
minus 40).
|
||||
|
||||
This is used to overcome criminally braindead ISPs or servers which
|
||||
block ICMP Fragmentation Needed packets. The symptoms of this
|
||||
problem are that everything works fine from your Linux
|
||||
firewall/router, but machines behind it can never exchange large
|
||||
packets:
|
||||
1) Web browsers connect, then hang with no data received.
|
||||
2) Small mail works fine, but large emails hang.
|
||||
3) ssh works fine, but scp hangs after initial handshaking.
|
||||
|
||||
Workaround: activate this option and add a rule to your firewall
|
||||
configuration like:
|
||||
|
||||
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
|
||||
-j TCPMSS --clamp-mss-to-pmtu
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_COMMENT
|
||||
tristate '"comment" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `comment' dummy-match, which allows you to put
|
||||
comments in your iptables ruleset.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_CONNBYTES
|
||||
tristate '"connbytes" per-connection counter match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
select IP_NF_CT_ACCT if IP_NF_CONNTRACK
|
||||
select NF_CT_ACCT if NF_CONNTRACK
|
||||
help
|
||||
This option adds a `connbytes' match, which allows you to match the
|
||||
number of bytes and/or packets for each direction within a connection.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_CONNMARK
|
||||
tristate '"connmark" connection mark match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
select IP_NF_CONNTRACK_MARK if IP_NF_CONNTRACK
|
||||
select NF_CONNTRACK_MARK if NF_CONNTRACK
|
||||
help
|
||||
This option adds a `connmark' match, which allows you to match the
|
||||
connection mark value previously set for the session by `CONNMARK'.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. The module will be called
|
||||
ipt_connmark.o. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_CONNTRACK
|
||||
tristate '"conntrack" connection tracking match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
help
|
||||
This is a general conntrack match module, a superset of the state match.
|
||||
|
||||
It allows matching on additional conntrack information, which is
|
||||
useful in complex configurations, such as NAT gateways with multiple
|
||||
internet links or tunnels.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_DCCP
|
||||
tristate '"DCCP" protocol match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
With this option enabled, you will be able to use the iptables
|
||||
`dccp' match in order to match on DCCP source/destination ports
|
||||
and DCCP flags.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_DSCP
|
||||
tristate '"DSCP" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `DSCP' match, which allows you to match against
|
||||
the IPv4/IPv6 header DSCP field (differentiated services codepoint).
|
||||
|
||||
The DSCP field can have any value between 0x0 and 0x3f inclusive.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_ESP
|
||||
tristate '"ESP" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This match extension allows you to match a range of SPIs
|
||||
inside ESP header of IPSec packets.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_HELPER
|
||||
tristate '"helper" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
help
|
||||
Helper matching allows you to match packets in dynamic connections
|
||||
tracked by a conntrack-helper, ie. ip_conntrack_ftp
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say Y.
|
||||
|
||||
config NETFILTER_XT_MATCH_LENGTH
|
||||
tristate '"length" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option allows you to match the length of a packet against a
|
||||
specific value or range of values.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_LIMIT
|
||||
tristate '"limit" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
limit matching allows you to control the rate at which a rule can be
|
||||
matched: mainly useful in combination with the LOG target ("LOG
|
||||
target support", below) and to avoid some Denial of Service attacks.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_MAC
|
||||
tristate '"mac" address match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
MAC matching allows you to match packets based on the source
|
||||
Ethernet address of the packet.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_MARK
|
||||
tristate '"mark" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
Netfilter mark matching allows you to match packets based on the
|
||||
`nfmark' value in the packet. This can be set by the MARK target
|
||||
(see below).
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_POLICY
|
||||
tristate 'IPsec "policy" match support'
|
||||
depends on NETFILTER_XTABLES && XFRM
|
||||
help
|
||||
Policy matching allows you to match packets based on the
|
||||
IPsec policy that was used during decapsulation/will
|
||||
be used during encapsulation.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_MULTIPORT
|
||||
tristate "Multiple port match support"
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
Multiport matching allows you to match TCP or UDP packets based on
|
||||
a series of source or destination ports: normally a rule can only
|
||||
match a single range of ports.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_PHYSDEV
|
||||
tristate '"physdev" match support'
|
||||
depends on NETFILTER_XTABLES && BRIDGE && BRIDGE_NETFILTER
|
||||
help
|
||||
Physdev packet matching matches against the physical bridge ports
|
||||
the IP packet arrived on or will leave by.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_PKTTYPE
|
||||
tristate '"pkttype" packet type match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
Packet type matching allows you to match a packet by
|
||||
its "class", eg. BROADCAST, MULTICAST, ...
|
||||
|
||||
Typical usage:
|
||||
iptables -A INPUT -m pkttype --pkt-type broadcast -j LOG
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_QUOTA
|
||||
tristate '"quota" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `quota' match, which allows to match on a
|
||||
byte counter.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_REALM
|
||||
tristate '"realm" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
select NET_CLS_ROUTE
|
||||
help
|
||||
This option adds a `realm' match, which allows you to use the realm
|
||||
key from the routing subsystem inside iptables.
|
||||
|
||||
This match pretty much resembles the CONFIG_NET_CLS_ROUTE4 option
|
||||
in tc world.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_SCTP
|
||||
tristate '"sctp" protocol match support (EXPERIMENTAL)'
|
||||
depends on NETFILTER_XTABLES && EXPERIMENTAL
|
||||
help
|
||||
With this option enabled, you will be able to use the
|
||||
`sctp' match in order to match on SCTP source/destination ports
|
||||
and SCTP chunk types.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
<file:Documentation/modules.txt>. If unsure, say `N'.
|
||||
|
||||
config NETFILTER_XT_MATCH_STATE
|
||||
tristate '"state" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
depends on IP_NF_CONNTRACK || NF_CONNTRACK
|
||||
help
|
||||
Connection state matching allows you to match packets based on their
|
||||
relationship to a tracked connection (ie. previous packets). This
|
||||
is a powerful tool for packet classification.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_STATISTIC
|
||||
tristate '"statistic" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `statistic' match, which allows you to match
|
||||
on packets periodically or randomly with a given percentage.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_STRING
|
||||
tristate '"string" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
select TEXTSEARCH
|
||||
select TEXTSEARCH_KMP
|
||||
select TEXTSEARCH_BM
|
||||
select TEXTSEARCH_FSM
|
||||
help
|
||||
This option adds a `string' match, which allows you to look for
|
||||
pattern matchings in packets.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_TCPMSS
|
||||
tristate '"tcpmss" match support'
|
||||
depends on NETFILTER_XTABLES
|
||||
help
|
||||
This option adds a `tcpmss' match, which allows you to examine the
|
||||
MSS value of TCP SYN packets, which control the maximum packet size
|
||||
for that connection.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NETFILTER_XT_MATCH_HASHLIMIT
|
||||
tristate '"hashlimit" match support'
|
||||
depends on NETFILTER_XTABLES && (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)
|
||||
help
|
||||
This option adds a `hashlimit' match.
|
||||
|
||||
As opposed to `limit', this match dynamically creates a hash table
|
||||
of limit buckets, based on your selection of source/destination
|
||||
addresses and/or ports.
|
||||
|
||||
It enables you to express policies like `10kpps for any given
|
||||
destination address' or `500pps from any given source address'
|
||||
with a single rule.
|
||||
|
||||
endmenu
|
||||
|
||||
75
net/netfilter/Makefile
Normal file
75
net/netfilter/Makefile
Normal file
@@ -0,0 +1,75 @@
|
||||
netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o
|
||||
|
||||
nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o
|
||||
nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
|
||||
|
||||
obj-$(CONFIG_NETFILTER) = netfilter.o
|
||||
obj-$(CONFIG_SYSCTL) += nf_sysctl.o
|
||||
|
||||
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
|
||||
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
|
||||
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
|
||||
|
||||
# connection tracking
|
||||
obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
|
||||
|
||||
# SCTP protocol connection tracking
|
||||
obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o
|
||||
obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o
|
||||
|
||||
# netlink interface for nf_conntrack
|
||||
obj-$(CONFIG_NF_CT_NETLINK) += nf_conntrack_netlink.o
|
||||
|
||||
# connection tracking helpers
|
||||
nf_conntrack_h323-objs := nf_conntrack_h323_main.o nf_conntrack_h323_asn1.o
|
||||
|
||||
obj-$(CONFIG_NF_CONNTRACK_AMANDA) += nf_conntrack_amanda.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o
|
||||
|
||||
# generic X tables
|
||||
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
|
||||
|
||||
# targets
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
|
||||
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
|
||||
|
||||
# matches
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNMARK) += xt_connmark.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_DCCP) += xt_dccp.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
|
||||
obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o
|
||||
290
net/netfilter/core.c
Normal file
290
net/netfilter/core.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/* netfilter.c: look after the filters for various protocols.
|
||||
* Heavily influenced by the old firewall.c by David Bonn and Alan Cox.
|
||||
*
|
||||
* Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
|
||||
* way.
|
||||
*
|
||||
* Rusty Russell (C)2000 -- This code is GPL.
|
||||
*
|
||||
* February 2000: Modified by James Morris to have 1 queue per protocol.
|
||||
* 15-Mar-2000: Added NF_REPEAT --RR.
|
||||
* 08-May-2003: Internal logging interface added by Jozsef Kadlecsik.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/protocol.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "nf_internals.h"
|
||||
|
||||
static DEFINE_MUTEX(afinfo_mutex);
|
||||
|
||||
struct nf_afinfo *nf_afinfo[NPROTO] __read_mostly;
|
||||
EXPORT_SYMBOL(nf_afinfo);
|
||||
|
||||
int nf_register_afinfo(struct nf_afinfo *afinfo)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&afinfo_mutex);
|
||||
if (err < 0)
|
||||
return err;
|
||||
rcu_assign_pointer(nf_afinfo[afinfo->family], afinfo);
|
||||
mutex_unlock(&afinfo_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_register_afinfo);
|
||||
|
||||
void nf_unregister_afinfo(struct nf_afinfo *afinfo)
|
||||
{
|
||||
mutex_lock(&afinfo_mutex);
|
||||
rcu_assign_pointer(nf_afinfo[afinfo->family], NULL);
|
||||
mutex_unlock(&afinfo_mutex);
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_unregister_afinfo);
|
||||
|
||||
/* In this code, we can be waiting indefinitely for userspace to
|
||||
* service a packet if a hook returns NF_QUEUE. We could keep a count
|
||||
* of skbuffs queued for userspace, and not deregister a hook unless
|
||||
* this is zero, but that sucks. Now, we simply check when the
|
||||
* packets come back: if the hook is gone, the packet is discarded. */
|
||||
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS] __read_mostly;
|
||||
EXPORT_SYMBOL(nf_hooks);
|
||||
static DEFINE_MUTEX(nf_hook_mutex);
|
||||
|
||||
int nf_register_hook(struct nf_hook_ops *reg)
|
||||
{
|
||||
struct list_head *i;
|
||||
int err;
|
||||
|
||||
err = mutex_lock_interruptible(&nf_hook_mutex);
|
||||
if (err < 0)
|
||||
return err;
|
||||
list_for_each(i, &nf_hooks[reg->pf][reg->hooknum]) {
|
||||
if (reg->priority < ((struct nf_hook_ops *)i)->priority)
|
||||
break;
|
||||
}
|
||||
list_add_rcu(®->list, i->prev);
|
||||
mutex_unlock(&nf_hook_mutex);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_register_hook);
|
||||
|
||||
void nf_unregister_hook(struct nf_hook_ops *reg)
|
||||
{
|
||||
mutex_lock(&nf_hook_mutex);
|
||||
list_del_rcu(®->list);
|
||||
mutex_unlock(&nf_hook_mutex);
|
||||
|
||||
synchronize_net();
|
||||
}
|
||||
EXPORT_SYMBOL(nf_unregister_hook);
|
||||
|
||||
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
err = nf_register_hook(®[i]);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
return err;
|
||||
|
||||
err:
|
||||
if (i > 0)
|
||||
nf_unregister_hooks(reg, i);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_register_hooks);
|
||||
|
||||
void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
nf_unregister_hook(®[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_unregister_hooks);
|
||||
|
||||
unsigned int nf_iterate(struct list_head *head,
|
||||
struct sk_buff **skb,
|
||||
int hook,
|
||||
const struct net_device *indev,
|
||||
const struct net_device *outdev,
|
||||
struct list_head **i,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
int hook_thresh)
|
||||
{
|
||||
unsigned int verdict;
|
||||
|
||||
/*
|
||||
* The caller must not block between calls to this
|
||||
* function because of risk of continuing from deleted element.
|
||||
*/
|
||||
list_for_each_continue_rcu(*i, head) {
|
||||
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
|
||||
|
||||
if (hook_thresh > elem->priority)
|
||||
continue;
|
||||
|
||||
/* Optimization: we don't need to hold module
|
||||
reference here, since function can't sleep. --RR */
|
||||
verdict = elem->hook(hook, skb, indev, outdev, okfn);
|
||||
if (verdict != NF_ACCEPT) {
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
if (unlikely((verdict & NF_VERDICT_MASK)
|
||||
> NF_MAX_VERDICT)) {
|
||||
NFDEBUG("Evil return from %p(%u).\n",
|
||||
elem->hook, hook);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (verdict != NF_REPEAT)
|
||||
return verdict;
|
||||
*i = (*i)->prev;
|
||||
}
|
||||
}
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
/* Returns 1 if okfn() needs to be executed by the caller,
|
||||
* -EPERM for NF_DROP, 0 otherwise. */
|
||||
int nf_hook_slow(int pf, unsigned int hook, struct sk_buff **pskb,
|
||||
struct net_device *indev,
|
||||
struct net_device *outdev,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
int hook_thresh)
|
||||
{
|
||||
struct list_head *elem;
|
||||
unsigned int verdict;
|
||||
int ret = 0;
|
||||
|
||||
/* We may already have this, but read-locks nest anyway */
|
||||
rcu_read_lock();
|
||||
|
||||
elem = &nf_hooks[pf][hook];
|
||||
next_hook:
|
||||
verdict = nf_iterate(&nf_hooks[pf][hook], pskb, hook, indev,
|
||||
outdev, &elem, okfn, hook_thresh);
|
||||
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
|
||||
ret = 1;
|
||||
goto unlock;
|
||||
} else if (verdict == NF_DROP) {
|
||||
kfree_skb(*pskb);
|
||||
ret = -EPERM;
|
||||
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
|
||||
NFDEBUG("nf_hook: Verdict = QUEUE.\n");
|
||||
if (!nf_queue(*pskb, elem, pf, hook, indev, outdev, okfn,
|
||||
verdict >> NF_VERDICT_BITS))
|
||||
goto next_hook;
|
||||
}
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_hook_slow);
|
||||
|
||||
|
||||
int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len)
|
||||
{
|
||||
struct sk_buff *nskb;
|
||||
|
||||
if (writable_len > (*pskb)->len)
|
||||
return 0;
|
||||
|
||||
/* Not exclusive use of packet? Must copy. */
|
||||
if (skb_shared(*pskb) || skb_cloned(*pskb))
|
||||
goto copy_skb;
|
||||
|
||||
return pskb_may_pull(*pskb, writable_len);
|
||||
|
||||
copy_skb:
|
||||
nskb = skb_copy(*pskb, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
return 0;
|
||||
BUG_ON(skb_is_nonlinear(nskb));
|
||||
|
||||
/* Rest of kernel will get very unhappy if we pass it a
|
||||
suddenly-orphaned skbuff */
|
||||
if ((*pskb)->sk)
|
||||
skb_set_owner_w(nskb, (*pskb)->sk);
|
||||
kfree_skb(*pskb);
|
||||
*pskb = nskb;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(skb_make_writable);
|
||||
|
||||
void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
|
||||
__be32 from, __be32 to, int pseudohdr)
|
||||
{
|
||||
__be32 diff[] = { ~from, to };
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
*sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
|
||||
~csum_unfold(*sum)));
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
|
||||
skb->csum = ~csum_partial((char *)diff, sizeof(diff),
|
||||
~skb->csum);
|
||||
} else if (pseudohdr)
|
||||
*sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff),
|
||||
csum_unfold(*sum)));
|
||||
}
|
||||
EXPORT_SYMBOL(nf_proto_csum_replace4);
|
||||
|
||||
/* This does not belong here, but locally generated errors need it if connection
|
||||
tracking in use: without this, connection may not be in hash table, and hence
|
||||
manufactured ICMP or RST packets will not be associated with it. */
|
||||
void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
|
||||
EXPORT_SYMBOL(ip_ct_attach);
|
||||
|
||||
void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb)
|
||||
{
|
||||
void (*attach)(struct sk_buff *, struct sk_buff *);
|
||||
|
||||
if (skb->nfct) {
|
||||
rcu_read_lock();
|
||||
attach = rcu_dereference(ip_ct_attach);
|
||||
if (attach)
|
||||
attach(new, skb);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(nf_ct_attach);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *proc_net_netfilter;
|
||||
EXPORT_SYMBOL(proc_net_netfilter);
|
||||
#endif
|
||||
|
||||
void __init netfilter_init(void)
|
||||
{
|
||||
int i, h;
|
||||
for (i = 0; i < NPROTO; i++) {
|
||||
for (h = 0; h < NF_MAX_HOOKS; h++)
|
||||
INIT_LIST_HEAD(&nf_hooks[i][h]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc_net_netfilter = proc_mkdir("netfilter", proc_net);
|
||||
if (!proc_net_netfilter)
|
||||
panic("cannot create netfilter proc entry");
|
||||
#endif
|
||||
|
||||
if (netfilter_queue_init() < 0)
|
||||
panic("cannot initialize nf_queue");
|
||||
if (netfilter_log_init() < 0)
|
||||
panic("cannot initialize nf_log");
|
||||
}
|
||||
238
net/netfilter/nf_conntrack_amanda.c
Normal file
238
net/netfilter/nf_conntrack_amanda.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/* Amanda extension for IP connection tracking
|
||||
*
|
||||
* (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
|
||||
* based on HW's ip_conntrack_irc.c as well as other modules
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/textsearch.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/netfilter.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_amanda.h>
|
||||
|
||||
static unsigned int master_timeout __read_mostly = 300;
|
||||
static char *ts_algo = "kmp";
|
||||
|
||||
MODULE_AUTHOR("Brian J. Murrell <netfilter@interlinx.bc.ca>");
|
||||
MODULE_DESCRIPTION("Amanda connection tracking module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_conntrack_amanda");
|
||||
|
||||
module_param(master_timeout, uint, 0600);
|
||||
MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
|
||||
module_param(ts_algo, charp, 0400);
|
||||
MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
|
||||
|
||||
unsigned int (*nf_nat_amanda_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp)
|
||||
__read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_amanda_hook);
|
||||
|
||||
enum amanda_strings {
|
||||
SEARCH_CONNECT,
|
||||
SEARCH_NEWLINE,
|
||||
SEARCH_DATA,
|
||||
SEARCH_MESG,
|
||||
SEARCH_INDEX,
|
||||
};
|
||||
|
||||
static struct {
|
||||
char *string;
|
||||
size_t len;
|
||||
struct ts_config *ts;
|
||||
} search[] __read_mostly = {
|
||||
[SEARCH_CONNECT] = {
|
||||
.string = "CONNECT ",
|
||||
.len = 8,
|
||||
},
|
||||
[SEARCH_NEWLINE] = {
|
||||
.string = "\n",
|
||||
.len = 1,
|
||||
},
|
||||
[SEARCH_DATA] = {
|
||||
.string = "DATA ",
|
||||
.len = 5,
|
||||
},
|
||||
[SEARCH_MESG] = {
|
||||
.string = "MESG ",
|
||||
.len = 5,
|
||||
},
|
||||
[SEARCH_INDEX] = {
|
||||
.string = "INDEX ",
|
||||
.len = 6,
|
||||
},
|
||||
};
|
||||
|
||||
static int amanda_help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
struct ts_state ts;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_tuple *tuple;
|
||||
unsigned int dataoff, start, stop, off, i;
|
||||
char pbuf[sizeof("65535")], *tmp;
|
||||
u_int16_t len;
|
||||
__be16 port;
|
||||
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||
int ret = NF_ACCEPT;
|
||||
typeof(nf_nat_amanda_hook) nf_nat_amanda;
|
||||
|
||||
/* Only look at packets from the Amanda server */
|
||||
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* increase the UDP timeout of the master connection as replies from
|
||||
* Amanda clients to the server can be quite delayed */
|
||||
nf_ct_refresh(ct, *pskb, master_timeout * HZ);
|
||||
|
||||
/* No data? */
|
||||
dataoff = protoff + sizeof(struct udphdr);
|
||||
if (dataoff >= (*pskb)->len) {
|
||||
if (net_ratelimit())
|
||||
printk("amanda_help: skblen = %u\n", (*pskb)->len);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
start = skb_find_text(*pskb, dataoff, (*pskb)->len,
|
||||
search[SEARCH_CONNECT].ts, &ts);
|
||||
if (start == UINT_MAX)
|
||||
goto out;
|
||||
start += dataoff + search[SEARCH_CONNECT].len;
|
||||
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
stop = skb_find_text(*pskb, start, (*pskb)->len,
|
||||
search[SEARCH_NEWLINE].ts, &ts);
|
||||
if (stop == UINT_MAX)
|
||||
goto out;
|
||||
stop += start;
|
||||
|
||||
for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) {
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
off = skb_find_text(*pskb, start, stop, search[i].ts, &ts);
|
||||
if (off == UINT_MAX)
|
||||
continue;
|
||||
off += start + search[i].len;
|
||||
|
||||
len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off);
|
||||
if (skb_copy_bits(*pskb, off, pbuf, len))
|
||||
break;
|
||||
pbuf[len] = '\0';
|
||||
|
||||
port = htons(simple_strtoul(pbuf, &tmp, 10));
|
||||
len = tmp - pbuf;
|
||||
if (port == 0 || len > 5)
|
||||
break;
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
nf_conntrack_expect_init(exp, family,
|
||||
&tuple->src.u3, &tuple->dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
|
||||
nf_nat_amanda = rcu_dereference(nf_nat_amanda_hook);
|
||||
if (nf_nat_amanda && ct->status & IPS_NAT_MASK)
|
||||
ret = nf_nat_amanda(pskb, ctinfo, off - dataoff,
|
||||
len, exp);
|
||||
else if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
nf_conntrack_expect_put(exp);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper amanda_helper[2] __read_mostly = {
|
||||
{
|
||||
.name = "amanda",
|
||||
.max_expected = 3,
|
||||
.timeout = 180,
|
||||
.me = THIS_MODULE,
|
||||
.help = amanda_help,
|
||||
.tuple.src.l3num = AF_INET,
|
||||
.tuple.src.u.udp.port = __constant_htons(10080),
|
||||
.tuple.dst.protonum = IPPROTO_UDP,
|
||||
.mask.src.l3num = 0xFFFF,
|
||||
.mask.src.u.udp.port = __constant_htons(0xFFFF),
|
||||
.mask.dst.protonum = 0xFF,
|
||||
},
|
||||
{
|
||||
.name = "amanda",
|
||||
.max_expected = 3,
|
||||
.timeout = 180,
|
||||
.me = THIS_MODULE,
|
||||
.help = amanda_help,
|
||||
.tuple.src.l3num = AF_INET6,
|
||||
.tuple.src.u.udp.port = __constant_htons(10080),
|
||||
.tuple.dst.protonum = IPPROTO_UDP,
|
||||
.mask.src.l3num = 0xFFFF,
|
||||
.mask.src.u.udp.port = __constant_htons(0xFFFF),
|
||||
.mask.dst.protonum = 0xFF,
|
||||
},
|
||||
};
|
||||
|
||||
static void __exit nf_conntrack_amanda_fini(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
nf_conntrack_helper_unregister(&amanda_helper[0]);
|
||||
nf_conntrack_helper_unregister(&amanda_helper[1]);
|
||||
for (i = 0; i < ARRAY_SIZE(search); i++)
|
||||
textsearch_destroy(search[i].ts);
|
||||
}
|
||||
|
||||
static int __init nf_conntrack_amanda_init(void)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
ret = -ENOMEM;
|
||||
for (i = 0; i < ARRAY_SIZE(search); i++) {
|
||||
search[i].ts = textsearch_prepare(ts_algo, search[i].string,
|
||||
search[i].len,
|
||||
GFP_KERNEL, TS_AUTOLOAD);
|
||||
if (search[i].ts == NULL)
|
||||
goto err1;
|
||||
}
|
||||
ret = nf_conntrack_helper_register(&amanda_helper[0]);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
ret = nf_conntrack_helper_register(&amanda_helper[1]);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
nf_conntrack_helper_unregister(&amanda_helper[0]);
|
||||
err1:
|
||||
for (; i >= 0; i--) {
|
||||
if (search[i].ts)
|
||||
textsearch_destroy(search[i].ts);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_amanda_init);
|
||||
module_exit(nf_conntrack_amanda_fini);
|
||||
1312
net/netfilter/nf_conntrack_core.c
Normal file
1312
net/netfilter/nf_conntrack_core.c
Normal file
File diff suppressed because it is too large
Load Diff
93
net/netfilter/nf_conntrack_ecache.c
Normal file
93
net/netfilter/nf_conntrack_ecache.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Event cache for netfilter. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(nf_conntrack_chain);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_chain);
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(nf_conntrack_expect_chain);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_chain);
|
||||
|
||||
DEFINE_PER_CPU(struct nf_conntrack_ecache, nf_conntrack_ecache);
|
||||
EXPORT_PER_CPU_SYMBOL_GPL(nf_conntrack_ecache);
|
||||
|
||||
/* deliver cached events and clear cache entry - must be called with locally
|
||||
* disabled softirqs */
|
||||
static inline void
|
||||
__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
|
||||
{
|
||||
if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct)
|
||||
&& ecache->events)
|
||||
atomic_notifier_call_chain(&nf_conntrack_chain, ecache->events,
|
||||
ecache->ct);
|
||||
|
||||
ecache->events = 0;
|
||||
nf_ct_put(ecache->ct);
|
||||
ecache->ct = NULL;
|
||||
}
|
||||
|
||||
/* Deliver all cached events for a particular conntrack. This is called
|
||||
* by code prior to async packet handling for freeing the skb */
|
||||
void nf_ct_deliver_cached_events(const struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conntrack_ecache *ecache;
|
||||
|
||||
local_bh_disable();
|
||||
ecache = &__get_cpu_var(nf_conntrack_ecache);
|
||||
if (ecache->ct == ct)
|
||||
__nf_ct_deliver_cached_events(ecache);
|
||||
local_bh_enable();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
|
||||
|
||||
/* Deliver cached events for old pending events, if current conntrack != old */
|
||||
void __nf_ct_event_cache_init(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conntrack_ecache *ecache;
|
||||
|
||||
/* take care of delivering potentially old events */
|
||||
ecache = &__get_cpu_var(nf_conntrack_ecache);
|
||||
BUG_ON(ecache->ct == ct);
|
||||
if (ecache->ct)
|
||||
__nf_ct_deliver_cached_events(ecache);
|
||||
/* initialize for this conntrack/packet */
|
||||
ecache->ct = ct;
|
||||
nf_conntrack_get(&ct->ct_general);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__nf_ct_event_cache_init);
|
||||
|
||||
/* flush the event cache - touches other CPU's data and must not be called
|
||||
* while packets are still passing through the code */
|
||||
void nf_ct_event_cache_flush(void)
|
||||
{
|
||||
struct nf_conntrack_ecache *ecache;
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
ecache = &per_cpu(nf_conntrack_ecache, cpu);
|
||||
if (ecache->ct)
|
||||
nf_ct_put(ecache->ct);
|
||||
}
|
||||
}
|
||||
|
||||
445
net/netfilter/nf_conntrack_expect.c
Normal file
445
net/netfilter/nf_conntrack_expect.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/* Expectation handling for nf_conntrack. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
|
||||
LIST_HEAD(nf_conntrack_expect_list);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_list);
|
||||
|
||||
struct kmem_cache *nf_conntrack_expect_cachep __read_mostly;
|
||||
static unsigned int nf_conntrack_expect_next_id;
|
||||
|
||||
/* nf_conntrack_expect helper functions */
|
||||
void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_conn_help *master_help = nfct_help(exp->master);
|
||||
|
||||
NF_CT_ASSERT(master_help);
|
||||
NF_CT_ASSERT(!timer_pending(&exp->timeout));
|
||||
|
||||
list_del(&exp->list);
|
||||
NF_CT_STAT_INC(expect_delete);
|
||||
master_help->expecting--;
|
||||
nf_conntrack_expect_put(exp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_unlink_expect);
|
||||
|
||||
static void expectation_timed_out(unsigned long ul_expect)
|
||||
{
|
||||
struct nf_conntrack_expect *exp = (void *)ul_expect;
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
nf_ct_unlink_expect(exp);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
nf_conntrack_expect_put(exp);
|
||||
}
|
||||
|
||||
struct nf_conntrack_expect *
|
||||
__nf_conntrack_expect_find(const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_expect *i;
|
||||
|
||||
list_for_each_entry(i, &nf_conntrack_expect_list, list) {
|
||||
if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask))
|
||||
return i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__nf_conntrack_expect_find);
|
||||
|
||||
/* Just find a expectation corresponding to a tuple. */
|
||||
struct nf_conntrack_expect *
|
||||
nf_conntrack_expect_find_get(const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_expect *i;
|
||||
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
i = __nf_conntrack_expect_find(tuple);
|
||||
if (i)
|
||||
atomic_inc(&i->use);
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_find_get);
|
||||
|
||||
/* If an expectation for this connection is found, it gets delete from
|
||||
* global list then returned. */
|
||||
struct nf_conntrack_expect *
|
||||
find_expectation(const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_expect *exp;
|
||||
|
||||
exp = __nf_conntrack_expect_find(tuple);
|
||||
if (!exp)
|
||||
return NULL;
|
||||
|
||||
/* If master is not in hash table yet (ie. packet hasn't left
|
||||
this machine yet), how can other end know about expected?
|
||||
Hence these are not the droids you are looking for (if
|
||||
master ct never got confirmed, we'd hold a reference to it
|
||||
and weird things would happen to future packets). */
|
||||
if (!nf_ct_is_confirmed(exp->master))
|
||||
return NULL;
|
||||
|
||||
if (exp->flags & NF_CT_EXPECT_PERMANENT) {
|
||||
atomic_inc(&exp->use);
|
||||
return exp;
|
||||
} else if (del_timer(&exp->timeout)) {
|
||||
nf_ct_unlink_expect(exp);
|
||||
return exp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* delete all expectations for this conntrack */
|
||||
void nf_ct_remove_expectations(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conntrack_expect *i, *tmp;
|
||||
struct nf_conn_help *help = nfct_help(ct);
|
||||
|
||||
/* Optimization: most connection never expect any others. */
|
||||
if (!help || help->expecting == 0)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(i, tmp, &nf_conntrack_expect_list, list) {
|
||||
if (i->master == ct && del_timer(&i->timeout)) {
|
||||
nf_ct_unlink_expect(i);
|
||||
nf_conntrack_expect_put(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
|
||||
|
||||
/* Would two expected things clash? */
|
||||
static inline int expect_clash(const struct nf_conntrack_expect *a,
|
||||
const struct nf_conntrack_expect *b)
|
||||
{
|
||||
/* Part covered by intersection of masks must be unequal,
|
||||
otherwise they clash */
|
||||
struct nf_conntrack_tuple intersect_mask;
|
||||
int count;
|
||||
|
||||
intersect_mask.src.l3num = a->mask.src.l3num & b->mask.src.l3num;
|
||||
intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
|
||||
intersect_mask.dst.u.all = a->mask.dst.u.all & b->mask.dst.u.all;
|
||||
intersect_mask.dst.protonum = a->mask.dst.protonum
|
||||
& b->mask.dst.protonum;
|
||||
|
||||
for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
|
||||
intersect_mask.src.u3.all[count] =
|
||||
a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
|
||||
}
|
||||
|
||||
for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
|
||||
intersect_mask.dst.u3.all[count] =
|
||||
a->mask.dst.u3.all[count] & b->mask.dst.u3.all[count];
|
||||
}
|
||||
|
||||
return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask);
|
||||
}
|
||||
|
||||
static inline int expect_matches(const struct nf_conntrack_expect *a,
|
||||
const struct nf_conntrack_expect *b)
|
||||
{
|
||||
return a->master == b->master
|
||||
&& nf_ct_tuple_equal(&a->tuple, &b->tuple)
|
||||
&& nf_ct_tuple_equal(&a->mask, &b->mask);
|
||||
}
|
||||
|
||||
/* Generally a bad idea to call this: could have matched already. */
|
||||
void nf_conntrack_unexpect_related(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_conntrack_expect *i;
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
/* choose the the oldest expectation to evict */
|
||||
list_for_each_entry_reverse(i, &nf_conntrack_expect_list, list) {
|
||||
if (expect_matches(i, exp) && del_timer(&i->timeout)) {
|
||||
nf_ct_unlink_expect(i);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
nf_conntrack_expect_put(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_unexpect_related);
|
||||
|
||||
/* We don't increase the master conntrack refcount for non-fulfilled
|
||||
* conntracks. During the conntrack destruction, the expectations are
|
||||
* always killed before the conntrack itself */
|
||||
struct nf_conntrack_expect *nf_conntrack_expect_alloc(struct nf_conn *me)
|
||||
{
|
||||
struct nf_conntrack_expect *new;
|
||||
|
||||
new = kmem_cache_alloc(nf_conntrack_expect_cachep, GFP_ATOMIC);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
new->master = me;
|
||||
atomic_set(&new->use, 1);
|
||||
return new;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_alloc);
|
||||
|
||||
void nf_conntrack_expect_init(struct nf_conntrack_expect *exp, int family,
|
||||
union nf_conntrack_address *saddr,
|
||||
union nf_conntrack_address *daddr,
|
||||
u_int8_t proto, __be16 *src, __be16 *dst)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (family == AF_INET)
|
||||
len = 4;
|
||||
else
|
||||
len = 16;
|
||||
|
||||
exp->flags = 0;
|
||||
exp->expectfn = NULL;
|
||||
exp->helper = NULL;
|
||||
exp->tuple.src.l3num = family;
|
||||
exp->tuple.dst.protonum = proto;
|
||||
exp->mask.src.l3num = 0xFFFF;
|
||||
exp->mask.dst.protonum = 0xFF;
|
||||
|
||||
if (saddr) {
|
||||
memcpy(&exp->tuple.src.u3, saddr, len);
|
||||
if (sizeof(exp->tuple.src.u3) > len)
|
||||
/* address needs to be cleared for nf_ct_tuple_equal */
|
||||
memset((void *)&exp->tuple.src.u3 + len, 0x00,
|
||||
sizeof(exp->tuple.src.u3) - len);
|
||||
memset(&exp->mask.src.u3, 0xFF, len);
|
||||
if (sizeof(exp->mask.src.u3) > len)
|
||||
memset((void *)&exp->mask.src.u3 + len, 0x00,
|
||||
sizeof(exp->mask.src.u3) - len);
|
||||
} else {
|
||||
memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
|
||||
memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
|
||||
}
|
||||
|
||||
if (daddr) {
|
||||
memcpy(&exp->tuple.dst.u3, daddr, len);
|
||||
if (sizeof(exp->tuple.dst.u3) > len)
|
||||
/* address needs to be cleared for nf_ct_tuple_equal */
|
||||
memset((void *)&exp->tuple.dst.u3 + len, 0x00,
|
||||
sizeof(exp->tuple.dst.u3) - len);
|
||||
memset(&exp->mask.dst.u3, 0xFF, len);
|
||||
if (sizeof(exp->mask.dst.u3) > len)
|
||||
memset((void *)&exp->mask.dst.u3 + len, 0x00,
|
||||
sizeof(exp->mask.dst.u3) - len);
|
||||
} else {
|
||||
memset(&exp->tuple.dst.u3, 0x00, sizeof(exp->tuple.dst.u3));
|
||||
memset(&exp->mask.dst.u3, 0x00, sizeof(exp->mask.dst.u3));
|
||||
}
|
||||
|
||||
if (src) {
|
||||
exp->tuple.src.u.all = (__force u16)*src;
|
||||
exp->mask.src.u.all = 0xFFFF;
|
||||
} else {
|
||||
exp->tuple.src.u.all = 0;
|
||||
exp->mask.src.u.all = 0;
|
||||
}
|
||||
|
||||
if (dst) {
|
||||
exp->tuple.dst.u.all = (__force u16)*dst;
|
||||
exp->mask.dst.u.all = 0xFFFF;
|
||||
} else {
|
||||
exp->tuple.dst.u.all = 0;
|
||||
exp->mask.dst.u.all = 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_init);
|
||||
|
||||
void nf_conntrack_expect_put(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
if (atomic_dec_and_test(&exp->use))
|
||||
kmem_cache_free(nf_conntrack_expect_cachep, exp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_put);
|
||||
|
||||
static void nf_conntrack_expect_insert(struct nf_conntrack_expect *exp)
|
||||
{
|
||||
struct nf_conn_help *master_help = nfct_help(exp->master);
|
||||
|
||||
atomic_inc(&exp->use);
|
||||
master_help->expecting++;
|
||||
list_add(&exp->list, &nf_conntrack_expect_list);
|
||||
|
||||
init_timer(&exp->timeout);
|
||||
exp->timeout.data = (unsigned long)exp;
|
||||
exp->timeout.function = expectation_timed_out;
|
||||
exp->timeout.expires = jiffies + master_help->helper->timeout * HZ;
|
||||
add_timer(&exp->timeout);
|
||||
|
||||
exp->id = ++nf_conntrack_expect_next_id;
|
||||
atomic_inc(&exp->use);
|
||||
NF_CT_STAT_INC(expect_create);
|
||||
}
|
||||
|
||||
/* Race with expectations being used means we could have none to find; OK. */
|
||||
static void evict_oldest_expect(struct nf_conn *master)
|
||||
{
|
||||
struct nf_conntrack_expect *i;
|
||||
|
||||
list_for_each_entry_reverse(i, &nf_conntrack_expect_list, list) {
|
||||
if (i->master == master) {
|
||||
if (del_timer(&i->timeout)) {
|
||||
nf_ct_unlink_expect(i);
|
||||
nf_conntrack_expect_put(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int refresh_timer(struct nf_conntrack_expect *i)
|
||||
{
|
||||
struct nf_conn_help *master_help = nfct_help(i->master);
|
||||
|
||||
if (!del_timer(&i->timeout))
|
||||
return 0;
|
||||
|
||||
i->timeout.expires = jiffies + master_help->helper->timeout*HZ;
|
||||
add_timer(&i->timeout);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nf_conntrack_expect_related(struct nf_conntrack_expect *expect)
|
||||
{
|
||||
struct nf_conntrack_expect *i;
|
||||
struct nf_conn *master = expect->master;
|
||||
struct nf_conn_help *master_help = nfct_help(master);
|
||||
int ret;
|
||||
|
||||
NF_CT_ASSERT(master_help);
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
list_for_each_entry(i, &nf_conntrack_expect_list, list) {
|
||||
if (expect_matches(i, expect)) {
|
||||
/* Refresh timer: if it's dying, ignore.. */
|
||||
if (refresh_timer(i)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
} else if (expect_clash(i, expect)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* Will be over limit? */
|
||||
if (master_help->helper->max_expected &&
|
||||
master_help->expecting >= master_help->helper->max_expected)
|
||||
evict_oldest_expect(master);
|
||||
|
||||
nf_conntrack_expect_insert(expect);
|
||||
nf_conntrack_expect_event(IPEXP_NEW, expect);
|
||||
ret = 0;
|
||||
out:
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_related);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *exp_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct list_head *e = &nf_conntrack_expect_list;
|
||||
loff_t i;
|
||||
|
||||
/* strange seq_file api calls stop even if we fail,
|
||||
* thus we need to grab lock since stop unlocks */
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
|
||||
if (list_empty(e))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i <= *pos; i++) {
|
||||
e = e->next;
|
||||
if (e == &nf_conntrack_expect_list)
|
||||
return NULL;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct list_head *e = v;
|
||||
|
||||
++*pos;
|
||||
e = e->next;
|
||||
|
||||
if (e == &nf_conntrack_expect_list)
|
||||
return NULL;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void exp_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
static int exp_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct nf_conntrack_expect *expect = v;
|
||||
|
||||
if (expect->timeout.function)
|
||||
seq_printf(s, "%ld ", timer_pending(&expect->timeout)
|
||||
? (long)(expect->timeout.expires - jiffies)/HZ : 0);
|
||||
else
|
||||
seq_printf(s, "- ");
|
||||
seq_printf(s, "l3proto = %u proto=%u ",
|
||||
expect->tuple.src.l3num,
|
||||
expect->tuple.dst.protonum);
|
||||
print_tuple(s, &expect->tuple,
|
||||
__nf_ct_l3proto_find(expect->tuple.src.l3num),
|
||||
__nf_ct_l4proto_find(expect->tuple.src.l3num,
|
||||
expect->tuple.dst.protonum));
|
||||
return seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
static struct seq_operations exp_seq_ops = {
|
||||
.start = exp_seq_start,
|
||||
.next = exp_seq_next,
|
||||
.stop = exp_seq_stop,
|
||||
.show = exp_seq_show
|
||||
};
|
||||
|
||||
static int exp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &exp_seq_ops);
|
||||
}
|
||||
|
||||
const struct file_operations exp_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = exp_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
624
net/netfilter/nf_conntrack_ftp.c
Normal file
624
net/netfilter/nf_conntrack_ftp.c
Normal file
@@ -0,0 +1,624 @@
|
||||
/* FTP extension for connection tracking. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with Layer 3 protocol independent connection tracking.
|
||||
* - track EPRT and EPSV commands with IPv6 address.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_ftp.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/inet.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_ftp.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
|
||||
MODULE_DESCRIPTION("ftp connection tracking helper");
|
||||
MODULE_ALIAS("ip_conntrack_ftp");
|
||||
|
||||
/* This is slow, but it's simple. --RR */
|
||||
static char *ftp_buffer;
|
||||
|
||||
static DEFINE_SPINLOCK(nf_ftp_lock);
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static u_int16_t ports[MAX_PORTS];
|
||||
static unsigned int ports_c;
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
|
||||
static int loose;
|
||||
module_param(loose, bool, 0600);
|
||||
|
||||
unsigned int (*nf_nat_ftp_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
enum nf_ct_ftp_type type,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp,
|
||||
u32 *seq);
|
||||
EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *,
|
||||
char);
|
||||
|
||||
static struct ftp_search {
|
||||
const char *pattern;
|
||||
size_t plen;
|
||||
char skip;
|
||||
char term;
|
||||
enum nf_ct_ftp_type ftptype;
|
||||
int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
} search[IP_CT_DIR_MAX][2] = {
|
||||
[IP_CT_DIR_ORIGINAL] = {
|
||||
{
|
||||
.pattern = "PORT",
|
||||
.plen = sizeof("PORT") - 1,
|
||||
.skip = ' ',
|
||||
.term = '\r',
|
||||
.ftptype = NF_CT_FTP_PORT,
|
||||
.getnum = try_rfc959,
|
||||
},
|
||||
{
|
||||
.pattern = "EPRT",
|
||||
.plen = sizeof("EPRT") - 1,
|
||||
.skip = ' ',
|
||||
.term = '\r',
|
||||
.ftptype = NF_CT_FTP_EPRT,
|
||||
.getnum = try_eprt,
|
||||
},
|
||||
},
|
||||
[IP_CT_DIR_REPLY] = {
|
||||
{
|
||||
.pattern = "227 ",
|
||||
.plen = sizeof("227 ") - 1,
|
||||
.skip = '(',
|
||||
.term = ')',
|
||||
.ftptype = NF_CT_FTP_PASV,
|
||||
.getnum = try_rfc959,
|
||||
},
|
||||
{
|
||||
.pattern = "229 ",
|
||||
.plen = sizeof("229 ") - 1,
|
||||
.skip = '(',
|
||||
.term = ')',
|
||||
.ftptype = NF_CT_FTP_EPSV,
|
||||
.getnum = try_epsv_response,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term)
|
||||
{
|
||||
const char *end;
|
||||
int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), (u8 *)dst, term, &end);
|
||||
if (ret > 0)
|
||||
return (int)(end - src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_number(const char *data, size_t dlen, u_int32_t array[],
|
||||
int array_size, char sep, char term)
|
||||
{
|
||||
u_int32_t i, len;
|
||||
|
||||
memset(array, 0, sizeof(array[0])*array_size);
|
||||
|
||||
/* Keep data pointing at next char. */
|
||||
for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) {
|
||||
if (*data >= '0' && *data <= '9') {
|
||||
array[i] = array[i]*10 + *data - '0';
|
||||
}
|
||||
else if (*data == sep)
|
||||
i++;
|
||||
else {
|
||||
/* Unexpected character; true if it's the
|
||||
terminator and we're finished. */
|
||||
if (*data == term && i == array_size - 1)
|
||||
return len;
|
||||
|
||||
DEBUGP("Char %u (got %u nums) `%u' unexpected\n",
|
||||
len, i, *data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: 192,168,1,1,5,6 */
|
||||
static int try_rfc959(const char *data, size_t dlen,
|
||||
struct nf_conntrack_man *cmd, char term)
|
||||
{
|
||||
int length;
|
||||
u_int32_t array[6];
|
||||
|
||||
length = try_number(data, dlen, array, 6, ',', term);
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) |
|
||||
(array[2] << 8) | array[3]);
|
||||
cmd->u.tcp.port = htons((array[4] << 8) | array[5]);
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Grab port: number up to delimiter */
|
||||
static int get_port(const char *data, int start, size_t dlen, char delim,
|
||||
__be16 *port)
|
||||
{
|
||||
u_int16_t tmp_port = 0;
|
||||
int i;
|
||||
|
||||
for (i = start; i < dlen; i++) {
|
||||
/* Finished? */
|
||||
if (data[i] == delim) {
|
||||
if (tmp_port == 0)
|
||||
break;
|
||||
*port = htons(tmp_port);
|
||||
DEBUGP("get_port: return %d\n", tmp_port);
|
||||
return i + 1;
|
||||
}
|
||||
else if (data[i] >= '0' && data[i] <= '9')
|
||||
tmp_port = tmp_port*10 + data[i] - '0';
|
||||
else { /* Some other crap */
|
||||
DEBUGP("get_port: invalid char.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
|
||||
static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
|
||||
char term)
|
||||
{
|
||||
char delim;
|
||||
int length;
|
||||
|
||||
/* First character is delimiter, then "1" for IPv4 or "2" for IPv6,
|
||||
then delimiter again. */
|
||||
if (dlen <= 3) {
|
||||
DEBUGP("EPRT: too short\n");
|
||||
return 0;
|
||||
}
|
||||
delim = data[0];
|
||||
if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) {
|
||||
DEBUGP("try_eprt: invalid delimitter.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((cmd->l3num == PF_INET && data[1] != '1') ||
|
||||
(cmd->l3num == PF_INET6 && data[1] != '2')) {
|
||||
DEBUGP("EPRT: invalid protocol number.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP("EPRT: Got %c%c%c\n", delim, data[1], delim);
|
||||
|
||||
if (data[1] == '1') {
|
||||
u_int32_t array[4];
|
||||
|
||||
/* Now we have IP address. */
|
||||
length = try_number(data + 3, dlen - 3, array, 4, '.', delim);
|
||||
if (length != 0)
|
||||
cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16)
|
||||
| (array[2] << 8) | array[3]);
|
||||
} else {
|
||||
/* Now we have IPv6 address. */
|
||||
length = get_ipv6_addr(data + 3, dlen - 3,
|
||||
(struct in6_addr *)cmd->u3.ip6, delim);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
DEBUGP("EPRT: Got IP address!\n");
|
||||
/* Start offset includes initial "|1|", and trailing delimiter */
|
||||
return get_port(data, 3 + length + 1, dlen, delim, &cmd->u.tcp.port);
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: |||6446| */
|
||||
static int try_epsv_response(const char *data, size_t dlen,
|
||||
struct nf_conntrack_man *cmd, char term)
|
||||
{
|
||||
char delim;
|
||||
|
||||
/* Three delimiters. */
|
||||
if (dlen <= 3) return 0;
|
||||
delim = data[0];
|
||||
if (isdigit(delim) || delim < 33 || delim > 126
|
||||
|| data[1] != delim || data[2] != delim)
|
||||
return 0;
|
||||
|
||||
return get_port(data, 3, dlen, delim, &cmd->u.tcp.port);
|
||||
}
|
||||
|
||||
/* Return 1 for match, 0 for accept, -1 for partial. */
|
||||
static int find_pattern(const char *data, size_t dlen,
|
||||
const char *pattern, size_t plen,
|
||||
char skip, char term,
|
||||
unsigned int *numoff,
|
||||
unsigned int *numlen,
|
||||
struct nf_conntrack_man *cmd,
|
||||
int (*getnum)(const char *, size_t,
|
||||
struct nf_conntrack_man *, char))
|
||||
{
|
||||
size_t i;
|
||||
|
||||
DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen);
|
||||
if (dlen == 0)
|
||||
return 0;
|
||||
|
||||
if (dlen <= plen) {
|
||||
/* Short packet: try for partial? */
|
||||
if (strnicmp(data, pattern, dlen) == 0)
|
||||
return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
if (strnicmp(data, pattern, plen) != 0) {
|
||||
#if 0
|
||||
size_t i;
|
||||
|
||||
DEBUGP("ftp: string mismatch\n");
|
||||
for (i = 0; i < plen; i++) {
|
||||
DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
|
||||
i, data[i], data[i],
|
||||
pattern[i], pattern[i]);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP("Pattern matches!\n");
|
||||
/* Now we've found the constant string, try to skip
|
||||
to the 'skip' character */
|
||||
for (i = plen; data[i] != skip; i++)
|
||||
if (i == dlen - 1) return -1;
|
||||
|
||||
/* Skip over the last character */
|
||||
i++;
|
||||
|
||||
DEBUGP("Skipped up to `%c'!\n", skip);
|
||||
|
||||
*numoff = i;
|
||||
*numlen = getnum(data + i, dlen - i, cmd, term);
|
||||
if (!*numlen)
|
||||
return -1;
|
||||
|
||||
DEBUGP("Match succeeded!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Look up to see if we're just after a \n. */
|
||||
static int find_nl_seq(u32 seq, const struct nf_ct_ftp_master *info, int dir)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < info->seq_aft_nl_num[dir]; i++)
|
||||
if (info->seq_aft_nl[dir][i] == seq)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't update if it's older than what we have. */
|
||||
static void update_nl_seq(u32 nl_seq, struct nf_ct_ftp_master *info, int dir,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned int i, oldest = NUM_SEQ_TO_REMEMBER;
|
||||
|
||||
/* Look for oldest: if we find exact match, we're done. */
|
||||
for (i = 0; i < info->seq_aft_nl_num[dir]; i++) {
|
||||
if (info->seq_aft_nl[dir][i] == nl_seq)
|
||||
return;
|
||||
|
||||
if (oldest == info->seq_aft_nl_num[dir]
|
||||
|| before(info->seq_aft_nl[dir][i], oldest))
|
||||
oldest = i;
|
||||
}
|
||||
|
||||
if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {
|
||||
info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
|
||||
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
|
||||
} else if (oldest != NUM_SEQ_TO_REMEMBER) {
|
||||
info->seq_aft_nl[dir][oldest] = nl_seq;
|
||||
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static int help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
unsigned int dataoff, datalen;
|
||||
struct tcphdr _tcph, *th;
|
||||
char *fb_ptr;
|
||||
int ret;
|
||||
u32 seq;
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
unsigned int matchlen, matchoff;
|
||||
struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_man cmd = {};
|
||||
unsigned int i;
|
||||
int found = 0, ends_in_nl;
|
||||
typeof(nf_nat_ftp_hook) nf_nat_ftp;
|
||||
|
||||
/* Until there's been traffic both ways, don't look in packets. */
|
||||
if (ctinfo != IP_CT_ESTABLISHED
|
||||
&& ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
|
||||
DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
th = skb_header_pointer(*pskb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
dataoff = protoff + th->doff * 4;
|
||||
/* No data? */
|
||||
if (dataoff >= (*pskb)->len) {
|
||||
DEBUGP("ftp: dataoff(%u) >= skblen(%u)\n", dataoff,
|
||||
(*pskb)->len);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
datalen = (*pskb)->len - dataoff;
|
||||
|
||||
spin_lock_bh(&nf_ftp_lock);
|
||||
fb_ptr = skb_header_pointer(*pskb, dataoff, datalen, ftp_buffer);
|
||||
BUG_ON(fb_ptr == NULL);
|
||||
|
||||
ends_in_nl = (fb_ptr[datalen - 1] == '\n');
|
||||
seq = ntohl(th->seq) + datalen;
|
||||
|
||||
/* Look up to see if we're just after a \n. */
|
||||
if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
|
||||
/* Now if this ends in \n, update ftp info. */
|
||||
DEBUGP("nf_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n",
|
||||
ct_ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)",
|
||||
ct_ftp_info->seq_aft_nl[dir][0],
|
||||
ct_ftp_info->seq_aft_nl_num[dir] > 1 ? "" : "(UNSET)",
|
||||
ct_ftp_info->seq_aft_nl[dir][1]);
|
||||
ret = NF_ACCEPT;
|
||||
goto out_update_nl;
|
||||
}
|
||||
|
||||
/* Initialize IP/IPv6 addr to expected address (it's not mentioned
|
||||
in EPSV responses) */
|
||||
cmd.l3num = ct->tuplehash[dir].tuple.src.l3num;
|
||||
memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
|
||||
sizeof(cmd.u3.all));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {
|
||||
found = find_pattern(fb_ptr, datalen,
|
||||
search[dir][i].pattern,
|
||||
search[dir][i].plen,
|
||||
search[dir][i].skip,
|
||||
search[dir][i].term,
|
||||
&matchoff, &matchlen,
|
||||
&cmd,
|
||||
search[dir][i].getnum);
|
||||
if (found) break;
|
||||
}
|
||||
if (found == -1) {
|
||||
/* We don't usually drop packets. After all, this is
|
||||
connection tracking, not packet filtering.
|
||||
However, it is necessary for accurate tracking in
|
||||
this case. */
|
||||
if (net_ratelimit())
|
||||
printk("conntrack_ftp: partial %s %u+%u\n",
|
||||
search[dir][i].pattern,
|
||||
ntohl(th->seq), datalen);
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
} else if (found == 0) { /* No match */
|
||||
ret = NF_ACCEPT;
|
||||
goto out_update_nl;
|
||||
}
|
||||
|
||||
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
|
||||
(int)matchlen, fb_ptr + matchoff,
|
||||
matchlen, ntohl(th->seq) + matchoff);
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We refer to the reverse direction ("!dir") tuples here,
|
||||
* because we're expecting something in the other direction.
|
||||
* Doesn't matter unless NAT is happening. */
|
||||
exp->tuple.dst.u3 = ct->tuplehash[!dir].tuple.dst.u3;
|
||||
|
||||
/* Update the ftp info */
|
||||
if ((cmd.l3num == ct->tuplehash[dir].tuple.src.l3num) &&
|
||||
memcmp(&cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
|
||||
sizeof(cmd.u3.all))) {
|
||||
/* Enrico Scholz's passive FTP to partially RNAT'd ftp
|
||||
server: it really wants us to connect to a
|
||||
different IP address. Simply don't record it for
|
||||
NAT. */
|
||||
if (cmd.l3num == PF_INET) {
|
||||
DEBUGP("conntrack_ftp: NOT RECORDING: " NIPQUAD_FMT " != " NIPQUAD_FMT "\n",
|
||||
NIPQUAD(cmd.u3.ip),
|
||||
NIPQUAD(ct->tuplehash[dir].tuple.src.u3.ip));
|
||||
} else {
|
||||
DEBUGP("conntrack_ftp: NOT RECORDING: " NIP6_FMT " != " NIP6_FMT "\n",
|
||||
NIP6(*((struct in6_addr *)cmd.u3.ip6)),
|
||||
NIP6(*((struct in6_addr *)ct->tuplehash[dir]
|
||||
.tuple.src.u3.ip6)));
|
||||
}
|
||||
|
||||
/* Thanks to Cristiano Lincoln Mattos
|
||||
<lincoln@cesar.org.br> for reporting this potential
|
||||
problem (DMZ machines opening holes to internal
|
||||
networks, or the packet filter itself). */
|
||||
if (!loose) {
|
||||
ret = NF_ACCEPT;
|
||||
goto out_put_expect;
|
||||
}
|
||||
memcpy(&exp->tuple.dst.u3, &cmd.u3.all,
|
||||
sizeof(exp->tuple.dst.u3));
|
||||
}
|
||||
|
||||
exp->tuple.src.u3 = ct->tuplehash[!dir].tuple.src.u3;
|
||||
exp->tuple.src.l3num = cmd.l3num;
|
||||
exp->tuple.src.u.tcp.port = 0;
|
||||
exp->tuple.dst.u.tcp.port = cmd.u.tcp.port;
|
||||
exp->tuple.dst.protonum = IPPROTO_TCP;
|
||||
|
||||
exp->mask = (struct nf_conntrack_tuple)
|
||||
{ .src = { .l3num = 0xFFFF,
|
||||
.u = { .tcp = { 0 }},
|
||||
},
|
||||
.dst = { .protonum = 0xFF,
|
||||
.u = { .tcp = { __constant_htons(0xFFFF) }},
|
||||
},
|
||||
};
|
||||
if (cmd.l3num == PF_INET) {
|
||||
exp->mask.src.u3.ip = htonl(0xFFFFFFFF);
|
||||
exp->mask.dst.u3.ip = htonl(0xFFFFFFFF);
|
||||
} else {
|
||||
memset(exp->mask.src.u3.ip6, 0xFF,
|
||||
sizeof(exp->mask.src.u3.ip6));
|
||||
memset(exp->mask.dst.u3.ip6, 0xFF,
|
||||
sizeof(exp->mask.src.u3.ip6));
|
||||
}
|
||||
|
||||
exp->expectfn = NULL;
|
||||
exp->helper = NULL;
|
||||
exp->flags = 0;
|
||||
|
||||
/* Now, NAT might want to mangle the packet, and register the
|
||||
* (possibly changed) expectation itself. */
|
||||
nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook);
|
||||
if (nf_nat_ftp && ct->status & IPS_NAT_MASK)
|
||||
ret = nf_nat_ftp(pskb, ctinfo, search[dir][i].ftptype,
|
||||
matchoff, matchlen, exp, &seq);
|
||||
else {
|
||||
/* Can't expect this? Best to drop packet now. */
|
||||
if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
else
|
||||
ret = NF_ACCEPT;
|
||||
}
|
||||
|
||||
out_put_expect:
|
||||
nf_conntrack_expect_put(exp);
|
||||
|
||||
out_update_nl:
|
||||
/* Now if this ends in \n, update ftp info. Seq may have been
|
||||
* adjusted by NAT code. */
|
||||
if (ends_in_nl)
|
||||
update_nl_seq(seq, ct_ftp_info, dir, *pskb);
|
||||
out:
|
||||
spin_unlock_bh(&nf_ftp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper ftp[MAX_PORTS][2];
|
||||
static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")];
|
||||
|
||||
/* don't make this __exit, since it's called from __init ! */
|
||||
static void nf_conntrack_ftp_fini(void)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (ftp[i][j].me == NULL)
|
||||
continue;
|
||||
|
||||
DEBUGP("nf_ct_ftp: unregistering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_helper_unregister(&ftp[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(ftp_buffer);
|
||||
}
|
||||
|
||||
static int __init nf_conntrack_ftp_init(void)
|
||||
{
|
||||
int i, j = -1, ret = 0;
|
||||
char *tmpname;
|
||||
|
||||
ftp_buffer = kmalloc(65536, GFP_KERNEL);
|
||||
if (!ftp_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = FTP_PORT;
|
||||
|
||||
/* FIXME should be configurable whether IPv4 and IPv6 FTP connections
|
||||
are tracked or not - YK */
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
ftp[i][0].tuple.src.l3num = PF_INET;
|
||||
ftp[i][1].tuple.src.l3num = PF_INET6;
|
||||
for (j = 0; j < 2; j++) {
|
||||
ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);
|
||||
ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
|
||||
ftp[i][j].mask.src.l3num = 0xFFFF;
|
||||
ftp[i][j].mask.src.u.tcp.port = htons(0xFFFF);
|
||||
ftp[i][j].mask.dst.protonum = 0xFF;
|
||||
ftp[i][j].max_expected = 1;
|
||||
ftp[i][j].timeout = 5 * 60; /* 5 Minutes */
|
||||
ftp[i][j].me = THIS_MODULE;
|
||||
ftp[i][j].help = help;
|
||||
tmpname = &ftp_names[i][j][0];
|
||||
if (ports[i] == FTP_PORT)
|
||||
sprintf(tmpname, "ftp");
|
||||
else
|
||||
sprintf(tmpname, "ftp-%d", ports[i]);
|
||||
ftp[i][j].name = tmpname;
|
||||
|
||||
DEBUGP("nf_ct_ftp: registering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
ret = nf_conntrack_helper_register(&ftp[i][j]);
|
||||
if (ret) {
|
||||
printk("nf_ct_ftp: failed to register helper "
|
||||
" for pf: %d port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_ftp_fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_ftp_init);
|
||||
module_exit(nf_conntrack_ftp_fini);
|
||||
874
net/netfilter/nf_conntrack_h323_asn1.c
Normal file
874
net/netfilter/nf_conntrack_h323_asn1.c
Normal file
@@ -0,0 +1,874 @@
|
||||
/****************************************************************************
|
||||
* ip_conntrack_helper_h323_asn1.c - BER and PER decoding library for H.323
|
||||
* conntrack/NAT module.
|
||||
*
|
||||
* Copyright (c) 2006 by Jing Min Zhao <zhaojingmin@users.sourceforge.net>
|
||||
*
|
||||
* This source code is licensed under General Public License version 2.
|
||||
*
|
||||
* See ip_conntrack_helper_h323_asn1.h for details.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/kernel.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <linux/netfilter/nf_conntrack_h323_asn1.h>
|
||||
|
||||
/* Trace Flag */
|
||||
#ifndef H323_TRACE
|
||||
#define H323_TRACE 0
|
||||
#endif
|
||||
|
||||
#if H323_TRACE
|
||||
#define TAB_SIZE 4
|
||||
#define IFTHEN(cond, act) if(cond){act;}
|
||||
#ifdef __KERNEL__
|
||||
#define PRINT printk
|
||||
#else
|
||||
#define PRINT printf
|
||||
#endif
|
||||
#define FNAME(name) name,
|
||||
#else
|
||||
#define IFTHEN(cond, act)
|
||||
#define PRINT(fmt, args...)
|
||||
#define FNAME(name)
|
||||
#endif
|
||||
|
||||
/* ASN.1 Types */
|
||||
#define NUL 0
|
||||
#define BOOL 1
|
||||
#define OID 2
|
||||
#define INT 3
|
||||
#define ENUM 4
|
||||
#define BITSTR 5
|
||||
#define NUMSTR 6
|
||||
#define NUMDGT 6
|
||||
#define TBCDSTR 6
|
||||
#define OCTSTR 7
|
||||
#define PRTSTR 7
|
||||
#define IA5STR 7
|
||||
#define GENSTR 7
|
||||
#define BMPSTR 8
|
||||
#define SEQ 9
|
||||
#define SET 9
|
||||
#define SEQOF 10
|
||||
#define SETOF 10
|
||||
#define CHOICE 11
|
||||
|
||||
/* Constraint Types */
|
||||
#define FIXD 0
|
||||
/* #define BITS 1-8 */
|
||||
#define BYTE 9
|
||||
#define WORD 10
|
||||
#define CONS 11
|
||||
#define SEMI 12
|
||||
#define UNCO 13
|
||||
|
||||
/* ASN.1 Type Attributes */
|
||||
#define SKIP 0
|
||||
#define STOP 1
|
||||
#define DECODE 2
|
||||
#define EXT 4
|
||||
#define OPEN 8
|
||||
#define OPT 16
|
||||
|
||||
|
||||
/* ASN.1 Field Structure */
|
||||
typedef struct field_t {
|
||||
#if H323_TRACE
|
||||
char *name;
|
||||
#endif
|
||||
unsigned char type;
|
||||
unsigned char sz;
|
||||
unsigned char lb;
|
||||
unsigned char ub;
|
||||
unsigned short attr;
|
||||
unsigned short offset;
|
||||
struct field_t *fields;
|
||||
} field_t;
|
||||
|
||||
/* Bit Stream */
|
||||
typedef struct {
|
||||
unsigned char *buf;
|
||||
unsigned char *beg;
|
||||
unsigned char *end;
|
||||
unsigned char *cur;
|
||||
unsigned bit;
|
||||
} bitstr_t;
|
||||
|
||||
/* Tool Functions */
|
||||
#define INC_BIT(bs) if((++bs->bit)>7){bs->cur++;bs->bit=0;}
|
||||
#define INC_BITS(bs,b) if((bs->bit+=b)>7){bs->cur+=bs->bit>>3;bs->bit&=7;}
|
||||
#define BYTE_ALIGN(bs) if(bs->bit){bs->cur++;bs->bit=0;}
|
||||
#define CHECK_BOUND(bs,n) if(bs->cur+(n)>bs->end)return(H323_ERROR_BOUND)
|
||||
static unsigned get_len(bitstr_t * bs);
|
||||
static unsigned get_bit(bitstr_t * bs);
|
||||
static unsigned get_bits(bitstr_t * bs, unsigned b);
|
||||
static unsigned get_bitmap(bitstr_t * bs, unsigned b);
|
||||
static unsigned get_uint(bitstr_t * bs, int b);
|
||||
|
||||
/* Decoder Functions */
|
||||
static int decode_nul(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_bool(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_oid(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_int(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_enum(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_bitstr(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_numstr(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_octstr(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_bmpstr(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_seq(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_seqof(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
static int decode_choice(bitstr_t * bs, field_t * f, char *base, int level);
|
||||
|
||||
/* Decoder Functions Vector */
|
||||
typedef int (*decoder_t) (bitstr_t *, field_t *, char *, int);
|
||||
static decoder_t Decoders[] = {
|
||||
decode_nul,
|
||||
decode_bool,
|
||||
decode_oid,
|
||||
decode_int,
|
||||
decode_enum,
|
||||
decode_bitstr,
|
||||
decode_numstr,
|
||||
decode_octstr,
|
||||
decode_bmpstr,
|
||||
decode_seq,
|
||||
decode_seqof,
|
||||
decode_choice,
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* H.323 Types
|
||||
****************************************************************************/
|
||||
#include "nf_conntrack_h323_types.c"
|
||||
|
||||
/****************************************************************************
|
||||
* Functions
|
||||
****************************************************************************/
|
||||
/* Assume bs is aligned && v < 16384 */
|
||||
unsigned get_len(bitstr_t * bs)
|
||||
{
|
||||
unsigned v;
|
||||
|
||||
v = *bs->cur++;
|
||||
|
||||
if (v & 0x80) {
|
||||
v &= 0x3f;
|
||||
v <<= 8;
|
||||
v += *bs->cur++;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
unsigned get_bit(bitstr_t * bs)
|
||||
{
|
||||
unsigned b = (*bs->cur) & (0x80 >> bs->bit);
|
||||
|
||||
INC_BIT(bs);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Assume b <= 8 */
|
||||
unsigned get_bits(bitstr_t * bs, unsigned b)
|
||||
{
|
||||
unsigned v, l;
|
||||
|
||||
v = (*bs->cur) & (0xffU >> bs->bit);
|
||||
l = b + bs->bit;
|
||||
|
||||
if (l < 8) {
|
||||
v >>= 8 - l;
|
||||
bs->bit = l;
|
||||
} else if (l == 8) {
|
||||
bs->cur++;
|
||||
bs->bit = 0;
|
||||
} else { /* l > 8 */
|
||||
|
||||
v <<= 8;
|
||||
v += *(++bs->cur);
|
||||
v >>= 16 - l;
|
||||
bs->bit = l - 8;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Assume b <= 32 */
|
||||
unsigned get_bitmap(bitstr_t * bs, unsigned b)
|
||||
{
|
||||
unsigned v, l, shift, bytes;
|
||||
|
||||
if (!b)
|
||||
return 0;
|
||||
|
||||
l = bs->bit + b;
|
||||
|
||||
if (l < 8) {
|
||||
v = (unsigned) (*bs->cur) << (bs->bit + 24);
|
||||
bs->bit = l;
|
||||
} else if (l == 8) {
|
||||
v = (unsigned) (*bs->cur++) << (bs->bit + 24);
|
||||
bs->bit = 0;
|
||||
} else {
|
||||
for (bytes = l >> 3, shift = 24, v = 0; bytes;
|
||||
bytes--, shift -= 8)
|
||||
v |= (unsigned) (*bs->cur++) << shift;
|
||||
|
||||
if (l < 32) {
|
||||
v |= (unsigned) (*bs->cur) << shift;
|
||||
v <<= bs->bit;
|
||||
} else if (l > 32) {
|
||||
v <<= bs->bit;
|
||||
v |= (*bs->cur) >> (8 - bs->bit);
|
||||
}
|
||||
|
||||
bs->bit = l & 0x7;
|
||||
}
|
||||
|
||||
v &= 0xffffffff << (32 - b);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Assume bs is aligned and sizeof(unsigned int) == 4
|
||||
****************************************************************************/
|
||||
unsigned get_uint(bitstr_t * bs, int b)
|
||||
{
|
||||
unsigned v = 0;
|
||||
|
||||
switch (b) {
|
||||
case 4:
|
||||
v |= *bs->cur++;
|
||||
v <<= 8;
|
||||
case 3:
|
||||
v |= *bs->cur++;
|
||||
v <<= 8;
|
||||
case 2:
|
||||
v |= *bs->cur++;
|
||||
v <<= 8;
|
||||
case 1:
|
||||
v |= *bs->cur++;
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_nul(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_bool(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
INC_BIT(bs);
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_oid(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
int len;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 1);
|
||||
len = *bs->cur++;
|
||||
bs->cur += len;
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_int(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
PRINT("%*.s%s", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
switch (f->sz) {
|
||||
case BYTE: /* Range == 256 */
|
||||
BYTE_ALIGN(bs);
|
||||
bs->cur++;
|
||||
break;
|
||||
case WORD: /* 257 <= Range <= 64K */
|
||||
BYTE_ALIGN(bs);
|
||||
bs->cur += 2;
|
||||
break;
|
||||
case CONS: /* 64K < Range < 4G */
|
||||
len = get_bits(bs, 2) + 1;
|
||||
BYTE_ALIGN(bs);
|
||||
if (base && (f->attr & DECODE)) { /* timeToLive */
|
||||
unsigned v = get_uint(bs, len) + f->lb;
|
||||
PRINT(" = %u", v);
|
||||
*((unsigned *) (base + f->offset)) = v;
|
||||
}
|
||||
bs->cur += len;
|
||||
break;
|
||||
case UNCO:
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs);
|
||||
bs->cur += len;
|
||||
break;
|
||||
default: /* 2 <= Range <= 255 */
|
||||
INC_BITS(bs, f->sz);
|
||||
break;
|
||||
}
|
||||
|
||||
PRINT("\n");
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_enum(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
if ((f->attr & EXT) && get_bit(bs)) {
|
||||
INC_BITS(bs, 7);
|
||||
} else {
|
||||
INC_BITS(bs, f->sz);
|
||||
}
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_bitstr(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
BYTE_ALIGN(bs);
|
||||
switch (f->sz) {
|
||||
case FIXD: /* fixed length > 16 */
|
||||
len = f->lb;
|
||||
break;
|
||||
case WORD: /* 2-byte length */
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = (*bs->cur++) << 8;
|
||||
len += (*bs->cur++) + f->lb;
|
||||
break;
|
||||
case SEMI:
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs);
|
||||
break;
|
||||
default:
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
bs->cur += len >> 3;
|
||||
bs->bit = len & 7;
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_numstr(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
/* 2 <= Range <= 255 */
|
||||
len = get_bits(bs, f->sz) + f->lb;
|
||||
|
||||
BYTE_ALIGN(bs);
|
||||
INC_BITS(bs, (len << 2));
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_octstr(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
PRINT("%*.s%s", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
switch (f->sz) {
|
||||
case FIXD: /* Range == 1 */
|
||||
if (f->lb > 2) {
|
||||
BYTE_ALIGN(bs);
|
||||
if (base && (f->attr & DECODE)) {
|
||||
/* The IP Address */
|
||||
IFTHEN(f->lb == 4,
|
||||
PRINT(" = %d.%d.%d.%d:%d",
|
||||
bs->cur[0], bs->cur[1],
|
||||
bs->cur[2], bs->cur[3],
|
||||
bs->cur[4] * 256 + bs->cur[5]));
|
||||
*((unsigned *) (base + f->offset)) =
|
||||
bs->cur - bs->buf;
|
||||
}
|
||||
}
|
||||
len = f->lb;
|
||||
break;
|
||||
case BYTE: /* Range == 256 */
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 1);
|
||||
len = (*bs->cur++) + f->lb;
|
||||
break;
|
||||
case SEMI:
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs) + f->lb;
|
||||
break;
|
||||
default: /* 2 <= Range <= 255 */
|
||||
len = get_bits(bs, f->sz) + f->lb;
|
||||
BYTE_ALIGN(bs);
|
||||
break;
|
||||
}
|
||||
|
||||
bs->cur += len;
|
||||
|
||||
PRINT("\n");
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_bmpstr(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
switch (f->sz) {
|
||||
case BYTE: /* Range == 256 */
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 1);
|
||||
len = (*bs->cur++) + f->lb;
|
||||
break;
|
||||
default: /* 2 <= Range <= 255 */
|
||||
len = get_bits(bs, f->sz) + f->lb;
|
||||
BYTE_ALIGN(bs);
|
||||
break;
|
||||
}
|
||||
|
||||
bs->cur += len << 1;
|
||||
|
||||
CHECK_BOUND(bs, 0);
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_seq(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned ext, bmp, i, opt, len = 0, bmp2, bmp2_len;
|
||||
int err;
|
||||
field_t *son;
|
||||
unsigned char *beg = NULL;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
/* Decode? */
|
||||
base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
|
||||
|
||||
/* Extensible? */
|
||||
ext = (f->attr & EXT) ? get_bit(bs) : 0;
|
||||
|
||||
/* Get fields bitmap */
|
||||
bmp = get_bitmap(bs, f->sz);
|
||||
if (base)
|
||||
*(unsigned *) base = bmp;
|
||||
|
||||
/* Decode the root components */
|
||||
for (i = opt = 0, son = f->fields; i < f->lb; i++, son++) {
|
||||
if (son->attr & STOP) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
|
||||
son->name);
|
||||
return H323_ERROR_STOP;
|
||||
}
|
||||
|
||||
if (son->attr & OPT) { /* Optional component */
|
||||
if (!((0x80000000U >> (opt++)) & bmp)) /* Not exist */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Decode */
|
||||
if (son->attr & OPEN) { /* Open field */
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
if (!base) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
|
||||
" ", son->name);
|
||||
bs->cur += len;
|
||||
continue;
|
||||
}
|
||||
beg = bs->cur;
|
||||
|
||||
/* Decode */
|
||||
if ((err = (Decoders[son->type]) (bs, son, base,
|
||||
level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
bs->cur = beg + len;
|
||||
bs->bit = 0;
|
||||
} else if ((err = (Decoders[son->type]) (bs, son, base,
|
||||
level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* No extension? */
|
||||
if (!ext)
|
||||
return H323_ERROR_NONE;
|
||||
|
||||
/* Get the extension bitmap */
|
||||
bmp2_len = get_bits(bs, 7) + 1;
|
||||
CHECK_BOUND(bs, (bmp2_len + 7) >> 3);
|
||||
bmp2 = get_bitmap(bs, bmp2_len);
|
||||
bmp |= bmp2 >> f->sz;
|
||||
if (base)
|
||||
*(unsigned *) base = bmp;
|
||||
BYTE_ALIGN(bs);
|
||||
|
||||
/* Decode the extension components */
|
||||
for (opt = 0; opt < bmp2_len; opt++, i++, son++) {
|
||||
if (i < f->ub && son->attr & STOP) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
|
||||
son->name);
|
||||
return H323_ERROR_STOP;
|
||||
}
|
||||
|
||||
if (!((0x80000000 >> opt) & bmp2)) /* Not present */
|
||||
continue;
|
||||
|
||||
/* Check Range */
|
||||
if (i >= f->ub) { /* Newer Version? */
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
bs->cur += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK_BOUND(bs, 2);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
if (!base || !(son->attr & DECODE)) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
|
||||
son->name);
|
||||
bs->cur += len;
|
||||
continue;
|
||||
}
|
||||
beg = bs->cur;
|
||||
|
||||
if ((err = (Decoders[son->type]) (bs, son, base,
|
||||
level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
bs->cur = beg + len;
|
||||
bs->bit = 0;
|
||||
}
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_seqof(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned count, effective_count = 0, i, len = 0;
|
||||
int err;
|
||||
field_t *son;
|
||||
unsigned char *beg = NULL;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
/* Decode? */
|
||||
base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
|
||||
|
||||
/* Decode item count */
|
||||
switch (f->sz) {
|
||||
case BYTE:
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 1);
|
||||
count = *bs->cur++;
|
||||
break;
|
||||
case WORD:
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 2);
|
||||
count = *bs->cur++;
|
||||
count <<= 8;
|
||||
count = *bs->cur++;
|
||||
break;
|
||||
case SEMI:
|
||||
BYTE_ALIGN(bs);
|
||||
CHECK_BOUND(bs, 2);
|
||||
count = get_len(bs);
|
||||
break;
|
||||
default:
|
||||
count = get_bits(bs, f->sz);
|
||||
break;
|
||||
}
|
||||
count += f->lb;
|
||||
|
||||
/* Write Count */
|
||||
if (base) {
|
||||
effective_count = count > f->ub ? f->ub : count;
|
||||
*(unsigned *) base = effective_count;
|
||||
base += sizeof(unsigned);
|
||||
}
|
||||
|
||||
/* Decode nested field */
|
||||
son = f->fields;
|
||||
if (base)
|
||||
base -= son->offset;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (son->attr & OPEN) {
|
||||
BYTE_ALIGN(bs);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
if (!base || !(son->attr & DECODE)) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
|
||||
" ", son->name);
|
||||
bs->cur += len;
|
||||
continue;
|
||||
}
|
||||
beg = bs->cur;
|
||||
|
||||
if ((err = (Decoders[son->type]) (bs, son,
|
||||
i <
|
||||
effective_count ?
|
||||
base : NULL,
|
||||
level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
bs->cur = beg + len;
|
||||
bs->bit = 0;
|
||||
} else
|
||||
if ((err = (Decoders[son->type]) (bs, son,
|
||||
i <
|
||||
effective_count ?
|
||||
base : NULL,
|
||||
level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
if (base)
|
||||
base += son->offset;
|
||||
}
|
||||
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
int decode_choice(bitstr_t * bs, field_t * f, char *base, int level)
|
||||
{
|
||||
unsigned type, ext, len = 0;
|
||||
int err;
|
||||
field_t *son;
|
||||
unsigned char *beg = NULL;
|
||||
|
||||
PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
|
||||
|
||||
/* Decode? */
|
||||
base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
|
||||
|
||||
/* Decode the choice index number */
|
||||
if ((f->attr & EXT) && get_bit(bs)) {
|
||||
ext = 1;
|
||||
type = get_bits(bs, 7) + f->lb;
|
||||
} else {
|
||||
ext = 0;
|
||||
type = get_bits(bs, f->sz);
|
||||
}
|
||||
|
||||
/* Write Type */
|
||||
if (base)
|
||||
*(unsigned *) base = type;
|
||||
|
||||
/* Check Range */
|
||||
if (type >= f->ub) { /* Newer version? */
|
||||
BYTE_ALIGN(bs);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
bs->cur += len;
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/* Transfer to son level */
|
||||
son = &f->fields[type];
|
||||
if (son->attr & STOP) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", son->name);
|
||||
return H323_ERROR_STOP;
|
||||
}
|
||||
|
||||
if (ext || (son->attr & OPEN)) {
|
||||
BYTE_ALIGN(bs);
|
||||
len = get_len(bs);
|
||||
CHECK_BOUND(bs, len);
|
||||
if (!base || !(son->attr & DECODE)) {
|
||||
PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
|
||||
son->name);
|
||||
bs->cur += len;
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
beg = bs->cur;
|
||||
|
||||
if ((err = (Decoders[son->type]) (bs, son, base, level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
bs->cur = beg + len;
|
||||
bs->bit = 0;
|
||||
} else if ((err = (Decoders[son->type]) (bs, son, base, level + 1)) <
|
||||
H323_ERROR_NONE)
|
||||
return err;
|
||||
|
||||
return H323_ERROR_NONE;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int DecodeRasMessage(unsigned char *buf, size_t sz, RasMessage * ras)
|
||||
{
|
||||
static field_t ras_message = {
|
||||
FNAME("RasMessage") CHOICE, 5, 24, 32, DECODE | EXT,
|
||||
0, _RasMessage
|
||||
};
|
||||
bitstr_t bs;
|
||||
|
||||
bs.buf = bs.beg = bs.cur = buf;
|
||||
bs.end = buf + sz;
|
||||
bs.bit = 0;
|
||||
|
||||
return decode_choice(&bs, &ras_message, (char *) ras, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
static int DecodeH323_UserInformation(unsigned char *buf, unsigned char *beg,
|
||||
size_t sz, H323_UserInformation * uuie)
|
||||
{
|
||||
static field_t h323_userinformation = {
|
||||
FNAME("H323-UserInformation") SEQ, 1, 2, 2, DECODE | EXT,
|
||||
0, _H323_UserInformation
|
||||
};
|
||||
bitstr_t bs;
|
||||
|
||||
bs.buf = buf;
|
||||
bs.beg = bs.cur = beg;
|
||||
bs.end = beg + sz;
|
||||
bs.bit = 0;
|
||||
|
||||
return decode_seq(&bs, &h323_userinformation, (char *) uuie, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int DecodeMultimediaSystemControlMessage(unsigned char *buf, size_t sz,
|
||||
MultimediaSystemControlMessage *
|
||||
mscm)
|
||||
{
|
||||
static field_t multimediasystemcontrolmessage = {
|
||||
FNAME("MultimediaSystemControlMessage") CHOICE, 2, 4, 4,
|
||||
DECODE | EXT, 0, _MultimediaSystemControlMessage
|
||||
};
|
||||
bitstr_t bs;
|
||||
|
||||
bs.buf = bs.beg = bs.cur = buf;
|
||||
bs.end = buf + sz;
|
||||
bs.bit = 0;
|
||||
|
||||
return decode_choice(&bs, &multimediasystemcontrolmessage,
|
||||
(char *) mscm, 0);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
int DecodeQ931(unsigned char *buf, size_t sz, Q931 * q931)
|
||||
{
|
||||
unsigned char *p = buf;
|
||||
int len;
|
||||
|
||||
if (!p || sz < 1)
|
||||
return H323_ERROR_BOUND;
|
||||
|
||||
/* Protocol Discriminator */
|
||||
if (*p != 0x08) {
|
||||
PRINT("Unknown Protocol Discriminator\n");
|
||||
return H323_ERROR_RANGE;
|
||||
}
|
||||
p++;
|
||||
sz--;
|
||||
|
||||
/* CallReferenceValue */
|
||||
if (sz < 1)
|
||||
return H323_ERROR_BOUND;
|
||||
len = *p++;
|
||||
sz--;
|
||||
if (sz < len)
|
||||
return H323_ERROR_BOUND;
|
||||
p += len;
|
||||
sz -= len;
|
||||
|
||||
/* Message Type */
|
||||
if (sz < 1)
|
||||
return H323_ERROR_BOUND;
|
||||
q931->MessageType = *p++;
|
||||
PRINT("MessageType = %02X\n", q931->MessageType);
|
||||
if (*p & 0x80) {
|
||||
p++;
|
||||
sz--;
|
||||
}
|
||||
|
||||
/* Decode Information Elements */
|
||||
while (sz > 0) {
|
||||
if (*p == 0x7e) { /* UserUserIE */
|
||||
if (sz < 3)
|
||||
break;
|
||||
p++;
|
||||
len = *p++ << 8;
|
||||
len |= *p++;
|
||||
sz -= 3;
|
||||
if (sz < len)
|
||||
break;
|
||||
p++;
|
||||
len--;
|
||||
return DecodeH323_UserInformation(buf, p, len,
|
||||
&q931->UUIE);
|
||||
}
|
||||
p++;
|
||||
sz--;
|
||||
if (sz < 1)
|
||||
break;
|
||||
len = *p++;
|
||||
if (sz < len)
|
||||
break;
|
||||
p += len;
|
||||
sz -= len;
|
||||
}
|
||||
|
||||
PRINT("Q.931 UUIE not found\n");
|
||||
|
||||
return H323_ERROR_BOUND;
|
||||
}
|
||||
1856
net/netfilter/nf_conntrack_h323_main.c
Normal file
1856
net/netfilter/nf_conntrack_h323_main.c
Normal file
File diff suppressed because it is too large
Load Diff
1927
net/netfilter/nf_conntrack_h323_types.c
Normal file
1927
net/netfilter/nf_conntrack_h323_types.c
Normal file
File diff suppressed because it is too large
Load Diff
155
net/netfilter/nf_conntrack_helper.c
Normal file
155
net/netfilter/nf_conntrack_helper.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* Helper handling for netfilter. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
|
||||
static __read_mostly LIST_HEAD(helpers);
|
||||
|
||||
struct nf_conntrack_helper *
|
||||
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_helper *h;
|
||||
|
||||
list_for_each_entry(h, &helpers, list) {
|
||||
if (nf_ct_tuple_mask_cmp(tuple, &h->tuple, &h->mask))
|
||||
return h;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nf_conntrack_helper *
|
||||
nf_ct_helper_find_get( const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_helper *helper;
|
||||
|
||||
/* need nf_conntrack_lock to assure that helper exists until
|
||||
* try_module_get() is called */
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
|
||||
helper = __nf_ct_helper_find(tuple);
|
||||
if (helper) {
|
||||
/* need to increase module usage count to assure helper will
|
||||
* not go away while the caller is e.g. busy putting a
|
||||
* conntrack in the hash that uses the helper */
|
||||
if (!try_module_get(helper->me))
|
||||
helper = NULL;
|
||||
}
|
||||
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
return helper;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_helper_find_get);
|
||||
|
||||
void nf_ct_helper_put(struct nf_conntrack_helper *helper)
|
||||
{
|
||||
module_put(helper->me);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_helper_put);
|
||||
|
||||
struct nf_conntrack_helper *
|
||||
__nf_conntrack_helper_find_byname(const char *name)
|
||||
{
|
||||
struct nf_conntrack_helper *h;
|
||||
|
||||
list_for_each_entry(h, &helpers, list) {
|
||||
if (!strcmp(h->name, name))
|
||||
return h;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find_byname);
|
||||
|
||||
static inline int unhelp(struct nf_conntrack_tuple_hash *i,
|
||||
const struct nf_conntrack_helper *me)
|
||||
{
|
||||
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(i);
|
||||
struct nf_conn_help *help = nfct_help(ct);
|
||||
|
||||
if (help && help->helper == me) {
|
||||
nf_conntrack_event(IPCT_HELPER, ct);
|
||||
help->helper = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
|
||||
{
|
||||
int size, ret;
|
||||
|
||||
BUG_ON(me->timeout == 0);
|
||||
|
||||
size = ALIGN(sizeof(struct nf_conn), __alignof__(struct nf_conn_help)) +
|
||||
sizeof(struct nf_conn_help);
|
||||
ret = nf_conntrack_register_cache(NF_CT_F_HELP, "nf_conntrack:help",
|
||||
size);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "nf_conntrack_helper_register: Unable to create slab cache for conntracks\n");
|
||||
return ret;
|
||||
}
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
list_add(&me->list, &helpers);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_helper_register);
|
||||
|
||||
void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
|
||||
{
|
||||
unsigned int i;
|
||||
struct nf_conntrack_tuple_hash *h;
|
||||
struct nf_conntrack_expect *exp, *tmp;
|
||||
|
||||
/* Need write lock here, to delete helper. */
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
list_del(&me->list);
|
||||
|
||||
/* Get rid of expectations */
|
||||
list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list, list) {
|
||||
struct nf_conn_help *help = nfct_help(exp->master);
|
||||
if ((help->helper == me || exp->helper == me) &&
|
||||
del_timer(&exp->timeout)) {
|
||||
nf_ct_unlink_expect(exp);
|
||||
nf_conntrack_expect_put(exp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get rid of expecteds, set helpers to NULL. */
|
||||
list_for_each_entry(h, &unconfirmed, list)
|
||||
unhelp(h, me);
|
||||
for (i = 0; i < nf_conntrack_htable_size; i++) {
|
||||
list_for_each_entry(h, &nf_conntrack_hash[i], list)
|
||||
unhelp(h, me);
|
||||
}
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
/* Someone could be still looking at the helper in a bh. */
|
||||
synchronize_net();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister);
|
||||
281
net/netfilter/nf_conntrack_irc.c
Normal file
281
net/netfilter/nf_conntrack_irc.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/* IRC extension for IP connection tracking, Version 1.21
|
||||
* (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
|
||||
* based on RR's ip_conntrack_ftp.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/netfilter.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_irc.h>
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static unsigned short ports[MAX_PORTS];
|
||||
static int ports_c;
|
||||
static unsigned int max_dcc_channels = 8;
|
||||
static unsigned int dcc_timeout __read_mostly = 300;
|
||||
/* This is slow, but it's simple. --RR */
|
||||
static char *irc_buffer;
|
||||
static DEFINE_SPINLOCK(irc_buffer_lock);
|
||||
|
||||
unsigned int (*nf_nat_irc_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_irc_hook);
|
||||
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("IRC (DCC) connection tracking helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_conntrack_irc");
|
||||
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
MODULE_PARM_DESC(ports, "port numbers of IRC servers");
|
||||
module_param(max_dcc_channels, uint, 0400);
|
||||
MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per "
|
||||
"IRC session");
|
||||
module_param(dcc_timeout, uint, 0400);
|
||||
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
|
||||
|
||||
static const char *dccprotos[] = {
|
||||
"SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT "
|
||||
};
|
||||
|
||||
#define MINMATCHLEN 5
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s:" format, \
|
||||
__FILE__, __FUNCTION__ , ## args)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
/* tries to get the ip_addr and port out of a dcc command
|
||||
* return value: -1 on failure, 0 on success
|
||||
* data pointer to first byte of DCC command data
|
||||
* data_end pointer to last byte of dcc command data
|
||||
* ip returns parsed ip of dcc command
|
||||
* port returns parsed port of dcc command
|
||||
* ad_beg_p returns pointer to first byte of addr data
|
||||
* ad_end_p returns pointer to last byte of addr data
|
||||
*/
|
||||
static int parse_dcc(char *data, char *data_end, u_int32_t *ip,
|
||||
u_int16_t *port, char **ad_beg_p, char **ad_end_p)
|
||||
{
|
||||
/* at least 12: "AAAAAAAA P\1\n" */
|
||||
while (*data++ != ' ')
|
||||
if (data > data_end - 12)
|
||||
return -1;
|
||||
|
||||
*ad_beg_p = data;
|
||||
*ip = simple_strtoul(data, &data, 10);
|
||||
|
||||
/* skip blanks between ip and port */
|
||||
while (*data == ' ') {
|
||||
if (data >= data_end)
|
||||
return -1;
|
||||
data++;
|
||||
}
|
||||
|
||||
*port = simple_strtoul(data, &data, 10);
|
||||
*ad_end_p = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(struct sk_buff **pskb, unsigned int protoff,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
unsigned int dataoff;
|
||||
struct tcphdr _tcph, *th;
|
||||
char *data, *data_limit, *ib_ptr;
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_tuple *tuple;
|
||||
u_int32_t dcc_ip;
|
||||
u_int16_t dcc_port;
|
||||
__be16 port;
|
||||
int i, ret = NF_ACCEPT;
|
||||
char *addr_beg_p, *addr_end_p;
|
||||
typeof(nf_nat_irc_hook) nf_nat_irc;
|
||||
|
||||
/* If packet is coming from IRC server */
|
||||
if (dir == IP_CT_DIR_REPLY)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Until there's been traffic both ways, don't look in packets. */
|
||||
if (ctinfo != IP_CT_ESTABLISHED &&
|
||||
ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Not a full tcp header? */
|
||||
th = skb_header_pointer(*pskb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* No data? */
|
||||
dataoff = protoff + th->doff*4;
|
||||
if (dataoff >= (*pskb)->len)
|
||||
return NF_ACCEPT;
|
||||
|
||||
spin_lock_bh(&irc_buffer_lock);
|
||||
ib_ptr = skb_header_pointer(*pskb, dataoff, (*pskb)->len - dataoff,
|
||||
irc_buffer);
|
||||
BUG_ON(ib_ptr == NULL);
|
||||
|
||||
data = ib_ptr;
|
||||
data_limit = ib_ptr + (*pskb)->len - dataoff;
|
||||
|
||||
/* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
|
||||
* 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
|
||||
while (data < data_limit - (19 + MINMATCHLEN)) {
|
||||
if (memcmp(data, "\1DCC ", 5)) {
|
||||
data++;
|
||||
continue;
|
||||
}
|
||||
data += 5;
|
||||
/* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
|
||||
|
||||
DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n",
|
||||
NIPQUAD(iph->saddr), ntohs(th->source),
|
||||
NIPQUAD(iph->daddr), ntohs(th->dest));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
|
||||
if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
|
||||
/* no match */
|
||||
continue;
|
||||
}
|
||||
data += strlen(dccprotos[i]);
|
||||
DEBUGP("DCC %s detected\n", dccprotos[i]);
|
||||
|
||||
/* we have at least
|
||||
* (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
|
||||
* data left (== 14/13 bytes) */
|
||||
if (parse_dcc((char *)data, data_limit, &dcc_ip,
|
||||
&dcc_port, &addr_beg_p, &addr_end_p)) {
|
||||
DEBUGP("unable to parse dcc command\n");
|
||||
continue;
|
||||
}
|
||||
DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n",
|
||||
HIPQUAD(dcc_ip), dcc_port);
|
||||
|
||||
/* dcc_ip can be the internal OR external (NAT'ed) IP */
|
||||
tuple = &ct->tuplehash[dir].tuple;
|
||||
if (tuple->src.u3.ip != htonl(dcc_ip) &&
|
||||
tuple->dst.u3.ip != htonl(dcc_ip)) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_WARNING
|
||||
"Forged DCC command from "
|
||||
"%u.%u.%u.%u: %u.%u.%u.%u:%u\n",
|
||||
NIPQUAD(tuple->src.u3.ip),
|
||||
HIPQUAD(dcc_ip), dcc_port);
|
||||
continue;
|
||||
}
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
tuple = &ct->tuplehash[!dir].tuple;
|
||||
port = htons(dcc_port);
|
||||
nf_conntrack_expect_init(exp, tuple->src.l3num,
|
||||
NULL, &tuple->dst.u3,
|
||||
IPPROTO_TCP, NULL, &port);
|
||||
|
||||
nf_nat_irc = rcu_dereference(nf_nat_irc_hook);
|
||||
if (nf_nat_irc && ct->status & IPS_NAT_MASK)
|
||||
ret = nf_nat_irc(pskb, ctinfo,
|
||||
addr_beg_p - ib_ptr,
|
||||
addr_end_p - addr_beg_p,
|
||||
exp);
|
||||
else if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
nf_conntrack_expect_put(exp);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
spin_unlock_bh(&irc_buffer_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly;
|
||||
static char irc_names[MAX_PORTS][sizeof("irc-65535")] __read_mostly;
|
||||
|
||||
static void nf_conntrack_irc_fini(void);
|
||||
|
||||
static int __init nf_conntrack_irc_init(void)
|
||||
{
|
||||
int i, ret;
|
||||
char *tmpname;
|
||||
|
||||
if (max_dcc_channels < 1) {
|
||||
printk("nf_ct_irc: max_dcc_channels must not be zero\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irc_buffer = kmalloc(65536, GFP_KERNEL);
|
||||
if (!irc_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If no port given, default to standard irc port */
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = IRC_PORT;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
irc[i].tuple.src.l3num = AF_INET;
|
||||
irc[i].tuple.src.u.tcp.port = htons(ports[i]);
|
||||
irc[i].tuple.dst.protonum = IPPROTO_TCP;
|
||||
irc[i].mask.src.l3num = 0xFFFF;
|
||||
irc[i].mask.src.u.tcp.port = htons(0xFFFF);
|
||||
irc[i].mask.dst.protonum = 0xFF;
|
||||
irc[i].max_expected = max_dcc_channels;
|
||||
irc[i].timeout = dcc_timeout;
|
||||
irc[i].me = THIS_MODULE;
|
||||
irc[i].help = help;
|
||||
|
||||
tmpname = &irc_names[i][0];
|
||||
if (ports[i] == IRC_PORT)
|
||||
sprintf(tmpname, "irc");
|
||||
else
|
||||
sprintf(tmpname, "irc-%u", i);
|
||||
irc[i].name = tmpname;
|
||||
|
||||
ret = nf_conntrack_helper_register(&irc[i]);
|
||||
if (ret) {
|
||||
printk("nf_ct_irc: failed to register helper "
|
||||
"for pf: %u port: %u\n",
|
||||
irc[i].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_irc_fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function is intentionally _NOT_ defined as __exit, because
|
||||
* it is needed by the init function */
|
||||
static void nf_conntrack_irc_fini(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ports_c; i++)
|
||||
nf_conntrack_helper_unregister(&irc[i]);
|
||||
kfree(irc_buffer);
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_irc_init);
|
||||
module_exit(nf_conntrack_irc_fini);
|
||||
95
net/netfilter/nf_conntrack_l3proto_generic.c
Normal file
95
net/netfilter/nf_conntrack_l3proto_generic.c
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* Based largely upon the original ip_conntrack code which
|
||||
* had the following copyright information:
|
||||
*
|
||||
* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Author:
|
||||
* Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
static int generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
memset(&tuple->src.u3, 0, sizeof(tuple->src.u3));
|
||||
memset(&tuple->dst.u3, 0, sizeof(tuple->dst.u3));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
memset(&tuple->src.u3, 0, sizeof(tuple->src.u3));
|
||||
memset(&tuple->dst.u3, 0, sizeof(tuple->dst.u3));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
generic_prepare(struct sk_buff **pskb, unsigned int hooknum,
|
||||
unsigned int *dataoff, u_int8_t *protonum)
|
||||
{
|
||||
/* Never track !!! */
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
static u_int32_t generic_get_features(const struct nf_conntrack_tuple *tuple)
|
||||
|
||||
{
|
||||
return NF_CT_F_BASIC;
|
||||
}
|
||||
|
||||
struct nf_conntrack_l3proto nf_conntrack_l3proto_generic = {
|
||||
.l3proto = PF_UNSPEC,
|
||||
.name = "unknown",
|
||||
.pkt_to_tuple = generic_pkt_to_tuple,
|
||||
.invert_tuple = generic_invert_tuple,
|
||||
.print_tuple = generic_print_tuple,
|
||||
.print_conntrack = generic_print_conntrack,
|
||||
.prepare = generic_prepare,
|
||||
.get_features = generic_get_features,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_generic);
|
||||
127
net/netfilter/nf_conntrack_netbios_ns.c
Normal file
127
net/netfilter/nf_conntrack_netbios_ns.c
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* NetBIOS name service broadcast connection tracking helper
|
||||
*
|
||||
* (c) 2005 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* 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 helper tracks locally originating NetBIOS name service
|
||||
* requests by issuing permanent expectations (valid until
|
||||
* timing out) matching all reply connections from the
|
||||
* destination network. The only NetBIOS specific thing is
|
||||
* actually the port number.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
|
||||
#define NMBD_PORT 137
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("NetBIOS name service broadcast connection tracking helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_conntrack_netbios_ns");
|
||||
|
||||
static unsigned int timeout __read_mostly = 3;
|
||||
module_param(timeout, uint, 0400);
|
||||
MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds");
|
||||
|
||||
static int help(struct sk_buff **pskb, unsigned int protoff,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct iphdr *iph = (*pskb)->nh.iph;
|
||||
struct rtable *rt = (struct rtable *)(*pskb)->dst;
|
||||
struct in_device *in_dev;
|
||||
__be32 mask = 0;
|
||||
|
||||
/* we're only interested in locally generated packets */
|
||||
if ((*pskb)->sk == NULL)
|
||||
goto out;
|
||||
if (rt == NULL || !(rt->rt_flags & RTCF_BROADCAST))
|
||||
goto out;
|
||||
if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
|
||||
goto out;
|
||||
|
||||
rcu_read_lock();
|
||||
in_dev = __in_dev_get_rcu(rt->u.dst.dev);
|
||||
if (in_dev != NULL) {
|
||||
for_primary_ifa(in_dev) {
|
||||
if (ifa->ifa_broadcast == iph->daddr) {
|
||||
mask = ifa->ifa_mask;
|
||||
break;
|
||||
}
|
||||
} endfor_ifa(in_dev);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (mask == 0)
|
||||
goto out;
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL)
|
||||
goto out;
|
||||
|
||||
exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
||||
exp->tuple.src.u.udp.port = htons(NMBD_PORT);
|
||||
|
||||
exp->mask.src.u3.ip = mask;
|
||||
exp->mask.src.u.udp.port = htons(0xFFFF);
|
||||
exp->mask.dst.u3.ip = htonl(0xFFFFFFFF);
|
||||
exp->mask.dst.u.udp.port = htons(0xFFFF);
|
||||
exp->mask.dst.protonum = 0xFF;
|
||||
|
||||
exp->expectfn = NULL;
|
||||
exp->flags = NF_CT_EXPECT_PERMANENT;
|
||||
exp->helper = NULL;
|
||||
|
||||
nf_conntrack_expect_related(exp);
|
||||
nf_conntrack_expect_put(exp);
|
||||
|
||||
nf_ct_refresh(ct, *pskb, timeout * HZ);
|
||||
out:
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper helper __read_mostly = {
|
||||
.name = "netbios-ns",
|
||||
.tuple.src.l3num = AF_INET,
|
||||
.tuple.src.u.udp.port = __constant_htons(NMBD_PORT),
|
||||
.tuple.dst.protonum = IPPROTO_UDP,
|
||||
.mask.src.l3num = 0xFFFF,
|
||||
.mask.src.u.udp.port = __constant_htons(0xFFFF),
|
||||
.mask.dst.protonum = 0xFF,
|
||||
.max_expected = 1,
|
||||
.me = THIS_MODULE,
|
||||
.help = help,
|
||||
};
|
||||
|
||||
static int __init nf_conntrack_netbios_ns_init(void)
|
||||
{
|
||||
helper.timeout = timeout;
|
||||
return nf_conntrack_helper_register(&helper);
|
||||
}
|
||||
|
||||
static void __exit nf_conntrack_netbios_ns_fini(void)
|
||||
{
|
||||
nf_conntrack_helper_unregister(&helper);
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_netbios_ns_init);
|
||||
module_exit(nf_conntrack_netbios_ns_fini);
|
||||
1617
net/netfilter/nf_conntrack_netlink.c
Normal file
1617
net/netfilter/nf_conntrack_netlink.c
Normal file
File diff suppressed because it is too large
Load Diff
607
net/netfilter/nf_conntrack_pptp.c
Normal file
607
net/netfilter/nf_conntrack_pptp.c
Normal file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* Connection tracking support for PPTP (Point to Point Tunneling Protocol).
|
||||
* PPTP is a a protocol for creating virtual private networks.
|
||||
* It is a specification defined by Microsoft and some vendors
|
||||
* working with Microsoft. PPTP is built on top of a modified
|
||||
* version of the Internet Generic Routing Encapsulation Protocol.
|
||||
* GRE is defined in RFC 1701 and RFC 1702. Documentation of
|
||||
* PPTP can be found in RFC 2637
|
||||
*
|
||||
* (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* Development of this code funded by Astaro AG (http://www.astaro.com/)
|
||||
*
|
||||
* Limitations:
|
||||
* - We blindly assume that control connections are always
|
||||
* established in PNS->PAC direction. This is a violation
|
||||
* of RFFC2673
|
||||
* - We can only support one single call within each session
|
||||
* TODO:
|
||||
* - testing of incoming PPTP calls
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/tcp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_proto_gre.h>
|
||||
#include <linux/netfilter/nf_conntrack_pptp.h>
|
||||
|
||||
#define NF_CT_PPTP_VERSION "3.1"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
|
||||
MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP");
|
||||
MODULE_ALIAS("ip_conntrack_pptp");
|
||||
|
||||
static DEFINE_SPINLOCK(nf_pptp_lock);
|
||||
|
||||
int
|
||||
(*nf_nat_pptp_hook_outbound)(struct sk_buff **pskb,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_outbound);
|
||||
|
||||
int
|
||||
(*nf_nat_pptp_hook_inbound)(struct sk_buff **pskb,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_inbound);
|
||||
|
||||
void
|
||||
(*nf_nat_pptp_hook_exp_gre)(struct nf_conntrack_expect *expect_orig,
|
||||
struct nf_conntrack_expect *expect_reply)
|
||||
__read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_exp_gre);
|
||||
|
||||
void
|
||||
(*nf_nat_pptp_hook_expectfn)(struct nf_conn *ct,
|
||||
struct nf_conntrack_expect *exp) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_pptp_hook_expectfn);
|
||||
|
||||
#if 0
|
||||
/* PptpControlMessageType names */
|
||||
const char *pptp_msg_name[] = {
|
||||
"UNKNOWN_MESSAGE",
|
||||
"START_SESSION_REQUEST",
|
||||
"START_SESSION_REPLY",
|
||||
"STOP_SESSION_REQUEST",
|
||||
"STOP_SESSION_REPLY",
|
||||
"ECHO_REQUEST",
|
||||
"ECHO_REPLY",
|
||||
"OUT_CALL_REQUEST",
|
||||
"OUT_CALL_REPLY",
|
||||
"IN_CALL_REQUEST",
|
||||
"IN_CALL_REPLY",
|
||||
"IN_CALL_CONNECT",
|
||||
"CALL_CLEAR_REQUEST",
|
||||
"CALL_DISCONNECT_NOTIFY",
|
||||
"WAN_ERROR_NOTIFY",
|
||||
"SET_LINK_INFO"
|
||||
};
|
||||
EXPORT_SYMBOL(pptp_msg_name);
|
||||
#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
#define SECS *HZ
|
||||
#define MINS * 60 SECS
|
||||
#define HOURS * 60 MINS
|
||||
|
||||
#define PPTP_GRE_TIMEOUT (10 MINS)
|
||||
#define PPTP_GRE_STREAM_TIMEOUT (5 HOURS)
|
||||
|
||||
static void pptp_expectfn(struct nf_conn *ct,
|
||||
struct nf_conntrack_expect *exp)
|
||||
{
|
||||
typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn;
|
||||
DEBUGP("increasing timeouts\n");
|
||||
|
||||
/* increase timeout of GRE data channel conntrack entry */
|
||||
ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
|
||||
ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;
|
||||
|
||||
/* Can you see how rusty this code is, compared with the pre-2.6.11
|
||||
* one? That's what happened to my shiny newnat of 2002 ;( -HW */
|
||||
|
||||
rcu_read_lock();
|
||||
nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn);
|
||||
if (nf_nat_pptp_expectfn && ct->master->status & IPS_NAT_MASK)
|
||||
nf_nat_pptp_expectfn(ct, exp);
|
||||
else {
|
||||
struct nf_conntrack_tuple inv_t;
|
||||
struct nf_conntrack_expect *exp_other;
|
||||
|
||||
/* obviously this tuple inversion only works until you do NAT */
|
||||
nf_ct_invert_tuplepr(&inv_t, &exp->tuple);
|
||||
DEBUGP("trying to unexpect other dir: ");
|
||||
NF_CT_DUMP_TUPLE(&inv_t);
|
||||
|
||||
exp_other = nf_conntrack_expect_find_get(&inv_t);
|
||||
if (exp_other) {
|
||||
/* delete other expectation. */
|
||||
DEBUGP("found\n");
|
||||
nf_conntrack_unexpect_related(exp_other);
|
||||
nf_conntrack_expect_put(exp_other);
|
||||
} else {
|
||||
DEBUGP("not found\n");
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int destroy_sibling_or_exp(const struct nf_conntrack_tuple *t)
|
||||
{
|
||||
struct nf_conntrack_tuple_hash *h;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conn *sibling;
|
||||
|
||||
DEBUGP("trying to timeout ct or exp for tuple ");
|
||||
NF_CT_DUMP_TUPLE(t);
|
||||
|
||||
h = nf_conntrack_find_get(t, NULL);
|
||||
if (h) {
|
||||
sibling = nf_ct_tuplehash_to_ctrack(h);
|
||||
DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
|
||||
sibling->proto.gre.timeout = 0;
|
||||
sibling->proto.gre.stream_timeout = 0;
|
||||
if (del_timer(&sibling->timeout))
|
||||
sibling->timeout.function((unsigned long)sibling);
|
||||
nf_ct_put(sibling);
|
||||
return 1;
|
||||
} else {
|
||||
exp = nf_conntrack_expect_find_get(t);
|
||||
if (exp) {
|
||||
DEBUGP("unexpect_related of expect %p\n", exp);
|
||||
nf_conntrack_unexpect_related(exp);
|
||||
nf_conntrack_expect_put(exp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* timeout GRE data connections */
|
||||
static void pptp_destroy_siblings(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conn_help *help = nfct_help(ct);
|
||||
struct nf_conntrack_tuple t;
|
||||
|
||||
nf_ct_gre_keymap_destroy(ct);
|
||||
|
||||
/* try original (pns->pac) tuple */
|
||||
memcpy(&t, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, sizeof(t));
|
||||
t.dst.protonum = IPPROTO_GRE;
|
||||
t.src.u.gre.key = help->help.ct_pptp_info.pns_call_id;
|
||||
t.dst.u.gre.key = help->help.ct_pptp_info.pac_call_id;
|
||||
if (!destroy_sibling_or_exp(&t))
|
||||
DEBUGP("failed to timeout original pns->pac ct/exp\n");
|
||||
|
||||
/* try reply (pac->pns) tuple */
|
||||
memcpy(&t, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, sizeof(t));
|
||||
t.dst.protonum = IPPROTO_GRE;
|
||||
t.src.u.gre.key = help->help.ct_pptp_info.pac_call_id;
|
||||
t.dst.u.gre.key = help->help.ct_pptp_info.pns_call_id;
|
||||
if (!destroy_sibling_or_exp(&t))
|
||||
DEBUGP("failed to timeout reply pac->pns ct/exp\n");
|
||||
}
|
||||
|
||||
/* expect GRE connections (PNS->PAC and PAC->PNS direction) */
|
||||
static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid)
|
||||
{
|
||||
struct nf_conntrack_expect *exp_orig, *exp_reply;
|
||||
enum ip_conntrack_dir dir;
|
||||
int ret = 1;
|
||||
typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre;
|
||||
|
||||
exp_orig = nf_conntrack_expect_alloc(ct);
|
||||
if (exp_orig == NULL)
|
||||
goto out;
|
||||
|
||||
exp_reply = nf_conntrack_expect_alloc(ct);
|
||||
if (exp_reply == NULL)
|
||||
goto out_put_orig;
|
||||
|
||||
/* original direction, PNS->PAC */
|
||||
dir = IP_CT_DIR_ORIGINAL;
|
||||
nf_conntrack_expect_init(exp_orig, ct->tuplehash[dir].tuple.src.l3num,
|
||||
&ct->tuplehash[dir].tuple.src.u3,
|
||||
&ct->tuplehash[dir].tuple.dst.u3,
|
||||
IPPROTO_GRE, &peer_callid, &callid);
|
||||
exp_orig->expectfn = pptp_expectfn;
|
||||
|
||||
/* reply direction, PAC->PNS */
|
||||
dir = IP_CT_DIR_REPLY;
|
||||
nf_conntrack_expect_init(exp_reply, ct->tuplehash[dir].tuple.src.l3num,
|
||||
&ct->tuplehash[dir].tuple.src.u3,
|
||||
&ct->tuplehash[dir].tuple.dst.u3,
|
||||
IPPROTO_GRE, &callid, &peer_callid);
|
||||
exp_reply->expectfn = pptp_expectfn;
|
||||
|
||||
nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre);
|
||||
if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK)
|
||||
nf_nat_pptp_exp_gre(exp_orig, exp_reply);
|
||||
if (nf_conntrack_expect_related(exp_orig) != 0)
|
||||
goto out_put_both;
|
||||
if (nf_conntrack_expect_related(exp_reply) != 0)
|
||||
goto out_unexpect_orig;
|
||||
|
||||
/* Add GRE keymap entries */
|
||||
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0)
|
||||
goto out_unexpect_both;
|
||||
if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) {
|
||||
nf_ct_gre_keymap_destroy(ct);
|
||||
goto out_unexpect_both;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out_put_both:
|
||||
nf_conntrack_expect_put(exp_reply);
|
||||
out_put_orig:
|
||||
nf_conntrack_expect_put(exp_orig);
|
||||
out:
|
||||
return ret;
|
||||
|
||||
out_unexpect_both:
|
||||
nf_conntrack_unexpect_related(exp_reply);
|
||||
out_unexpect_orig:
|
||||
nf_conntrack_unexpect_related(exp_orig);
|
||||
goto out_put_both;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pptp_inbound_pkt(struct sk_buff **pskb,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq,
|
||||
unsigned int reqlen,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
|
||||
u_int16_t msg;
|
||||
__be16 cid = 0, pcid = 0;
|
||||
typeof(nf_nat_pptp_hook_inbound) nf_nat_pptp_inbound;
|
||||
|
||||
msg = ntohs(ctlh->messageType);
|
||||
DEBUGP("inbound control message %s\n", pptp_msg_name[msg]);
|
||||
|
||||
switch (msg) {
|
||||
case PPTP_START_SESSION_REPLY:
|
||||
/* server confirms new control session */
|
||||
if (info->sstate < PPTP_SESSION_REQUESTED)
|
||||
goto invalid;
|
||||
if (pptpReq->srep.resultCode == PPTP_START_OK)
|
||||
info->sstate = PPTP_SESSION_CONFIRMED;
|
||||
else
|
||||
info->sstate = PPTP_SESSION_ERROR;
|
||||
break;
|
||||
|
||||
case PPTP_STOP_SESSION_REPLY:
|
||||
/* server confirms end of control session */
|
||||
if (info->sstate > PPTP_SESSION_STOPREQ)
|
||||
goto invalid;
|
||||
if (pptpReq->strep.resultCode == PPTP_STOP_OK)
|
||||
info->sstate = PPTP_SESSION_NONE;
|
||||
else
|
||||
info->sstate = PPTP_SESSION_ERROR;
|
||||
break;
|
||||
|
||||
case PPTP_OUT_CALL_REPLY:
|
||||
/* server accepted call, we now expect GRE frames */
|
||||
if (info->sstate != PPTP_SESSION_CONFIRMED)
|
||||
goto invalid;
|
||||
if (info->cstate != PPTP_CALL_OUT_REQ &&
|
||||
info->cstate != PPTP_CALL_OUT_CONF)
|
||||
goto invalid;
|
||||
|
||||
cid = pptpReq->ocack.callID;
|
||||
pcid = pptpReq->ocack.peersCallID;
|
||||
if (info->pns_call_id != pcid)
|
||||
goto invalid;
|
||||
DEBUGP("%s, CID=%X, PCID=%X\n", pptp_msg_name[msg],
|
||||
ntohs(cid), ntohs(pcid));
|
||||
|
||||
if (pptpReq->ocack.resultCode == PPTP_OUTCALL_CONNECT) {
|
||||
info->cstate = PPTP_CALL_OUT_CONF;
|
||||
info->pac_call_id = cid;
|
||||
exp_gre(ct, cid, pcid);
|
||||
} else
|
||||
info->cstate = PPTP_CALL_NONE;
|
||||
break;
|
||||
|
||||
case PPTP_IN_CALL_REQUEST:
|
||||
/* server tells us about incoming call request */
|
||||
if (info->sstate != PPTP_SESSION_CONFIRMED)
|
||||
goto invalid;
|
||||
|
||||
cid = pptpReq->icreq.callID;
|
||||
DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
|
||||
info->cstate = PPTP_CALL_IN_REQ;
|
||||
info->pac_call_id = cid;
|
||||
break;
|
||||
|
||||
case PPTP_IN_CALL_CONNECT:
|
||||
/* server tells us about incoming call established */
|
||||
if (info->sstate != PPTP_SESSION_CONFIRMED)
|
||||
goto invalid;
|
||||
if (info->cstate != PPTP_CALL_IN_REP &&
|
||||
info->cstate != PPTP_CALL_IN_CONF)
|
||||
goto invalid;
|
||||
|
||||
pcid = pptpReq->iccon.peersCallID;
|
||||
cid = info->pac_call_id;
|
||||
|
||||
if (info->pns_call_id != pcid)
|
||||
goto invalid;
|
||||
|
||||
DEBUGP("%s, PCID=%X\n", pptp_msg_name[msg], ntohs(pcid));
|
||||
info->cstate = PPTP_CALL_IN_CONF;
|
||||
|
||||
/* we expect a GRE connection from PAC to PNS */
|
||||
exp_gre(ct, cid, pcid);
|
||||
break;
|
||||
|
||||
case PPTP_CALL_DISCONNECT_NOTIFY:
|
||||
/* server confirms disconnect */
|
||||
cid = pptpReq->disc.callID;
|
||||
DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
|
||||
info->cstate = PPTP_CALL_NONE;
|
||||
|
||||
/* untrack this call id, unexpect GRE packets */
|
||||
pptp_destroy_siblings(ct);
|
||||
break;
|
||||
|
||||
case PPTP_WAN_ERROR_NOTIFY:
|
||||
case PPTP_ECHO_REQUEST:
|
||||
case PPTP_ECHO_REPLY:
|
||||
/* I don't have to explain these ;) */
|
||||
break;
|
||||
|
||||
default:
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
nf_nat_pptp_inbound = rcu_dereference(nf_nat_pptp_hook_inbound);
|
||||
if (nf_nat_pptp_inbound && ct->status & IPS_NAT_MASK)
|
||||
return nf_nat_pptp_inbound(pskb, ct, ctinfo, ctlh, pptpReq);
|
||||
return NF_ACCEPT;
|
||||
|
||||
invalid:
|
||||
DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
|
||||
"cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
|
||||
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
|
||||
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
|
||||
ntohs(info->pns_call_id), ntohs(info->pac_call_id));
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pptp_outbound_pkt(struct sk_buff **pskb,
|
||||
struct PptpControlHeader *ctlh,
|
||||
union pptp_ctrl_union *pptpReq,
|
||||
unsigned int reqlen,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
|
||||
u_int16_t msg;
|
||||
__be16 cid = 0, pcid = 0;
|
||||
typeof(nf_nat_pptp_hook_outbound) nf_nat_pptp_outbound;
|
||||
|
||||
msg = ntohs(ctlh->messageType);
|
||||
DEBUGP("outbound control message %s\n", pptp_msg_name[msg]);
|
||||
|
||||
switch (msg) {
|
||||
case PPTP_START_SESSION_REQUEST:
|
||||
/* client requests for new control session */
|
||||
if (info->sstate != PPTP_SESSION_NONE)
|
||||
goto invalid;
|
||||
info->sstate = PPTP_SESSION_REQUESTED;
|
||||
break;
|
||||
|
||||
case PPTP_STOP_SESSION_REQUEST:
|
||||
/* client requests end of control session */
|
||||
info->sstate = PPTP_SESSION_STOPREQ;
|
||||
break;
|
||||
|
||||
case PPTP_OUT_CALL_REQUEST:
|
||||
/* client initiating connection to server */
|
||||
if (info->sstate != PPTP_SESSION_CONFIRMED)
|
||||
goto invalid;
|
||||
info->cstate = PPTP_CALL_OUT_REQ;
|
||||
/* track PNS call id */
|
||||
cid = pptpReq->ocreq.callID;
|
||||
DEBUGP("%s, CID=%X\n", pptp_msg_name[msg], ntohs(cid));
|
||||
info->pns_call_id = cid;
|
||||
break;
|
||||
|
||||
case PPTP_IN_CALL_REPLY:
|
||||
/* client answers incoming call */
|
||||
if (info->cstate != PPTP_CALL_IN_REQ &&
|
||||
info->cstate != PPTP_CALL_IN_REP)
|
||||
goto invalid;
|
||||
|
||||
cid = pptpReq->icack.callID;
|
||||
pcid = pptpReq->icack.peersCallID;
|
||||
if (info->pac_call_id != pcid)
|
||||
goto invalid;
|
||||
DEBUGP("%s, CID=%X PCID=%X\n", pptp_msg_name[msg],
|
||||
ntohs(cid), ntohs(pcid));
|
||||
|
||||
if (pptpReq->icack.resultCode == PPTP_INCALL_ACCEPT) {
|
||||
/* part two of the three-way handshake */
|
||||
info->cstate = PPTP_CALL_IN_REP;
|
||||
info->pns_call_id = cid;
|
||||
} else
|
||||
info->cstate = PPTP_CALL_NONE;
|
||||
break;
|
||||
|
||||
case PPTP_CALL_CLEAR_REQUEST:
|
||||
/* client requests hangup of call */
|
||||
if (info->sstate != PPTP_SESSION_CONFIRMED)
|
||||
goto invalid;
|
||||
/* FUTURE: iterate over all calls and check if
|
||||
* call ID is valid. We don't do this without newnat,
|
||||
* because we only know about last call */
|
||||
info->cstate = PPTP_CALL_CLEAR_REQ;
|
||||
break;
|
||||
|
||||
case PPTP_SET_LINK_INFO:
|
||||
case PPTP_ECHO_REQUEST:
|
||||
case PPTP_ECHO_REPLY:
|
||||
/* I don't have to explain these ;) */
|
||||
break;
|
||||
|
||||
default:
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
nf_nat_pptp_outbound = rcu_dereference(nf_nat_pptp_hook_outbound);
|
||||
if (nf_nat_pptp_outbound && ct->status & IPS_NAT_MASK)
|
||||
return nf_nat_pptp_outbound(pskb, ct, ctinfo, ctlh, pptpReq);
|
||||
return NF_ACCEPT;
|
||||
|
||||
invalid:
|
||||
DEBUGP("invalid %s: type=%d cid=%u pcid=%u "
|
||||
"cstate=%d sstate=%d pns_cid=%u pac_cid=%u\n",
|
||||
msg <= PPTP_MSG_MAX ? pptp_msg_name[msg] : pptp_msg_name[0],
|
||||
msg, ntohs(cid), ntohs(pcid), info->cstate, info->sstate,
|
||||
ntohs(info->pns_call_id), ntohs(info->pac_call_id));
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static const unsigned int pptp_msg_size[] = {
|
||||
[PPTP_START_SESSION_REQUEST] = sizeof(struct PptpStartSessionRequest),
|
||||
[PPTP_START_SESSION_REPLY] = sizeof(struct PptpStartSessionReply),
|
||||
[PPTP_STOP_SESSION_REQUEST] = sizeof(struct PptpStopSessionRequest),
|
||||
[PPTP_STOP_SESSION_REPLY] = sizeof(struct PptpStopSessionReply),
|
||||
[PPTP_OUT_CALL_REQUEST] = sizeof(struct PptpOutCallRequest),
|
||||
[PPTP_OUT_CALL_REPLY] = sizeof(struct PptpOutCallReply),
|
||||
[PPTP_IN_CALL_REQUEST] = sizeof(struct PptpInCallRequest),
|
||||
[PPTP_IN_CALL_REPLY] = sizeof(struct PptpInCallReply),
|
||||
[PPTP_IN_CALL_CONNECT] = sizeof(struct PptpInCallConnected),
|
||||
[PPTP_CALL_CLEAR_REQUEST] = sizeof(struct PptpClearCallRequest),
|
||||
[PPTP_CALL_DISCONNECT_NOTIFY] = sizeof(struct PptpCallDisconnectNotify),
|
||||
[PPTP_WAN_ERROR_NOTIFY] = sizeof(struct PptpWanErrorNotify),
|
||||
[PPTP_SET_LINK_INFO] = sizeof(struct PptpSetLinkInfo),
|
||||
};
|
||||
|
||||
/* track caller id inside control connection, call expect_related */
|
||||
static int
|
||||
conntrack_pptp_help(struct sk_buff **pskb, unsigned int protoff,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
||||
|
||||
{
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
struct nf_ct_pptp_master *info = &nfct_help(ct)->help.ct_pptp_info;
|
||||
struct tcphdr _tcph, *tcph;
|
||||
struct pptp_pkt_hdr _pptph, *pptph;
|
||||
struct PptpControlHeader _ctlh, *ctlh;
|
||||
union pptp_ctrl_union _pptpReq, *pptpReq;
|
||||
unsigned int tcplen = (*pskb)->len - protoff;
|
||||
unsigned int datalen, reqlen, nexthdr_off;
|
||||
int oldsstate, oldcstate;
|
||||
int ret;
|
||||
u_int16_t msg;
|
||||
|
||||
/* don't do any tracking before tcp handshake complete */
|
||||
if (ctinfo != IP_CT_ESTABLISHED &&
|
||||
ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY)
|
||||
return NF_ACCEPT;
|
||||
|
||||
nexthdr_off = protoff;
|
||||
tcph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_tcph), &_tcph);
|
||||
BUG_ON(!tcph);
|
||||
nexthdr_off += tcph->doff * 4;
|
||||
datalen = tcplen - tcph->doff * 4;
|
||||
|
||||
pptph = skb_header_pointer(*pskb, nexthdr_off, sizeof(_pptph), &_pptph);
|
||||
if (!pptph) {
|
||||
DEBUGP("no full PPTP header, can't track\n");
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
nexthdr_off += sizeof(_pptph);
|
||||
datalen -= sizeof(_pptph);
|
||||
|
||||
/* if it's not a control message we can't do anything with it */
|
||||
if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL ||
|
||||
ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) {
|
||||
DEBUGP("not a control packet\n");
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
ctlh = skb_header_pointer(*pskb, nexthdr_off, sizeof(_ctlh), &_ctlh);
|
||||
if (!ctlh)
|
||||
return NF_ACCEPT;
|
||||
nexthdr_off += sizeof(_ctlh);
|
||||
datalen -= sizeof(_ctlh);
|
||||
|
||||
reqlen = datalen;
|
||||
msg = ntohs(ctlh->messageType);
|
||||
if (msg > 0 && msg <= PPTP_MSG_MAX && reqlen < pptp_msg_size[msg])
|
||||
return NF_ACCEPT;
|
||||
if (reqlen > sizeof(*pptpReq))
|
||||
reqlen = sizeof(*pptpReq);
|
||||
|
||||
pptpReq = skb_header_pointer(*pskb, nexthdr_off, reqlen, &_pptpReq);
|
||||
if (!pptpReq)
|
||||
return NF_ACCEPT;
|
||||
|
||||
oldsstate = info->sstate;
|
||||
oldcstate = info->cstate;
|
||||
|
||||
spin_lock_bh(&nf_pptp_lock);
|
||||
|
||||
/* FIXME: We just blindly assume that the control connection is always
|
||||
* established from PNS->PAC. However, RFC makes no guarantee */
|
||||
if (dir == IP_CT_DIR_ORIGINAL)
|
||||
/* client -> server (PNS -> PAC) */
|
||||
ret = pptp_outbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
|
||||
ctinfo);
|
||||
else
|
||||
/* server -> client (PAC -> PNS) */
|
||||
ret = pptp_inbound_pkt(pskb, ctlh, pptpReq, reqlen, ct,
|
||||
ctinfo);
|
||||
DEBUGP("sstate: %d->%d, cstate: %d->%d\n",
|
||||
oldsstate, info->sstate, oldcstate, info->cstate);
|
||||
spin_unlock_bh(&nf_pptp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* control protocol helper */
|
||||
static struct nf_conntrack_helper pptp __read_mostly = {
|
||||
.name = "pptp",
|
||||
.me = THIS_MODULE,
|
||||
.max_expected = 2,
|
||||
.timeout = 5 * 60,
|
||||
.tuple.src.l3num = AF_INET,
|
||||
.tuple.src.u.tcp.port = __constant_htons(PPTP_CONTROL_PORT),
|
||||
.tuple.dst.protonum = IPPROTO_TCP,
|
||||
.mask.src.l3num = 0xffff,
|
||||
.mask.src.u.tcp.port = __constant_htons(0xffff),
|
||||
.mask.dst.protonum = 0xff,
|
||||
.help = conntrack_pptp_help,
|
||||
.destroy = pptp_destroy_siblings,
|
||||
};
|
||||
|
||||
static int __init nf_conntrack_pptp_init(void)
|
||||
{
|
||||
return nf_conntrack_helper_register(&pptp);
|
||||
}
|
||||
|
||||
static void __exit nf_conntrack_pptp_fini(void)
|
||||
{
|
||||
nf_conntrack_helper_unregister(&pptp);
|
||||
nf_ct_gre_keymap_flush();
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_pptp_init);
|
||||
module_exit(nf_conntrack_pptp_fini);
|
||||
379
net/netfilter/nf_conntrack_proto.c
Normal file
379
net/netfilter/nf_conntrack_proto.c
Normal file
@@ -0,0 +1,379 @@
|
||||
/* L3/L4 protocol support for nf_conntrack. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
|
||||
struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
|
||||
struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l3protos);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex);
|
||||
|
||||
static int
|
||||
nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path,
|
||||
struct ctl_table *table, unsigned int *users)
|
||||
{
|
||||
if (*header == NULL) {
|
||||
*header = nf_register_sysctl_table(path, table);
|
||||
if (*header == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (users != NULL)
|
||||
(*users)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nf_ct_unregister_sysctl(struct ctl_table_header **header,
|
||||
struct ctl_table *table, unsigned int *users)
|
||||
{
|
||||
if (users != NULL && --*users > 0)
|
||||
return;
|
||||
nf_unregister_sysctl_table(*header, table);
|
||||
*header = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct nf_conntrack_l4proto *
|
||||
__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
|
||||
{
|
||||
if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
|
||||
return &nf_conntrack_l4proto_generic;
|
||||
|
||||
return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
|
||||
|
||||
/* this is guaranteed to always return a valid protocol helper, since
|
||||
* it falls back to generic_protocol */
|
||||
struct nf_conntrack_l4proto *
|
||||
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
|
||||
{
|
||||
struct nf_conntrack_l4proto *p;
|
||||
|
||||
rcu_read_lock();
|
||||
p = __nf_ct_l4proto_find(l3proto, l4proto);
|
||||
if (!try_module_get(p->me))
|
||||
p = &nf_conntrack_l4proto_generic;
|
||||
rcu_read_unlock();
|
||||
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
|
||||
|
||||
void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
|
||||
{
|
||||
module_put(p->me);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
|
||||
|
||||
struct nf_conntrack_l3proto *
|
||||
nf_ct_l3proto_find_get(u_int16_t l3proto)
|
||||
{
|
||||
struct nf_conntrack_l3proto *p;
|
||||
|
||||
rcu_read_lock();
|
||||
p = __nf_ct_l3proto_find(l3proto);
|
||||
if (!try_module_get(p->me))
|
||||
p = &nf_conntrack_l3proto_generic;
|
||||
rcu_read_unlock();
|
||||
|
||||
return p;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
|
||||
|
||||
void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
|
||||
{
|
||||
module_put(p->me);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
|
||||
|
||||
int
|
||||
nf_ct_l3proto_try_module_get(unsigned short l3proto)
|
||||
{
|
||||
int ret;
|
||||
struct nf_conntrack_l3proto *p;
|
||||
|
||||
retry: p = nf_ct_l3proto_find_get(l3proto);
|
||||
if (p == &nf_conntrack_l3proto_generic) {
|
||||
ret = request_module("nf_conntrack-%d", l3proto);
|
||||
if (!ret)
|
||||
goto retry;
|
||||
|
||||
return -EPROTOTYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
|
||||
|
||||
void nf_ct_l3proto_module_put(unsigned short l3proto)
|
||||
{
|
||||
struct nf_conntrack_l3proto *p;
|
||||
|
||||
/* rcu_read_lock not necessary since the caller holds a reference */
|
||||
p = __nf_ct_l3proto_find(l3proto);
|
||||
module_put(p->me);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
|
||||
|
||||
static int kill_l3proto(struct nf_conn *i, void *data)
|
||||
{
|
||||
return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
|
||||
((struct nf_conntrack_l3proto *)data)->l3proto);
|
||||
}
|
||||
|
||||
static int kill_l4proto(struct nf_conn *i, void *data)
|
||||
{
|
||||
struct nf_conntrack_l4proto *l4proto;
|
||||
l4proto = (struct nf_conntrack_l4proto *)data;
|
||||
return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
|
||||
l4proto->l4proto) &&
|
||||
(i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
|
||||
l4proto->l3proto);
|
||||
}
|
||||
|
||||
static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
mutex_lock(&nf_ct_proto_sysctl_mutex);
|
||||
if (l3proto->ctl_table != NULL) {
|
||||
err = nf_ct_register_sysctl(&l3proto->ctl_table_header,
|
||||
l3proto->ctl_table_path,
|
||||
l3proto->ctl_table, NULL);
|
||||
}
|
||||
mutex_unlock(&nf_ct_proto_sysctl_mutex);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
|
||||
{
|
||||
#ifdef CONFIG_SYSCTL
|
||||
mutex_lock(&nf_ct_proto_sysctl_mutex);
|
||||
if (l3proto->ctl_table_header != NULL)
|
||||
nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
|
||||
l3proto->ctl_table, NULL);
|
||||
mutex_unlock(&nf_ct_proto_sysctl_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (proto->l3proto >= AF_MAX) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
ret = nf_ct_l3proto_register_sysctl(proto);
|
||||
if (ret < 0)
|
||||
nf_conntrack_l3proto_unregister(proto);
|
||||
return ret;
|
||||
|
||||
out_unlock:
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
|
||||
|
||||
void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
|
||||
{
|
||||
BUG_ON(proto->l3proto >= AF_MAX);
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
BUG_ON(nf_ct_l3protos[proto->l3proto] != proto);
|
||||
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
|
||||
&nf_conntrack_l3proto_generic);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
synchronize_rcu();
|
||||
|
||||
nf_ct_l3proto_unregister_sysctl(proto);
|
||||
|
||||
/* Remove all contrack entries for this protocol */
|
||||
nf_ct_iterate_cleanup(kill_l3proto, proto);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
|
||||
|
||||
static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
mutex_lock(&nf_ct_proto_sysctl_mutex);
|
||||
if (l4proto->ctl_table != NULL) {
|
||||
err = nf_ct_register_sysctl(l4proto->ctl_table_header,
|
||||
nf_net_netfilter_sysctl_path,
|
||||
l4proto->ctl_table,
|
||||
l4proto->ctl_table_users);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
if (l4proto->ctl_compat_table != NULL) {
|
||||
err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header,
|
||||
nf_net_ipv4_netfilter_sysctl_path,
|
||||
l4proto->ctl_compat_table, NULL);
|
||||
if (err == 0)
|
||||
goto out;
|
||||
nf_ct_unregister_sysctl(l4proto->ctl_table_header,
|
||||
l4proto->ctl_table,
|
||||
l4proto->ctl_table_users);
|
||||
}
|
||||
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
|
||||
out:
|
||||
mutex_unlock(&nf_ct_proto_sysctl_mutex);
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
#ifdef CONFIG_SYSCTL
|
||||
mutex_lock(&nf_ct_proto_sysctl_mutex);
|
||||
if (l4proto->ctl_table_header != NULL &&
|
||||
*l4proto->ctl_table_header != NULL)
|
||||
nf_ct_unregister_sysctl(l4proto->ctl_table_header,
|
||||
l4proto->ctl_table,
|
||||
l4proto->ctl_table_users);
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
if (l4proto->ctl_compat_table_header != NULL)
|
||||
nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header,
|
||||
l4proto->ctl_compat_table, NULL);
|
||||
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
|
||||
mutex_unlock(&nf_ct_proto_sysctl_mutex);
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
}
|
||||
|
||||
/* FIXME: Allow NULL functions and sub in pointers to generic for
|
||||
them. --RR */
|
||||
int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (l4proto->l3proto >= PF_MAX) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (l4proto == &nf_conntrack_l4proto_generic)
|
||||
return nf_ct_l4proto_register_sysctl(l4proto);
|
||||
|
||||
retry:
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_protos[l4proto->l3proto]) {
|
||||
if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
|
||||
!= &nf_conntrack_l4proto_generic) {
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
} else {
|
||||
/* l3proto may be loaded latter. */
|
||||
struct nf_conntrack_l4proto **proto_array;
|
||||
int i;
|
||||
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
proto_array = (struct nf_conntrack_l4proto **)
|
||||
kmalloc(MAX_NF_CT_PROTO *
|
||||
sizeof(struct nf_conntrack_l4proto *),
|
||||
GFP_KERNEL);
|
||||
if (proto_array == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < MAX_NF_CT_PROTO; i++)
|
||||
proto_array[i] = &nf_conntrack_l4proto_generic;
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_protos[l4proto->l3proto]) {
|
||||
/* bad timing, but no problem */
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
kfree(proto_array);
|
||||
} else {
|
||||
nf_ct_protos[l4proto->l3proto] = proto_array;
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just once because array is never freed until unloading
|
||||
* nf_conntrack.ko
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
ret = nf_ct_l4proto_register_sysctl(l4proto);
|
||||
if (ret < 0)
|
||||
nf_conntrack_l4proto_unregister(l4proto);
|
||||
return ret;
|
||||
|
||||
out_unlock:
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
|
||||
|
||||
void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
BUG_ON(l4proto->l3proto >= PF_MAX);
|
||||
|
||||
if (l4proto == &nf_conntrack_l4proto_generic) {
|
||||
nf_ct_l4proto_unregister_sysctl(l4proto);
|
||||
return;
|
||||
}
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
BUG_ON(nf_ct_protos[l4proto->l3proto][l4proto->l4proto] != l4proto);
|
||||
rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
|
||||
&nf_conntrack_l4proto_generic);
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
synchronize_rcu();
|
||||
|
||||
nf_ct_l4proto_unregister_sysctl(l4proto);
|
||||
|
||||
/* Remove all contrack entries for this protocol */
|
||||
nf_ct_iterate_cleanup(kill_l4proto, l4proto);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
|
||||
124
net/netfilter/nf_conntrack_proto_generic.c
Normal file
124
net/netfilter/nf_conntrack_proto_generic.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with L3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_proto_generic.c
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
|
||||
static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ;
|
||||
|
||||
static int generic_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
tuple->src.u.all = 0;
|
||||
tuple->dst.u.all = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
tuple->src.u.all = 0;
|
||||
tuple->dst.u.all = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int generic_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int generic_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, or -1 for invalid. */
|
||||
static int packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_generic_timeout);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static struct ctl_table_header *generic_sysctl_header;
|
||||
static struct ctl_table generic_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_GENERIC_TIMEOUT,
|
||||
.procname = "nf_conntrack_generic_timeout",
|
||||
.data = &nf_ct_generic_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
static struct ctl_table generic_compat_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_GENERIC_TIMEOUT,
|
||||
.procname = "ip_conntrack_generic_timeout",
|
||||
.data = &nf_ct_generic_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
struct nf_conntrack_l4proto nf_conntrack_l4proto_generic =
|
||||
{
|
||||
.l3proto = PF_UNSPEC,
|
||||
.l4proto = 0,
|
||||
.name = "unknown",
|
||||
.pkt_to_tuple = generic_pkt_to_tuple,
|
||||
.invert_tuple = generic_invert_tuple,
|
||||
.print_tuple = generic_print_tuple,
|
||||
.print_conntrack = generic_print_conntrack,
|
||||
.packet = packet,
|
||||
.new = new,
|
||||
#ifdef CONFIG_SYSCTL
|
||||
.ctl_table_header = &generic_sysctl_header,
|
||||
.ctl_table = generic_sysctl_table,
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
.ctl_compat_table = generic_compat_sysctl_table,
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
304
net/netfilter/nf_conntrack_proto_gre.c
Normal file
304
net/netfilter/nf_conntrack_proto_gre.c
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* ip_conntrack_proto_gre.c - Version 3.0
|
||||
*
|
||||
* Connection tracking protocol helper module for GRE.
|
||||
*
|
||||
* GRE is a generic encapsulation protocol, which is generally not very
|
||||
* suited for NAT, as it has no protocol-specific part as port numbers.
|
||||
*
|
||||
* It has an optional key field, which may help us distinguishing two
|
||||
* connections between the same two hosts.
|
||||
*
|
||||
* GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
|
||||
*
|
||||
* PPTP is built on top of a modified version of GRE, and has a mandatory
|
||||
* field called "CallID", which serves us for the same purpose as the key
|
||||
* field in plain GRE.
|
||||
*
|
||||
* Documentation about PPTP can be found in RFC 2637
|
||||
*
|
||||
* (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* Development of this code funded by Astaro AG (http://www.astaro.com/)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <linux/netfilter/nf_conntrack_proto_gre.h>
|
||||
#include <linux/netfilter/nf_conntrack_pptp.h>
|
||||
|
||||
#define GRE_TIMEOUT (30 * HZ)
|
||||
#define GRE_STREAM_TIMEOUT (180 * HZ)
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, args...) printk(KERN_DEBUG "%s:%s: " format, __FILE__, __FUNCTION__, ## args)
|
||||
#else
|
||||
#define DEBUGP(x, args...)
|
||||
#endif
|
||||
|
||||
static DEFINE_RWLOCK(nf_ct_gre_lock);
|
||||
static LIST_HEAD(gre_keymap_list);
|
||||
|
||||
void nf_ct_gre_keymap_flush(void)
|
||||
{
|
||||
struct list_head *pos, *n;
|
||||
|
||||
write_lock_bh(&nf_ct_gre_lock);
|
||||
list_for_each_safe(pos, n, &gre_keymap_list) {
|
||||
list_del(pos);
|
||||
kfree(pos);
|
||||
}
|
||||
write_unlock_bh(&nf_ct_gre_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_ct_gre_keymap_flush);
|
||||
|
||||
static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
|
||||
const struct nf_conntrack_tuple *t)
|
||||
{
|
||||
return km->tuple.src.l3num == t->src.l3num &&
|
||||
!memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
|
||||
!memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
|
||||
km->tuple.dst.protonum == t->dst.protonum &&
|
||||
km->tuple.dst.u.all == t->dst.u.all;
|
||||
}
|
||||
|
||||
/* look up the source key for a given tuple */
|
||||
static __be16 gre_keymap_lookup(struct nf_conntrack_tuple *t)
|
||||
{
|
||||
struct nf_ct_gre_keymap *km;
|
||||
__be16 key = 0;
|
||||
|
||||
read_lock_bh(&nf_ct_gre_lock);
|
||||
list_for_each_entry(km, &gre_keymap_list, list) {
|
||||
if (gre_key_cmpfn(km, t)) {
|
||||
key = km->tuple.src.u.gre.key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&nf_ct_gre_lock);
|
||||
|
||||
DEBUGP("lookup src key 0x%x for ", key);
|
||||
NF_CT_DUMP_TUPLE(t);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/* add a single keymap entry, associate with specified master ct */
|
||||
int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
|
||||
struct nf_conntrack_tuple *t)
|
||||
{
|
||||
struct nf_conn_help *help = nfct_help(ct);
|
||||
struct nf_ct_gre_keymap **kmp, *km;
|
||||
|
||||
BUG_ON(strcmp(help->helper->name, "pptp"));
|
||||
kmp = &help->help.ct_pptp_info.keymap[dir];
|
||||
if (*kmp) {
|
||||
/* check whether it's a retransmission */
|
||||
list_for_each_entry(km, &gre_keymap_list, list) {
|
||||
if (gre_key_cmpfn(km, t) && km == *kmp)
|
||||
return 0;
|
||||
}
|
||||
DEBUGP("trying to override keymap_%s for ct %p\n",
|
||||
dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
km = kmalloc(sizeof(*km), GFP_ATOMIC);
|
||||
if (!km)
|
||||
return -ENOMEM;
|
||||
memcpy(&km->tuple, t, sizeof(*t));
|
||||
*kmp = km;
|
||||
|
||||
DEBUGP("adding new entry %p: ", km);
|
||||
NF_CT_DUMP_TUPLE(&km->tuple);
|
||||
|
||||
write_lock_bh(&nf_ct_gre_lock);
|
||||
list_add_tail(&km->list, &gre_keymap_list);
|
||||
write_unlock_bh(&nf_ct_gre_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
|
||||
|
||||
/* destroy the keymap entries associated with specified master ct */
|
||||
void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conn_help *help = nfct_help(ct);
|
||||
enum ip_conntrack_dir dir;
|
||||
|
||||
DEBUGP("entering for ct %p\n", ct);
|
||||
BUG_ON(strcmp(help->helper->name, "pptp"));
|
||||
|
||||
write_lock_bh(&nf_ct_gre_lock);
|
||||
for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
|
||||
if (help->help.ct_pptp_info.keymap[dir]) {
|
||||
DEBUGP("removing %p from list\n",
|
||||
help->help.ct_pptp_info.keymap[dir]);
|
||||
list_del(&help->help.ct_pptp_info.keymap[dir]->list);
|
||||
kfree(help->help.ct_pptp_info.keymap[dir]);
|
||||
help->help.ct_pptp_info.keymap[dir] = NULL;
|
||||
}
|
||||
}
|
||||
write_unlock_bh(&nf_ct_gre_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
|
||||
|
||||
/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
|
||||
|
||||
/* invert gre part of tuple */
|
||||
static int gre_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
tuple->dst.u.gre.key = orig->src.u.gre.key;
|
||||
tuple->src.u.gre.key = orig->dst.u.gre.key;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* gre hdr info to tuple */
|
||||
static int gre_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct gre_hdr_pptp _pgrehdr, *pgrehdr;
|
||||
__be16 srckey;
|
||||
struct gre_hdr _grehdr, *grehdr;
|
||||
|
||||
/* first only delinearize old RFC1701 GRE header */
|
||||
grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
|
||||
if (!grehdr || grehdr->version != GRE_VERSION_PPTP) {
|
||||
/* try to behave like "nf_conntrack_proto_generic" */
|
||||
tuple->src.u.all = 0;
|
||||
tuple->dst.u.all = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PPTP header is variable length, only need up to the call_id field */
|
||||
pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
|
||||
if (!pgrehdr)
|
||||
return 1;
|
||||
|
||||
if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) {
|
||||
DEBUGP("GRE_VERSION_PPTP but unknown proto\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tuple->dst.u.gre.key = pgrehdr->call_id;
|
||||
srckey = gre_keymap_lookup(tuple);
|
||||
tuple->src.u.gre.key = srckey;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* print gre part of tuple */
|
||||
static int gre_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
|
||||
ntohs(tuple->src.u.gre.key),
|
||||
ntohs(tuple->dst.u.gre.key));
|
||||
}
|
||||
|
||||
/* print private data for conntrack */
|
||||
static int gre_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *ct)
|
||||
{
|
||||
return seq_printf(s, "timeout=%u, stream_timeout=%u ",
|
||||
(ct->proto.gre.timeout / HZ),
|
||||
(ct->proto.gre.stream_timeout / HZ));
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, and may modify conntrack */
|
||||
static int gre_packet(struct nf_conn *ct,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
/* If we've seen traffic both ways, this is a GRE connection.
|
||||
* Extend timeout. */
|
||||
if (ct->status & IPS_SEEN_REPLY) {
|
||||
nf_ct_refresh_acct(ct, ctinfo, skb,
|
||||
ct->proto.gre.stream_timeout);
|
||||
/* Also, more likely to be important, and not a probe. */
|
||||
set_bit(IPS_ASSURED_BIT, &ct->status);
|
||||
nf_conntrack_event_cache(IPCT_STATUS, skb);
|
||||
} else
|
||||
nf_ct_refresh_acct(ct, ctinfo, skb,
|
||||
ct->proto.gre.timeout);
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int gre_new(struct nf_conn *ct, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
DEBUGP(": ");
|
||||
NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
||||
|
||||
/* initialize to sane value. Ideally a conntrack helper
|
||||
* (e.g. in case of pptp) is increasing them */
|
||||
ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT;
|
||||
ct->proto.gre.timeout = GRE_TIMEOUT;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Called when a conntrack entry has already been removed from the hashes
|
||||
* and is about to be deleted from memory */
|
||||
static void gre_destroy(struct nf_conn *ct)
|
||||
{
|
||||
struct nf_conn *master = ct->master;
|
||||
DEBUGP(" entering\n");
|
||||
|
||||
if (!master)
|
||||
DEBUGP("no master !?!\n");
|
||||
else
|
||||
nf_ct_gre_keymap_destroy(master);
|
||||
}
|
||||
|
||||
/* protocol helper struct */
|
||||
static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 = {
|
||||
.l3proto = AF_INET,
|
||||
.l4proto = IPPROTO_GRE,
|
||||
.name = "gre",
|
||||
.pkt_to_tuple = gre_pkt_to_tuple,
|
||||
.invert_tuple = gre_invert_tuple,
|
||||
.print_tuple = gre_print_tuple,
|
||||
.print_conntrack = gre_print_conntrack,
|
||||
.packet = gre_packet,
|
||||
.new = gre_new,
|
||||
.destroy = gre_destroy,
|
||||
.me = THIS_MODULE,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
|
||||
.nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init nf_ct_proto_gre_init(void)
|
||||
{
|
||||
return nf_conntrack_l4proto_register(&nf_conntrack_l4proto_gre4);
|
||||
}
|
||||
|
||||
static void nf_ct_proto_gre_fini(void)
|
||||
{
|
||||
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_gre4);
|
||||
nf_ct_gre_keymap_flush();
|
||||
}
|
||||
|
||||
module_init(nf_ct_proto_gre_init);
|
||||
module_exit(nf_ct_proto_gre_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
719
net/netfilter/nf_conntrack_proto_sctp.c
Normal file
719
net/netfilter/nf_conntrack_proto_sctp.c
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* Connection tracking protocol helper module for SCTP.
|
||||
*
|
||||
* SCTP is defined in RFC 2960. References to various sections in this code
|
||||
* are to this RFC.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 17 Oct 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with L3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/ip_conntrack_sctp.c
|
||||
*/
|
||||
|
||||
/*
|
||||
* Added support for proc manipulation of timeouts.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
/* Protects conntrack->proto.sctp */
|
||||
static DEFINE_RWLOCK(sctp_lock);
|
||||
|
||||
/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
|
||||
closely. They're more complex. --RR
|
||||
|
||||
And so for me for SCTP :D -Kiran */
|
||||
|
||||
static const char *sctp_conntrack_names[] = {
|
||||
"NONE",
|
||||
"CLOSED",
|
||||
"COOKIE_WAIT",
|
||||
"COOKIE_ECHOED",
|
||||
"ESTABLISHED",
|
||||
"SHUTDOWN_SENT",
|
||||
"SHUTDOWN_RECD",
|
||||
"SHUTDOWN_ACK_SENT",
|
||||
};
|
||||
|
||||
#define SECS * HZ
|
||||
#define MINS * 60 SECS
|
||||
#define HOURS * 60 MINS
|
||||
#define DAYS * 24 HOURS
|
||||
|
||||
static unsigned int nf_ct_sctp_timeout_closed __read_mostly = 10 SECS;
|
||||
static unsigned int nf_ct_sctp_timeout_cookie_wait __read_mostly = 3 SECS;
|
||||
static unsigned int nf_ct_sctp_timeout_cookie_echoed __read_mostly = 3 SECS;
|
||||
static unsigned int nf_ct_sctp_timeout_established __read_mostly = 5 DAYS;
|
||||
static unsigned int nf_ct_sctp_timeout_shutdown_sent __read_mostly = 300 SECS / 1000;
|
||||
static unsigned int nf_ct_sctp_timeout_shutdown_recd __read_mostly = 300 SECS / 1000;
|
||||
static unsigned int nf_ct_sctp_timeout_shutdown_ack_sent __read_mostly = 3 SECS;
|
||||
|
||||
static unsigned int * sctp_timeouts[]
|
||||
= { NULL, /* SCTP_CONNTRACK_NONE */
|
||||
&nf_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */
|
||||
&nf_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */
|
||||
&nf_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */
|
||||
&nf_ct_sctp_timeout_established, /* SCTP_CONNTRACK_ESTABLISHED */
|
||||
&nf_ct_sctp_timeout_shutdown_sent, /* SCTP_CONNTRACK_SHUTDOWN_SENT */
|
||||
&nf_ct_sctp_timeout_shutdown_recd, /* SCTP_CONNTRACK_SHUTDOWN_RECD */
|
||||
&nf_ct_sctp_timeout_shutdown_ack_sent /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */
|
||||
};
|
||||
|
||||
#define sNO SCTP_CONNTRACK_NONE
|
||||
#define sCL SCTP_CONNTRACK_CLOSED
|
||||
#define sCW SCTP_CONNTRACK_COOKIE_WAIT
|
||||
#define sCE SCTP_CONNTRACK_COOKIE_ECHOED
|
||||
#define sES SCTP_CONNTRACK_ESTABLISHED
|
||||
#define sSS SCTP_CONNTRACK_SHUTDOWN_SENT
|
||||
#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD
|
||||
#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
|
||||
#define sIV SCTP_CONNTRACK_MAX
|
||||
|
||||
/*
|
||||
These are the descriptions of the states:
|
||||
|
||||
NOTE: These state names are tantalizingly similar to the states of an
|
||||
SCTP endpoint. But the interpretation of the states is a little different,
|
||||
considering that these are the states of the connection and not of an end
|
||||
point. Please note the subtleties. -Kiran
|
||||
|
||||
NONE - Nothing so far.
|
||||
COOKIE WAIT - We have seen an INIT chunk in the original direction, or also
|
||||
an INIT_ACK chunk in the reply direction.
|
||||
COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction.
|
||||
ESTABLISHED - We have seen a COOKIE_ACK in the reply direction.
|
||||
SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction.
|
||||
SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin.
|
||||
SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
|
||||
to that of the SHUTDOWN chunk.
|
||||
CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
|
||||
the SHUTDOWN chunk. Connection is closed.
|
||||
*/
|
||||
|
||||
/* TODO
|
||||
- I have assumed that the first INIT is in the original direction.
|
||||
This messes things when an INIT comes in the reply direction in CLOSED
|
||||
state.
|
||||
- Check the error type in the reply dir before transitioning from
|
||||
cookie echoed to closed.
|
||||
- Sec 5.2.4 of RFC 2960
|
||||
- Multi Homing support.
|
||||
*/
|
||||
|
||||
/* SCTP conntrack state transitions */
|
||||
static enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = {
|
||||
{
|
||||
/* ORIGINAL */
|
||||
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
|
||||
/* init */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
|
||||
/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA},
|
||||
/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA},
|
||||
/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/
|
||||
/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */
|
||||
/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */
|
||||
/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL}
|
||||
},
|
||||
{
|
||||
/* REPLY */
|
||||
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
|
||||
/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */
|
||||
/* init_ack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
|
||||
/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA},
|
||||
/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA},
|
||||
/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA},
|
||||
/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */
|
||||
/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA},
|
||||
/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL}
|
||||
}
|
||||
};
|
||||
|
||||
static int sctp_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
sctp_sctphdr_t _hdr, *hp;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
/* Actually only need first 8 bytes. */
|
||||
hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
|
||||
if (hp == NULL)
|
||||
return 0;
|
||||
|
||||
tuple->src.u.sctp.port = hp->source;
|
||||
tuple->dst.u.sctp.port = hp->dest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sctp_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
tuple->src.u.sctp.port = orig->dst.u.sctp.port;
|
||||
tuple->dst.u.sctp.port = orig->src.u.sctp.port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int sctp_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
return seq_printf(s, "sport=%hu dport=%hu ",
|
||||
ntohs(tuple->src.u.sctp.port),
|
||||
ntohs(tuple->dst.u.sctp.port));
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int sctp_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
enum sctp_conntrack state;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
read_lock_bh(&sctp_lock);
|
||||
state = conntrack->proto.sctp.state;
|
||||
read_unlock_bh(&sctp_lock);
|
||||
|
||||
return seq_printf(s, "%s ", sctp_conntrack_names[state]);
|
||||
}
|
||||
|
||||
#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \
|
||||
for (offset = dataoff + sizeof(sctp_sctphdr_t), count = 0; \
|
||||
offset < skb->len && \
|
||||
(sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch)); \
|
||||
offset += (ntohs(sch->length) + 3) & ~3, count++)
|
||||
|
||||
/* Some validity checks to make sure the chunks are fine */
|
||||
static int do_basic_checks(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
char *map)
|
||||
{
|
||||
u_int32_t offset, count;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
int flag;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
flag = 0;
|
||||
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
DEBUGP("Chunk Num: %d Type: %d\n", count, sch->type);
|
||||
|
||||
if (sch->type == SCTP_CID_INIT
|
||||
|| sch->type == SCTP_CID_INIT_ACK
|
||||
|| sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
|
||||
flag = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cookie Ack/Echo chunks not the first OR
|
||||
* Init / Init Ack / Shutdown compl chunks not the only chunks
|
||||
* OR zero-length.
|
||||
*/
|
||||
if (((sch->type == SCTP_CID_COOKIE_ACK
|
||||
|| sch->type == SCTP_CID_COOKIE_ECHO
|
||||
|| flag)
|
||||
&& count !=0) || !sch->length) {
|
||||
DEBUGP("Basic checks failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (map) {
|
||||
set_bit(sch->type, (void *)map);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP("Basic checks passed\n");
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
static int new_state(enum ip_conntrack_dir dir,
|
||||
enum sctp_conntrack cur_state,
|
||||
int chunk_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
DEBUGP("Chunk type: %d\n", chunk_type);
|
||||
|
||||
switch (chunk_type) {
|
||||
case SCTP_CID_INIT:
|
||||
DEBUGP("SCTP_CID_INIT\n");
|
||||
i = 0; break;
|
||||
case SCTP_CID_INIT_ACK:
|
||||
DEBUGP("SCTP_CID_INIT_ACK\n");
|
||||
i = 1; break;
|
||||
case SCTP_CID_ABORT:
|
||||
DEBUGP("SCTP_CID_ABORT\n");
|
||||
i = 2; break;
|
||||
case SCTP_CID_SHUTDOWN:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN\n");
|
||||
i = 3; break;
|
||||
case SCTP_CID_SHUTDOWN_ACK:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN_ACK\n");
|
||||
i = 4; break;
|
||||
case SCTP_CID_ERROR:
|
||||
DEBUGP("SCTP_CID_ERROR\n");
|
||||
i = 5; break;
|
||||
case SCTP_CID_COOKIE_ECHO:
|
||||
DEBUGP("SCTP_CID_COOKIE_ECHO\n");
|
||||
i = 6; break;
|
||||
case SCTP_CID_COOKIE_ACK:
|
||||
DEBUGP("SCTP_CID_COOKIE_ACK\n");
|
||||
i = 7; break;
|
||||
case SCTP_CID_SHUTDOWN_COMPLETE:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n");
|
||||
i = 8; break;
|
||||
default:
|
||||
/* Other chunks like DATA, SACK, HEARTBEAT and
|
||||
its ACK do not cause a change in state */
|
||||
DEBUGP("Unknown chunk type, Will stay in %s\n",
|
||||
sctp_conntrack_names[cur_state]);
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
DEBUGP("dir: %d cur_state: %s chunk_type: %d new_state: %s\n",
|
||||
dir, sctp_conntrack_names[cur_state], chunk_type,
|
||||
sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]);
|
||||
|
||||
return sctp_conntracks[dir][i][cur_state];
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, or -1 for invalid. */
|
||||
static int sctp_packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
enum sctp_conntrack newconntrack, oldsctpstate;
|
||||
sctp_sctphdr_t _sctph, *sh;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
u_int32_t offset, count;
|
||||
char map[256 / sizeof (char)] = {0};
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
|
||||
if (sh == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
|
||||
return -1;
|
||||
|
||||
/* Check the verification tag (Sec 8.5) */
|
||||
if (!test_bit(SCTP_CID_INIT, (void *)map)
|
||||
&& !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)
|
||||
&& !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map)
|
||||
&& !test_bit(SCTP_CID_ABORT, (void *)map)
|
||||
&& !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map)
|
||||
&& (sh->vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
|
||||
DEBUGP("Verification tag check failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX;
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
write_lock_bh(&sctp_lock);
|
||||
|
||||
/* Special cases of Verification tag check (Sec 8.5.1) */
|
||||
if (sch->type == SCTP_CID_INIT) {
|
||||
/* Sec 8.5.1 (A) */
|
||||
if (sh->vtag != 0) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_ABORT) {
|
||||
/* Sec 8.5.1 (B) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
|
||||
&& !(sh->vtag == conntrack->proto.sctp.vtag
|
||||
[1 - CTINFO2DIR(ctinfo)])) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
|
||||
/* Sec 8.5.1 (C) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
|
||||
&& !(sh->vtag == conntrack->proto.sctp.vtag
|
||||
[1 - CTINFO2DIR(ctinfo)]
|
||||
&& (sch->flags & 1))) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_COOKIE_ECHO) {
|
||||
/* Sec 8.5.1 (D) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oldsctpstate = conntrack->proto.sctp.state;
|
||||
newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch->type);
|
||||
|
||||
/* Invalid */
|
||||
if (newconntrack == SCTP_CONNTRACK_MAX) {
|
||||
DEBUGP("nf_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n",
|
||||
CTINFO2DIR(ctinfo), sch->type, oldsctpstate);
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If it is an INIT or an INIT ACK note down the vtag */
|
||||
if (sch->type == SCTP_CID_INIT
|
||||
|| sch->type == SCTP_CID_INIT_ACK) {
|
||||
sctp_inithdr_t _inithdr, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
|
||||
sizeof(_inithdr), &_inithdr);
|
||||
if (ih == NULL) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
DEBUGP("Setting vtag %x for dir %d\n",
|
||||
ih->init_tag, !CTINFO2DIR(ctinfo));
|
||||
conntrack->proto.sctp.vtag[!CTINFO2DIR(ctinfo)] = ih->init_tag;
|
||||
}
|
||||
|
||||
conntrack->proto.sctp.state = newconntrack;
|
||||
if (oldsctpstate != newconntrack)
|
||||
nf_conntrack_event_cache(IPCT_PROTOINFO, skb);
|
||||
write_unlock_bh(&sctp_lock);
|
||||
}
|
||||
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]);
|
||||
|
||||
if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED
|
||||
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
|
||||
&& newconntrack == SCTP_CONNTRACK_ESTABLISHED) {
|
||||
DEBUGP("Setting assured bit\n");
|
||||
set_bit(IPS_ASSURED_BIT, &conntrack->status);
|
||||
nf_conntrack_event_cache(IPCT_STATUS, skb);
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int sctp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
enum sctp_conntrack newconntrack;
|
||||
sctp_sctphdr_t _sctph, *sh;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
u_int32_t offset, count;
|
||||
char map[256 / sizeof (char)] = {0};
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
|
||||
if (sh == NULL)
|
||||
return 0;
|
||||
|
||||
if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
|
||||
return 0;
|
||||
|
||||
/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
|
||||
if ((test_bit (SCTP_CID_ABORT, (void *)map))
|
||||
|| (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map))
|
||||
|| (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
newconntrack = SCTP_CONNTRACK_MAX;
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
/* Don't need lock here: this conntrack not in circulation yet */
|
||||
newconntrack = new_state(IP_CT_DIR_ORIGINAL,
|
||||
SCTP_CONNTRACK_NONE, sch->type);
|
||||
|
||||
/* Invalid: delete conntrack */
|
||||
if (newconntrack == SCTP_CONNTRACK_NONE ||
|
||||
newconntrack == SCTP_CONNTRACK_MAX) {
|
||||
DEBUGP("nf_conntrack_sctp: invalid new deleting.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the vtag into the state info */
|
||||
if (sch->type == SCTP_CID_INIT) {
|
||||
if (sh->vtag == 0) {
|
||||
sctp_inithdr_t _inithdr, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
|
||||
sizeof(_inithdr), &_inithdr);
|
||||
if (ih == NULL)
|
||||
return 0;
|
||||
|
||||
DEBUGP("Setting vtag %x for new conn\n",
|
||||
ih->init_tag);
|
||||
|
||||
conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] =
|
||||
ih->init_tag;
|
||||
} else {
|
||||
/* Sec 8.5.1 (A) */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* If it is a shutdown ack OOTB packet, we expect a return
|
||||
shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
|
||||
else {
|
||||
DEBUGP("Setting vtag %x for new conn OOTB\n",
|
||||
sh->vtag);
|
||||
conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
|
||||
}
|
||||
|
||||
conntrack->proto.sctp.state = newconntrack;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static unsigned int sctp_sysctl_table_users;
|
||||
static struct ctl_table_header *sctp_sysctl_header;
|
||||
static struct ctl_table sctp_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED,
|
||||
.procname = "nf_conntrack_sctp_timeout_closed",
|
||||
.data = &nf_ct_sctp_timeout_closed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT,
|
||||
.procname = "nf_conntrack_sctp_timeout_cookie_wait",
|
||||
.data = &nf_ct_sctp_timeout_cookie_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED,
|
||||
.procname = "nf_conntrack_sctp_timeout_cookie_echoed",
|
||||
.data = &nf_ct_sctp_timeout_cookie_echoed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED,
|
||||
.procname = "nf_conntrack_sctp_timeout_established",
|
||||
.data = &nf_ct_sctp_timeout_established,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_recd",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_recd,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_ack_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_ack_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
static struct ctl_table sctp_compat_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED,
|
||||
.procname = "ip_conntrack_sctp_timeout_closed",
|
||||
.data = &nf_ct_sctp_timeout_closed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT,
|
||||
.procname = "ip_conntrack_sctp_timeout_cookie_wait",
|
||||
.data = &nf_ct_sctp_timeout_cookie_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED,
|
||||
.procname = "ip_conntrack_sctp_timeout_cookie_echoed",
|
||||
.data = &nf_ct_sctp_timeout_cookie_echoed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED,
|
||||
.procname = "ip_conntrack_sctp_timeout_established",
|
||||
.data = &nf_ct_sctp_timeout_established,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT,
|
||||
.procname = "ip_conntrack_sctp_timeout_shutdown_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD,
|
||||
.procname = "ip_conntrack_sctp_timeout_shutdown_recd",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_recd,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT,
|
||||
.procname = "ip_conntrack_sctp_timeout_shutdown_ack_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_ack_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
|
||||
#endif
|
||||
|
||||
struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 = {
|
||||
.l3proto = PF_INET,
|
||||
.l4proto = IPPROTO_SCTP,
|
||||
.name = "sctp",
|
||||
.pkt_to_tuple = sctp_pkt_to_tuple,
|
||||
.invert_tuple = sctp_invert_tuple,
|
||||
.print_tuple = sctp_print_tuple,
|
||||
.print_conntrack = sctp_print_conntrack,
|
||||
.packet = sctp_packet,
|
||||
.new = sctp_new,
|
||||
.me = THIS_MODULE,
|
||||
#ifdef CONFIG_SYSCTL
|
||||
.ctl_table_users = &sctp_sysctl_table_users,
|
||||
.ctl_table_header = &sctp_sysctl_header,
|
||||
.ctl_table = sctp_sysctl_table,
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
.ctl_compat_table = sctp_compat_sysctl_table,
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 = {
|
||||
.l3proto = PF_INET6,
|
||||
.l4proto = IPPROTO_SCTP,
|
||||
.name = "sctp",
|
||||
.pkt_to_tuple = sctp_pkt_to_tuple,
|
||||
.invert_tuple = sctp_invert_tuple,
|
||||
.print_tuple = sctp_print_tuple,
|
||||
.print_conntrack = sctp_print_conntrack,
|
||||
.packet = sctp_packet,
|
||||
.new = sctp_new,
|
||||
.me = THIS_MODULE,
|
||||
#ifdef CONFIG_SYSCTL
|
||||
.ctl_table_users = &sctp_sysctl_table_users,
|
||||
.ctl_table_header = &sctp_sysctl_header,
|
||||
.ctl_table = sctp_sysctl_table,
|
||||
#endif
|
||||
};
|
||||
|
||||
int __init nf_conntrack_proto_sctp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_sctp4);
|
||||
if (ret) {
|
||||
printk("nf_conntrack_l4proto_sctp4: protocol register failed\n");
|
||||
goto out;
|
||||
}
|
||||
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_sctp6);
|
||||
if (ret) {
|
||||
printk("nf_conntrack_l4proto_sctp6: protocol register failed\n");
|
||||
goto cleanup_sctp4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
cleanup_sctp4:
|
||||
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
|
||||
out:
|
||||
DEBUGP("SCTP conntrack module loading %s\n",
|
||||
ret ? "failed": "succeeded");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __exit nf_conntrack_proto_sctp_fini(void)
|
||||
{
|
||||
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp6);
|
||||
nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
|
||||
DEBUGP("SCTP conntrack module unloaded\n");
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_proto_sctp_init);
|
||||
module_exit(nf_conntrack_proto_sctp_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kiran Kumar Immidi");
|
||||
MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP");
|
||||
MODULE_ALIAS("ip_conntrack_proto_sctp");
|
||||
1423
net/netfilter/nf_conntrack_proto_tcp.c
Normal file
1423
net/netfilter/nf_conntrack_proto_tcp.c
Normal file
File diff suppressed because it is too large
Load Diff
248
net/netfilter/nf_conntrack_proto_udp.c
Normal file
248
net/netfilter/nf_conntrack_proto_udp.c
Normal file
@@ -0,0 +1,248 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with Layer 3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_proto_udp.c
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include <net/checksum.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
|
||||
static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ;
|
||||
static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ;
|
||||
|
||||
static int udp_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct udphdr _hdr, *hp;
|
||||
|
||||
/* Actually only need first 8 bytes. */
|
||||
hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
|
||||
if (hp == NULL)
|
||||
return 0;
|
||||
|
||||
tuple->src.u.udp.port = hp->source;
|
||||
tuple->dst.u.udp.port = hp->dest;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int udp_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
tuple->src.u.udp.port = orig->dst.u.udp.port;
|
||||
tuple->dst.u.udp.port = orig->src.u.udp.port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int udp_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return seq_printf(s, "sport=%hu dport=%hu ",
|
||||
ntohs(tuple->src.u.udp.port),
|
||||
ntohs(tuple->dst.u.udp.port));
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int udp_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, and may modify conntracktype */
|
||||
static int udp_packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
/* If we've seen traffic both ways, this is some kind of UDP
|
||||
stream. Extend timeout. */
|
||||
if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb,
|
||||
nf_ct_udp_timeout_stream);
|
||||
/* Also, more likely to be important, and not a probe */
|
||||
if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status))
|
||||
nf_conntrack_event_cache(IPCT_STATUS, skb);
|
||||
} else
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_udp_timeout);
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int udp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int udp_error(struct sk_buff *skb, unsigned int dataoff,
|
||||
enum ip_conntrack_info *ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
unsigned int udplen = skb->len - dataoff;
|
||||
struct udphdr _hdr, *hdr;
|
||||
|
||||
/* Header is too small? */
|
||||
hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
|
||||
if (hdr == NULL) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: short packet ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Truncated/malformed packets */
|
||||
if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: truncated/malformed packet ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Packet with no checksum */
|
||||
if (!hdr->check)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Checksum invalid? Ignore.
|
||||
* We skip checking packets on the outgoing path
|
||||
* because the checksum is assumed to be correct.
|
||||
* FIXME: Source route IP option packets --RR */
|
||||
if (nf_conntrack_checksum &&
|
||||
((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
|
||||
(pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) &&
|
||||
nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: bad UDP checksum ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static unsigned int udp_sysctl_table_users;
|
||||
static struct ctl_table_header *udp_sysctl_header;
|
||||
static struct ctl_table udp_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_UDP_TIMEOUT,
|
||||
.procname = "nf_conntrack_udp_timeout",
|
||||
.data = &nf_ct_udp_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_UDP_TIMEOUT_STREAM,
|
||||
.procname = "nf_conntrack_udp_timeout_stream",
|
||||
.data = &nf_ct_udp_timeout_stream,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
static struct ctl_table udp_compat_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT,
|
||||
.procname = "ip_conntrack_udp_timeout",
|
||||
.data = &nf_ct_udp_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_IPV4_NF_CONNTRACK_UDP_TIMEOUT_STREAM,
|
||||
.procname = "ip_conntrack_udp_timeout_stream",
|
||||
.data = &nf_ct_udp_timeout_stream,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 =
|
||||
{
|
||||
.l3proto = PF_INET,
|
||||
.l4proto = IPPROTO_UDP,
|
||||
.name = "udp",
|
||||
.pkt_to_tuple = udp_pkt_to_tuple,
|
||||
.invert_tuple = udp_invert_tuple,
|
||||
.print_tuple = udp_print_tuple,
|
||||
.print_conntrack = udp_print_conntrack,
|
||||
.packet = udp_packet,
|
||||
.new = udp_new,
|
||||
.error = udp_error,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
|
||||
.nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
|
||||
#endif
|
||||
#ifdef CONFIG_SYSCTL
|
||||
.ctl_table_users = &udp_sysctl_table_users,
|
||||
.ctl_table_header = &udp_sysctl_header,
|
||||
.ctl_table = udp_sysctl_table,
|
||||
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
|
||||
.ctl_compat_table = udp_compat_sysctl_table,
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp4);
|
||||
|
||||
struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 =
|
||||
{
|
||||
.l3proto = PF_INET6,
|
||||
.l4proto = IPPROTO_UDP,
|
||||
.name = "udp",
|
||||
.pkt_to_tuple = udp_pkt_to_tuple,
|
||||
.invert_tuple = udp_invert_tuple,
|
||||
.print_tuple = udp_print_tuple,
|
||||
.print_conntrack = udp_print_conntrack,
|
||||
.packet = udp_packet,
|
||||
.new = udp_new,
|
||||
.error = udp_error,
|
||||
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
|
||||
.tuple_to_nfattr = nf_ct_port_tuple_to_nfattr,
|
||||
.nfattr_to_tuple = nf_ct_port_nfattr_to_tuple,
|
||||
#endif
|
||||
#ifdef CONFIG_SYSCTL
|
||||
.ctl_table_users = &udp_sysctl_table_users,
|
||||
.ctl_table_header = &udp_sysctl_header,
|
||||
.ctl_table = udp_sysctl_table,
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_udp6);
|
||||
242
net/netfilter/nf_conntrack_sane.c
Normal file
242
net/netfilter/nf_conntrack_sane.c
Normal file
@@ -0,0 +1,242 @@
|
||||
/* SANE connection tracking helper
|
||||
* (SANE = Scanner Access Now Easy)
|
||||
* For documentation about the SANE network protocol see
|
||||
* http://www.sane-project.org/html/doc015.html
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2007 Red Hat, Inc.
|
||||
* Author: Michal Schmidt <mschmidt@redhat.com>
|
||||
* Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c):
|
||||
* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
* (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <linux/netfilter/nf_conntrack_sane.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Michal Schmidt <mschmidt@redhat.com>");
|
||||
MODULE_DESCRIPTION("SANE connection tracking helper");
|
||||
|
||||
static char *sane_buffer;
|
||||
|
||||
static DEFINE_SPINLOCK(nf_sane_lock);
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static u_int16_t ports[MAX_PORTS];
|
||||
static unsigned int ports_c;
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
struct sane_request {
|
||||
__be32 RPC_code;
|
||||
#define SANE_NET_START 7 /* RPC code */
|
||||
|
||||
__be32 handle;
|
||||
};
|
||||
|
||||
struct sane_reply_net_start {
|
||||
__be32 status;
|
||||
#define SANE_STATUS_SUCCESS 0
|
||||
|
||||
__be16 zero;
|
||||
__be16 port;
|
||||
/* other fields aren't interesting for conntrack */
|
||||
};
|
||||
|
||||
static int help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
unsigned int dataoff, datalen;
|
||||
struct tcphdr _tcph, *th;
|
||||
char *sb_ptr;
|
||||
int ret = NF_ACCEPT;
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
struct nf_ct_sane_master *ct_sane_info;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_tuple *tuple;
|
||||
struct sane_request *req;
|
||||
struct sane_reply_net_start *reply;
|
||||
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||
|
||||
ct_sane_info = &nfct_help(ct)->help.ct_sane_info;
|
||||
/* Until there's been traffic both ways, don't look in packets. */
|
||||
if (ctinfo != IP_CT_ESTABLISHED &&
|
||||
ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Not a full tcp header? */
|
||||
th = skb_header_pointer(*pskb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* No data? */
|
||||
dataoff = protoff + th->doff * 4;
|
||||
if (dataoff >= (*pskb)->len)
|
||||
return NF_ACCEPT;
|
||||
|
||||
datalen = (*pskb)->len - dataoff;
|
||||
|
||||
spin_lock_bh(&nf_sane_lock);
|
||||
sb_ptr = skb_header_pointer(*pskb, dataoff, datalen, sane_buffer);
|
||||
BUG_ON(sb_ptr == NULL);
|
||||
|
||||
if (dir == IP_CT_DIR_ORIGINAL) {
|
||||
if (datalen != sizeof(struct sane_request))
|
||||
goto out;
|
||||
|
||||
req = (struct sane_request *)sb_ptr;
|
||||
if (req->RPC_code != htonl(SANE_NET_START)) {
|
||||
/* Not an interesting command */
|
||||
ct_sane_info->state = SANE_STATE_NORMAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We're interested in the next reply */
|
||||
ct_sane_info->state = SANE_STATE_START_REQUESTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Is it a reply to an uninteresting command? */
|
||||
if (ct_sane_info->state != SANE_STATE_START_REQUESTED)
|
||||
goto out;
|
||||
|
||||
/* It's a reply to SANE_NET_START. */
|
||||
ct_sane_info->state = SANE_STATE_NORMAL;
|
||||
|
||||
if (datalen < sizeof(struct sane_reply_net_start)) {
|
||||
DEBUGP("nf_ct_sane: NET_START reply too short\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
reply = (struct sane_reply_net_start *)sb_ptr;
|
||||
if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
|
||||
/* saned refused the command */
|
||||
DEBUGP("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
|
||||
ntohl(reply->status));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Invalid saned reply? Ignore it. */
|
||||
if (reply->zero != 0)
|
||||
goto out;
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
||||
nf_conntrack_expect_init(exp, family,
|
||||
&tuple->src.u3, &tuple->dst.u3,
|
||||
IPPROTO_TCP,
|
||||
NULL, &reply->port);
|
||||
|
||||
DEBUGP("nf_ct_sane: expect: ");
|
||||
NF_CT_DUMP_TUPLE(&exp->tuple);
|
||||
NF_CT_DUMP_TUPLE(&exp->mask);
|
||||
|
||||
/* Can't expect this? Best to drop packet now. */
|
||||
if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
|
||||
nf_conntrack_expect_put(exp);
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&nf_sane_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper sane[MAX_PORTS][2];
|
||||
static char sane_names[MAX_PORTS][2][sizeof("sane-65535")];
|
||||
|
||||
/* don't make this __exit, since it's called from __init ! */
|
||||
static void nf_conntrack_sane_fini(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
DEBUGP("nf_ct_sane: unregistering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
sane[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_helper_unregister(&sane[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(sane_buffer);
|
||||
}
|
||||
|
||||
static int __init nf_conntrack_sane_init(void)
|
||||
{
|
||||
int i, j = -1, ret = 0;
|
||||
char *tmpname;
|
||||
|
||||
sane_buffer = kmalloc(65536, GFP_KERNEL);
|
||||
if (!sane_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = SANE_PORT;
|
||||
|
||||
/* FIXME should be configurable whether IPv4 and IPv6 connections
|
||||
are tracked or not - YK */
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
sane[i][0].tuple.src.l3num = PF_INET;
|
||||
sane[i][1].tuple.src.l3num = PF_INET6;
|
||||
for (j = 0; j < 2; j++) {
|
||||
sane[i][j].tuple.src.u.tcp.port = htons(ports[i]);
|
||||
sane[i][j].tuple.dst.protonum = IPPROTO_TCP;
|
||||
sane[i][j].mask.src.u.tcp.port = 0xFFFF;
|
||||
sane[i][j].mask.dst.protonum = 0xFF;
|
||||
sane[i][j].max_expected = 1;
|
||||
sane[i][j].timeout = 5 * 60; /* 5 Minutes */
|
||||
sane[i][j].me = THIS_MODULE;
|
||||
sane[i][j].help = help;
|
||||
tmpname = &sane_names[i][j][0];
|
||||
if (ports[i] == SANE_PORT)
|
||||
sprintf(tmpname, "sane");
|
||||
else
|
||||
sprintf(tmpname, "sane-%d", ports[i]);
|
||||
sane[i][j].name = tmpname;
|
||||
|
||||
DEBUGP("nf_ct_sane: registering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
sane[i][j].tuple.src.l3num, ports[i]);
|
||||
ret = nf_conntrack_helper_register(&sane[i][j]);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "nf_ct_sane: failed to "
|
||||
"register helper for pf: %d port: %d\n",
|
||||
sane[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_sane_fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_sane_init);
|
||||
module_exit(nf_conntrack_sane_fini);
|
||||
537
net/netfilter/nf_conntrack_sip.c
Normal file
537
net/netfilter/nf_conntrack_sip.c
Normal file
@@ -0,0 +1,537 @@
|
||||
/* SIP extension for IP connection tracking.
|
||||
*
|
||||
* (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
|
||||
* based on RR's ip_conntrack_ftp.c and other modules.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/netfilter.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_sip.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
|
||||
MODULE_DESCRIPTION("SIP connection tracking helper");
|
||||
MODULE_ALIAS("ip_conntrack_sip");
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static unsigned short ports[MAX_PORTS];
|
||||
static int ports_c;
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
MODULE_PARM_DESC(ports, "port numbers of SIP servers");
|
||||
|
||||
static unsigned int sip_timeout __read_mostly = SIP_TIMEOUT;
|
||||
module_param(sip_timeout, uint, 0600);
|
||||
MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
|
||||
|
||||
unsigned int (*nf_nat_sip_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
struct nf_conn *ct,
|
||||
const char **dptr) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_sip_hook);
|
||||
|
||||
unsigned int (*nf_nat_sdp_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
struct nf_conntrack_expect *exp,
|
||||
const char *dptr) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
|
||||
|
||||
static int digits_len(struct nf_conn *, const char *, const char *, int *);
|
||||
static int epaddr_len(struct nf_conn *, const char *, const char *, int *);
|
||||
static int skp_digits_len(struct nf_conn *, const char *, const char *, int *);
|
||||
static int skp_epaddr_len(struct nf_conn *, const char *, const char *, int *);
|
||||
|
||||
struct sip_header_nfo {
|
||||
const char *lname;
|
||||
const char *sname;
|
||||
const char *ln_str;
|
||||
size_t lnlen;
|
||||
size_t snlen;
|
||||
size_t ln_strlen;
|
||||
int case_sensitive;
|
||||
int (*match_len)(struct nf_conn *, const char *,
|
||||
const char *, int *);
|
||||
};
|
||||
|
||||
static const struct sip_header_nfo ct_sip_hdrs[] = {
|
||||
[POS_REG_REQ_URI] = { /* SIP REGISTER request URI */
|
||||
.lname = "sip:",
|
||||
.lnlen = sizeof("sip:") - 1,
|
||||
.ln_str = ":",
|
||||
.ln_strlen = sizeof(":") - 1,
|
||||
.match_len = epaddr_len,
|
||||
},
|
||||
[POS_REQ_URI] = { /* SIP request URI */
|
||||
.lname = "sip:",
|
||||
.lnlen = sizeof("sip:") - 1,
|
||||
.ln_str = "@",
|
||||
.ln_strlen = sizeof("@") - 1,
|
||||
.match_len = epaddr_len,
|
||||
},
|
||||
[POS_FROM] = { /* SIP From header */
|
||||
.lname = "From:",
|
||||
.lnlen = sizeof("From:") - 1,
|
||||
.sname = "\r\nf:",
|
||||
.snlen = sizeof("\r\nf:") - 1,
|
||||
.ln_str = "sip:",
|
||||
.ln_strlen = sizeof("sip:") - 1,
|
||||
.match_len = skp_epaddr_len,
|
||||
},
|
||||
[POS_TO] = { /* SIP To header */
|
||||
.lname = "To:",
|
||||
.lnlen = sizeof("To:") - 1,
|
||||
.sname = "\r\nt:",
|
||||
.snlen = sizeof("\r\nt:") - 1,
|
||||
.ln_str = "sip:",
|
||||
.ln_strlen = sizeof("sip:") - 1,
|
||||
.match_len = skp_epaddr_len
|
||||
},
|
||||
[POS_VIA] = { /* SIP Via header */
|
||||
.lname = "Via:",
|
||||
.lnlen = sizeof("Via:") - 1,
|
||||
.sname = "\r\nv:",
|
||||
.snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
|
||||
.ln_str = "UDP ",
|
||||
.ln_strlen = sizeof("UDP ") - 1,
|
||||
.match_len = epaddr_len,
|
||||
},
|
||||
[POS_CONTACT] = { /* SIP Contact header */
|
||||
.lname = "Contact:",
|
||||
.lnlen = sizeof("Contact:") - 1,
|
||||
.sname = "\r\nm:",
|
||||
.snlen = sizeof("\r\nm:") - 1,
|
||||
.ln_str = "sip:",
|
||||
.ln_strlen = sizeof("sip:") - 1,
|
||||
.match_len = skp_epaddr_len
|
||||
},
|
||||
[POS_CONTENT] = { /* SIP Content length header */
|
||||
.lname = "Content-Length:",
|
||||
.lnlen = sizeof("Content-Length:") - 1,
|
||||
.sname = "\r\nl:",
|
||||
.snlen = sizeof("\r\nl:") - 1,
|
||||
.ln_str = ":",
|
||||
.ln_strlen = sizeof(":") - 1,
|
||||
.match_len = skp_digits_len
|
||||
},
|
||||
[POS_MEDIA] = { /* SDP media info */
|
||||
.case_sensitive = 1,
|
||||
.lname = "\nm=",
|
||||
.lnlen = sizeof("\nm=") - 1,
|
||||
.sname = "\rm=",
|
||||
.snlen = sizeof("\rm=") - 1,
|
||||
.ln_str = "audio ",
|
||||
.ln_strlen = sizeof("audio ") - 1,
|
||||
.match_len = digits_len
|
||||
},
|
||||
[POS_OWNER_IP4] = { /* SDP owner address*/
|
||||
.case_sensitive = 1,
|
||||
.lname = "\no=",
|
||||
.lnlen = sizeof("\no=") - 1,
|
||||
.sname = "\ro=",
|
||||
.snlen = sizeof("\ro=") - 1,
|
||||
.ln_str = "IN IP4 ",
|
||||
.ln_strlen = sizeof("IN IP4 ") - 1,
|
||||
.match_len = epaddr_len
|
||||
},
|
||||
[POS_CONNECTION_IP4] = {/* SDP connection info */
|
||||
.case_sensitive = 1,
|
||||
.lname = "\nc=",
|
||||
.lnlen = sizeof("\nc=") - 1,
|
||||
.sname = "\rc=",
|
||||
.snlen = sizeof("\rc=") - 1,
|
||||
.ln_str = "IN IP4 ",
|
||||
.ln_strlen = sizeof("IN IP4 ") - 1,
|
||||
.match_len = epaddr_len
|
||||
},
|
||||
[POS_OWNER_IP6] = { /* SDP owner address*/
|
||||
.case_sensitive = 1,
|
||||
.lname = "\no=",
|
||||
.lnlen = sizeof("\no=") - 1,
|
||||
.sname = "\ro=",
|
||||
.snlen = sizeof("\ro=") - 1,
|
||||
.ln_str = "IN IP6 ",
|
||||
.ln_strlen = sizeof("IN IP6 ") - 1,
|
||||
.match_len = epaddr_len
|
||||
},
|
||||
[POS_CONNECTION_IP6] = {/* SDP connection info */
|
||||
.case_sensitive = 1,
|
||||
.lname = "\nc=",
|
||||
.lnlen = sizeof("\nc=") - 1,
|
||||
.sname = "\rc=",
|
||||
.snlen = sizeof("\rc=") - 1,
|
||||
.ln_str = "IN IP6 ",
|
||||
.ln_strlen = sizeof("IN IP6 ") - 1,
|
||||
.match_len = epaddr_len
|
||||
},
|
||||
[POS_SDP_HEADER] = { /* SDP version header */
|
||||
.case_sensitive = 1,
|
||||
.lname = "\nv=",
|
||||
.lnlen = sizeof("\nv=") - 1,
|
||||
.sname = "\rv=",
|
||||
.snlen = sizeof("\rv=") - 1,
|
||||
.ln_str = "=",
|
||||
.ln_strlen = sizeof("=") - 1,
|
||||
.match_len = digits_len
|
||||
}
|
||||
};
|
||||
|
||||
/* get line lenght until first CR or LF seen. */
|
||||
int ct_sip_lnlen(const char *line, const char *limit)
|
||||
{
|
||||
const char *k = line;
|
||||
|
||||
while ((line <= limit) && (*line == '\r' || *line == '\n'))
|
||||
line++;
|
||||
|
||||
while (line <= limit) {
|
||||
if (*line == '\r' || *line == '\n')
|
||||
break;
|
||||
line++;
|
||||
}
|
||||
return line - k;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ct_sip_lnlen);
|
||||
|
||||
/* Linear string search, case sensitive. */
|
||||
const char *ct_sip_search(const char *needle, const char *haystack,
|
||||
size_t needle_len, size_t haystack_len,
|
||||
int case_sensitive)
|
||||
{
|
||||
const char *limit = haystack + (haystack_len - needle_len);
|
||||
|
||||
while (haystack <= limit) {
|
||||
if (case_sensitive) {
|
||||
if (strncmp(haystack, needle, needle_len) == 0)
|
||||
return haystack;
|
||||
} else {
|
||||
if (strnicmp(haystack, needle, needle_len) == 0)
|
||||
return haystack;
|
||||
}
|
||||
haystack++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ct_sip_search);
|
||||
|
||||
static int digits_len(struct nf_conn *ct, const char *dptr,
|
||||
const char *limit, int *shift)
|
||||
{
|
||||
int len = 0;
|
||||
while (dptr <= limit && isdigit(*dptr)) {
|
||||
dptr++;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* get digits lenght, skiping blank spaces. */
|
||||
static int skp_digits_len(struct nf_conn *ct, const char *dptr,
|
||||
const char *limit, int *shift)
|
||||
{
|
||||
for (; dptr <= limit && *dptr == ' '; dptr++)
|
||||
(*shift)++;
|
||||
|
||||
return digits_len(ct, dptr, limit, shift);
|
||||
}
|
||||
|
||||
static int parse_addr(struct nf_conn *ct, const char *cp, const char **endp,
|
||||
union nf_conntrack_address *addr, const char *limit)
|
||||
{
|
||||
const char *end;
|
||||
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||
int ret = 0;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
|
||||
break;
|
||||
case AF_INET6:
|
||||
ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (ret == 0 || end == cp)
|
||||
return 0;
|
||||
if (endp)
|
||||
*endp = end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* skip ip address. returns its length. */
|
||||
static int epaddr_len(struct nf_conn *ct, const char *dptr,
|
||||
const char *limit, int *shift)
|
||||
{
|
||||
union nf_conntrack_address addr;
|
||||
const char *aux = dptr;
|
||||
|
||||
if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
|
||||
DEBUGP("ip: %s parse failed.!\n", dptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Port number */
|
||||
if (*dptr == ':') {
|
||||
dptr++;
|
||||
dptr += digits_len(ct, dptr, limit, shift);
|
||||
}
|
||||
return dptr - aux;
|
||||
}
|
||||
|
||||
/* get address length, skiping user info. */
|
||||
static int skp_epaddr_len(struct nf_conn *ct, const char *dptr,
|
||||
const char *limit, int *shift)
|
||||
{
|
||||
int s = *shift;
|
||||
|
||||
/* Search for @, but stop at the end of the line.
|
||||
* We are inside a sip: URI, so we don't need to worry about
|
||||
* continuation lines. */
|
||||
while (dptr <= limit &&
|
||||
*dptr != '@' && *dptr != '\r' && *dptr != '\n') {
|
||||
(*shift)++;
|
||||
dptr++;
|
||||
}
|
||||
|
||||
if (dptr <= limit && *dptr == '@') {
|
||||
dptr++;
|
||||
(*shift)++;
|
||||
} else
|
||||
*shift = s;
|
||||
|
||||
return epaddr_len(ct, dptr, limit, shift);
|
||||
}
|
||||
|
||||
/* Returns 0 if not found, -1 error parsing. */
|
||||
int ct_sip_get_info(struct nf_conn *ct,
|
||||
const char *dptr, size_t dlen,
|
||||
unsigned int *matchoff,
|
||||
unsigned int *matchlen,
|
||||
enum sip_header_pos pos)
|
||||
{
|
||||
const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];
|
||||
const char *limit, *aux, *k = dptr;
|
||||
int shift = 0;
|
||||
|
||||
limit = dptr + (dlen - hnfo->lnlen);
|
||||
|
||||
while (dptr <= limit) {
|
||||
if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
|
||||
(strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
|
||||
dptr++;
|
||||
continue;
|
||||
}
|
||||
aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
|
||||
ct_sip_lnlen(dptr, limit),
|
||||
hnfo->case_sensitive);
|
||||
if (!aux) {
|
||||
DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str,
|
||||
hnfo->lname);
|
||||
return -1;
|
||||
}
|
||||
aux += hnfo->ln_strlen;
|
||||
|
||||
*matchlen = hnfo->match_len(ct, aux, limit, &shift);
|
||||
if (!*matchlen)
|
||||
return -1;
|
||||
|
||||
*matchoff = (aux - k) + shift;
|
||||
|
||||
DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname,
|
||||
*matchlen);
|
||||
return 1;
|
||||
}
|
||||
DEBUGP("%s header not found.\n", hnfo->lname);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ct_sip_get_info);
|
||||
|
||||
static int set_expected_rtp(struct sk_buff **pskb,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
union nf_conntrack_address *addr,
|
||||
__be16 port,
|
||||
const char *dptr)
|
||||
{
|
||||
struct nf_conntrack_expect *exp;
|
||||
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
|
||||
int family = ct->tuplehash[!dir].tuple.src.l3num;
|
||||
int ret;
|
||||
typeof(nf_nat_sdp_hook) nf_nat_sdp;
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL)
|
||||
return NF_DROP;
|
||||
nf_conntrack_expect_init(exp, family,
|
||||
&ct->tuplehash[!dir].tuple.src.u3, addr,
|
||||
IPPROTO_UDP, NULL, &port);
|
||||
|
||||
nf_nat_sdp = rcu_dereference(nf_nat_sdp_hook);
|
||||
if (nf_nat_sdp && ct->status & IPS_NAT_MASK)
|
||||
ret = nf_nat_sdp(pskb, ctinfo, exp, dptr);
|
||||
else {
|
||||
if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
else
|
||||
ret = NF_ACCEPT;
|
||||
}
|
||||
nf_conntrack_expect_put(exp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sip_help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||
union nf_conntrack_address addr;
|
||||
unsigned int dataoff, datalen;
|
||||
const char *dptr;
|
||||
int ret = NF_ACCEPT;
|
||||
int matchoff, matchlen;
|
||||
u_int16_t port;
|
||||
enum sip_header_pos pos;
|
||||
typeof(nf_nat_sip_hook) nf_nat_sip;
|
||||
|
||||
/* No Data ? */
|
||||
dataoff = protoff + sizeof(struct udphdr);
|
||||
if (dataoff >= (*pskb)->len)
|
||||
return NF_ACCEPT;
|
||||
|
||||
nf_ct_refresh(ct, *pskb, sip_timeout * HZ);
|
||||
|
||||
if (!skb_is_nonlinear(*pskb))
|
||||
dptr = (*pskb)->data + dataoff;
|
||||
else {
|
||||
DEBUGP("Copy of skbuff not supported yet.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
|
||||
if (nf_nat_sip && ct->status & IPS_NAT_MASK) {
|
||||
if (!nf_nat_sip(pskb, ctinfo, ct, &dptr)) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
datalen = (*pskb)->len - dataoff;
|
||||
if (datalen < sizeof("SIP/2.0 200") - 1)
|
||||
goto out;
|
||||
|
||||
/* RTP info only in some SDP pkts */
|
||||
if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&
|
||||
memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {
|
||||
goto out;
|
||||
}
|
||||
/* Get address and port from SDP packet. */
|
||||
pos = family == AF_INET ? POS_CONNECTION_IP4 : POS_CONNECTION_IP6;
|
||||
if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen, pos) > 0) {
|
||||
|
||||
/* We'll drop only if there are parse problems. */
|
||||
if (!parse_addr(ct, dptr + matchoff, NULL, &addr,
|
||||
dptr + datalen)) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
if (ct_sip_get_info(ct, dptr, datalen, &matchoff, &matchlen,
|
||||
POS_MEDIA) > 0) {
|
||||
|
||||
port = simple_strtoul(dptr + matchoff, NULL, 10);
|
||||
if (port < 1024) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
ret = set_expected_rtp(pskb, ct, ctinfo, &addr,
|
||||
htons(port), dptr);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper sip[MAX_PORTS][2] __read_mostly;
|
||||
static char sip_names[MAX_PORTS][2][sizeof("sip-65535")] __read_mostly;
|
||||
|
||||
static void nf_conntrack_sip_fini(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (sip[i][j].me == NULL)
|
||||
continue;
|
||||
nf_conntrack_helper_unregister(&sip[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __init nf_conntrack_sip_init(void)
|
||||
{
|
||||
int i, j, ret;
|
||||
char *tmpname;
|
||||
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = SIP_PORT;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
memset(&sip[i], 0, sizeof(sip[i]));
|
||||
|
||||
sip[i][0].tuple.src.l3num = AF_INET;
|
||||
sip[i][1].tuple.src.l3num = AF_INET6;
|
||||
for (j = 0; j < 2; j++) {
|
||||
sip[i][j].tuple.dst.protonum = IPPROTO_UDP;
|
||||
sip[i][j].tuple.src.u.udp.port = htons(ports[i]);
|
||||
sip[i][j].mask.src.l3num = 0xFFFF;
|
||||
sip[i][j].mask.src.u.udp.port = htons(0xFFFF);
|
||||
sip[i][j].mask.dst.protonum = 0xFF;
|
||||
sip[i][j].max_expected = 2;
|
||||
sip[i][j].timeout = 3 * 60; /* 3 minutes */
|
||||
sip[i][j].me = THIS_MODULE;
|
||||
sip[i][j].help = sip_help;
|
||||
|
||||
tmpname = &sip_names[i][j][0];
|
||||
if (ports[i] == SIP_PORT)
|
||||
sprintf(tmpname, "sip");
|
||||
else
|
||||
sprintf(tmpname, "sip-%u", i);
|
||||
sip[i][j].name = tmpname;
|
||||
|
||||
DEBUGP("port #%u: %u\n", i, ports[i]);
|
||||
|
||||
ret = nf_conntrack_helper_register(&sip[i][j]);
|
||||
if (ret) {
|
||||
printk("nf_ct_sip: failed to register helper "
|
||||
"for pf: %u port: %u\n",
|
||||
sip[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_sip_fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_sip_init);
|
||||
module_exit(nf_conntrack_sip_fini);
|
||||
493
net/netfilter/nf_conntrack_standalone.c
Normal file
493
net/netfilter/nf_conntrack_standalone.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/* This file contains all the functions required for the standalone
|
||||
nf_conntrack module.
|
||||
|
||||
These are not required by the compatibility layer.
|
||||
*/
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - generalize L3 protocol dependent part.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_standalone.c
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/netdevice.h>
|
||||
#ifdef CONFIG_SYSCTL
|
||||
#include <linux/sysctl.h>
|
||||
#endif
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_l4proto.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
int
|
||||
print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_l3proto *l3proto,
|
||||
struct nf_conntrack_l4proto *l4proto)
|
||||
{
|
||||
return l3proto->print_tuple(s, tuple) || l4proto->print_tuple(s, tuple);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(print_tuple);
|
||||
|
||||
#ifdef CONFIG_NF_CT_ACCT
|
||||
static unsigned int
|
||||
seq_print_counters(struct seq_file *s,
|
||||
const struct ip_conntrack_counter *counter)
|
||||
{
|
||||
return seq_printf(s, "packets=%llu bytes=%llu ",
|
||||
(unsigned long long)counter->packets,
|
||||
(unsigned long long)counter->bytes);
|
||||
}
|
||||
#else
|
||||
#define seq_print_counters(x, y) 0
|
||||
#endif
|
||||
|
||||
struct ct_iter_state {
|
||||
unsigned int bucket;
|
||||
};
|
||||
|
||||
static struct list_head *ct_get_first(struct seq_file *seq)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
|
||||
for (st->bucket = 0;
|
||||
st->bucket < nf_conntrack_htable_size;
|
||||
st->bucket++) {
|
||||
if (!list_empty(&nf_conntrack_hash[st->bucket]))
|
||||
return nf_conntrack_hash[st->bucket].next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
|
||||
head = head->next;
|
||||
while (head == &nf_conntrack_hash[st->bucket]) {
|
||||
if (++st->bucket >= nf_conntrack_htable_size)
|
||||
return NULL;
|
||||
head = nf_conntrack_hash[st->bucket].next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
struct list_head *head = ct_get_first(seq);
|
||||
|
||||
if (head)
|
||||
while (pos && (head = ct_get_next(seq, head)))
|
||||
pos--;
|
||||
return pos ? NULL : head;
|
||||
}
|
||||
|
||||
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
return ct_get_idx(seq, *pos);
|
||||
}
|
||||
|
||||
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return ct_get_next(s, v);
|
||||
}
|
||||
|
||||
static void ct_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
/* return 0 on success, 1 in case of error */
|
||||
static int ct_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
const struct nf_conntrack_tuple_hash *hash = v;
|
||||
const struct nf_conn *conntrack = nf_ct_tuplehash_to_ctrack(hash);
|
||||
struct nf_conntrack_l3proto *l3proto;
|
||||
struct nf_conntrack_l4proto *l4proto;
|
||||
|
||||
NF_CT_ASSERT(conntrack);
|
||||
|
||||
/* we only want to print DIR_ORIGINAL */
|
||||
if (NF_CT_DIRECTION(hash))
|
||||
return 0;
|
||||
|
||||
l3proto = __nf_ct_l3proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.src.l3num);
|
||||
|
||||
NF_CT_ASSERT(l3proto);
|
||||
l4proto = __nf_ct_l4proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.src.l3num,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.dst.protonum);
|
||||
NF_CT_ASSERT(l4proto);
|
||||
|
||||
if (seq_printf(s, "%-8s %u %-8s %u %ld ",
|
||||
l3proto->name,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num,
|
||||
l4proto->name,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
|
||||
timer_pending(&conntrack->timeout)
|
||||
? (long)(conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
|
||||
return -ENOSPC;
|
||||
|
||||
if (l3proto->print_conntrack(s, conntrack))
|
||||
return -ENOSPC;
|
||||
|
||||
if (l4proto->print_conntrack(s, conntrack))
|
||||
return -ENOSPC;
|
||||
|
||||
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
|
||||
l3proto, l4proto))
|
||||
return -ENOSPC;
|
||||
|
||||
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
|
||||
return -ENOSPC;
|
||||
|
||||
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
|
||||
if (seq_printf(s, "[UNREPLIED] "))
|
||||
return -ENOSPC;
|
||||
|
||||
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
|
||||
l3proto, l4proto))
|
||||
return -ENOSPC;
|
||||
|
||||
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
|
||||
return -ENOSPC;
|
||||
|
||||
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
|
||||
if (seq_printf(s, "[ASSURED] "))
|
||||
return -ENOSPC;
|
||||
|
||||
#if defined(CONFIG_NF_CONNTRACK_MARK)
|
||||
if (seq_printf(s, "mark=%u ", conntrack->mark))
|
||||
return -ENOSPC;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_SECMARK
|
||||
if (seq_printf(s, "secmark=%u ", conntrack->secmark))
|
||||
return -ENOSPC;
|
||||
#endif
|
||||
|
||||
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ct_seq_ops = {
|
||||
.start = ct_seq_start,
|
||||
.next = ct_seq_next,
|
||||
.stop = ct_seq_stop,
|
||||
.show = ct_seq_show
|
||||
};
|
||||
|
||||
static int ct_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
struct ct_iter_state *st;
|
||||
int ret;
|
||||
|
||||
st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
|
||||
if (st == NULL)
|
||||
return -ENOMEM;
|
||||
ret = seq_open(file, &ct_seq_ops);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
seq = file->private_data;
|
||||
seq->private = st;
|
||||
memset(st, 0, sizeof(struct ct_iter_state));
|
||||
return ret;
|
||||
out_free:
|
||||
kfree(st);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations ct_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ct_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
|
||||
static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
|
||||
if (!cpu_possible(cpu))
|
||||
continue;
|
||||
*pos = cpu + 1;
|
||||
return &per_cpu(nf_conntrack_stat, cpu);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
|
||||
if (!cpu_possible(cpu))
|
||||
continue;
|
||||
*pos = cpu + 1;
|
||||
return &per_cpu(nf_conntrack_stat, cpu);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int ct_cpu_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
|
||||
struct ip_conntrack_stat *st = v;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x "
|
||||
"%08x %08x %08x %08x %08x %08x %08x %08x \n",
|
||||
nr_conntracks,
|
||||
st->searched,
|
||||
st->found,
|
||||
st->new,
|
||||
st->invalid,
|
||||
st->ignore,
|
||||
st->delete,
|
||||
st->delete_list,
|
||||
st->insert,
|
||||
st->insert_failed,
|
||||
st->drop,
|
||||
st->early_drop,
|
||||
st->error,
|
||||
|
||||
st->expect_new,
|
||||
st->expect_create,
|
||||
st->expect_delete
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ct_cpu_seq_ops = {
|
||||
.start = ct_cpu_seq_start,
|
||||
.next = ct_cpu_seq_next,
|
||||
.stop = ct_cpu_seq_stop,
|
||||
.show = ct_cpu_seq_show,
|
||||
};
|
||||
|
||||
static int ct_cpu_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &ct_cpu_seq_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations ct_cpu_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ct_cpu_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
/* Sysctl support */
|
||||
|
||||
int nf_conntrack_checksum __read_mostly = 1;
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_checksum);
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
/* Log invalid packets of a given protocol */
|
||||
static int log_invalid_proto_min = 0;
|
||||
static int log_invalid_proto_max = 255;
|
||||
|
||||
static struct ctl_table_header *nf_ct_sysctl_header;
|
||||
|
||||
static ctl_table nf_ct_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_MAX,
|
||||
.procname = "nf_conntrack_max",
|
||||
.data = &nf_conntrack_max,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_COUNT,
|
||||
.procname = "nf_conntrack_count",
|
||||
.data = &nf_conntrack_count,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_BUCKETS,
|
||||
.procname = "nf_conntrack_buckets",
|
||||
.data = &nf_conntrack_htable_size,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_CHECKSUM,
|
||||
.procname = "nf_conntrack_checksum",
|
||||
.data = &nf_conntrack_checksum,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_LOG_INVALID,
|
||||
.procname = "nf_conntrack_log_invalid",
|
||||
.data = &nf_ct_log_invalid,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &log_invalid_proto_min,
|
||||
.extra2 = &log_invalid_proto_max,
|
||||
},
|
||||
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
#define NET_NF_CONNTRACK_MAX 2089
|
||||
|
||||
static ctl_table nf_ct_netfilter_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NETFILTER,
|
||||
.procname = "netfilter",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_sysctl_table,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_MAX,
|
||||
.procname = "nf_conntrack_max",
|
||||
.data = &nf_conntrack_max,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nf_ct_net_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_netfilter_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_ct_log_invalid);
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static int __init nf_conntrack_standalone_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *proc, *proc_exp, *proc_stat;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
ret = nf_conntrack_init();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc = proc_net_fops_create("nf_conntrack", 0440, &ct_file_ops);
|
||||
if (!proc) goto cleanup_init;
|
||||
|
||||
proc_exp = proc_net_fops_create("nf_conntrack_expect", 0440,
|
||||
&exp_file_ops);
|
||||
if (!proc_exp) goto cleanup_proc;
|
||||
|
||||
proc_stat = create_proc_entry("nf_conntrack", S_IRUGO, proc_net_stat);
|
||||
if (!proc_stat)
|
||||
goto cleanup_proc_exp;
|
||||
|
||||
proc_stat->proc_fops = &ct_cpu_seq_fops;
|
||||
proc_stat->owner = THIS_MODULE;
|
||||
#endif
|
||||
#ifdef CONFIG_SYSCTL
|
||||
nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table);
|
||||
if (nf_ct_sysctl_header == NULL) {
|
||||
printk("nf_conntrack: can't register to sysctl.\n");
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_proc_stat;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
cleanup_proc_stat:
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
remove_proc_entry("nf_conntrack", proc_net_stat);
|
||||
cleanup_proc_exp:
|
||||
proc_net_remove("nf_conntrack_expect");
|
||||
cleanup_proc:
|
||||
proc_net_remove("nf_conntrack");
|
||||
cleanup_init:
|
||||
#endif /* CNFIG_PROC_FS */
|
||||
nf_conntrack_cleanup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit nf_conntrack_standalone_fini(void)
|
||||
{
|
||||
#ifdef CONFIG_SYSCTL
|
||||
unregister_sysctl_table(nf_ct_sysctl_header);
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
remove_proc_entry("nf_conntrack", proc_net_stat);
|
||||
proc_net_remove("nf_conntrack_expect");
|
||||
proc_net_remove("nf_conntrack");
|
||||
#endif /* CNFIG_PROC_FS */
|
||||
nf_conntrack_cleanup();
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_standalone_init);
|
||||
module_exit(nf_conntrack_standalone_fini);
|
||||
|
||||
/* Some modules need us, but don't depend directly on any symbol.
|
||||
They should call this. */
|
||||
void need_conntrack(void)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(need_conntrack);
|
||||
160
net/netfilter/nf_conntrack_tftp.c
Normal file
160
net/netfilter/nf_conntrack_tftp.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/* (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/netfilter.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
#include <net/netfilter/nf_conntrack_expect.h>
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_tftp.h>
|
||||
|
||||
MODULE_AUTHOR("Magnus Boden <mb@ozaba.mine.nu>");
|
||||
MODULE_DESCRIPTION("TFTP connection tracking helper");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ip_conntrack_tftp");
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static unsigned short ports[MAX_PORTS];
|
||||
static int ports_c;
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
MODULE_PARM_DESC(ports, "Port numbers of TFTP servers");
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, args...) printk("%s:%s:" format, \
|
||||
__FILE__, __FUNCTION__ , ## args)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
unsigned int (*nf_nat_tftp_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
struct nf_conntrack_expect *exp) __read_mostly;
|
||||
EXPORT_SYMBOL_GPL(nf_nat_tftp_hook);
|
||||
|
||||
static int tftp_help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
struct tftphdr _tftph, *tfh;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_tuple *tuple;
|
||||
unsigned int ret = NF_ACCEPT;
|
||||
int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num;
|
||||
typeof(nf_nat_tftp_hook) nf_nat_tftp;
|
||||
|
||||
tfh = skb_header_pointer(*pskb, protoff + sizeof(struct udphdr),
|
||||
sizeof(_tftph), &_tftph);
|
||||
if (tfh == NULL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
switch (ntohs(tfh->opcode)) {
|
||||
case TFTP_OPCODE_READ:
|
||||
case TFTP_OPCODE_WRITE:
|
||||
/* RRQ and WRQ works the same way */
|
||||
DEBUGP("");
|
||||
NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
||||
NF_CT_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL)
|
||||
return NF_DROP;
|
||||
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
|
||||
nf_conntrack_expect_init(exp, family,
|
||||
&tuple->src.u3, &tuple->dst.u3,
|
||||
IPPROTO_UDP,
|
||||
NULL, &tuple->dst.u.udp.port);
|
||||
|
||||
DEBUGP("expect: ");
|
||||
NF_CT_DUMP_TUPLE(&exp->tuple);
|
||||
NF_CT_DUMP_TUPLE(&exp->mask);
|
||||
|
||||
nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook);
|
||||
if (nf_nat_tftp && ct->status & IPS_NAT_MASK)
|
||||
ret = nf_nat_tftp(pskb, ctinfo, exp);
|
||||
else if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
nf_conntrack_expect_put(exp);
|
||||
break;
|
||||
case TFTP_OPCODE_DATA:
|
||||
case TFTP_OPCODE_ACK:
|
||||
DEBUGP("Data/ACK opcode\n");
|
||||
break;
|
||||
case TFTP_OPCODE_ERROR:
|
||||
DEBUGP("Error opcode\n");
|
||||
break;
|
||||
default:
|
||||
DEBUGP("Unknown opcode\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper tftp[MAX_PORTS][2] __read_mostly;
|
||||
static char tftp_names[MAX_PORTS][2][sizeof("tftp-65535")] __read_mostly;
|
||||
|
||||
static void nf_conntrack_tftp_fini(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
for (j = 0; j < 2; j++)
|
||||
nf_conntrack_helper_unregister(&tftp[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init nf_conntrack_tftp_init(void)
|
||||
{
|
||||
int i, j, ret;
|
||||
char *tmpname;
|
||||
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = TFTP_PORT;
|
||||
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
memset(&tftp[i], 0, sizeof(tftp[i]));
|
||||
|
||||
tftp[i][0].tuple.src.l3num = AF_INET;
|
||||
tftp[i][1].tuple.src.l3num = AF_INET6;
|
||||
for (j = 0; j < 2; j++) {
|
||||
tftp[i][j].tuple.dst.protonum = IPPROTO_UDP;
|
||||
tftp[i][j].tuple.src.u.udp.port = htons(ports[i]);
|
||||
tftp[i][j].mask.src.l3num = 0xFFFF;
|
||||
tftp[i][j].mask.dst.protonum = 0xFF;
|
||||
tftp[i][j].mask.src.u.udp.port = htons(0xFFFF);
|
||||
tftp[i][j].max_expected = 1;
|
||||
tftp[i][j].timeout = 5 * 60; /* 5 minutes */
|
||||
tftp[i][j].me = THIS_MODULE;
|
||||
tftp[i][j].help = tftp_help;
|
||||
|
||||
tmpname = &tftp_names[i][j][0];
|
||||
if (ports[i] == TFTP_PORT)
|
||||
sprintf(tmpname, "tftp");
|
||||
else
|
||||
sprintf(tmpname, "tftp-%u", i);
|
||||
tftp[i][j].name = tmpname;
|
||||
|
||||
ret = nf_conntrack_helper_register(&tftp[i][j]);
|
||||
if (ret) {
|
||||
printk("nf_ct_tftp: failed to register helper "
|
||||
"for pf: %u port: %u\n",
|
||||
tftp[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_tftp_fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nf_conntrack_tftp_init);
|
||||
module_exit(nf_conntrack_tftp_fini);
|
||||
38
net/netfilter/nf_internals.h
Normal file
38
net/netfilter/nf_internals.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef _NF_INTERNALS_H
|
||||
#define _NF_INTERNALS_H
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#ifdef CONFIG_NETFILTER_DEBUG
|
||||
#define NFDEBUG(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define NFDEBUG(format, args...)
|
||||
#endif
|
||||
|
||||
|
||||
/* core.c */
|
||||
extern unsigned int nf_iterate(struct list_head *head,
|
||||
struct sk_buff **skb,
|
||||
int hook,
|
||||
const struct net_device *indev,
|
||||
const struct net_device *outdev,
|
||||
struct list_head **i,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
int hook_thresh);
|
||||
|
||||
/* nf_queue.c */
|
||||
extern int nf_queue(struct sk_buff *skb,
|
||||
struct list_head *elem,
|
||||
int pf, unsigned int hook,
|
||||
struct net_device *indev,
|
||||
struct net_device *outdev,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
unsigned int queuenum);
|
||||
extern int __init netfilter_queue_init(void);
|
||||
|
||||
/* nf_log.c */
|
||||
extern int __init netfilter_log_init(void);
|
||||
|
||||
#endif
|
||||
178
net/netfilter/nf_log.c
Normal file
178
net/netfilter/nf_log.c
Normal file
@@ -0,0 +1,178 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <net/protocol.h>
|
||||
|
||||
#include "nf_internals.h"
|
||||
|
||||
/* Internal logging interface, which relies on the real
|
||||
LOG target modules */
|
||||
|
||||
#define NF_LOG_PREFIXLEN 128
|
||||
|
||||
static struct nf_logger *nf_loggers[NPROTO];
|
||||
static DEFINE_MUTEX(nf_log_mutex);
|
||||
|
||||
/* return EBUSY if somebody else is registered, EEXIST if the same logger
|
||||
* is registred, 0 on success. */
|
||||
int nf_log_register(int pf, struct nf_logger *logger)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pf >= NPROTO)
|
||||
return -EINVAL;
|
||||
|
||||
/* Any setup of logging members must be done before
|
||||
* substituting pointer. */
|
||||
ret = mutex_lock_interruptible(&nf_log_mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!nf_loggers[pf])
|
||||
rcu_assign_pointer(nf_loggers[pf], logger);
|
||||
else if (nf_loggers[pf] == logger)
|
||||
ret = -EEXIST;
|
||||
else
|
||||
ret = -EBUSY;
|
||||
|
||||
mutex_unlock(&nf_log_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_log_register);
|
||||
|
||||
void nf_log_unregister_pf(int pf)
|
||||
{
|
||||
if (pf >= NPROTO)
|
||||
return;
|
||||
mutex_lock(&nf_log_mutex);
|
||||
rcu_assign_pointer(nf_loggers[pf], NULL);
|
||||
mutex_unlock(&nf_log_mutex);
|
||||
|
||||
/* Give time to concurrent readers. */
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL(nf_log_unregister_pf);
|
||||
|
||||
void nf_log_unregister(struct nf_logger *logger)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&nf_log_mutex);
|
||||
for (i = 0; i < NPROTO; i++) {
|
||||
if (nf_loggers[i] == logger)
|
||||
rcu_assign_pointer(nf_loggers[i], NULL);
|
||||
}
|
||||
mutex_unlock(&nf_log_mutex);
|
||||
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL(nf_log_unregister);
|
||||
|
||||
void nf_log_packet(int pf,
|
||||
unsigned int hooknum,
|
||||
const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
struct nf_loginfo *loginfo,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char prefix[NF_LOG_PREFIXLEN];
|
||||
struct nf_logger *logger;
|
||||
|
||||
rcu_read_lock();
|
||||
logger = rcu_dereference(nf_loggers[pf]);
|
||||
if (logger) {
|
||||
va_start(args, fmt);
|
||||
vsnprintf(prefix, sizeof(prefix), fmt, args);
|
||||
va_end(args);
|
||||
/* We must read logging before nf_logfn[pf] */
|
||||
logger->logfn(pf, hooknum, skb, in, out, loginfo, prefix);
|
||||
} else if (net_ratelimit()) {
|
||||
printk(KERN_WARNING "nf_log_packet: can\'t log since "
|
||||
"no backend logging module loaded in! Please either "
|
||||
"load one, or disable logging explicitly\n");
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(nf_log_packet);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
rcu_read_lock();
|
||||
|
||||
if (*pos >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
|
||||
if (*pos >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *pos = v;
|
||||
const struct nf_logger *logger;
|
||||
|
||||
logger = rcu_dereference(nf_loggers[*pos]);
|
||||
|
||||
if (!logger)
|
||||
return seq_printf(s, "%2lld NONE\n", *pos);
|
||||
|
||||
return seq_printf(s, "%2lld %s\n", *pos, logger->name);
|
||||
}
|
||||
|
||||
static struct seq_operations nflog_seq_ops = {
|
||||
.start = seq_start,
|
||||
.next = seq_next,
|
||||
.stop = seq_stop,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
static int nflog_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &nflog_seq_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations nflog_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = nflog_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
#endif /* PROC_FS */
|
||||
|
||||
|
||||
int __init netfilter_log_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *pde;
|
||||
|
||||
pde = create_proc_entry("nf_log", S_IRUGO, proc_net_netfilter);
|
||||
if (!pde)
|
||||
return -1;
|
||||
|
||||
pde->proc_fops = &nflog_file_ops;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
356
net/netfilter/nf_queue.c
Normal file
356
net/netfilter/nf_queue.c
Normal file
@@ -0,0 +1,356 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <net/protocol.h>
|
||||
|
||||
#include "nf_internals.h"
|
||||
|
||||
/*
|
||||
* A queue handler may be registered for each protocol. Each is protected by
|
||||
* long term mutex. The handler must provide an an outfn() to accept packets
|
||||
* for queueing and must reinject all packets it receives, no matter what.
|
||||
*/
|
||||
static struct nf_queue_handler *queue_handler[NPROTO];
|
||||
|
||||
static DEFINE_RWLOCK(queue_handler_lock);
|
||||
|
||||
/* return EBUSY when somebody else is registered, return EEXIST if the
|
||||
* same handler is registered, return 0 in case of success. */
|
||||
int nf_register_queue_handler(int pf, struct nf_queue_handler *qh)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pf >= NPROTO)
|
||||
return -EINVAL;
|
||||
|
||||
write_lock_bh(&queue_handler_lock);
|
||||
if (queue_handler[pf] == qh)
|
||||
ret = -EEXIST;
|
||||
else if (queue_handler[pf])
|
||||
ret = -EBUSY;
|
||||
else {
|
||||
queue_handler[pf] = qh;
|
||||
ret = 0;
|
||||
}
|
||||
write_unlock_bh(&queue_handler_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_register_queue_handler);
|
||||
|
||||
/* The caller must flush their queue before this */
|
||||
int nf_unregister_queue_handler(int pf)
|
||||
{
|
||||
if (pf >= NPROTO)
|
||||
return -EINVAL;
|
||||
|
||||
write_lock_bh(&queue_handler_lock);
|
||||
queue_handler[pf] = NULL;
|
||||
write_unlock_bh(&queue_handler_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_unregister_queue_handler);
|
||||
|
||||
void nf_unregister_queue_handlers(struct nf_queue_handler *qh)
|
||||
{
|
||||
int pf;
|
||||
|
||||
write_lock_bh(&queue_handler_lock);
|
||||
for (pf = 0; pf < NPROTO; pf++) {
|
||||
if (queue_handler[pf] == qh)
|
||||
queue_handler[pf] = NULL;
|
||||
}
|
||||
write_unlock_bh(&queue_handler_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
|
||||
|
||||
/*
|
||||
* Any packet that leaves via this function must come back
|
||||
* through nf_reinject().
|
||||
*/
|
||||
static int __nf_queue(struct sk_buff *skb,
|
||||
struct list_head *elem,
|
||||
int pf, unsigned int hook,
|
||||
struct net_device *indev,
|
||||
struct net_device *outdev,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
unsigned int queuenum)
|
||||
{
|
||||
int status;
|
||||
struct nf_info *info;
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
struct net_device *physindev = NULL;
|
||||
struct net_device *physoutdev = NULL;
|
||||
#endif
|
||||
struct nf_afinfo *afinfo;
|
||||
|
||||
/* QUEUE == DROP if noone is waiting, to be safe. */
|
||||
read_lock(&queue_handler_lock);
|
||||
if (!queue_handler[pf]) {
|
||||
read_unlock(&queue_handler_lock);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
afinfo = nf_get_afinfo(pf);
|
||||
if (!afinfo) {
|
||||
read_unlock(&queue_handler_lock);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
info = kmalloc(sizeof(*info) + afinfo->route_key_size, GFP_ATOMIC);
|
||||
if (!info) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_ERR "OOM queueing packet %p\n",
|
||||
skb);
|
||||
read_unlock(&queue_handler_lock);
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
*info = (struct nf_info) {
|
||||
(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
|
||||
|
||||
/* If it's going away, ignore hook. */
|
||||
if (!try_module_get(info->elem->owner)) {
|
||||
read_unlock(&queue_handler_lock);
|
||||
kfree(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bump dev refs so they don't vanish while packet is out */
|
||||
if (indev) dev_hold(indev);
|
||||
if (outdev) dev_hold(outdev);
|
||||
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
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
|
||||
afinfo->saveroute(skb, info);
|
||||
status = queue_handler[pf]->outfn(skb, info, queuenum,
|
||||
queue_handler[pf]->data);
|
||||
|
||||
read_unlock(&queue_handler_lock);
|
||||
|
||||
if (status < 0) {
|
||||
/* James M doesn't say fuck enough. */
|
||||
if (indev) dev_put(indev);
|
||||
if (outdev) dev_put(outdev);
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
if (physindev) dev_put(physindev);
|
||||
if (physoutdev) dev_put(physoutdev);
|
||||
#endif
|
||||
module_put(info->elem->owner);
|
||||
kfree(info);
|
||||
kfree_skb(skb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nf_queue(struct sk_buff *skb,
|
||||
struct list_head *elem,
|
||||
int pf, unsigned int hook,
|
||||
struct net_device *indev,
|
||||
struct net_device *outdev,
|
||||
int (*okfn)(struct sk_buff *),
|
||||
unsigned int queuenum)
|
||||
{
|
||||
struct sk_buff *segs;
|
||||
|
||||
if (!skb_is_gso(skb))
|
||||
return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
|
||||
queuenum);
|
||||
|
||||
switch (pf) {
|
||||
case AF_INET:
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
break;
|
||||
case AF_INET6:
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
break;
|
||||
}
|
||||
|
||||
segs = skb_gso_segment(skb, 0);
|
||||
kfree_skb(skb);
|
||||
if (unlikely(IS_ERR(segs)))
|
||||
return 1;
|
||||
|
||||
do {
|
||||
struct sk_buff *nskb = segs->next;
|
||||
|
||||
segs->next = NULL;
|
||||
if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
|
||||
queuenum))
|
||||
kfree_skb(segs);
|
||||
segs = nskb;
|
||||
} while (segs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void nf_reinject(struct sk_buff *skb, struct nf_info *info,
|
||||
unsigned int verdict)
|
||||
{
|
||||
struct list_head *elem = &info->elem->list;
|
||||
struct list_head *i;
|
||||
struct nf_afinfo *afinfo;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Release those devices we held, or Alexey will kill me. */
|
||||
if (info->indev) dev_put(info->indev);
|
||||
if (info->outdev) dev_put(info->outdev);
|
||||
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||
if (skb->nf_bridge) {
|
||||
if (skb->nf_bridge->physindev)
|
||||
dev_put(skb->nf_bridge->physindev);
|
||||
if (skb->nf_bridge->physoutdev)
|
||||
dev_put(skb->nf_bridge->physoutdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Drop reference to owner of hook which queued us. */
|
||||
module_put(info->elem->owner);
|
||||
|
||||
list_for_each_rcu(i, &nf_hooks[info->pf][info->hook]) {
|
||||
if (i == elem)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == &nf_hooks[info->pf][info->hook]) {
|
||||
/* The module which sent it to userspace is gone. */
|
||||
NFDEBUG("%s: module disappeared, dropping packet.\n",
|
||||
__FUNCTION__);
|
||||
verdict = NF_DROP;
|
||||
}
|
||||
|
||||
/* Continue traversal iff userspace said ok... */
|
||||
if (verdict == NF_REPEAT) {
|
||||
elem = elem->prev;
|
||||
verdict = NF_ACCEPT;
|
||||
}
|
||||
|
||||
if (verdict == NF_ACCEPT) {
|
||||
afinfo = nf_get_afinfo(info->pf);
|
||||
if (!afinfo || afinfo->reroute(&skb, info) < 0)
|
||||
verdict = NF_DROP;
|
||||
}
|
||||
|
||||
if (verdict == NF_ACCEPT) {
|
||||
next_hook:
|
||||
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
|
||||
&skb, info->hook,
|
||||
info->indev, info->outdev, &elem,
|
||||
info->okfn, INT_MIN);
|
||||
}
|
||||
|
||||
switch (verdict & NF_VERDICT_MASK) {
|
||||
case NF_ACCEPT:
|
||||
case NF_STOP:
|
||||
info->okfn(skb);
|
||||
case NF_STOLEN:
|
||||
break;
|
||||
case NF_QUEUE:
|
||||
if (!__nf_queue(skb, elem, info->pf, info->hook,
|
||||
info->indev, info->outdev, info->okfn,
|
||||
verdict >> NF_VERDICT_BITS))
|
||||
goto next_hook;
|
||||
break;
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
kfree(info);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_reinject);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void *seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
|
||||
if (*pos >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static int seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
int ret;
|
||||
loff_t *pos = v;
|
||||
struct nf_queue_handler *qh;
|
||||
|
||||
read_lock_bh(&queue_handler_lock);
|
||||
qh = queue_handler[*pos];
|
||||
if (!qh)
|
||||
ret = seq_printf(s, "%2lld NONE\n", *pos);
|
||||
else
|
||||
ret = seq_printf(s, "%2lld %s\n", *pos, qh->name);
|
||||
read_unlock_bh(&queue_handler_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct seq_operations nfqueue_seq_ops = {
|
||||
.start = seq_start,
|
||||
.next = seq_next,
|
||||
.stop = seq_stop,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
static int nfqueue_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &nfqueue_seq_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations nfqueue_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = nfqueue_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
#endif /* PROC_FS */
|
||||
|
||||
|
||||
int __init netfilter_queue_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *pde;
|
||||
|
||||
pde = create_proc_entry("nf_queue", S_IRUGO, proc_net_netfilter);
|
||||
if (!pde)
|
||||
return -1;
|
||||
pde->proc_fops = &nfqueue_file_ops;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
201
net/netfilter/nf_sockopt.c
Normal file
201
net/netfilter/nf_sockopt.c
Normal file
@@ -0,0 +1,201 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "nf_internals.h"
|
||||
|
||||
/* Sockopts only registered and called from user context, so
|
||||
net locking would be overkill. Also, [gs]etsockopt calls may
|
||||
sleep. */
|
||||
static DEFINE_MUTEX(nf_sockopt_mutex);
|
||||
static LIST_HEAD(nf_sockopts);
|
||||
|
||||
/* Do exclusive ranges overlap? */
|
||||
static inline int overlap(int min1, int max1, int min2, int max2)
|
||||
{
|
||||
return max1 > min2 && min1 < max2;
|
||||
}
|
||||
|
||||
/* Functions to register sockopt ranges (exclusive). */
|
||||
int nf_register_sockopt(struct nf_sockopt_ops *reg)
|
||||
{
|
||||
struct list_head *i;
|
||||
int ret = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
|
||||
return -EINTR;
|
||||
|
||||
list_for_each(i, &nf_sockopts) {
|
||||
struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i;
|
||||
if (ops->pf == reg->pf
|
||||
&& (overlap(ops->set_optmin, ops->set_optmax,
|
||||
reg->set_optmin, reg->set_optmax)
|
||||
|| overlap(ops->get_optmin, ops->get_optmax,
|
||||
reg->get_optmin, reg->get_optmax))) {
|
||||
NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n",
|
||||
ops->set_optmin, ops->set_optmax,
|
||||
ops->get_optmin, ops->get_optmax,
|
||||
reg->set_optmin, reg->set_optmax,
|
||||
reg->get_optmin, reg->get_optmax);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
list_add(®->list, &nf_sockopts);
|
||||
out:
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(nf_register_sockopt);
|
||||
|
||||
void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
|
||||
{
|
||||
/* No point being interruptible: we're probably in cleanup_module() */
|
||||
restart:
|
||||
mutex_lock(&nf_sockopt_mutex);
|
||||
if (reg->use != 0) {
|
||||
/* To be woken by nf_sockopt call... */
|
||||
/* FIXME: Stuart Young's name appears gratuitously. */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
reg->cleanup_task = current;
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
schedule();
|
||||
goto restart;
|
||||
}
|
||||
list_del(®->list);
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_unregister_sockopt);
|
||||
|
||||
/* Call get/setsockopt() */
|
||||
static int nf_sockopt(struct sock *sk, int pf, int val,
|
||||
char __user *opt, int *len, int get)
|
||||
{
|
||||
struct list_head *i;
|
||||
struct nf_sockopt_ops *ops;
|
||||
int ret;
|
||||
|
||||
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
|
||||
return -EINTR;
|
||||
|
||||
list_for_each(i, &nf_sockopts) {
|
||||
ops = (struct nf_sockopt_ops *)i;
|
||||
if (ops->pf == pf) {
|
||||
if (get) {
|
||||
if (val >= ops->get_optmin
|
||||
&& val < ops->get_optmax) {
|
||||
ops->use++;
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
ret = ops->get(sk, val, opt, len);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (val >= ops->set_optmin
|
||||
&& val < ops->set_optmax) {
|
||||
ops->use++;
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
ret = ops->set(sk, val, opt, *len);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
out:
|
||||
mutex_lock(&nf_sockopt_mutex);
|
||||
ops->use--;
|
||||
if (ops->cleanup_task)
|
||||
wake_up_process(ops->cleanup_task);
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nf_setsockopt(struct sock *sk, int pf, int val, char __user *opt,
|
||||
int len)
|
||||
{
|
||||
return nf_sockopt(sk, pf, val, opt, &len, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_setsockopt);
|
||||
|
||||
int nf_getsockopt(struct sock *sk, int pf, int val, char __user *opt, int *len)
|
||||
{
|
||||
return nf_sockopt(sk, pf, val, opt, len, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(nf_getsockopt);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int compat_nf_sockopt(struct sock *sk, int pf, int val,
|
||||
char __user *opt, int *len, int get)
|
||||
{
|
||||
struct list_head *i;
|
||||
struct nf_sockopt_ops *ops;
|
||||
int ret;
|
||||
|
||||
if (mutex_lock_interruptible(&nf_sockopt_mutex) != 0)
|
||||
return -EINTR;
|
||||
|
||||
list_for_each(i, &nf_sockopts) {
|
||||
ops = (struct nf_sockopt_ops *)i;
|
||||
if (ops->pf == pf) {
|
||||
if (get) {
|
||||
if (val >= ops->get_optmin
|
||||
&& val < ops->get_optmax) {
|
||||
ops->use++;
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
if (ops->compat_get)
|
||||
ret = ops->compat_get(sk,
|
||||
val, opt, len);
|
||||
else
|
||||
ret = ops->get(sk,
|
||||
val, opt, len);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (val >= ops->set_optmin
|
||||
&& val < ops->set_optmax) {
|
||||
ops->use++;
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
if (ops->compat_set)
|
||||
ret = ops->compat_set(sk,
|
||||
val, opt, *len);
|
||||
else
|
||||
ret = ops->set(sk,
|
||||
val, opt, *len);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
return -ENOPROTOOPT;
|
||||
|
||||
out:
|
||||
mutex_lock(&nf_sockopt_mutex);
|
||||
ops->use--;
|
||||
if (ops->cleanup_task)
|
||||
wake_up_process(ops->cleanup_task);
|
||||
mutex_unlock(&nf_sockopt_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int compat_nf_setsockopt(struct sock *sk, int pf,
|
||||
int val, char __user *opt, int len)
|
||||
{
|
||||
return compat_nf_sockopt(sk, pf, val, opt, &len, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(compat_nf_setsockopt);
|
||||
|
||||
int compat_nf_getsockopt(struct sock *sk, int pf,
|
||||
int val, char __user *opt, int *len)
|
||||
{
|
||||
return compat_nf_sockopt(sk, pf, val, opt, len, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(compat_nf_getsockopt);
|
||||
#endif
|
||||
134
net/netfilter/nf_sysctl.c
Normal file
134
net/netfilter/nf_sysctl.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/* nf_sysctl.c netfilter sysctl registration/unregistation
|
||||
*
|
||||
* Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static void
|
||||
path_free(struct ctl_table *path, struct ctl_table *table)
|
||||
{
|
||||
struct ctl_table *t, *next;
|
||||
|
||||
for (t = path; t != NULL && t != table; t = next) {
|
||||
next = t->child;
|
||||
kfree(t);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ctl_table *
|
||||
path_dup(struct ctl_table *path, struct ctl_table *table)
|
||||
{
|
||||
struct ctl_table *t, *last = NULL, *tmp;
|
||||
|
||||
for (t = path; t != NULL; t = t->child) {
|
||||
/* twice the size since path elements are terminated by an
|
||||
* empty element */
|
||||
tmp = kmemdup(t, 2 * sizeof(*t), GFP_KERNEL);
|
||||
if (tmp == NULL) {
|
||||
if (last != NULL)
|
||||
path_free(path, table);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last != NULL)
|
||||
last->child = tmp;
|
||||
else
|
||||
path = tmp;
|
||||
last = tmp;
|
||||
}
|
||||
|
||||
if (last != NULL)
|
||||
last->child = table;
|
||||
else
|
||||
path = table;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
struct ctl_table_header *
|
||||
nf_register_sysctl_table(struct ctl_table *path, struct ctl_table *table)
|
||||
{
|
||||
struct ctl_table_header *header;
|
||||
|
||||
path = path_dup(path, table);
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
header = register_sysctl_table(path);
|
||||
if (header == NULL)
|
||||
path_free(path, table);
|
||||
return header;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_register_sysctl_table);
|
||||
|
||||
void
|
||||
nf_unregister_sysctl_table(struct ctl_table_header *header,
|
||||
struct ctl_table *table)
|
||||
{
|
||||
struct ctl_table *path = header->ctl_table;
|
||||
|
||||
unregister_sysctl_table(header);
|
||||
path_free(path, table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_unregister_sysctl_table);
|
||||
|
||||
/* net/netfilter */
|
||||
static struct ctl_table nf_net_netfilter_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NETFILTER,
|
||||
.procname = "netfilter",
|
||||
.mode = 0555,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
struct ctl_table nf_net_netfilter_sysctl_path[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = nf_net_netfilter_table,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_net_netfilter_sysctl_path);
|
||||
|
||||
/* net/ipv4/netfilter */
|
||||
static struct ctl_table nf_net_ipv4_netfilter_table[] = {
|
||||
{
|
||||
.ctl_name = NET_IPV4_NETFILTER,
|
||||
.procname = "netfilter",
|
||||
.mode = 0555,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
static struct ctl_table nf_net_ipv4_table[] = {
|
||||
{
|
||||
.ctl_name = NET_IPV4,
|
||||
.procname = "ipv4",
|
||||
.mode = 0555,
|
||||
.child = nf_net_ipv4_netfilter_table,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
struct ctl_table nf_net_ipv4_netfilter_sysctl_path[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = nf_net_ipv4_table,
|
||||
},
|
||||
{
|
||||
.ctl_name = 0
|
||||
}
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nf_net_ipv4_netfilter_sysctl_path);
|
||||
375
net/netfilter/nfnetlink.c
Normal file
375
net/netfilter/nfnetlink.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/* Netfilter messages via netlink socket. Allows for user space
|
||||
* protocol helpers and general trouble making from userspace.
|
||||
*
|
||||
* (C) 2001 by Jay Schulist <jschlst@samba.org>,
|
||||
* (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
|
||||
*
|
||||
* Initial netfilter messages via netlink development funded and
|
||||
* generally made possible by Network Robots, Inc. (www.networkrobots.com)
|
||||
*
|
||||
* Further development of this code funded by Astaro AG (http://www.astaro.com)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
|
||||
|
||||
static char __initdata nfversion[] = "0.30";
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, args...) \
|
||||
printk(KERN_DEBUG "%s(%d):%s(): " format, __FILE__, \
|
||||
__LINE__, __FUNCTION__, ## args)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
static struct sock *nfnl = NULL;
|
||||
static struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
|
||||
DECLARE_MUTEX(nfnl_sem);
|
||||
|
||||
void nfnl_lock(void)
|
||||
{
|
||||
nfnl_shlock();
|
||||
}
|
||||
|
||||
void nfnl_unlock(void)
|
||||
{
|
||||
nfnl_shunlock();
|
||||
}
|
||||
|
||||
int nfnetlink_subsys_register(struct nfnetlink_subsystem *n)
|
||||
{
|
||||
DEBUGP("registering subsystem ID %u\n", n->subsys_id);
|
||||
|
||||
nfnl_lock();
|
||||
if (subsys_table[n->subsys_id]) {
|
||||
nfnl_unlock();
|
||||
return -EBUSY;
|
||||
}
|
||||
subsys_table[n->subsys_id] = n;
|
||||
nfnl_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfnetlink_subsys_unregister(struct nfnetlink_subsystem *n)
|
||||
{
|
||||
DEBUGP("unregistering subsystem ID %u\n", n->subsys_id);
|
||||
|
||||
nfnl_lock();
|
||||
subsys_table[n->subsys_id] = NULL;
|
||||
nfnl_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
|
||||
{
|
||||
u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
|
||||
|
||||
if (subsys_id >= NFNL_SUBSYS_COUNT
|
||||
|| subsys_table[subsys_id] == NULL)
|
||||
return NULL;
|
||||
|
||||
return subsys_table[subsys_id];
|
||||
}
|
||||
|
||||
static inline struct nfnl_callback *
|
||||
nfnetlink_find_client(u_int16_t type, struct nfnetlink_subsystem *ss)
|
||||
{
|
||||
u_int8_t cb_id = NFNL_MSG_TYPE(type);
|
||||
|
||||
if (cb_id >= ss->cb_count) {
|
||||
DEBUGP("msgtype %u >= %u, returning\n", type, ss->cb_count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &ss->cb[cb_id];
|
||||
}
|
||||
|
||||
void __nfa_fill(struct sk_buff *skb, int attrtype, int attrlen,
|
||||
const void *data)
|
||||
{
|
||||
struct nfattr *nfa;
|
||||
int size = NFA_LENGTH(attrlen);
|
||||
|
||||
nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size));
|
||||
nfa->nfa_type = attrtype;
|
||||
nfa->nfa_len = size;
|
||||
memcpy(NFA_DATA(nfa), data, attrlen);
|
||||
memset(NFA_DATA(nfa) + attrlen, 0, NFA_ALIGN(size) - size);
|
||||
}
|
||||
|
||||
void nfattr_parse(struct nfattr *tb[], int maxattr, struct nfattr *nfa, int len)
|
||||
{
|
||||
memset(tb, 0, sizeof(struct nfattr *) * maxattr);
|
||||
|
||||
while (NFA_OK(nfa, len)) {
|
||||
unsigned flavor = NFA_TYPE(nfa);
|
||||
if (flavor && flavor <= maxattr)
|
||||
tb[flavor-1] = nfa;
|
||||
nfa = NFA_NEXT(nfa, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* nfnetlink_check_attributes - check and parse nfnetlink attributes
|
||||
*
|
||||
* subsys: nfnl subsystem for which this message is to be parsed
|
||||
* nlmsghdr: netlink message to be checked/parsed
|
||||
* cda: array of pointers, needs to be at least subsys->attr_count big
|
||||
*
|
||||
*/
|
||||
static int
|
||||
nfnetlink_check_attributes(struct nfnetlink_subsystem *subsys,
|
||||
struct nlmsghdr *nlh, struct nfattr *cda[])
|
||||
{
|
||||
int min_len;
|
||||
u_int16_t attr_count;
|
||||
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
|
||||
|
||||
if (unlikely(cb_id >= subsys->cb_count)) {
|
||||
DEBUGP("msgtype %u >= %u, returning\n",
|
||||
cb_id, subsys->cb_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
|
||||
if (unlikely(nlh->nlmsg_len < min_len))
|
||||
return -EINVAL;
|
||||
|
||||
attr_count = subsys->cb[cb_id].attr_count;
|
||||
memset(cda, 0, sizeof(struct nfattr *) * attr_count);
|
||||
|
||||
/* check attribute lengths. */
|
||||
if (likely(nlh->nlmsg_len > min_len)) {
|
||||
struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
|
||||
int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
|
||||
|
||||
while (NFA_OK(attr, attrlen)) {
|
||||
unsigned flavor = NFA_TYPE(attr);
|
||||
if (flavor) {
|
||||
if (flavor > attr_count)
|
||||
return -EINVAL;
|
||||
cda[flavor - 1] = attr;
|
||||
}
|
||||
attr = NFA_NEXT(attr, attrlen);
|
||||
}
|
||||
}
|
||||
|
||||
/* implicit: if nlmsg_len == min_len, we return 0, and an empty
|
||||
* (zeroed) cda[] array. The message is valid, but empty. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfnetlink_has_listeners(unsigned int group)
|
||||
{
|
||||
return netlink_has_listeners(nfnl, group);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
|
||||
|
||||
int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
NETLINK_CB(skb).dst_group = group;
|
||||
if (echo)
|
||||
atomic_inc(&skb->users);
|
||||
netlink_broadcast(nfnl, skb, pid, group, gfp_any());
|
||||
if (echo)
|
||||
err = netlink_unicast(nfnl, skb, pid, MSG_DONTWAIT);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags)
|
||||
{
|
||||
return netlink_unicast(nfnl, skb, pid, flags);
|
||||
}
|
||||
|
||||
/* Process one complete nfnetlink message. */
|
||||
static int nfnetlink_rcv_msg(struct sk_buff *skb,
|
||||
struct nlmsghdr *nlh, int *errp)
|
||||
{
|
||||
struct nfnl_callback *nc;
|
||||
struct nfnetlink_subsystem *ss;
|
||||
int type, err = 0;
|
||||
|
||||
DEBUGP("entered; subsys=%u, msgtype=%u\n",
|
||||
NFNL_SUBSYS_ID(nlh->nlmsg_type),
|
||||
NFNL_MSG_TYPE(nlh->nlmsg_type));
|
||||
|
||||
if (security_netlink_recv(skb, CAP_NET_ADMIN)) {
|
||||
DEBUGP("missing CAP_NET_ADMIN\n");
|
||||
*errp = -EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Only requests are handled by kernel now. */
|
||||
if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
|
||||
DEBUGP("received non-request message\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* All the messages must at least contain nfgenmsg */
|
||||
if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) {
|
||||
DEBUGP("received message was too short\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
type = nlh->nlmsg_type;
|
||||
ss = nfnetlink_get_subsys(type);
|
||||
if (!ss) {
|
||||
#ifdef CONFIG_KMOD
|
||||
/* don't call nfnl_shunlock, since it would reenter
|
||||
* with further packet processing */
|
||||
up(&nfnl_sem);
|
||||
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
|
||||
nfnl_shlock();
|
||||
ss = nfnetlink_get_subsys(type);
|
||||
if (!ss)
|
||||
#endif
|
||||
goto err_inval;
|
||||
}
|
||||
|
||||
nc = nfnetlink_find_client(type, ss);
|
||||
if (!nc) {
|
||||
DEBUGP("unable to find client for type %d\n", type);
|
||||
goto err_inval;
|
||||
}
|
||||
|
||||
{
|
||||
u_int16_t attr_count =
|
||||
ss->cb[NFNL_MSG_TYPE(nlh->nlmsg_type)].attr_count;
|
||||
struct nfattr *cda[attr_count];
|
||||
|
||||
memset(cda, 0, sizeof(struct nfattr *) * attr_count);
|
||||
|
||||
err = nfnetlink_check_attributes(ss, nlh, cda);
|
||||
if (err < 0)
|
||||
goto err_inval;
|
||||
|
||||
DEBUGP("calling handler\n");
|
||||
err = nc->call(nfnl, skb, nlh, cda, errp);
|
||||
*errp = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
err_inval:
|
||||
DEBUGP("returning -EINVAL\n");
|
||||
*errp = -EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Process one packet of messages. */
|
||||
static inline int nfnetlink_rcv_skb(struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
while (skb->len >= NLMSG_SPACE(0)) {
|
||||
u32 rlen;
|
||||
|
||||
nlh = (struct nlmsghdr *)skb->data;
|
||||
if (nlh->nlmsg_len < sizeof(struct nlmsghdr)
|
||||
|| skb->len < nlh->nlmsg_len)
|
||||
return 0;
|
||||
rlen = NLMSG_ALIGN(nlh->nlmsg_len);
|
||||
if (rlen > skb->len)
|
||||
rlen = skb->len;
|
||||
if (nfnetlink_rcv_msg(skb, nlh, &err)) {
|
||||
if (!err)
|
||||
return -1;
|
||||
netlink_ack(skb, nlh, err);
|
||||
} else
|
||||
if (nlh->nlmsg_flags & NLM_F_ACK)
|
||||
netlink_ack(skb, nlh, 0);
|
||||
skb_pull(skb, rlen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nfnetlink_rcv(struct sock *sk, int len)
|
||||
{
|
||||
do {
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (nfnl_shlock_nowait())
|
||||
return;
|
||||
|
||||
while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
|
||||
if (nfnetlink_rcv_skb(skb)) {
|
||||
if (skb->len)
|
||||
skb_queue_head(&sk->sk_receive_queue,
|
||||
skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* don't call nfnl_shunlock, since it would reenter
|
||||
* with further packet processing */
|
||||
up(&nfnl_sem);
|
||||
} while(nfnl && nfnl->sk_receive_queue.qlen);
|
||||
}
|
||||
|
||||
static void __exit nfnetlink_exit(void)
|
||||
{
|
||||
printk("Removing netfilter NETLINK layer.\n");
|
||||
sock_release(nfnl->sk_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
static int __init nfnetlink_init(void)
|
||||
{
|
||||
printk("Netfilter messages via NETLINK v%s.\n", nfversion);
|
||||
|
||||
nfnl = netlink_kernel_create(NETLINK_NETFILTER, NFNLGRP_MAX,
|
||||
nfnetlink_rcv, THIS_MODULE);
|
||||
if (!nfnl) {
|
||||
printk(KERN_ERR "cannot initialize nfnetlink!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(nfnetlink_init);
|
||||
module_exit(nfnetlink_exit);
|
||||
|
||||
EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
|
||||
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
|
||||
EXPORT_SYMBOL_GPL(nfnetlink_send);
|
||||
EXPORT_SYMBOL_GPL(nfnetlink_unicast);
|
||||
EXPORT_SYMBOL_GPL(nfattr_parse);
|
||||
EXPORT_SYMBOL_GPL(__nfa_fill);
|
||||
1102
net/netfilter/nfnetlink_log.c
Normal file
1102
net/netfilter/nfnetlink_log.c
Normal file
File diff suppressed because it is too large
Load Diff
1144
net/netfilter/nfnetlink_queue.c
Normal file
1144
net/netfilter/nfnetlink_queue.c
Normal file
File diff suppressed because it is too large
Load Diff
890
net/netfilter/x_tables.c
Normal file
890
net/netfilter/x_tables.c
Normal file
@@ -0,0 +1,890 @@
|
||||
/*
|
||||
* x_tables core - Backend for {ip,ip6,arp}_tables
|
||||
*
|
||||
* Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* Based on existing ip_tables code which is
|
||||
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
|
||||
* Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter_arp.h>
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("[ip,ip6,arp]_tables backend module");
|
||||
|
||||
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
|
||||
|
||||
struct xt_af {
|
||||
struct mutex mutex;
|
||||
struct list_head match;
|
||||
struct list_head target;
|
||||
struct list_head tables;
|
||||
struct mutex compat_mutex;
|
||||
};
|
||||
|
||||
static struct xt_af *xt;
|
||||
|
||||
#ifdef DEBUG_IP_FIREWALL_USER
|
||||
#define duprintf(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define duprintf(format, args...)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
TABLE,
|
||||
TARGET,
|
||||
MATCH,
|
||||
};
|
||||
|
||||
static const char *xt_prefix[NPROTO] = {
|
||||
[AF_INET] = "ip",
|
||||
[AF_INET6] = "ip6",
|
||||
[NF_ARP] = "arp",
|
||||
};
|
||||
|
||||
/* Registration hooks for targets. */
|
||||
int
|
||||
xt_register_target(struct xt_target *target)
|
||||
{
|
||||
int ret, af = target->family;
|
||||
|
||||
ret = mutex_lock_interruptible(&xt[af].mutex);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
list_add(&target->list, &xt[af].target);
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_register_target);
|
||||
|
||||
void
|
||||
xt_unregister_target(struct xt_target *target)
|
||||
{
|
||||
int af = target->family;
|
||||
|
||||
mutex_lock(&xt[af].mutex);
|
||||
list_del(&target->list);
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_unregister_target);
|
||||
|
||||
int
|
||||
xt_register_targets(struct xt_target *target, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
err = xt_register_target(&target[i]);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
return err;
|
||||
|
||||
err:
|
||||
if (i > 0)
|
||||
xt_unregister_targets(target, i);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_register_targets);
|
||||
|
||||
void
|
||||
xt_unregister_targets(struct xt_target *target, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
xt_unregister_target(&target[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_unregister_targets);
|
||||
|
||||
int
|
||||
xt_register_match(struct xt_match *match)
|
||||
{
|
||||
int ret, af = match->family;
|
||||
|
||||
ret = mutex_lock_interruptible(&xt[af].mutex);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
list_add(&match->list, &xt[af].match);
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_register_match);
|
||||
|
||||
void
|
||||
xt_unregister_match(struct xt_match *match)
|
||||
{
|
||||
int af = match->family;
|
||||
|
||||
mutex_lock(&xt[af].mutex);
|
||||
list_del(&match->list);
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_unregister_match);
|
||||
|
||||
int
|
||||
xt_register_matches(struct xt_match *match, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
err = xt_register_match(&match[i]);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
return err;
|
||||
|
||||
err:
|
||||
if (i > 0)
|
||||
xt_unregister_matches(match, i);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_register_matches);
|
||||
|
||||
void
|
||||
xt_unregister_matches(struct xt_match *match, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
xt_unregister_match(&match[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_unregister_matches);
|
||||
|
||||
|
||||
/*
|
||||
* These are weird, but module loading must not be done with mutex
|
||||
* held (since they will register), and we have to have a single
|
||||
* function to use try_then_request_module().
|
||||
*/
|
||||
|
||||
/* Find match, grabs ref. Returns ERR_PTR() on error. */
|
||||
struct xt_match *xt_find_match(int af, const char *name, u8 revision)
|
||||
{
|
||||
struct xt_match *m;
|
||||
int err = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&xt[af].mutex) != 0)
|
||||
return ERR_PTR(-EINTR);
|
||||
|
||||
list_for_each_entry(m, &xt[af].match, list) {
|
||||
if (strcmp(m->name, name) == 0) {
|
||||
if (m->revision == revision) {
|
||||
if (try_module_get(m->me)) {
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return m;
|
||||
}
|
||||
} else
|
||||
err = -EPROTOTYPE; /* Found something. */
|
||||
}
|
||||
}
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_find_match);
|
||||
|
||||
/* Find target, grabs ref. Returns ERR_PTR() on error. */
|
||||
struct xt_target *xt_find_target(int af, const char *name, u8 revision)
|
||||
{
|
||||
struct xt_target *t;
|
||||
int err = 0;
|
||||
|
||||
if (mutex_lock_interruptible(&xt[af].mutex) != 0)
|
||||
return ERR_PTR(-EINTR);
|
||||
|
||||
list_for_each_entry(t, &xt[af].target, list) {
|
||||
if (strcmp(t->name, name) == 0) {
|
||||
if (t->revision == revision) {
|
||||
if (try_module_get(t->me)) {
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return t;
|
||||
}
|
||||
} else
|
||||
err = -EPROTOTYPE; /* Found something. */
|
||||
}
|
||||
}
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_find_target);
|
||||
|
||||
struct xt_target *xt_request_find_target(int af, const char *name, u8 revision)
|
||||
{
|
||||
struct xt_target *target;
|
||||
|
||||
target = try_then_request_module(xt_find_target(af, name, revision),
|
||||
"%st_%s", xt_prefix[af], name);
|
||||
if (IS_ERR(target) || !target)
|
||||
return NULL;
|
||||
return target;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_request_find_target);
|
||||
|
||||
static int match_revfn(int af, const char *name, u8 revision, int *bestp)
|
||||
{
|
||||
struct xt_match *m;
|
||||
int have_rev = 0;
|
||||
|
||||
list_for_each_entry(m, &xt[af].match, list) {
|
||||
if (strcmp(m->name, name) == 0) {
|
||||
if (m->revision > *bestp)
|
||||
*bestp = m->revision;
|
||||
if (m->revision == revision)
|
||||
have_rev = 1;
|
||||
}
|
||||
}
|
||||
return have_rev;
|
||||
}
|
||||
|
||||
static int target_revfn(int af, const char *name, u8 revision, int *bestp)
|
||||
{
|
||||
struct xt_target *t;
|
||||
int have_rev = 0;
|
||||
|
||||
list_for_each_entry(t, &xt[af].target, list) {
|
||||
if (strcmp(t->name, name) == 0) {
|
||||
if (t->revision > *bestp)
|
||||
*bestp = t->revision;
|
||||
if (t->revision == revision)
|
||||
have_rev = 1;
|
||||
}
|
||||
}
|
||||
return have_rev;
|
||||
}
|
||||
|
||||
/* Returns true or false (if no such extension at all) */
|
||||
int xt_find_revision(int af, const char *name, u8 revision, int target,
|
||||
int *err)
|
||||
{
|
||||
int have_rev, best = -1;
|
||||
|
||||
if (mutex_lock_interruptible(&xt[af].mutex) != 0) {
|
||||
*err = -EINTR;
|
||||
return 1;
|
||||
}
|
||||
if (target == 1)
|
||||
have_rev = target_revfn(af, name, revision, &best);
|
||||
else
|
||||
have_rev = match_revfn(af, name, revision, &best);
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
|
||||
/* Nothing at all? Return 0 to try loading module. */
|
||||
if (best == -1) {
|
||||
*err = -ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*err = best;
|
||||
if (!have_rev)
|
||||
*err = -EPROTONOSUPPORT;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_find_revision);
|
||||
|
||||
int xt_check_match(const struct xt_match *match, unsigned short family,
|
||||
unsigned int size, const char *table, unsigned int hook_mask,
|
||||
unsigned short proto, int inv_proto)
|
||||
{
|
||||
if (XT_ALIGN(match->matchsize) != size) {
|
||||
printk("%s_tables: %s match: invalid size %Zu != %u\n",
|
||||
xt_prefix[family], match->name,
|
||||
XT_ALIGN(match->matchsize), size);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (match->table && strcmp(match->table, table)) {
|
||||
printk("%s_tables: %s match: only valid in %s table, not %s\n",
|
||||
xt_prefix[family], match->name, match->table, table);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (match->hooks && (hook_mask & ~match->hooks) != 0) {
|
||||
printk("%s_tables: %s match: bad hook_mask %u\n",
|
||||
xt_prefix[family], match->name, hook_mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (match->proto && (match->proto != proto || inv_proto)) {
|
||||
printk("%s_tables: %s match: only valid for protocol %u\n",
|
||||
xt_prefix[family], match->name, match->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_check_match);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_match_offset(struct xt_match *match)
|
||||
{
|
||||
u_int16_t csize = match->compatsize ? : match->matchsize;
|
||||
return XT_ALIGN(match->matchsize) - COMPAT_XT_ALIGN(csize);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_offset);
|
||||
|
||||
void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_match *match = m->u.kernel.match;
|
||||
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
|
||||
int pad, off = xt_compat_match_offset(match);
|
||||
u_int16_t msize = cm->u.user.match_size;
|
||||
|
||||
m = *dstptr;
|
||||
memcpy(m, cm, sizeof(*cm));
|
||||
if (match->compat_from_user)
|
||||
match->compat_from_user(m->data, cm->data);
|
||||
else
|
||||
memcpy(m->data, cm->data, msize - sizeof(*cm));
|
||||
pad = XT_ALIGN(match->matchsize) - match->matchsize;
|
||||
if (pad > 0)
|
||||
memset(m->data + match->matchsize, 0, pad);
|
||||
|
||||
msize += off;
|
||||
m->u.user.match_size = msize;
|
||||
|
||||
*size += off;
|
||||
*dstptr += msize;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
|
||||
|
||||
int xt_compat_match_to_user(struct xt_entry_match *m, void __user **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_match *match = m->u.kernel.match;
|
||||
struct compat_xt_entry_match __user *cm = *dstptr;
|
||||
int off = xt_compat_match_offset(match);
|
||||
u_int16_t msize = m->u.user.match_size - off;
|
||||
|
||||
if (copy_to_user(cm, m, sizeof(*cm)) ||
|
||||
put_user(msize, &cm->u.user.match_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (match->compat_to_user) {
|
||||
if (match->compat_to_user((void __user *)cm->data, m->data))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_to_user(cm->data, m->data, msize - sizeof(*cm)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*size -= off;
|
||||
*dstptr += msize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
int xt_check_target(const struct xt_target *target, unsigned short family,
|
||||
unsigned int size, const char *table, unsigned int hook_mask,
|
||||
unsigned short proto, int inv_proto)
|
||||
{
|
||||
if (XT_ALIGN(target->targetsize) != size) {
|
||||
printk("%s_tables: %s target: invalid size %Zu != %u\n",
|
||||
xt_prefix[family], target->name,
|
||||
XT_ALIGN(target->targetsize), size);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (target->table && strcmp(target->table, table)) {
|
||||
printk("%s_tables: %s target: only valid in %s table, not %s\n",
|
||||
xt_prefix[family], target->name, target->table, table);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (target->hooks && (hook_mask & ~target->hooks) != 0) {
|
||||
printk("%s_tables: %s target: bad hook_mask %u\n",
|
||||
xt_prefix[family], target->name, hook_mask);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (target->proto && (target->proto != proto || inv_proto)) {
|
||||
printk("%s_tables: %s target: only valid for protocol %u\n",
|
||||
xt_prefix[family], target->name, target->proto);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_check_target);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
int xt_compat_target_offset(struct xt_target *target)
|
||||
{
|
||||
u_int16_t csize = target->compatsize ? : target->targetsize;
|
||||
return XT_ALIGN(target->targetsize) - COMPAT_XT_ALIGN(csize);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_offset);
|
||||
|
||||
void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_target *target = t->u.kernel.target;
|
||||
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
|
||||
int pad, off = xt_compat_target_offset(target);
|
||||
u_int16_t tsize = ct->u.user.target_size;
|
||||
|
||||
t = *dstptr;
|
||||
memcpy(t, ct, sizeof(*ct));
|
||||
if (target->compat_from_user)
|
||||
target->compat_from_user(t->data, ct->data);
|
||||
else
|
||||
memcpy(t->data, ct->data, tsize - sizeof(*ct));
|
||||
pad = XT_ALIGN(target->targetsize) - target->targetsize;
|
||||
if (pad > 0)
|
||||
memset(t->data + target->targetsize, 0, pad);
|
||||
|
||||
tsize += off;
|
||||
t->u.user.target_size = tsize;
|
||||
|
||||
*size += off;
|
||||
*dstptr += tsize;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_from_user);
|
||||
|
||||
int xt_compat_target_to_user(struct xt_entry_target *t, void __user **dstptr,
|
||||
int *size)
|
||||
{
|
||||
struct xt_target *target = t->u.kernel.target;
|
||||
struct compat_xt_entry_target __user *ct = *dstptr;
|
||||
int off = xt_compat_target_offset(target);
|
||||
u_int16_t tsize = t->u.user.target_size - off;
|
||||
|
||||
if (copy_to_user(ct, t, sizeof(*ct)) ||
|
||||
put_user(tsize, &ct->u.user.target_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (target->compat_to_user) {
|
||||
if (target->compat_to_user((void __user *)ct->data, t->data))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
if (copy_to_user(ct->data, t->data, tsize - sizeof(*ct)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*size -= off;
|
||||
*dstptr += tsize;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_target_to_user);
|
||||
#endif
|
||||
|
||||
struct xt_table_info *xt_alloc_table_info(unsigned int size)
|
||||
{
|
||||
struct xt_table_info *newinfo;
|
||||
int cpu;
|
||||
|
||||
/* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
|
||||
if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages)
|
||||
return NULL;
|
||||
|
||||
newinfo = kzalloc(sizeof(struct xt_table_info), GFP_KERNEL);
|
||||
if (!newinfo)
|
||||
return NULL;
|
||||
|
||||
newinfo->size = size;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (size <= PAGE_SIZE)
|
||||
newinfo->entries[cpu] = kmalloc_node(size,
|
||||
GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
else
|
||||
newinfo->entries[cpu] = vmalloc_node(size,
|
||||
cpu_to_node(cpu));
|
||||
|
||||
if (newinfo->entries[cpu] == NULL) {
|
||||
xt_free_table_info(newinfo);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return newinfo;
|
||||
}
|
||||
EXPORT_SYMBOL(xt_alloc_table_info);
|
||||
|
||||
void xt_free_table_info(struct xt_table_info *info)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (info->size <= PAGE_SIZE)
|
||||
kfree(info->entries[cpu]);
|
||||
else
|
||||
vfree(info->entries[cpu]);
|
||||
}
|
||||
kfree(info);
|
||||
}
|
||||
EXPORT_SYMBOL(xt_free_table_info);
|
||||
|
||||
/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */
|
||||
struct xt_table *xt_find_table_lock(int af, const char *name)
|
||||
{
|
||||
struct xt_table *t;
|
||||
|
||||
if (mutex_lock_interruptible(&xt[af].mutex) != 0)
|
||||
return ERR_PTR(-EINTR);
|
||||
|
||||
list_for_each_entry(t, &xt[af].tables, list)
|
||||
if (strcmp(t->name, name) == 0 && try_module_get(t->me))
|
||||
return t;
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_find_table_lock);
|
||||
|
||||
void xt_table_unlock(struct xt_table *table)
|
||||
{
|
||||
mutex_unlock(&xt[table->af].mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_table_unlock);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
void xt_compat_lock(int af)
|
||||
{
|
||||
mutex_lock(&xt[af].compat_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_lock);
|
||||
|
||||
void xt_compat_unlock(int af)
|
||||
{
|
||||
mutex_unlock(&xt[af].compat_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_compat_unlock);
|
||||
#endif
|
||||
|
||||
struct xt_table_info *
|
||||
xt_replace_table(struct xt_table *table,
|
||||
unsigned int num_counters,
|
||||
struct xt_table_info *newinfo,
|
||||
int *error)
|
||||
{
|
||||
struct xt_table_info *oldinfo, *private;
|
||||
|
||||
/* Do the substitution. */
|
||||
write_lock_bh(&table->lock);
|
||||
private = table->private;
|
||||
/* Check inside lock: is the old number correct? */
|
||||
if (num_counters != private->number) {
|
||||
duprintf("num_counters != table->private->number (%u/%u)\n",
|
||||
num_counters, private->number);
|
||||
write_unlock_bh(&table->lock);
|
||||
*error = -EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
oldinfo = private;
|
||||
table->private = newinfo;
|
||||
newinfo->initial_entries = oldinfo->initial_entries;
|
||||
write_unlock_bh(&table->lock);
|
||||
|
||||
return oldinfo;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_replace_table);
|
||||
|
||||
int xt_register_table(struct xt_table *table,
|
||||
struct xt_table_info *bootstrap,
|
||||
struct xt_table_info *newinfo)
|
||||
{
|
||||
int ret;
|
||||
struct xt_table_info *private;
|
||||
struct xt_table *t;
|
||||
|
||||
ret = mutex_lock_interruptible(&xt[table->af].mutex);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* Don't autoload: we'd eat our tail... */
|
||||
list_for_each_entry(t, &xt[table->af].tables, list) {
|
||||
if (strcmp(t->name, table->name) == 0) {
|
||||
ret = -EEXIST;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* Simplifies replace_table code. */
|
||||
table->private = bootstrap;
|
||||
rwlock_init(&table->lock);
|
||||
if (!xt_replace_table(table, 0, newinfo, &ret))
|
||||
goto unlock;
|
||||
|
||||
private = table->private;
|
||||
duprintf("table->private->number = %u\n", private->number);
|
||||
|
||||
/* save number of initial entries */
|
||||
private->initial_entries = private->number;
|
||||
|
||||
list_add(&table->list, &xt[table->af].tables);
|
||||
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&xt[table->af].mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_register_table);
|
||||
|
||||
void *xt_unregister_table(struct xt_table *table)
|
||||
{
|
||||
struct xt_table_info *private;
|
||||
|
||||
mutex_lock(&xt[table->af].mutex);
|
||||
private = table->private;
|
||||
list_del(&table->list);
|
||||
mutex_unlock(&xt[table->af].mutex);
|
||||
|
||||
return private;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_unregister_table);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static char *xt_proto_prefix[NPROTO] = {
|
||||
[AF_INET] = "ip",
|
||||
[AF_INET6] = "ip6",
|
||||
[NF_ARP] = "arp",
|
||||
};
|
||||
|
||||
static struct list_head *xt_get_idx(struct list_head *list, struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
struct list_head *head = list->next;
|
||||
|
||||
if (!head || list_empty(list))
|
||||
return NULL;
|
||||
|
||||
while (pos && (head = head->next)) {
|
||||
if (head == list)
|
||||
return NULL;
|
||||
pos--;
|
||||
}
|
||||
return pos ? NULL : head;
|
||||
}
|
||||
|
||||
static struct list_head *type2list(u_int16_t af, u_int16_t type)
|
||||
{
|
||||
struct list_head *list;
|
||||
|
||||
switch (type) {
|
||||
case TARGET:
|
||||
list = &xt[af].target;
|
||||
break;
|
||||
case MATCH:
|
||||
list = &xt[af].match;
|
||||
break;
|
||||
case TABLE:
|
||||
list = &xt[af].tables;
|
||||
break;
|
||||
default:
|
||||
list = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void *xt_tgt_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
struct proc_dir_entry *pde = (struct proc_dir_entry *) seq->private;
|
||||
u_int16_t af = (unsigned long)pde->data & 0xffff;
|
||||
u_int16_t type = (unsigned long)pde->data >> 16;
|
||||
struct list_head *list;
|
||||
|
||||
if (af >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
list = type2list(af, type);
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
if (mutex_lock_interruptible(&xt[af].mutex) != 0)
|
||||
return NULL;
|
||||
|
||||
return xt_get_idx(list, seq, *pos);
|
||||
}
|
||||
|
||||
static void *xt_tgt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct proc_dir_entry *pde = seq->private;
|
||||
u_int16_t af = (unsigned long)pde->data & 0xffff;
|
||||
u_int16_t type = (unsigned long)pde->data >> 16;
|
||||
struct list_head *list;
|
||||
|
||||
if (af >= NPROTO)
|
||||
return NULL;
|
||||
|
||||
list = type2list(af, type);
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
(*pos)++;
|
||||
return xt_get_idx(list, seq, *pos);
|
||||
}
|
||||
|
||||
static void xt_tgt_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct proc_dir_entry *pde = seq->private;
|
||||
u_int16_t af = (unsigned long)pde->data & 0xffff;
|
||||
|
||||
mutex_unlock(&xt[af].mutex);
|
||||
}
|
||||
|
||||
static int xt_name_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
char *name = (char *)v + sizeof(struct list_head);
|
||||
|
||||
if (strlen(name))
|
||||
return seq_printf(seq, "%s\n", name);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations xt_tgt_seq_ops = {
|
||||
.start = xt_tgt_seq_start,
|
||||
.next = xt_tgt_seq_next,
|
||||
.stop = xt_tgt_seq_stop,
|
||||
.show = xt_name_seq_show,
|
||||
};
|
||||
|
||||
static int xt_tgt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &xt_tgt_seq_ops);
|
||||
if (!ret) {
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct proc_dir_entry *pde = PDE(inode);
|
||||
|
||||
seq->private = pde;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations xt_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = xt_tgt_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
#define FORMAT_TABLES "_tables_names"
|
||||
#define FORMAT_MATCHES "_tables_matches"
|
||||
#define FORMAT_TARGETS "_tables_targets"
|
||||
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
int xt_proto_init(int af)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
char buf[XT_FUNCTION_MAXNAMELEN];
|
||||
struct proc_dir_entry *proc;
|
||||
#endif
|
||||
|
||||
if (af >= NPROTO)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_TABLES, sizeof(buf));
|
||||
proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
|
||||
if (!proc)
|
||||
goto out;
|
||||
proc->data = (void *) ((unsigned long) af | (TABLE << 16));
|
||||
|
||||
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_MATCHES, sizeof(buf));
|
||||
proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
|
||||
if (!proc)
|
||||
goto out_remove_tables;
|
||||
proc->data = (void *) ((unsigned long) af | (MATCH << 16));
|
||||
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_TARGETS, sizeof(buf));
|
||||
proc = proc_net_fops_create(buf, 0440, &xt_file_ops);
|
||||
if (!proc)
|
||||
goto out_remove_matches;
|
||||
proc->data = (void *) ((unsigned long) af | (TARGET << 16));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
out_remove_matches:
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_MATCHES, sizeof(buf));
|
||||
proc_net_remove(buf);
|
||||
|
||||
out_remove_tables:
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_TABLES, sizeof(buf));
|
||||
proc_net_remove(buf);
|
||||
out:
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_proto_init);
|
||||
|
||||
void xt_proto_fini(int af)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
char buf[XT_FUNCTION_MAXNAMELEN];
|
||||
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_TABLES, sizeof(buf));
|
||||
proc_net_remove(buf);
|
||||
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_TARGETS, sizeof(buf));
|
||||
proc_net_remove(buf);
|
||||
|
||||
strlcpy(buf, xt_proto_prefix[af], sizeof(buf));
|
||||
strlcat(buf, FORMAT_MATCHES, sizeof(buf));
|
||||
proc_net_remove(buf);
|
||||
#endif /*CONFIG_PROC_FS*/
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xt_proto_fini);
|
||||
|
||||
|
||||
static int __init xt_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
xt = kmalloc(sizeof(struct xt_af) * NPROTO, GFP_KERNEL);
|
||||
if (!xt)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < NPROTO; i++) {
|
||||
mutex_init(&xt[i].mutex);
|
||||
#ifdef CONFIG_COMPAT
|
||||
mutex_init(&xt[i].compat_mutex);
|
||||
#endif
|
||||
INIT_LIST_HEAD(&xt[i].target);
|
||||
INIT_LIST_HEAD(&xt[i].match);
|
||||
INIT_LIST_HEAD(&xt[i].tables);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit xt_fini(void)
|
||||
{
|
||||
kfree(xt);
|
||||
}
|
||||
|
||||
module_init(xt_init);
|
||||
module_exit(xt_fini);
|
||||
|
||||
80
net/netfilter/xt_CLASSIFY.c
Normal file
80
net/netfilter/xt_CLASSIFY.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This is a module which is used for setting the skb->priority field
|
||||
* of an skb for qdisc classification.
|
||||
*/
|
||||
|
||||
/* (C) 2001-2002 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/checksum.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_CLASSIFY.h>
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("iptables qdisc classification target module");
|
||||
MODULE_ALIAS("ipt_CLASSIFY");
|
||||
|
||||
static unsigned int
|
||||
target(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_classify_target_info *clinfo = targinfo;
|
||||
|
||||
(*pskb)->priority = clinfo->priority;
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static struct xt_target xt_classify_target[] = {
|
||||
{
|
||||
.family = AF_INET,
|
||||
.name = "CLASSIFY",
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_classify_target_info),
|
||||
.table = "mangle",
|
||||
.hooks = (1 << NF_IP_LOCAL_OUT) |
|
||||
(1 << NF_IP_FORWARD) |
|
||||
(1 << NF_IP_POST_ROUTING),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "CLASSIFY",
|
||||
.family = AF_INET6,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_classify_target_info),
|
||||
.table = "mangle",
|
||||
.hooks = (1 << NF_IP6_LOCAL_OUT) |
|
||||
(1 << NF_IP6_FORWARD) |
|
||||
(1 << NF_IP6_POST_ROUTING),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_classify_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_classify_target,
|
||||
ARRAY_SIZE(xt_classify_target));
|
||||
}
|
||||
|
||||
static void __exit xt_classify_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_classify_target,
|
||||
ARRAY_SIZE(xt_classify_target));
|
||||
}
|
||||
|
||||
module_init(xt_classify_init);
|
||||
module_exit(xt_classify_fini);
|
||||
194
net/netfilter/xt_CONNMARK.c
Normal file
194
net/netfilter/xt_CONNMARK.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/* This kernel module is used to modify the connection mark values, or
|
||||
* to optionally restore the skb nfmark from the connection mark
|
||||
*
|
||||
* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
|
||||
* by Henrik Nordstrom <hno@marasystems.com>
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/checksum.h>
|
||||
|
||||
MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>");
|
||||
MODULE_DESCRIPTION("IP tables CONNMARK matching module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_CONNMARK");
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_CONNMARK.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
|
||||
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||
#endif
|
||||
|
||||
static unsigned int
|
||||
target(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_connmark_target_info *markinfo = targinfo;
|
||||
u_int32_t diff;
|
||||
u_int32_t mark;
|
||||
u_int32_t newmark;
|
||||
u_int32_t ctinfo;
|
||||
u_int32_t *ctmark = nf_ct_get_mark(*pskb, &ctinfo);
|
||||
|
||||
if (ctmark) {
|
||||
switch(markinfo->mode) {
|
||||
case XT_CONNMARK_SET:
|
||||
newmark = (*ctmark & ~markinfo->mask) | markinfo->mark;
|
||||
if (newmark != *ctmark) {
|
||||
*ctmark = newmark;
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
ip_conntrack_event_cache(IPCT_MARK, *pskb);
|
||||
#else
|
||||
nf_conntrack_event_cache(IPCT_MARK, *pskb);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case XT_CONNMARK_SAVE:
|
||||
newmark = (*ctmark & ~markinfo->mask) |
|
||||
((*pskb)->mark & markinfo->mask);
|
||||
if (*ctmark != newmark) {
|
||||
*ctmark = newmark;
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
ip_conntrack_event_cache(IPCT_MARK, *pskb);
|
||||
#else
|
||||
nf_conntrack_event_cache(IPCT_MARK, *pskb);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case XT_CONNMARK_RESTORE:
|
||||
mark = (*pskb)->mark;
|
||||
diff = (*ctmark ^ mark) & markinfo->mask;
|
||||
(*pskb)->mark = mark ^ diff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *entry,
|
||||
const struct xt_target *target,
|
||||
void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_connmark_target_info *matchinfo = targinfo;
|
||||
|
||||
if (nf_ct_l3proto_try_module_get(target->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", target->family);
|
||||
return 0;
|
||||
}
|
||||
if (matchinfo->mode == XT_CONNMARK_RESTORE) {
|
||||
if (strcmp(tablename, "mangle") != 0) {
|
||||
printk(KERN_WARNING "CONNMARK: restore can only be "
|
||||
"called from \"mangle\" table, not \"%s\"\n",
|
||||
tablename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (matchinfo->mark > 0xffffffff || matchinfo->mask > 0xffffffff) {
|
||||
printk(KERN_WARNING "CONNMARK: Only supports 32bit mark\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(const struct xt_target *target, void *targinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(target->family);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_xt_connmark_target_info {
|
||||
compat_ulong_t mark, mask;
|
||||
u_int8_t mode;
|
||||
u_int8_t __pad1;
|
||||
u_int16_t __pad2;
|
||||
};
|
||||
|
||||
static void compat_from_user(void *dst, void *src)
|
||||
{
|
||||
struct compat_xt_connmark_target_info *cm = src;
|
||||
struct xt_connmark_target_info m = {
|
||||
.mark = cm->mark,
|
||||
.mask = cm->mask,
|
||||
.mode = cm->mode,
|
||||
};
|
||||
memcpy(dst, &m, sizeof(m));
|
||||
}
|
||||
|
||||
static int compat_to_user(void __user *dst, void *src)
|
||||
{
|
||||
struct xt_connmark_target_info *m = src;
|
||||
struct compat_xt_connmark_target_info cm = {
|
||||
.mark = m->mark,
|
||||
.mask = m->mask,
|
||||
.mode = m->mode,
|
||||
};
|
||||
return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
static struct xt_target xt_connmark_target[] = {
|
||||
{
|
||||
.name = "CONNMARK",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.destroy = destroy,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_connmark_target_info),
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compatsize = sizeof(struct compat_xt_connmark_target_info),
|
||||
.compat_from_user = compat_from_user,
|
||||
.compat_to_user = compat_to_user,
|
||||
#endif
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "CONNMARK",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.destroy = destroy,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_connmark_target_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_connmark_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_connmark_target,
|
||||
ARRAY_SIZE(xt_connmark_target));
|
||||
}
|
||||
|
||||
static void __exit xt_connmark_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_connmark_target,
|
||||
ARRAY_SIZE(xt_connmark_target));
|
||||
}
|
||||
|
||||
module_init(xt_connmark_init);
|
||||
module_exit(xt_connmark_fini);
|
||||
154
net/netfilter/xt_CONNSECMARK.c
Normal file
154
net/netfilter/xt_CONNSECMARK.c
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This module is used to copy security markings from packets
|
||||
* to connections, and restore security markings from connections
|
||||
* back to packets. This would normally be performed in conjunction
|
||||
* with the SECMARK target and state match.
|
||||
*
|
||||
* Based somewhat on CONNMARK:
|
||||
* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
|
||||
* by Henrik Nordstrom <hno@marasystems.com>
|
||||
*
|
||||
* (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_CONNSECMARK.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
|
||||
#define PFX "CONNSECMARK: "
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
|
||||
MODULE_DESCRIPTION("ip[6]tables CONNSECMARK module");
|
||||
MODULE_ALIAS("ipt_CONNSECMARK");
|
||||
MODULE_ALIAS("ip6t_CONNSECMARK");
|
||||
|
||||
/*
|
||||
* If the packet has a security mark and the connection does not, copy
|
||||
* the security mark from the packet to the connection.
|
||||
*/
|
||||
static void secmark_save(struct sk_buff *skb)
|
||||
{
|
||||
if (skb->secmark) {
|
||||
u32 *connsecmark;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
|
||||
connsecmark = nf_ct_get_secmark(skb, &ctinfo);
|
||||
if (connsecmark && !*connsecmark)
|
||||
*connsecmark = skb->secmark;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If packet has no security mark, and the connection does, restore the
|
||||
* security mark from the connection to the packet.
|
||||
*/
|
||||
static void secmark_restore(struct sk_buff *skb)
|
||||
{
|
||||
if (!skb->secmark) {
|
||||
u32 *connsecmark;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
|
||||
connsecmark = nf_ct_get_secmark(skb, &ctinfo);
|
||||
if (connsecmark && *connsecmark)
|
||||
skb->secmark = *connsecmark;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int target(struct sk_buff **pskb, const struct net_device *in,
|
||||
const struct net_device *out, unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
const struct xt_connsecmark_target_info *info = targinfo;
|
||||
|
||||
switch (info->mode) {
|
||||
case CONNSECMARK_SAVE:
|
||||
secmark_save(skb);
|
||||
break;
|
||||
|
||||
case CONNSECMARK_RESTORE:
|
||||
secmark_restore(skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int checkentry(const char *tablename, const void *entry,
|
||||
const struct xt_target *target, void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_connsecmark_target_info *info = targinfo;
|
||||
|
||||
if (nf_ct_l3proto_try_module_get(target->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", target->family);
|
||||
return 0;
|
||||
}
|
||||
switch (info->mode) {
|
||||
case CONNSECMARK_SAVE:
|
||||
case CONNSECMARK_RESTORE:
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(const struct xt_target *target, void *targinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(target->family);
|
||||
}
|
||||
|
||||
static struct xt_target xt_connsecmark_target[] = {
|
||||
{
|
||||
.name = "CONNSECMARK",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.destroy = destroy,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_connsecmark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "CONNSECMARK",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.destroy = destroy,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_connsecmark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_connsecmark_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_connsecmark_target,
|
||||
ARRAY_SIZE(xt_connsecmark_target));
|
||||
}
|
||||
|
||||
static void __exit xt_connsecmark_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_connsecmark_target,
|
||||
ARRAY_SIZE(xt_connsecmark_target));
|
||||
}
|
||||
|
||||
module_init(xt_connsecmark_init);
|
||||
module_exit(xt_connsecmark_fini);
|
||||
118
net/netfilter/xt_DSCP.c
Normal file
118
net/netfilter/xt_DSCP.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/* x_tables module for setting the IPv4/IPv6 DSCP field, Version 1.8
|
||||
*
|
||||
* (C) 2002 by Harald Welte <laforge@netfilter.org>
|
||||
* based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* See RFC2474 for a description of the DSCP field within the IP Header.
|
||||
*
|
||||
* xt_DSCP.c,v 1.8 2002/08/06 18:41:57 laforge Exp
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/dsfield.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_DSCP.h>
|
||||
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("x_tables DSCP modification module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_DSCP");
|
||||
MODULE_ALIAS("ip6t_DSCP");
|
||||
|
||||
static unsigned int target(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_DSCP_info *dinfo = targinfo;
|
||||
u_int8_t dscp = ipv4_get_dsfield((*pskb)->nh.iph) >> XT_DSCP_SHIFT;
|
||||
|
||||
if (dscp != dinfo->dscp) {
|
||||
if (!skb_make_writable(pskb, sizeof(struct iphdr)))
|
||||
return NF_DROP;
|
||||
|
||||
ipv4_change_dsfield((*pskb)->nh.iph, (__u8)(~XT_DSCP_MASK),
|
||||
dinfo->dscp << XT_DSCP_SHIFT);
|
||||
|
||||
}
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static unsigned int target6(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_DSCP_info *dinfo = targinfo;
|
||||
u_int8_t dscp = ipv6_get_dsfield((*pskb)->nh.ipv6h) >> XT_DSCP_SHIFT;
|
||||
|
||||
if (dscp != dinfo->dscp) {
|
||||
if (!skb_make_writable(pskb, sizeof(struct ipv6hdr)))
|
||||
return NF_DROP;
|
||||
|
||||
ipv6_change_dsfield((*pskb)->nh.ipv6h, (__u8)(~XT_DSCP_MASK),
|
||||
dinfo->dscp << XT_DSCP_SHIFT);
|
||||
}
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int checkentry(const char *tablename,
|
||||
const void *e_void,
|
||||
const struct xt_target *target,
|
||||
void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const u_int8_t dscp = ((struct xt_DSCP_info *)targinfo)->dscp;
|
||||
|
||||
if ((dscp > XT_DSCP_MAX)) {
|
||||
printk(KERN_WARNING "DSCP: dscp %x out of range\n", dscp);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_target xt_dscp_target[] = {
|
||||
{
|
||||
.name = "DSCP",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_DSCP_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "DSCP",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.target = target6,
|
||||
.targetsize = sizeof(struct xt_DSCP_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_dscp_target_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_dscp_target, ARRAY_SIZE(xt_dscp_target));
|
||||
}
|
||||
|
||||
static void __exit xt_dscp_target_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_dscp_target, ARRAY_SIZE(xt_dscp_target));
|
||||
}
|
||||
|
||||
module_init(xt_dscp_target_init);
|
||||
module_exit(xt_dscp_target_fini);
|
||||
185
net/netfilter/xt_MARK.c
Normal file
185
net/netfilter/xt_MARK.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/* This is a module which is used for setting the NFMARK field of an skb. */
|
||||
|
||||
/* (C) 1999-2001 Marc Boucher <marc@mbsi.ca>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/checksum.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_MARK.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
|
||||
MODULE_DESCRIPTION("ip[6]tables MARK modification module");
|
||||
MODULE_ALIAS("ipt_MARK");
|
||||
MODULE_ALIAS("ip6t_MARK");
|
||||
|
||||
static unsigned int
|
||||
target_v0(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_mark_target_info *markinfo = targinfo;
|
||||
|
||||
(*pskb)->mark = markinfo->mark;
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
target_v1(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_mark_target_info_v1 *markinfo = targinfo;
|
||||
int mark = 0;
|
||||
|
||||
switch (markinfo->mode) {
|
||||
case XT_MARK_SET:
|
||||
mark = markinfo->mark;
|
||||
break;
|
||||
|
||||
case XT_MARK_AND:
|
||||
mark = (*pskb)->mark & markinfo->mark;
|
||||
break;
|
||||
|
||||
case XT_MARK_OR:
|
||||
mark = (*pskb)->mark | markinfo->mark;
|
||||
break;
|
||||
}
|
||||
|
||||
(*pskb)->mark = mark;
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
checkentry_v0(const char *tablename,
|
||||
const void *entry,
|
||||
const struct xt_target *target,
|
||||
void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_mark_target_info *markinfo = targinfo;
|
||||
|
||||
if (markinfo->mark > 0xffffffff) {
|
||||
printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry_v1(const char *tablename,
|
||||
const void *entry,
|
||||
const struct xt_target *target,
|
||||
void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_mark_target_info_v1 *markinfo = targinfo;
|
||||
|
||||
if (markinfo->mode != XT_MARK_SET
|
||||
&& markinfo->mode != XT_MARK_AND
|
||||
&& markinfo->mode != XT_MARK_OR) {
|
||||
printk(KERN_WARNING "MARK: unknown mode %u\n",
|
||||
markinfo->mode);
|
||||
return 0;
|
||||
}
|
||||
if (markinfo->mark > 0xffffffff) {
|
||||
printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_xt_mark_target_info_v1 {
|
||||
compat_ulong_t mark;
|
||||
u_int8_t mode;
|
||||
u_int8_t __pad1;
|
||||
u_int16_t __pad2;
|
||||
};
|
||||
|
||||
static void compat_from_user_v1(void *dst, void *src)
|
||||
{
|
||||
struct compat_xt_mark_target_info_v1 *cm = src;
|
||||
struct xt_mark_target_info_v1 m = {
|
||||
.mark = cm->mark,
|
||||
.mode = cm->mode,
|
||||
};
|
||||
memcpy(dst, &m, sizeof(m));
|
||||
}
|
||||
|
||||
static int compat_to_user_v1(void __user *dst, void *src)
|
||||
{
|
||||
struct xt_mark_target_info_v1 *m = src;
|
||||
struct compat_xt_mark_target_info_v1 cm = {
|
||||
.mark = m->mark,
|
||||
.mode = m->mode,
|
||||
};
|
||||
return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
static struct xt_target xt_mark_target[] = {
|
||||
{
|
||||
.name = "MARK",
|
||||
.family = AF_INET,
|
||||
.revision = 0,
|
||||
.checkentry = checkentry_v0,
|
||||
.target = target_v0,
|
||||
.targetsize = sizeof(struct xt_mark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "MARK",
|
||||
.family = AF_INET,
|
||||
.revision = 1,
|
||||
.checkentry = checkentry_v1,
|
||||
.target = target_v1,
|
||||
.targetsize = sizeof(struct xt_mark_target_info_v1),
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compatsize = sizeof(struct compat_xt_mark_target_info_v1),
|
||||
.compat_from_user = compat_from_user_v1,
|
||||
.compat_to_user = compat_to_user_v1,
|
||||
#endif
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "MARK",
|
||||
.family = AF_INET6,
|
||||
.revision = 0,
|
||||
.checkentry = checkentry_v0,
|
||||
.target = target_v0,
|
||||
.targetsize = sizeof(struct xt_mark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_mark_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_mark_target, ARRAY_SIZE(xt_mark_target));
|
||||
}
|
||||
|
||||
static void __exit xt_mark_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_mark_target, ARRAY_SIZE(xt_mark_target));
|
||||
}
|
||||
|
||||
module_init(xt_mark_init);
|
||||
module_exit(xt_mark_fini);
|
||||
86
net/netfilter/xt_NFLOG.c
Normal file
86
net/netfilter/xt_NFLOG.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_NFLOG.h>
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("x_tables NFLOG target");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_NFLOG");
|
||||
MODULE_ALIAS("ip6t_NFLOG");
|
||||
|
||||
static unsigned int
|
||||
nflog_target(struct sk_buff **pskb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
unsigned int hooknum, const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_nflog_info *info = targinfo;
|
||||
struct nf_loginfo li;
|
||||
|
||||
li.type = NF_LOG_TYPE_ULOG;
|
||||
li.u.ulog.copy_len = info->len;
|
||||
li.u.ulog.group = info->group;
|
||||
li.u.ulog.qthreshold = info->threshold;
|
||||
|
||||
nf_log_packet(target->family, hooknum, *pskb, in, out, &li,
|
||||
"%s", info->prefix);
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int
|
||||
nflog_checkentry(const char *tablename, const void *entry,
|
||||
const struct xt_target *target, void *targetinfo,
|
||||
unsigned int hookmask)
|
||||
{
|
||||
struct xt_nflog_info *info = targetinfo;
|
||||
|
||||
if (info->flags & ~XT_NFLOG_MASK)
|
||||
return 0;
|
||||
if (info->prefix[sizeof(info->prefix) - 1] != '\0')
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_target xt_nflog_target[] = {
|
||||
{
|
||||
.name = "NFLOG",
|
||||
.family = AF_INET,
|
||||
.checkentry = nflog_checkentry,
|
||||
.target = nflog_target,
|
||||
.targetsize = sizeof(struct xt_nflog_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "NFLOG",
|
||||
.family = AF_INET6,
|
||||
.checkentry = nflog_checkentry,
|
||||
.target = nflog_target,
|
||||
.targetsize = sizeof(struct xt_nflog_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_nflog_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_nflog_target,
|
||||
ARRAY_SIZE(xt_nflog_target));
|
||||
}
|
||||
|
||||
static void __exit xt_nflog_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_nflog_target, ARRAY_SIZE(xt_nflog_target));
|
||||
}
|
||||
|
||||
module_init(xt_nflog_init);
|
||||
module_exit(xt_nflog_fini);
|
||||
75
net/netfilter/xt_NFQUEUE.c
Normal file
75
net/netfilter/xt_NFQUEUE.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/* iptables module for using new netfilter netlink queue
|
||||
*
|
||||
* (C) 2005 by Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_arp.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_NFQUEUE.h>
|
||||
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("[ip,ip6,arp]_tables NFQUEUE target");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_NFQUEUE");
|
||||
MODULE_ALIAS("ip6t_NFQUEUE");
|
||||
MODULE_ALIAS("arpt_NFQUEUE");
|
||||
|
||||
static unsigned int
|
||||
target(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
const struct xt_NFQ_info *tinfo = targinfo;
|
||||
|
||||
return NF_QUEUE_NR(tinfo->queuenum);
|
||||
}
|
||||
|
||||
static struct xt_target xt_nfqueue_target[] = {
|
||||
{
|
||||
.name = "NFQUEUE",
|
||||
.family = AF_INET,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_NFQ_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "NFQUEUE",
|
||||
.family = AF_INET6,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_NFQ_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "NFQUEUE",
|
||||
.family = NF_ARP,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_NFQ_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_nfqueue_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_nfqueue_target,
|
||||
ARRAY_SIZE(xt_nfqueue_target));
|
||||
}
|
||||
|
||||
static void __exit xt_nfqueue_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_nfqueue_target, ARRAY_SIZE(xt_nfqueue_target));
|
||||
}
|
||||
|
||||
module_init(xt_nfqueue_init);
|
||||
module_exit(xt_nfqueue_fini);
|
||||
65
net/netfilter/xt_NOTRACK.c
Normal file
65
net/netfilter/xt_NOTRACK.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/* This is a module which is used for setting up fake conntracks
|
||||
* on packets so that they are not seen by the conntrack/NAT code.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_NOTRACK");
|
||||
|
||||
static unsigned int
|
||||
target(struct sk_buff **pskb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
/* Previously seen (loopback)? Ignore. */
|
||||
if ((*pskb)->nfct != NULL)
|
||||
return XT_CONTINUE;
|
||||
|
||||
/* Attach fake conntrack entry.
|
||||
If there is a real ct entry correspondig to this packet,
|
||||
it'll hang aroun till timing out. We don't deal with it
|
||||
for performance reasons. JK */
|
||||
nf_ct_untrack(*pskb);
|
||||
(*pskb)->nfctinfo = IP_CT_NEW;
|
||||
nf_conntrack_get((*pskb)->nfct);
|
||||
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static struct xt_target xt_notrack_target[] = {
|
||||
{
|
||||
.name = "NOTRACK",
|
||||
.family = AF_INET,
|
||||
.target = target,
|
||||
.table = "raw",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "NOTRACK",
|
||||
.family = AF_INET6,
|
||||
.target = target,
|
||||
.table = "raw",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_notrack_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_notrack_target,
|
||||
ARRAY_SIZE(xt_notrack_target));
|
||||
}
|
||||
|
||||
static void __exit xt_notrack_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_notrack_target, ARRAY_SIZE(xt_notrack_target));
|
||||
}
|
||||
|
||||
module_init(xt_notrack_init);
|
||||
module_exit(xt_notrack_fini);
|
||||
145
net/netfilter/xt_SECMARK.c
Normal file
145
net/netfilter/xt_SECMARK.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Module for modifying the secmark field of the skb, for use by
|
||||
* security subsystems.
|
||||
*
|
||||
* Based on the nfmark match by:
|
||||
* (C) 1999-2001 Marc Boucher <marc@mbsi.ca>
|
||||
*
|
||||
* (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/selinux.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_SECMARK.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("James Morris <jmorris@redhat.com>");
|
||||
MODULE_DESCRIPTION("ip[6]tables SECMARK modification module");
|
||||
MODULE_ALIAS("ipt_SECMARK");
|
||||
MODULE_ALIAS("ip6t_SECMARK");
|
||||
|
||||
#define PFX "SECMARK: "
|
||||
|
||||
static u8 mode;
|
||||
|
||||
static unsigned int target(struct sk_buff **pskb, const struct net_device *in,
|
||||
const struct net_device *out, unsigned int hooknum,
|
||||
const struct xt_target *target,
|
||||
const void *targinfo)
|
||||
{
|
||||
u32 secmark = 0;
|
||||
const struct xt_secmark_target_info *info = targinfo;
|
||||
|
||||
BUG_ON(info->mode != mode);
|
||||
|
||||
switch (mode) {
|
||||
case SECMARK_MODE_SEL:
|
||||
secmark = info->u.sel.selsid;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
(*pskb)->secmark = secmark;
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static int checkentry_selinux(struct xt_secmark_target_info *info)
|
||||
{
|
||||
int err;
|
||||
struct xt_secmark_target_selinux_info *sel = &info->u.sel;
|
||||
|
||||
sel->selctx[SECMARK_SELCTX_MAX - 1] = '\0';
|
||||
|
||||
err = selinux_string_to_sid(sel->selctx, &sel->selsid);
|
||||
if (err) {
|
||||
if (err == -EINVAL)
|
||||
printk(KERN_INFO PFX "invalid SELinux context \'%s\'\n",
|
||||
sel->selctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sel->selsid) {
|
||||
printk(KERN_INFO PFX "unable to map SELinux context \'%s\'\n",
|
||||
sel->selctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = selinux_relabel_packet_permission(sel->selsid);
|
||||
if (err) {
|
||||
printk(KERN_INFO PFX "unable to obtain relabeling permission\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int checkentry(const char *tablename, const void *entry,
|
||||
const struct xt_target *target, void *targinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_secmark_target_info *info = targinfo;
|
||||
|
||||
if (mode && mode != info->mode) {
|
||||
printk(KERN_INFO PFX "mode already set to %hu cannot mix with "
|
||||
"rules for mode %hu\n", mode, info->mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (info->mode) {
|
||||
case SECMARK_MODE_SEL:
|
||||
if (!checkentry_selinux(info))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mode)
|
||||
mode = info->mode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_target xt_secmark_target[] = {
|
||||
{
|
||||
.name = "SECMARK",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_secmark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "SECMARK",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.target = target,
|
||||
.targetsize = sizeof(struct xt_secmark_target_info),
|
||||
.table = "mangle",
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_secmark_init(void)
|
||||
{
|
||||
return xt_register_targets(xt_secmark_target,
|
||||
ARRAY_SIZE(xt_secmark_target));
|
||||
}
|
||||
|
||||
static void __exit xt_secmark_fini(void)
|
||||
{
|
||||
xt_unregister_targets(xt_secmark_target, ARRAY_SIZE(xt_secmark_target));
|
||||
}
|
||||
|
||||
module_init(xt_secmark_init);
|
||||
module_exit(xt_secmark_fini);
|
||||
61
net/netfilter/xt_comment.c
Normal file
61
net/netfilter/xt_comment.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Implements a dummy match to allow attaching comments to rules
|
||||
*
|
||||
* 2003-05-13 Brad Fisher (brad@info-link.net)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_comment.h>
|
||||
|
||||
MODULE_AUTHOR("Brad Fisher <brad@info-link.net>");
|
||||
MODULE_DESCRIPTION("iptables comment match module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_comment");
|
||||
MODULE_ALIAS("ip6t_comment");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protooff,
|
||||
int *hotdrop)
|
||||
{
|
||||
/* We always match */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_comment_match[] = {
|
||||
{
|
||||
.name = "comment",
|
||||
.family = AF_INET,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_comment_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "comment",
|
||||
.family = AF_INET6,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_comment_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_comment_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_comment_match,
|
||||
ARRAY_SIZE(xt_comment_match));
|
||||
}
|
||||
|
||||
static void __exit xt_comment_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_comment_match, ARRAY_SIZE(xt_comment_match));
|
||||
}
|
||||
|
||||
module_init(xt_comment_init);
|
||||
module_exit(xt_comment_fini);
|
||||
170
net/netfilter/xt_connbytes.c
Normal file
170
net/netfilter/xt_connbytes.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/* Kernel module to match connection tracking byte counter.
|
||||
* GPL (C) 2002 Martin Devera (devik@cdi.cz).
|
||||
*
|
||||
* 2004-07-20 Harald Welte <laforge@netfilter.org>
|
||||
* - reimplemented to use per-connection accounting counters
|
||||
* - add functionality to match number of packets
|
||||
* - add functionality to match average packet size
|
||||
* - add support to match directions seperately
|
||||
* 2005-10-16 Harald Welte <laforge@netfilter.org>
|
||||
* - Port to x_tables
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_connbytes.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
#include <asm/bitops.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("iptables match for matching number of pkts/bytes per connection");
|
||||
MODULE_ALIAS("ipt_connbytes");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_connbytes_info *sinfo = matchinfo;
|
||||
u_int64_t what = 0; /* initialize to make gcc happy */
|
||||
u_int64_t bytes = 0;
|
||||
u_int64_t pkts = 0;
|
||||
const struct ip_conntrack_counter *counters;
|
||||
|
||||
if (!(counters = nf_ct_get_counters(skb)))
|
||||
return 0; /* no match */
|
||||
|
||||
switch (sinfo->what) {
|
||||
case XT_CONNBYTES_PKTS:
|
||||
switch (sinfo->direction) {
|
||||
case XT_CONNBYTES_DIR_ORIGINAL:
|
||||
what = counters[IP_CT_DIR_ORIGINAL].packets;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_REPLY:
|
||||
what = counters[IP_CT_DIR_REPLY].packets;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_BOTH:
|
||||
what = counters[IP_CT_DIR_ORIGINAL].packets;
|
||||
what += counters[IP_CT_DIR_REPLY].packets;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XT_CONNBYTES_BYTES:
|
||||
switch (sinfo->direction) {
|
||||
case XT_CONNBYTES_DIR_ORIGINAL:
|
||||
what = counters[IP_CT_DIR_ORIGINAL].bytes;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_REPLY:
|
||||
what = counters[IP_CT_DIR_REPLY].bytes;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_BOTH:
|
||||
what = counters[IP_CT_DIR_ORIGINAL].bytes;
|
||||
what += counters[IP_CT_DIR_REPLY].bytes;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XT_CONNBYTES_AVGPKT:
|
||||
switch (sinfo->direction) {
|
||||
case XT_CONNBYTES_DIR_ORIGINAL:
|
||||
bytes = counters[IP_CT_DIR_ORIGINAL].bytes;
|
||||
pkts = counters[IP_CT_DIR_ORIGINAL].packets;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_REPLY:
|
||||
bytes = counters[IP_CT_DIR_REPLY].bytes;
|
||||
pkts = counters[IP_CT_DIR_REPLY].packets;
|
||||
break;
|
||||
case XT_CONNBYTES_DIR_BOTH:
|
||||
bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
|
||||
counters[IP_CT_DIR_REPLY].bytes;
|
||||
pkts = counters[IP_CT_DIR_ORIGINAL].packets +
|
||||
counters[IP_CT_DIR_REPLY].packets;
|
||||
break;
|
||||
}
|
||||
if (pkts != 0)
|
||||
what = div64_64(bytes, pkts);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sinfo->count.to)
|
||||
return (what <= sinfo->count.to && what >= sinfo->count.from);
|
||||
else
|
||||
return (what >= sinfo->count.from);
|
||||
}
|
||||
|
||||
static int check(const char *tablename,
|
||||
const void *ip,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_connbytes_info *sinfo = matchinfo;
|
||||
|
||||
if (sinfo->what != XT_CONNBYTES_PKTS &&
|
||||
sinfo->what != XT_CONNBYTES_BYTES &&
|
||||
sinfo->what != XT_CONNBYTES_AVGPKT)
|
||||
return 0;
|
||||
|
||||
if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
|
||||
sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
|
||||
sinfo->direction != XT_CONNBYTES_DIR_BOTH)
|
||||
return 0;
|
||||
|
||||
if (nf_ct_l3proto_try_module_get(match->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", match->family);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(match->family);
|
||||
}
|
||||
|
||||
static struct xt_match xt_connbytes_match[] = {
|
||||
{
|
||||
.name = "connbytes",
|
||||
.family = AF_INET,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_connbytes_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "connbytes",
|
||||
.family = AF_INET6,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_connbytes_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_connbytes_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_connbytes_match,
|
||||
ARRAY_SIZE(xt_connbytes_match));
|
||||
}
|
||||
|
||||
static void __exit xt_connbytes_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_connbytes_match,
|
||||
ARRAY_SIZE(xt_connbytes_match));
|
||||
}
|
||||
|
||||
module_init(xt_connbytes_init);
|
||||
module_exit(xt_connbytes_fini);
|
||||
266
net/netfilter/xt_conntrack.c
Normal file
266
net/netfilter/xt_conntrack.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/* Kernel module to match connection tracking information.
|
||||
* Superset of Rusty's minimalistic state match.
|
||||
*
|
||||
* (C) 2001 Marc Boucher (marc@mbsi.ca).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
#include <linux/netfilter_ipv4/ip_conntrack.h>
|
||||
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
|
||||
#else
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#endif
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
|
||||
MODULE_DESCRIPTION("iptables connection tracking match module");
|
||||
MODULE_ALIAS("ipt_conntrack");
|
||||
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_conntrack_info *sinfo = matchinfo;
|
||||
struct ip_conntrack *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
unsigned int statebit;
|
||||
|
||||
ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
|
||||
|
||||
#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg))
|
||||
|
||||
if (ct == &ip_conntrack_untracked)
|
||||
statebit = XT_CONNTRACK_STATE_UNTRACKED;
|
||||
else if (ct)
|
||||
statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
|
||||
else
|
||||
statebit = XT_CONNTRACK_STATE_INVALID;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_STATE) {
|
||||
if (ct) {
|
||||
if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
|
||||
statebit |= XT_CONNTRACK_STATE_SNAT;
|
||||
if (test_bit(IPS_DST_NAT_BIT, &ct->status))
|
||||
statebit |= XT_CONNTRACK_STATE_DNAT;
|
||||
}
|
||||
if (FWINV((statebit & sinfo->statemask) == 0,
|
||||
XT_CONNTRACK_STATE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ct == NULL) {
|
||||
if (sinfo->flags & ~XT_CONNTRACK_STATE)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_PROTO &&
|
||||
FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
|
||||
XT_CONNTRACK_PROTO))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip &
|
||||
sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
|
||||
XT_CONNTRACK_ORIGSRC))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip &
|
||||
sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
|
||||
XT_CONNTRACK_ORIGDST))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip &
|
||||
sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
|
||||
XT_CONNTRACK_REPLSRC))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_REPLDST &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip &
|
||||
sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
|
||||
XT_CONNTRACK_REPLDST))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_STATUS &&
|
||||
FWINV((ct->status & sinfo->statusmask) == 0,
|
||||
XT_CONNTRACK_STATUS))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_EXPIRES) {
|
||||
unsigned long expires = timer_pending(&ct->timeout) ?
|
||||
(ct->timeout.expires - jiffies)/HZ : 0;
|
||||
|
||||
if (FWINV(!(expires >= sinfo->expires_min &&
|
||||
expires <= sinfo->expires_max),
|
||||
XT_CONNTRACK_EXPIRES))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else /* CONFIG_IP_NF_CONNTRACK */
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_conntrack_info *sinfo = matchinfo;
|
||||
struct nf_conn *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
unsigned int statebit;
|
||||
|
||||
ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
|
||||
|
||||
#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg))
|
||||
|
||||
if (ct == &nf_conntrack_untracked)
|
||||
statebit = XT_CONNTRACK_STATE_UNTRACKED;
|
||||
else if (ct)
|
||||
statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
|
||||
else
|
||||
statebit = XT_CONNTRACK_STATE_INVALID;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_STATE) {
|
||||
if (ct) {
|
||||
if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
|
||||
statebit |= XT_CONNTRACK_STATE_SNAT;
|
||||
if (test_bit(IPS_DST_NAT_BIT, &ct->status))
|
||||
statebit |= XT_CONNTRACK_STATE_DNAT;
|
||||
}
|
||||
if (FWINV((statebit & sinfo->statemask) == 0,
|
||||
XT_CONNTRACK_STATE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ct == NULL) {
|
||||
if (sinfo->flags & ~XT_CONNTRACK_STATE)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_PROTO &&
|
||||
FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
|
||||
XT_CONNTRACK_PROTO))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip &
|
||||
sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
|
||||
XT_CONNTRACK_ORIGSRC))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip &
|
||||
sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
|
||||
XT_CONNTRACK_ORIGDST))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip &
|
||||
sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
|
||||
XT_CONNTRACK_REPLSRC))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_REPLDST &&
|
||||
FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip &
|
||||
sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
|
||||
sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
|
||||
XT_CONNTRACK_REPLDST))
|
||||
return 0;
|
||||
|
||||
if (sinfo->flags & XT_CONNTRACK_STATUS &&
|
||||
FWINV((ct->status & sinfo->statusmask) == 0,
|
||||
XT_CONNTRACK_STATUS))
|
||||
return 0;
|
||||
|
||||
if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
|
||||
unsigned long expires = timer_pending(&ct->timeout) ?
|
||||
(ct->timeout.expires - jiffies)/HZ : 0;
|
||||
|
||||
if (FWINV(!(expires >= sinfo->expires_min &&
|
||||
expires <= sinfo->expires_max),
|
||||
XT_CONNTRACK_EXPIRES))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NF_IP_CONNTRACK */
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *ip,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
if (nf_ct_l3proto_try_module_get(match->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", match->family);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(match->family);
|
||||
}
|
||||
|
||||
static struct xt_match conntrack_match = {
|
||||
.name = "conntrack",
|
||||
.match = match,
|
||||
.checkentry = checkentry,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_conntrack_info),
|
||||
.family = AF_INET,
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init xt_conntrack_init(void)
|
||||
{
|
||||
return xt_register_match(&conntrack_match);
|
||||
}
|
||||
|
||||
static void __exit xt_conntrack_fini(void)
|
||||
{
|
||||
xt_unregister_match(&conntrack_match);
|
||||
}
|
||||
|
||||
module_init(xt_conntrack_init);
|
||||
module_exit(xt_conntrack_fini);
|
||||
191
net/netfilter/xt_dccp.c
Normal file
191
net/netfilter/xt_dccp.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* iptables module for DCCP protocol header matching
|
||||
*
|
||||
* (C) 2005 by Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/dccp.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_dccp.h>
|
||||
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("Match for DCCP protocol packets");
|
||||
MODULE_ALIAS("ipt_dccp");
|
||||
|
||||
#define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
|
||||
|| (!!((invflag) & (option)) ^ (cond)))
|
||||
|
||||
static unsigned char *dccp_optbuf;
|
||||
static DEFINE_SPINLOCK(dccp_buflock);
|
||||
|
||||
static inline int
|
||||
dccp_find_option(u_int8_t option,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int protoff,
|
||||
const struct dccp_hdr *dh,
|
||||
int *hotdrop)
|
||||
{
|
||||
/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
|
||||
unsigned char *op;
|
||||
unsigned int optoff = __dccp_hdr_len(dh);
|
||||
unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
|
||||
unsigned int i;
|
||||
|
||||
if (dh->dccph_doff * 4 < __dccp_hdr_len(dh)) {
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!optlen)
|
||||
return 0;
|
||||
|
||||
spin_lock_bh(&dccp_buflock);
|
||||
op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
|
||||
if (op == NULL) {
|
||||
/* If we don't have the whole header, drop packet. */
|
||||
spin_unlock_bh(&dccp_buflock);
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < optlen; ) {
|
||||
if (op[i] == option) {
|
||||
spin_unlock_bh(&dccp_buflock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (op[i] < 2)
|
||||
i++;
|
||||
else
|
||||
i += op[i+1]?:1;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&dccp_buflock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
match_types(const struct dccp_hdr *dh, u_int16_t typemask)
|
||||
{
|
||||
return (typemask & (1 << dh->dccph_type));
|
||||
}
|
||||
|
||||
static inline int
|
||||
match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
|
||||
const struct dccp_hdr *dh, int *hotdrop)
|
||||
{
|
||||
return dccp_find_option(option, skb, protoff, dh, hotdrop);
|
||||
}
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_dccp_info *info = matchinfo;
|
||||
struct dccp_hdr _dh, *dh;
|
||||
|
||||
if (offset)
|
||||
return 0;
|
||||
|
||||
dh = skb_header_pointer(skb, protoff, sizeof(_dh), &_dh);
|
||||
if (dh == NULL) {
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DCCHECK(((ntohs(dh->dccph_sport) >= info->spts[0])
|
||||
&& (ntohs(dh->dccph_sport) <= info->spts[1])),
|
||||
XT_DCCP_SRC_PORTS, info->flags, info->invflags)
|
||||
&& DCCHECK(((ntohs(dh->dccph_dport) >= info->dpts[0])
|
||||
&& (ntohs(dh->dccph_dport) <= info->dpts[1])),
|
||||
XT_DCCP_DEST_PORTS, info->flags, info->invflags)
|
||||
&& DCCHECK(match_types(dh, info->typemask),
|
||||
XT_DCCP_TYPE, info->flags, info->invflags)
|
||||
&& DCCHECK(match_option(info->option, skb, protoff, dh,
|
||||
hotdrop),
|
||||
XT_DCCP_OPTION, info->flags, info->invflags);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_dccp_info *info = matchinfo;
|
||||
|
||||
return !(info->flags & ~XT_DCCP_VALID_FLAGS)
|
||||
&& !(info->invflags & ~XT_DCCP_VALID_FLAGS)
|
||||
&& !(info->invflags & ~info->flags);
|
||||
}
|
||||
|
||||
static struct xt_match xt_dccp_match[] = {
|
||||
{
|
||||
.name = "dccp",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_dccp_info),
|
||||
.proto = IPPROTO_DCCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "dccp",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_dccp_info),
|
||||
.proto = IPPROTO_DCCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_dccp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* doff is 8 bits, so the maximum option size is (4*256). Don't put
|
||||
* this in BSS since DaveM is worried about locked TLB's for kernel
|
||||
* BSS. */
|
||||
dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
|
||||
if (!dccp_optbuf)
|
||||
return -ENOMEM;
|
||||
ret = xt_register_matches(xt_dccp_match, ARRAY_SIZE(xt_dccp_match));
|
||||
if (ret)
|
||||
goto out_kfree;
|
||||
return ret;
|
||||
|
||||
out_kfree:
|
||||
kfree(dccp_optbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit xt_dccp_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_dccp_match, ARRAY_SIZE(xt_dccp_match));
|
||||
kfree(dccp_optbuf);
|
||||
}
|
||||
|
||||
module_init(xt_dccp_init);
|
||||
module_exit(xt_dccp_fini);
|
||||
126
net/netfilter/xt_esp.c
Normal file
126
net/netfilter/xt_esp.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/* Kernel module to match ESP parameters. */
|
||||
|
||||
/* (C) 1999-2000 Yon Uriarte <yon@astaro.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <linux/netfilter/xt_esp.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Yon Uriarte <yon@astaro.de>");
|
||||
MODULE_DESCRIPTION("x_tables ESP SPI match module");
|
||||
MODULE_ALIAS("ipt_esp");
|
||||
MODULE_ALIAS("ip6t_esp");
|
||||
|
||||
#if 0
|
||||
#define duprintf(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define duprintf(format, args...)
|
||||
#endif
|
||||
|
||||
/* Returns 1 if the spi is matched by the range, 0 otherwise */
|
||||
static inline int
|
||||
spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
|
||||
{
|
||||
int r = 0;
|
||||
duprintf("esp spi_match:%c 0x%x <= 0x%x <= 0x%x", invert ? '!' : ' ',
|
||||
min, spi, max);
|
||||
r = (spi >= min && spi <= max) ^ invert;
|
||||
duprintf(" result %s\n", r ? "PASS" : "FAILED");
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
struct ip_esp_hdr _esp, *eh;
|
||||
const struct xt_esp *espinfo = matchinfo;
|
||||
|
||||
/* Must not be a fragment. */
|
||||
if (offset)
|
||||
return 0;
|
||||
|
||||
eh = skb_header_pointer(skb, protoff, sizeof(_esp), &_esp);
|
||||
if (eh == NULL) {
|
||||
/* We've been asked to examine this packet, and we
|
||||
* can't. Hence, no choice but to drop.
|
||||
*/
|
||||
duprintf("Dropping evil ESP tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return spi_match(espinfo->spis[0], espinfo->spis[1], ntohl(eh->spi),
|
||||
!!(espinfo->invflags & XT_ESP_INV_SPI));
|
||||
}
|
||||
|
||||
/* Called when user tries to insert an entry of this type. */
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *ip_void,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_esp *espinfo = matchinfo;
|
||||
|
||||
if (espinfo->invflags & ~XT_ESP_INV_MASK) {
|
||||
duprintf("xt_esp: unknown flags %X\n", espinfo->invflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_esp_match[] = {
|
||||
{
|
||||
.name = "esp",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_esp),
|
||||
.proto = IPPROTO_ESP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "esp",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_esp),
|
||||
.proto = IPPROTO_ESP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_esp_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_esp_match, ARRAY_SIZE(xt_esp_match));
|
||||
}
|
||||
|
||||
static void __exit xt_esp_cleanup(void)
|
||||
{
|
||||
xt_unregister_matches(xt_esp_match, ARRAY_SIZE(xt_esp_match));
|
||||
}
|
||||
|
||||
module_init(xt_esp_init);
|
||||
module_exit(xt_esp_cleanup);
|
||||
774
net/netfilter/xt_hashlimit.c
Normal file
774
net/netfilter/xt_hashlimit.c
Normal file
@@ -0,0 +1,774 @@
|
||||
/* iptables match extension to limit the number of packets per second
|
||||
* seperately for each hashbucket (sourceip/sourceport/dstip/dstport)
|
||||
*
|
||||
* (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* $Id: xt_hashlimit.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu Exp $
|
||||
*
|
||||
* Development of this code was funded by Astaro AG, http://www.astaro.com/
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
#include <linux/netfilter/xt_hashlimit.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
|
||||
MODULE_DESCRIPTION("iptables match for limiting per hash-bucket");
|
||||
MODULE_ALIAS("ipt_hashlimit");
|
||||
MODULE_ALIAS("ip6t_hashlimit");
|
||||
|
||||
/* need to declare this at the top */
|
||||
static struct proc_dir_entry *hashlimit_procdir4;
|
||||
static struct proc_dir_entry *hashlimit_procdir6;
|
||||
static const struct file_operations dl_file_ops;
|
||||
|
||||
/* hash table crap */
|
||||
struct dsthash_dst {
|
||||
union {
|
||||
struct {
|
||||
__be32 src;
|
||||
__be32 dst;
|
||||
} ip;
|
||||
struct {
|
||||
__be32 src[4];
|
||||
__be32 dst[4];
|
||||
} ip6;
|
||||
} addr;
|
||||
__be16 src_port;
|
||||
__be16 dst_port;
|
||||
};
|
||||
|
||||
struct dsthash_ent {
|
||||
/* static / read-only parts in the beginning */
|
||||
struct hlist_node node;
|
||||
struct dsthash_dst dst;
|
||||
|
||||
/* modified structure members in the end */
|
||||
unsigned long expires; /* precalculated expiry time */
|
||||
struct {
|
||||
unsigned long prev; /* last modification */
|
||||
u_int32_t credit;
|
||||
u_int32_t credit_cap, cost;
|
||||
} rateinfo;
|
||||
};
|
||||
|
||||
struct xt_hashlimit_htable {
|
||||
struct hlist_node node; /* global list of all htables */
|
||||
atomic_t use;
|
||||
int family;
|
||||
|
||||
struct hashlimit_cfg cfg; /* config */
|
||||
|
||||
/* used internally */
|
||||
spinlock_t lock; /* lock for list_head */
|
||||
u_int32_t rnd; /* random seed for hash */
|
||||
int rnd_initialized;
|
||||
unsigned int count; /* number entries in table */
|
||||
struct timer_list timer; /* timer for gc */
|
||||
|
||||
/* seq_file stuff */
|
||||
struct proc_dir_entry *pde;
|
||||
|
||||
struct hlist_head hash[0]; /* hashtable itself */
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */
|
||||
static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */
|
||||
static HLIST_HEAD(hashlimit_htables);
|
||||
static struct kmem_cache *hashlimit_cachep __read_mostly;
|
||||
|
||||
static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b)
|
||||
{
|
||||
return !memcmp(&ent->dst, b, sizeof(ent->dst));
|
||||
}
|
||||
|
||||
static u_int32_t
|
||||
hash_dst(const struct xt_hashlimit_htable *ht, const struct dsthash_dst *dst)
|
||||
{
|
||||
return jhash(dst, sizeof(*dst), ht->rnd) % ht->cfg.size;
|
||||
}
|
||||
|
||||
static struct dsthash_ent *
|
||||
dsthash_find(const struct xt_hashlimit_htable *ht, struct dsthash_dst *dst)
|
||||
{
|
||||
struct dsthash_ent *ent;
|
||||
struct hlist_node *pos;
|
||||
u_int32_t hash = hash_dst(ht, dst);
|
||||
|
||||
if (!hlist_empty(&ht->hash[hash])) {
|
||||
hlist_for_each_entry(ent, pos, &ht->hash[hash], node)
|
||||
if (dst_cmp(ent, dst))
|
||||
return ent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate dsthash_ent, initialize dst, put in htable and lock it */
|
||||
static struct dsthash_ent *
|
||||
dsthash_alloc_init(struct xt_hashlimit_htable *ht, struct dsthash_dst *dst)
|
||||
{
|
||||
struct dsthash_ent *ent;
|
||||
|
||||
/* initialize hash with random val at the time we allocate
|
||||
* the first hashtable entry */
|
||||
if (!ht->rnd_initialized) {
|
||||
get_random_bytes(&ht->rnd, 4);
|
||||
ht->rnd_initialized = 1;
|
||||
}
|
||||
|
||||
if (ht->cfg.max && ht->count >= ht->cfg.max) {
|
||||
/* FIXME: do something. question is what.. */
|
||||
if (net_ratelimit())
|
||||
printk(KERN_WARNING
|
||||
"xt_hashlimit: max count of %u reached\n",
|
||||
ht->cfg.max);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC);
|
||||
if (!ent) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_ERR
|
||||
"xt_hashlimit: can't allocate dsthash_ent\n");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&ent->dst, dst, sizeof(ent->dst));
|
||||
|
||||
hlist_add_head(&ent->node, &ht->hash[hash_dst(ht, dst)]);
|
||||
ht->count++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
static inline void
|
||||
dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent)
|
||||
{
|
||||
hlist_del(&ent->node);
|
||||
kmem_cache_free(hashlimit_cachep, ent);
|
||||
ht->count--;
|
||||
}
|
||||
static void htable_gc(unsigned long htlong);
|
||||
|
||||
static int htable_create(struct xt_hashlimit_info *minfo, int family)
|
||||
{
|
||||
struct xt_hashlimit_htable *hinfo;
|
||||
unsigned int size;
|
||||
unsigned int i;
|
||||
|
||||
if (minfo->cfg.size)
|
||||
size = minfo->cfg.size;
|
||||
else {
|
||||
size = ((num_physpages << PAGE_SHIFT) / 16384) /
|
||||
sizeof(struct list_head);
|
||||
if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
|
||||
size = 8192;
|
||||
if (size < 16)
|
||||
size = 16;
|
||||
}
|
||||
/* FIXME: don't use vmalloc() here or anywhere else -HW */
|
||||
hinfo = vmalloc(sizeof(struct xt_hashlimit_htable) +
|
||||
sizeof(struct list_head) * size);
|
||||
if (!hinfo) {
|
||||
printk(KERN_ERR "xt_hashlimit: unable to create hashtable\n");
|
||||
return -1;
|
||||
}
|
||||
minfo->hinfo = hinfo;
|
||||
|
||||
/* copy match config into hashtable config */
|
||||
memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg));
|
||||
hinfo->cfg.size = size;
|
||||
if (!hinfo->cfg.max)
|
||||
hinfo->cfg.max = 8 * hinfo->cfg.size;
|
||||
else if (hinfo->cfg.max < hinfo->cfg.size)
|
||||
hinfo->cfg.max = hinfo->cfg.size;
|
||||
|
||||
for (i = 0; i < hinfo->cfg.size; i++)
|
||||
INIT_HLIST_HEAD(&hinfo->hash[i]);
|
||||
|
||||
atomic_set(&hinfo->use, 1);
|
||||
hinfo->count = 0;
|
||||
hinfo->family = family;
|
||||
hinfo->rnd_initialized = 0;
|
||||
spin_lock_init(&hinfo->lock);
|
||||
hinfo->pde = create_proc_entry(minfo->name, 0,
|
||||
family == AF_INET ? hashlimit_procdir4 :
|
||||
hashlimit_procdir6);
|
||||
if (!hinfo->pde) {
|
||||
vfree(hinfo);
|
||||
return -1;
|
||||
}
|
||||
hinfo->pde->proc_fops = &dl_file_ops;
|
||||
hinfo->pde->data = hinfo;
|
||||
|
||||
init_timer(&hinfo->timer);
|
||||
hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
|
||||
hinfo->timer.data = (unsigned long )hinfo;
|
||||
hinfo->timer.function = htable_gc;
|
||||
add_timer(&hinfo->timer);
|
||||
|
||||
spin_lock_bh(&hashlimit_lock);
|
||||
hlist_add_head(&hinfo->node, &hashlimit_htables);
|
||||
spin_unlock_bh(&hashlimit_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int select_all(struct xt_hashlimit_htable *ht, struct dsthash_ent *he)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int select_gc(struct xt_hashlimit_htable *ht, struct dsthash_ent *he)
|
||||
{
|
||||
return (jiffies >= he->expires);
|
||||
}
|
||||
|
||||
static void htable_selective_cleanup(struct xt_hashlimit_htable *ht,
|
||||
int (*select)(struct xt_hashlimit_htable *ht,
|
||||
struct dsthash_ent *he))
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* lock hash table and iterate over it */
|
||||
spin_lock_bh(&ht->lock);
|
||||
for (i = 0; i < ht->cfg.size; i++) {
|
||||
struct dsthash_ent *dh;
|
||||
struct hlist_node *pos, *n;
|
||||
hlist_for_each_entry_safe(dh, pos, n, &ht->hash[i], node) {
|
||||
if ((*select)(ht, dh))
|
||||
dsthash_free(ht, dh);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&ht->lock);
|
||||
}
|
||||
|
||||
/* hash table garbage collector, run by timer */
|
||||
static void htable_gc(unsigned long htlong)
|
||||
{
|
||||
struct xt_hashlimit_htable *ht = (struct xt_hashlimit_htable *)htlong;
|
||||
|
||||
htable_selective_cleanup(ht, select_gc);
|
||||
|
||||
/* re-add the timer accordingly */
|
||||
ht->timer.expires = jiffies + msecs_to_jiffies(ht->cfg.gc_interval);
|
||||
add_timer(&ht->timer);
|
||||
}
|
||||
|
||||
static void htable_destroy(struct xt_hashlimit_htable *hinfo)
|
||||
{
|
||||
/* remove timer, if it is pending */
|
||||
if (timer_pending(&hinfo->timer))
|
||||
del_timer(&hinfo->timer);
|
||||
|
||||
/* remove proc entry */
|
||||
remove_proc_entry(hinfo->pde->name,
|
||||
hinfo->family == AF_INET ? hashlimit_procdir4 :
|
||||
hashlimit_procdir6);
|
||||
htable_selective_cleanup(hinfo, select_all);
|
||||
vfree(hinfo);
|
||||
}
|
||||
|
||||
static struct xt_hashlimit_htable *htable_find_get(char *name, int family)
|
||||
{
|
||||
struct xt_hashlimit_htable *hinfo;
|
||||
struct hlist_node *pos;
|
||||
|
||||
spin_lock_bh(&hashlimit_lock);
|
||||
hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) {
|
||||
if (!strcmp(name, hinfo->pde->name) &&
|
||||
hinfo->family == family) {
|
||||
atomic_inc(&hinfo->use);
|
||||
spin_unlock_bh(&hashlimit_lock);
|
||||
return hinfo;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&hashlimit_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void htable_put(struct xt_hashlimit_htable *hinfo)
|
||||
{
|
||||
if (atomic_dec_and_test(&hinfo->use)) {
|
||||
spin_lock_bh(&hashlimit_lock);
|
||||
hlist_del(&hinfo->node);
|
||||
spin_unlock_bh(&hashlimit_lock);
|
||||
htable_destroy(hinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/* The algorithm used is the Simple Token Bucket Filter (TBF)
|
||||
* see net/sched/sch_tbf.c in the linux source tree
|
||||
*/
|
||||
|
||||
/* Rusty: This is my (non-mathematically-inclined) understanding of
|
||||
this algorithm. The `average rate' in jiffies becomes your initial
|
||||
amount of credit `credit' and the most credit you can ever have
|
||||
`credit_cap'. The `peak rate' becomes the cost of passing the
|
||||
test, `cost'.
|
||||
|
||||
`prev' tracks the last packet hit: you gain one credit per jiffy.
|
||||
If you get credit balance more than this, the extra credit is
|
||||
discarded. Every time the match passes, you lose `cost' credits;
|
||||
if you don't have that many, the test fails.
|
||||
|
||||
See Alexey's formal explanation in net/sched/sch_tbf.c.
|
||||
|
||||
To get the maximum range, we multiply by this factor (ie. you get N
|
||||
credits per jiffy). We want to allow a rate as low as 1 per day
|
||||
(slowest userspace tool allows), which means
|
||||
CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32 ie.
|
||||
*/
|
||||
#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))
|
||||
|
||||
/* Repeated shift and or gives us all 1s, final shift and add 1 gives
|
||||
* us the power of 2 below the theoretical max, so GCC simply does a
|
||||
* shift. */
|
||||
#define _POW2_BELOW2(x) ((x)|((x)>>1))
|
||||
#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))
|
||||
#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))
|
||||
#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))
|
||||
#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))
|
||||
#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)
|
||||
|
||||
#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
|
||||
|
||||
/* Precision saver. */
|
||||
static inline u_int32_t
|
||||
user2credits(u_int32_t user)
|
||||
{
|
||||
/* If multiplying would overflow... */
|
||||
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
||||
/* Divide first. */
|
||||
return (user / XT_HASHLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
|
||||
|
||||
return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE;
|
||||
}
|
||||
|
||||
static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now)
|
||||
{
|
||||
dh->rateinfo.credit += (now - dh->rateinfo.prev) * CREDITS_PER_JIFFY;
|
||||
if (dh->rateinfo.credit > dh->rateinfo.credit_cap)
|
||||
dh->rateinfo.credit = dh->rateinfo.credit_cap;
|
||||
dh->rateinfo.prev = now;
|
||||
}
|
||||
|
||||
static int
|
||||
hashlimit_init_dst(struct xt_hashlimit_htable *hinfo, struct dsthash_dst *dst,
|
||||
const struct sk_buff *skb, unsigned int protoff)
|
||||
{
|
||||
__be16 _ports[2], *ports;
|
||||
int nexthdr;
|
||||
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
|
||||
switch (hinfo->family) {
|
||||
case AF_INET:
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP)
|
||||
dst->addr.ip.dst = skb->nh.iph->daddr;
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP)
|
||||
dst->addr.ip.src = skb->nh.iph->saddr;
|
||||
|
||||
if (!(hinfo->cfg.mode &
|
||||
(XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT)))
|
||||
return 0;
|
||||
nexthdr = skb->nh.iph->protocol;
|
||||
break;
|
||||
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
|
||||
case AF_INET6:
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DIP)
|
||||
memcpy(&dst->addr.ip6.dst, &skb->nh.ipv6h->daddr,
|
||||
sizeof(dst->addr.ip6.dst));
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SIP)
|
||||
memcpy(&dst->addr.ip6.src, &skb->nh.ipv6h->saddr,
|
||||
sizeof(dst->addr.ip6.src));
|
||||
|
||||
if (!(hinfo->cfg.mode &
|
||||
(XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT)))
|
||||
return 0;
|
||||
nexthdr = ipv6_find_hdr(skb, &protoff, -1, NULL);
|
||||
if (nexthdr < 0)
|
||||
return -1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (nexthdr) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
case IPPROTO_UDPLITE:
|
||||
case IPPROTO_SCTP:
|
||||
case IPPROTO_DCCP:
|
||||
ports = skb_header_pointer(skb, protoff, sizeof(_ports),
|
||||
&_ports);
|
||||
break;
|
||||
default:
|
||||
_ports[0] = _ports[1] = 0;
|
||||
ports = _ports;
|
||||
break;
|
||||
}
|
||||
if (!ports)
|
||||
return -1;
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_SPT)
|
||||
dst->src_port = ports[0];
|
||||
if (hinfo->cfg.mode & XT_HASHLIMIT_HASH_DPT)
|
||||
dst->dst_port = ports[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hashlimit_match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
struct xt_hashlimit_info *r =
|
||||
((struct xt_hashlimit_info *)matchinfo)->u.master;
|
||||
struct xt_hashlimit_htable *hinfo = r->hinfo;
|
||||
unsigned long now = jiffies;
|
||||
struct dsthash_ent *dh;
|
||||
struct dsthash_dst dst;
|
||||
|
||||
if (hashlimit_init_dst(hinfo, &dst, skb, protoff) < 0)
|
||||
goto hotdrop;
|
||||
|
||||
spin_lock_bh(&hinfo->lock);
|
||||
dh = dsthash_find(hinfo, &dst);
|
||||
if (!dh) {
|
||||
dh = dsthash_alloc_init(hinfo, &dst);
|
||||
if (!dh) {
|
||||
spin_unlock_bh(&hinfo->lock);
|
||||
goto hotdrop;
|
||||
}
|
||||
|
||||
dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
dh->rateinfo.prev = jiffies;
|
||||
dh->rateinfo.credit = user2credits(hinfo->cfg.avg *
|
||||
hinfo->cfg.burst);
|
||||
dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg *
|
||||
hinfo->cfg.burst);
|
||||
dh->rateinfo.cost = user2credits(hinfo->cfg.avg);
|
||||
} else {
|
||||
/* update expiration timeout */
|
||||
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
|
||||
rateinfo_recalc(dh, now);
|
||||
}
|
||||
|
||||
if (dh->rateinfo.credit >= dh->rateinfo.cost) {
|
||||
/* We're underlimit. */
|
||||
dh->rateinfo.credit -= dh->rateinfo.cost;
|
||||
spin_unlock_bh(&hinfo->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&hinfo->lock);
|
||||
|
||||
/* default case: we're overlimit, thus don't match */
|
||||
return 0;
|
||||
|
||||
hotdrop:
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hashlimit_checkentry(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_hashlimit_info *r = matchinfo;
|
||||
|
||||
/* Check for overflow. */
|
||||
if (r->cfg.burst == 0 ||
|
||||
user2credits(r->cfg.avg * r->cfg.burst) < user2credits(r->cfg.avg)) {
|
||||
printk(KERN_ERR "xt_hashlimit: overflow, try lower: %u/%u\n",
|
||||
r->cfg.avg, r->cfg.burst);
|
||||
return 0;
|
||||
}
|
||||
if (r->cfg.mode == 0 ||
|
||||
r->cfg.mode > (XT_HASHLIMIT_HASH_DPT |
|
||||
XT_HASHLIMIT_HASH_DIP |
|
||||
XT_HASHLIMIT_HASH_SIP |
|
||||
XT_HASHLIMIT_HASH_SPT))
|
||||
return 0;
|
||||
if (!r->cfg.gc_interval)
|
||||
return 0;
|
||||
if (!r->cfg.expire)
|
||||
return 0;
|
||||
if (r->name[sizeof(r->name) - 1] != '\0')
|
||||
return 0;
|
||||
|
||||
/* This is the best we've got: We cannot release and re-grab lock,
|
||||
* since checkentry() is called before x_tables.c grabs xt_mutex.
|
||||
* We also cannot grab the hashtable spinlock, since htable_create will
|
||||
* call vmalloc, and that can sleep. And we cannot just re-search
|
||||
* the list of htable's in htable_create(), since then we would
|
||||
* create duplicate proc files. -HW */
|
||||
mutex_lock(&hlimit_mutex);
|
||||
r->hinfo = htable_find_get(r->name, match->family);
|
||||
if (!r->hinfo && htable_create(r, match->family) != 0) {
|
||||
mutex_unlock(&hlimit_mutex);
|
||||
return 0;
|
||||
}
|
||||
mutex_unlock(&hlimit_mutex);
|
||||
|
||||
/* Ugly hack: For SMP, we only want to use one set */
|
||||
r->u.master = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
hashlimit_destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
struct xt_hashlimit_info *r = matchinfo;
|
||||
|
||||
htable_put(r->hinfo);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_xt_hashlimit_info {
|
||||
char name[IFNAMSIZ];
|
||||
struct hashlimit_cfg cfg;
|
||||
compat_uptr_t hinfo;
|
||||
compat_uptr_t master;
|
||||
};
|
||||
|
||||
static void compat_from_user(void *dst, void *src)
|
||||
{
|
||||
int off = offsetof(struct compat_xt_hashlimit_info, hinfo);
|
||||
|
||||
memcpy(dst, src, off);
|
||||
memset(dst + off, 0, sizeof(struct compat_xt_hashlimit_info) - off);
|
||||
}
|
||||
|
||||
static int compat_to_user(void __user *dst, void *src)
|
||||
{
|
||||
int off = offsetof(struct compat_xt_hashlimit_info, hinfo);
|
||||
|
||||
return copy_to_user(dst, src, off) ? -EFAULT : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct xt_match xt_hashlimit[] = {
|
||||
{
|
||||
.name = "hashlimit",
|
||||
.family = AF_INET,
|
||||
.match = hashlimit_match,
|
||||
.matchsize = sizeof(struct xt_hashlimit_info),
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compatsize = sizeof(struct compat_xt_hashlimit_info),
|
||||
.compat_from_user = compat_from_user,
|
||||
.compat_to_user = compat_to_user,
|
||||
#endif
|
||||
.checkentry = hashlimit_checkentry,
|
||||
.destroy = hashlimit_destroy,
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "hashlimit",
|
||||
.family = AF_INET6,
|
||||
.match = hashlimit_match,
|
||||
.matchsize = sizeof(struct xt_hashlimit_info),
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compatsize = sizeof(struct compat_xt_hashlimit_info),
|
||||
.compat_from_user = compat_from_user,
|
||||
.compat_to_user = compat_to_user,
|
||||
#endif
|
||||
.checkentry = hashlimit_checkentry,
|
||||
.destroy = hashlimit_destroy,
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
/* PROC stuff */
|
||||
static void *dl_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct proc_dir_entry *pde = s->private;
|
||||
struct xt_hashlimit_htable *htable = pde->data;
|
||||
unsigned int *bucket;
|
||||
|
||||
spin_lock_bh(&htable->lock);
|
||||
if (*pos >= htable->cfg.size)
|
||||
return NULL;
|
||||
|
||||
bucket = kmalloc(sizeof(unsigned int), GFP_ATOMIC);
|
||||
if (!bucket)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
*bucket = *pos;
|
||||
return bucket;
|
||||
}
|
||||
|
||||
static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct proc_dir_entry *pde = s->private;
|
||||
struct xt_hashlimit_htable *htable = pde->data;
|
||||
unsigned int *bucket = (unsigned int *)v;
|
||||
|
||||
*pos = ++(*bucket);
|
||||
if (*pos >= htable->cfg.size) {
|
||||
kfree(v);
|
||||
return NULL;
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
|
||||
static void dl_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
struct proc_dir_entry *pde = s->private;
|
||||
struct xt_hashlimit_htable *htable = pde->data;
|
||||
unsigned int *bucket = (unsigned int *)v;
|
||||
|
||||
kfree(bucket);
|
||||
spin_unlock_bh(&htable->lock);
|
||||
}
|
||||
|
||||
static int dl_seq_real_show(struct dsthash_ent *ent, int family,
|
||||
struct seq_file *s)
|
||||
{
|
||||
/* recalculate to show accurate numbers */
|
||||
rateinfo_recalc(ent, jiffies);
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
return seq_printf(s, "%ld %u.%u.%u.%u:%u->"
|
||||
"%u.%u.%u.%u:%u %u %u %u\n",
|
||||
(long)(ent->expires - jiffies)/HZ,
|
||||
NIPQUAD(ent->dst.addr.ip.src),
|
||||
ntohs(ent->dst.src_port),
|
||||
NIPQUAD(ent->dst.addr.ip.dst),
|
||||
ntohs(ent->dst.dst_port),
|
||||
ent->rateinfo.credit, ent->rateinfo.credit_cap,
|
||||
ent->rateinfo.cost);
|
||||
case AF_INET6:
|
||||
return seq_printf(s, "%ld " NIP6_FMT ":%u->"
|
||||
NIP6_FMT ":%u %u %u %u\n",
|
||||
(long)(ent->expires - jiffies)/HZ,
|
||||
NIP6(*(struct in6_addr *)&ent->dst.addr.ip6.src),
|
||||
ntohs(ent->dst.src_port),
|
||||
NIP6(*(struct in6_addr *)&ent->dst.addr.ip6.dst),
|
||||
ntohs(ent->dst.dst_port),
|
||||
ent->rateinfo.credit, ent->rateinfo.credit_cap,
|
||||
ent->rateinfo.cost);
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int dl_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct proc_dir_entry *pde = s->private;
|
||||
struct xt_hashlimit_htable *htable = pde->data;
|
||||
unsigned int *bucket = (unsigned int *)v;
|
||||
struct dsthash_ent *ent;
|
||||
struct hlist_node *pos;
|
||||
|
||||
if (!hlist_empty(&htable->hash[*bucket])) {
|
||||
hlist_for_each_entry(ent, pos, &htable->hash[*bucket], node)
|
||||
if (dl_seq_real_show(ent, htable->family, s))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations dl_seq_ops = {
|
||||
.start = dl_seq_start,
|
||||
.next = dl_seq_next,
|
||||
.stop = dl_seq_stop,
|
||||
.show = dl_seq_show
|
||||
};
|
||||
|
||||
static int dl_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = seq_open(file, &dl_seq_ops);
|
||||
|
||||
if (!ret) {
|
||||
struct seq_file *sf = file->private_data;
|
||||
sf->private = PDE(inode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations dl_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dl_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
static int __init xt_hashlimit_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = xt_register_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit));
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
|
||||
err = -ENOMEM;
|
||||
hashlimit_cachep = kmem_cache_create("xt_hashlimit",
|
||||
sizeof(struct dsthash_ent), 0, 0,
|
||||
NULL, NULL);
|
||||
if (!hashlimit_cachep) {
|
||||
printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n");
|
||||
goto err2;
|
||||
}
|
||||
hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", proc_net);
|
||||
if (!hashlimit_procdir4) {
|
||||
printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
|
||||
"entry\n");
|
||||
goto err3;
|
||||
}
|
||||
hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", proc_net);
|
||||
if (!hashlimit_procdir6) {
|
||||
printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
|
||||
"entry\n");
|
||||
goto err4;
|
||||
}
|
||||
return 0;
|
||||
err4:
|
||||
remove_proc_entry("ipt_hashlimit", proc_net);
|
||||
err3:
|
||||
kmem_cache_destroy(hashlimit_cachep);
|
||||
err2:
|
||||
xt_unregister_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit));
|
||||
err1:
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void __exit xt_hashlimit_fini(void)
|
||||
{
|
||||
remove_proc_entry("ipt_hashlimit", proc_net);
|
||||
remove_proc_entry("ip6t_hashlimit", proc_net);
|
||||
kmem_cache_destroy(hashlimit_cachep);
|
||||
xt_unregister_matches(xt_hashlimit, ARRAY_SIZE(xt_hashlimit));
|
||||
}
|
||||
|
||||
module_init(xt_hashlimit_init);
|
||||
module_exit(xt_hashlimit_fini);
|
||||
196
net/netfilter/xt_helper.c
Normal file
196
net/netfilter/xt_helper.c
Normal file
@@ -0,0 +1,196 @@
|
||||
/* iptables module to match on related connections */
|
||||
/*
|
||||
* (C) 2001 Martin Josefsson <gandalf@wlug.westbo.se>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 19 Mar 2002 Harald Welte <laforge@gnumonks.org>:
|
||||
* - Port to newnat infrastructure
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter.h>
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
#include <linux/netfilter_ipv4/ip_conntrack.h>
|
||||
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
|
||||
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
|
||||
#else
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#endif
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_helper.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Martin Josefsson <gandalf@netfilter.org>");
|
||||
MODULE_DESCRIPTION("iptables helper match module");
|
||||
MODULE_ALIAS("ipt_helper");
|
||||
MODULE_ALIAS("ip6t_helper");
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_helper_info *info = matchinfo;
|
||||
struct ip_conntrack *ct;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
int ret = info->invert;
|
||||
|
||||
ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
|
||||
if (!ct) {
|
||||
DEBUGP("xt_helper: Eek! invalid conntrack?\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ct->master) {
|
||||
DEBUGP("xt_helper: conntrack %p has no master\n", ct);
|
||||
return ret;
|
||||
}
|
||||
|
||||
read_lock_bh(&ip_conntrack_lock);
|
||||
if (!ct->master->helper) {
|
||||
DEBUGP("xt_helper: master ct %p has no helper\n",
|
||||
exp->expectant);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
DEBUGP("master's name = %s , info->name = %s\n",
|
||||
ct->master->helper->name, info->name);
|
||||
|
||||
if (info->name[0] == '\0')
|
||||
ret ^= 1;
|
||||
else
|
||||
ret ^= !strncmp(ct->master->helper->name, info->name,
|
||||
strlen(ct->master->helper->name));
|
||||
out_unlock:
|
||||
read_unlock_bh(&ip_conntrack_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* CONFIG_IP_NF_CONNTRACK */
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_helper_info *info = matchinfo;
|
||||
struct nf_conn *ct;
|
||||
struct nf_conn_help *master_help;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
int ret = info->invert;
|
||||
|
||||
ct = nf_ct_get((struct sk_buff *)skb, &ctinfo);
|
||||
if (!ct) {
|
||||
DEBUGP("xt_helper: Eek! invalid conntrack?\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ct->master) {
|
||||
DEBUGP("xt_helper: conntrack %p has no master\n", ct);
|
||||
return ret;
|
||||
}
|
||||
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
master_help = nfct_help(ct->master);
|
||||
if (!master_help || !master_help->helper) {
|
||||
DEBUGP("xt_helper: master ct %p has no helper\n",
|
||||
exp->expectant);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
DEBUGP("master's name = %s , info->name = %s\n",
|
||||
ct->master->helper->name, info->name);
|
||||
|
||||
if (info->name[0] == '\0')
|
||||
ret ^= 1;
|
||||
else
|
||||
ret ^= !strncmp(master_help->helper->name, info->name,
|
||||
strlen(master_help->helper->name));
|
||||
out_unlock:
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int check(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_helper_info *info = matchinfo;
|
||||
|
||||
if (nf_ct_l3proto_try_module_get(match->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", match->family);
|
||||
return 0;
|
||||
}
|
||||
info->name[29] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(match->family);
|
||||
}
|
||||
|
||||
static struct xt_match xt_helper_match[] = {
|
||||
{
|
||||
.name = "helper",
|
||||
.family = AF_INET,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_helper_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "helper",
|
||||
.family = AF_INET6,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_helper_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_helper_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_helper_match,
|
||||
ARRAY_SIZE(xt_helper_match));
|
||||
}
|
||||
|
||||
static void __exit xt_helper_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_helper_match, ARRAY_SIZE(xt_helper_match));
|
||||
}
|
||||
|
||||
module_init(xt_helper_init);
|
||||
module_exit(xt_helper_fini);
|
||||
|
||||
84
net/netfilter/xt_length.c
Normal file
84
net/netfilter/xt_length.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Kernel module to match packet length. */
|
||||
/* (C) 1999-2001 James Morris <jmorros@intercode.com.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
#include <linux/netfilter/xt_length.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
|
||||
MODULE_DESCRIPTION("IP tables packet length matching module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_length");
|
||||
MODULE_ALIAS("ip6t_length");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_length_info *info = matchinfo;
|
||||
u_int16_t pktlen = ntohs(skb->nh.iph->tot_len);
|
||||
|
||||
return (pktlen >= info->min && pktlen <= info->max) ^ info->invert;
|
||||
}
|
||||
|
||||
static int
|
||||
match6(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_length_info *info = matchinfo;
|
||||
u_int16_t pktlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
|
||||
|
||||
return (pktlen >= info->min && pktlen <= info->max) ^ info->invert;
|
||||
}
|
||||
|
||||
static struct xt_match xt_length_match[] = {
|
||||
{
|
||||
.name = "length",
|
||||
.family = AF_INET,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_length_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "length",
|
||||
.family = AF_INET6,
|
||||
.match = match6,
|
||||
.matchsize = sizeof(struct xt_length_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_length_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_length_match,
|
||||
ARRAY_SIZE(xt_length_match));
|
||||
}
|
||||
|
||||
static void __exit xt_length_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_length_match, ARRAY_SIZE(xt_length_match));
|
||||
}
|
||||
|
||||
module_init(xt_length_init);
|
||||
module_exit(xt_length_fini);
|
||||
217
net/netfilter/xt_limit.c
Normal file
217
net/netfilter/xt_limit.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/* Kernel module to control the rate
|
||||
*
|
||||
* 2 September 1999: Changed from the target RATE to the match
|
||||
* `limit', removed logging. Did I mention that
|
||||
* Alexey is a fucking genius?
|
||||
* Rusty Russell (rusty@rustcorp.com.au). */
|
||||
|
||||
/* (C) 1999 J<>r<EFBFBD>me de Vivie <devivie@info.enserb.u-bordeaux.fr>
|
||||
* (C) 1999 Herv<72> Eychenne <eychenne@info.enserb.u-bordeaux.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_limit.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
|
||||
MODULE_DESCRIPTION("iptables rate limit match");
|
||||
MODULE_ALIAS("ipt_limit");
|
||||
MODULE_ALIAS("ip6t_limit");
|
||||
|
||||
/* The algorithm used is the Simple Token Bucket Filter (TBF)
|
||||
* see net/sched/sch_tbf.c in the linux source tree
|
||||
*/
|
||||
|
||||
static DEFINE_SPINLOCK(limit_lock);
|
||||
|
||||
/* Rusty: This is my (non-mathematically-inclined) understanding of
|
||||
this algorithm. The `average rate' in jiffies becomes your initial
|
||||
amount of credit `credit' and the most credit you can ever have
|
||||
`credit_cap'. The `peak rate' becomes the cost of passing the
|
||||
test, `cost'.
|
||||
|
||||
`prev' tracks the last packet hit: you gain one credit per jiffy.
|
||||
If you get credit balance more than this, the extra credit is
|
||||
discarded. Every time the match passes, you lose `cost' credits;
|
||||
if you don't have that many, the test fails.
|
||||
|
||||
See Alexey's formal explanation in net/sched/sch_tbf.c.
|
||||
|
||||
To get the maxmum range, we multiply by this factor (ie. you get N
|
||||
credits per jiffy). We want to allow a rate as low as 1 per day
|
||||
(slowest userspace tool allows), which means
|
||||
CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32. ie. */
|
||||
#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))
|
||||
|
||||
/* Repeated shift and or gives us all 1s, final shift and add 1 gives
|
||||
* us the power of 2 below the theoretical max, so GCC simply does a
|
||||
* shift. */
|
||||
#define _POW2_BELOW2(x) ((x)|((x)>>1))
|
||||
#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))
|
||||
#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))
|
||||
#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))
|
||||
#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))
|
||||
#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)
|
||||
|
||||
#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
|
||||
|
||||
static int
|
||||
ipt_limit_match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
struct xt_rateinfo *r = ((struct xt_rateinfo *)matchinfo)->master;
|
||||
unsigned long now = jiffies;
|
||||
|
||||
spin_lock_bh(&limit_lock);
|
||||
r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY;
|
||||
if (r->credit > r->credit_cap)
|
||||
r->credit = r->credit_cap;
|
||||
|
||||
if (r->credit >= r->cost) {
|
||||
/* We're not limited. */
|
||||
r->credit -= r->cost;
|
||||
spin_unlock_bh(&limit_lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&limit_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Precision saver. */
|
||||
static u_int32_t
|
||||
user2credits(u_int32_t user)
|
||||
{
|
||||
/* If multiplying would overflow... */
|
||||
if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
|
||||
/* Divide first. */
|
||||
return (user / XT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
|
||||
|
||||
return (user * HZ * CREDITS_PER_JIFFY) / XT_LIMIT_SCALE;
|
||||
}
|
||||
|
||||
static int
|
||||
ipt_limit_checkentry(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_rateinfo *r = matchinfo;
|
||||
|
||||
/* Check for overflow. */
|
||||
if (r->burst == 0
|
||||
|| user2credits(r->avg * r->burst) < user2credits(r->avg)) {
|
||||
printk("Overflow in xt_limit, try lower: %u/%u\n",
|
||||
r->avg, r->burst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For SMP, we only want to use one set of counters. */
|
||||
r->master = r;
|
||||
if (r->cost == 0) {
|
||||
/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
|
||||
128. */
|
||||
r->prev = jiffies;
|
||||
r->credit = user2credits(r->avg * r->burst); /* Credits full. */
|
||||
r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
|
||||
r->cost = user2credits(r->avg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
struct compat_xt_rateinfo {
|
||||
u_int32_t avg;
|
||||
u_int32_t burst;
|
||||
|
||||
compat_ulong_t prev;
|
||||
u_int32_t credit;
|
||||
u_int32_t credit_cap, cost;
|
||||
|
||||
u_int32_t master;
|
||||
};
|
||||
|
||||
/* To keep the full "prev" timestamp, the upper 32 bits are stored in the
|
||||
* master pointer, which does not need to be preserved. */
|
||||
static void compat_from_user(void *dst, void *src)
|
||||
{
|
||||
struct compat_xt_rateinfo *cm = src;
|
||||
struct xt_rateinfo m = {
|
||||
.avg = cm->avg,
|
||||
.burst = cm->burst,
|
||||
.prev = cm->prev | (unsigned long)cm->master << 32,
|
||||
.credit = cm->credit,
|
||||
.credit_cap = cm->credit_cap,
|
||||
.cost = cm->cost,
|
||||
};
|
||||
memcpy(dst, &m, sizeof(m));
|
||||
}
|
||||
|
||||
static int compat_to_user(void __user *dst, void *src)
|
||||
{
|
||||
struct xt_rateinfo *m = src;
|
||||
struct compat_xt_rateinfo cm = {
|
||||
.avg = m->avg,
|
||||
.burst = m->burst,
|
||||
.prev = m->prev,
|
||||
.credit = m->credit,
|
||||
.credit_cap = m->credit_cap,
|
||||
.cost = m->cost,
|
||||
.master = m->prev >> 32,
|
||||
};
|
||||
return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
static struct xt_match xt_limit_match[] = {
|
||||
{
|
||||
.name = "limit",
|
||||
.family = AF_INET,
|
||||
.checkentry = ipt_limit_checkentry,
|
||||
.match = ipt_limit_match,
|
||||
.matchsize = sizeof(struct xt_rateinfo),
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compatsize = sizeof(struct compat_xt_rateinfo),
|
||||
.compat_from_user = compat_from_user,
|
||||
.compat_to_user = compat_to_user,
|
||||
#endif
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "limit",
|
||||
.family = AF_INET6,
|
||||
.checkentry = ipt_limit_checkentry,
|
||||
.match = ipt_limit_match,
|
||||
.matchsize = sizeof(struct xt_rateinfo),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_limit_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_limit_match, ARRAY_SIZE(xt_limit_match));
|
||||
}
|
||||
|
||||
static void __exit xt_limit_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_limit_match, ARRAY_SIZE(xt_limit_match));
|
||||
}
|
||||
|
||||
module_init(xt_limit_init);
|
||||
module_exit(xt_limit_fini);
|
||||
81
net/netfilter/xt_mac.c
Normal file
81
net/netfilter/xt_mac.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* Kernel module to match MAC address parameters. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/netfilter/xt_mac.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
MODULE_DESCRIPTION("iptables mac matching module");
|
||||
MODULE_ALIAS("ipt_mac");
|
||||
MODULE_ALIAS("ip6t_mac");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_mac_info *info = matchinfo;
|
||||
|
||||
/* Is mac pointer valid? */
|
||||
return (skb->mac.raw >= skb->head
|
||||
&& (skb->mac.raw + ETH_HLEN) <= skb->data
|
||||
/* If so, compare... */
|
||||
&& ((!compare_ether_addr(eth_hdr(skb)->h_source, info->srcaddr))
|
||||
^ info->invert));
|
||||
}
|
||||
|
||||
static struct xt_match xt_mac_match[] = {
|
||||
{
|
||||
.name = "mac",
|
||||
.family = AF_INET,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_mac_info),
|
||||
.hooks = (1 << NF_IP_PRE_ROUTING) |
|
||||
(1 << NF_IP_LOCAL_IN) |
|
||||
(1 << NF_IP_FORWARD),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "mac",
|
||||
.family = AF_INET6,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_mac_info),
|
||||
.hooks = (1 << NF_IP6_PRE_ROUTING) |
|
||||
(1 << NF_IP6_LOCAL_IN) |
|
||||
(1 << NF_IP6_FORWARD),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_mac_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_mac_match, ARRAY_SIZE(xt_mac_match));
|
||||
}
|
||||
|
||||
static void __exit xt_mac_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_mac_match, ARRAY_SIZE(xt_mac_match));
|
||||
}
|
||||
|
||||
module_init(xt_mac_init);
|
||||
module_exit(xt_mac_fini);
|
||||
283
net/netfilter/xt_multiport.c
Normal file
283
net/netfilter/xt_multiport.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/* Kernel module to match one of a list of TCP/UDP(-Lite)/SCTP/DCCP ports:
|
||||
ports are in the same place so we can treat them as equal. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
|
||||
#include <linux/netfilter/xt_multiport.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
|
||||
MODULE_DESCRIPTION("x_tables multiple port match module");
|
||||
MODULE_ALIAS("ipt_multiport");
|
||||
MODULE_ALIAS("ip6t_multiport");
|
||||
|
||||
#if 0
|
||||
#define duprintf(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define duprintf(format, args...)
|
||||
#endif
|
||||
|
||||
/* Returns 1 if the port is matched by the test, 0 otherwise. */
|
||||
static inline int
|
||||
ports_match(const u_int16_t *portlist, enum xt_multiport_flags flags,
|
||||
u_int8_t count, u_int16_t src, u_int16_t dst)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
if (flags != XT_MULTIPORT_DESTINATION && portlist[i] == src)
|
||||
return 1;
|
||||
|
||||
if (flags != XT_MULTIPORT_SOURCE && portlist[i] == dst)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if the port is matched by the test, 0 otherwise. */
|
||||
static inline int
|
||||
ports_match_v1(const struct xt_multiport_v1 *minfo,
|
||||
u_int16_t src, u_int16_t dst)
|
||||
{
|
||||
unsigned int i;
|
||||
u_int16_t s, e;
|
||||
|
||||
for (i = 0; i < minfo->count; i++) {
|
||||
s = minfo->ports[i];
|
||||
|
||||
if (minfo->pflags[i]) {
|
||||
/* range port matching */
|
||||
e = minfo->ports[++i];
|
||||
duprintf("src or dst matches with %d-%d?\n", s, e);
|
||||
|
||||
if (minfo->flags == XT_MULTIPORT_SOURCE
|
||||
&& src >= s && src <= e)
|
||||
return 1 ^ minfo->invert;
|
||||
if (minfo->flags == XT_MULTIPORT_DESTINATION
|
||||
&& dst >= s && dst <= e)
|
||||
return 1 ^ minfo->invert;
|
||||
if (minfo->flags == XT_MULTIPORT_EITHER
|
||||
&& ((dst >= s && dst <= e)
|
||||
|| (src >= s && src <= e)))
|
||||
return 1 ^ minfo->invert;
|
||||
} else {
|
||||
/* exact port matching */
|
||||
duprintf("src or dst matches with %d?\n", s);
|
||||
|
||||
if (minfo->flags == XT_MULTIPORT_SOURCE
|
||||
&& src == s)
|
||||
return 1 ^ minfo->invert;
|
||||
if (minfo->flags == XT_MULTIPORT_DESTINATION
|
||||
&& dst == s)
|
||||
return 1 ^ minfo->invert;
|
||||
if (minfo->flags == XT_MULTIPORT_EITHER
|
||||
&& (src == s || dst == s))
|
||||
return 1 ^ minfo->invert;
|
||||
}
|
||||
}
|
||||
|
||||
return minfo->invert;
|
||||
}
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
__be16 _ports[2], *pptr;
|
||||
const struct xt_multiport *multiinfo = matchinfo;
|
||||
|
||||
if (offset)
|
||||
return 0;
|
||||
|
||||
pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports);
|
||||
if (pptr == NULL) {
|
||||
/* We've been asked to examine this packet, and we
|
||||
* can't. Hence, no choice but to drop.
|
||||
*/
|
||||
duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ports_match(multiinfo->ports,
|
||||
multiinfo->flags, multiinfo->count,
|
||||
ntohs(pptr[0]), ntohs(pptr[1]));
|
||||
}
|
||||
|
||||
static int
|
||||
match_v1(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
__be16 _ports[2], *pptr;
|
||||
const struct xt_multiport_v1 *multiinfo = matchinfo;
|
||||
|
||||
if (offset)
|
||||
return 0;
|
||||
|
||||
pptr = skb_header_pointer(skb, protoff, sizeof(_ports), _ports);
|
||||
if (pptr == NULL) {
|
||||
/* We've been asked to examine this packet, and we
|
||||
* can't. Hence, no choice but to drop.
|
||||
*/
|
||||
duprintf("xt_multiport: Dropping evil offset=0 tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ports_match_v1(multiinfo, ntohs(pptr[0]), ntohs(pptr[1]));
|
||||
}
|
||||
|
||||
static inline int
|
||||
check(u_int16_t proto,
|
||||
u_int8_t ip_invflags,
|
||||
u_int8_t match_flags,
|
||||
u_int8_t count)
|
||||
{
|
||||
/* Must specify supported protocol, no unknown flags or bad count */
|
||||
return (proto == IPPROTO_TCP || proto == IPPROTO_UDP
|
||||
|| proto == IPPROTO_UDPLITE
|
||||
|| proto == IPPROTO_SCTP || proto == IPPROTO_DCCP)
|
||||
&& !(ip_invflags & XT_INV_PROTO)
|
||||
&& (match_flags == XT_MULTIPORT_SOURCE
|
||||
|| match_flags == XT_MULTIPORT_DESTINATION
|
||||
|| match_flags == XT_MULTIPORT_EITHER)
|
||||
&& count <= XT_MULTI_PORTS;
|
||||
}
|
||||
|
||||
/* Called when user tries to insert an entry of this type. */
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct ipt_ip *ip = info;
|
||||
const struct xt_multiport *multiinfo = matchinfo;
|
||||
|
||||
return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
multiinfo->count);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry_v1(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct ipt_ip *ip = info;
|
||||
const struct xt_multiport_v1 *multiinfo = matchinfo;
|
||||
|
||||
return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
multiinfo->count);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry6(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct ip6t_ip6 *ip = info;
|
||||
const struct xt_multiport *multiinfo = matchinfo;
|
||||
|
||||
return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
multiinfo->count);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry6_v1(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct ip6t_ip6 *ip = info;
|
||||
const struct xt_multiport_v1 *multiinfo = matchinfo;
|
||||
|
||||
return check(ip->proto, ip->invflags, multiinfo->flags,
|
||||
multiinfo->count);
|
||||
}
|
||||
|
||||
static struct xt_match xt_multiport_match[] = {
|
||||
{
|
||||
.name = "multiport",
|
||||
.family = AF_INET,
|
||||
.revision = 0,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_multiport),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "multiport",
|
||||
.family = AF_INET,
|
||||
.revision = 1,
|
||||
.checkentry = checkentry_v1,
|
||||
.match = match_v1,
|
||||
.matchsize = sizeof(struct xt_multiport_v1),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "multiport",
|
||||
.family = AF_INET6,
|
||||
.revision = 0,
|
||||
.checkentry = checkentry6,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_multiport),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "multiport",
|
||||
.family = AF_INET6,
|
||||
.revision = 1,
|
||||
.checkentry = checkentry6_v1,
|
||||
.match = match_v1,
|
||||
.matchsize = sizeof(struct xt_multiport_v1),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_multiport_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_multiport_match,
|
||||
ARRAY_SIZE(xt_multiport_match));
|
||||
}
|
||||
|
||||
static void __exit xt_multiport_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_multiport_match,
|
||||
ARRAY_SIZE(xt_multiport_match));
|
||||
}
|
||||
|
||||
module_init(xt_multiport_init);
|
||||
module_exit(xt_multiport_fini);
|
||||
161
net/netfilter/xt_physdev.c
Normal file
161
net/netfilter/xt_physdev.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* Kernel module to match the bridge port in and
|
||||
* out device for IP packets coming into contact with a bridge. */
|
||||
|
||||
/* (C) 2001-2003 Bart De Schuymer <bdschuym@pandora.be>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/netfilter/xt_physdev.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#define MATCH 1
|
||||
#define NOMATCH 0
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
|
||||
MODULE_DESCRIPTION("iptables bridge physical device match module");
|
||||
MODULE_ALIAS("ipt_physdev");
|
||||
MODULE_ALIAS("ip6t_physdev");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
int i;
|
||||
static const char nulldevname[IFNAMSIZ];
|
||||
const struct xt_physdev_info *info = matchinfo;
|
||||
unsigned int ret;
|
||||
const char *indev, *outdev;
|
||||
struct nf_bridge_info *nf_bridge;
|
||||
|
||||
/* Not a bridged IP packet or no info available yet:
|
||||
* LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
|
||||
* the destination device will be a bridge. */
|
||||
if (!(nf_bridge = skb->nf_bridge)) {
|
||||
/* Return MATCH if the invert flags of the used options are on */
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
|
||||
!(info->invert & XT_PHYSDEV_OP_BRIDGED))
|
||||
return NOMATCH;
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_ISIN) &&
|
||||
!(info->invert & XT_PHYSDEV_OP_ISIN))
|
||||
return NOMATCH;
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_ISOUT) &&
|
||||
!(info->invert & XT_PHYSDEV_OP_ISOUT))
|
||||
return NOMATCH;
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_IN) &&
|
||||
!(info->invert & XT_PHYSDEV_OP_IN))
|
||||
return NOMATCH;
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_OUT) &&
|
||||
!(info->invert & XT_PHYSDEV_OP_OUT))
|
||||
return NOMATCH;
|
||||
return MATCH;
|
||||
}
|
||||
|
||||
/* This only makes sense in the FORWARD and POSTROUTING chains */
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
|
||||
(!!(nf_bridge->mask & BRNF_BRIDGED) ^
|
||||
!(info->invert & XT_PHYSDEV_OP_BRIDGED)))
|
||||
return NOMATCH;
|
||||
|
||||
if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
|
||||
(!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
|
||||
(info->bitmask & XT_PHYSDEV_OP_ISOUT &&
|
||||
(!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
|
||||
return NOMATCH;
|
||||
|
||||
if (!(info->bitmask & XT_PHYSDEV_OP_IN))
|
||||
goto match_outdev;
|
||||
indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
|
||||
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
|
||||
ret |= (((const unsigned int *)indev)[i]
|
||||
^ ((const unsigned int *)info->physindev)[i])
|
||||
& ((const unsigned int *)info->in_mask)[i];
|
||||
}
|
||||
|
||||
if ((ret == 0) ^ !(info->invert & XT_PHYSDEV_OP_IN))
|
||||
return NOMATCH;
|
||||
|
||||
match_outdev:
|
||||
if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
|
||||
return MATCH;
|
||||
outdev = nf_bridge->physoutdev ?
|
||||
nf_bridge->physoutdev->name : nulldevname;
|
||||
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned int); i++) {
|
||||
ret |= (((const unsigned int *)outdev)[i]
|
||||
^ ((const unsigned int *)info->physoutdev)[i])
|
||||
& ((const unsigned int *)info->out_mask)[i];
|
||||
}
|
||||
|
||||
return (ret != 0) ^ !(info->invert & XT_PHYSDEV_OP_OUT);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *ip,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_physdev_info *info = matchinfo;
|
||||
|
||||
if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
|
||||
info->bitmask & ~XT_PHYSDEV_OP_MASK)
|
||||
return 0;
|
||||
if (info->bitmask & XT_PHYSDEV_OP_OUT &&
|
||||
(!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
|
||||
info->invert & XT_PHYSDEV_OP_BRIDGED) &&
|
||||
hook_mask & ((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_FORWARD) |
|
||||
(1 << NF_IP_POST_ROUTING))) {
|
||||
printk(KERN_WARNING "physdev match: using --physdev-out in the "
|
||||
"OUTPUT, FORWARD and POSTROUTING chains for non-bridged "
|
||||
"traffic is not supported anymore.\n");
|
||||
if (hook_mask & (1 << NF_IP_LOCAL_OUT))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_physdev_match[] = {
|
||||
{
|
||||
.name = "physdev",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_physdev_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "physdev",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_physdev_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_physdev_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_physdev_match,
|
||||
ARRAY_SIZE(xt_physdev_match));
|
||||
}
|
||||
|
||||
static void __exit xt_physdev_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_physdev_match, ARRAY_SIZE(xt_physdev_match));
|
||||
}
|
||||
|
||||
module_init(xt_physdev_init);
|
||||
module_exit(xt_physdev_fini);
|
||||
75
net/netfilter/xt_pkttype.c
Normal file
75
net/netfilter/xt_pkttype.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/* (C) 1999-2001 Michal Ludvig <michal@logix.cz>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
|
||||
#include <linux/netfilter/xt_pkttype.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Michal Ludvig <michal@logix.cz>");
|
||||
MODULE_DESCRIPTION("IP tables match to match on linklayer packet type");
|
||||
MODULE_ALIAS("ipt_pkttype");
|
||||
MODULE_ALIAS("ip6t_pkttype");
|
||||
|
||||
static int match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
u_int8_t type;
|
||||
const struct xt_pkttype_info *info = matchinfo;
|
||||
|
||||
if (skb->pkt_type == PACKET_LOOPBACK)
|
||||
type = (MULTICAST(skb->nh.iph->daddr)
|
||||
? PACKET_MULTICAST
|
||||
: PACKET_BROADCAST);
|
||||
else
|
||||
type = skb->pkt_type;
|
||||
|
||||
return (type == info->pkttype) ^ info->invert;
|
||||
}
|
||||
|
||||
static struct xt_match xt_pkttype_match[] = {
|
||||
{
|
||||
.name = "pkttype",
|
||||
.family = AF_INET,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_pkttype_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "pkttype",
|
||||
.family = AF_INET6,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_pkttype_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_pkttype_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_pkttype_match,
|
||||
ARRAY_SIZE(xt_pkttype_match));
|
||||
}
|
||||
|
||||
static void __exit xt_pkttype_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_pkttype_match, ARRAY_SIZE(xt_pkttype_match));
|
||||
}
|
||||
|
||||
module_init(xt_pkttype_init);
|
||||
module_exit(xt_pkttype_fini);
|
||||
200
net/netfilter/xt_policy.c
Normal file
200
net/netfilter/xt_policy.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/* IP tables module for matching IPsec policy
|
||||
*
|
||||
* Copyright (c) 2004,2005 Patrick McHardy, <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
#include <linux/netfilter/xt_policy.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("Xtables IPsec policy matching module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static inline int
|
||||
xt_addr_cmp(const union xt_policy_addr *a1, const union xt_policy_addr *m,
|
||||
const union xt_policy_addr *a2, unsigned short family)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
return !((a1->a4.s_addr ^ a2->a4.s_addr) & m->a4.s_addr);
|
||||
case AF_INET6:
|
||||
return !ipv6_masked_addr_cmp(&a1->a6, &m->a6, &a2->a6);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
match_xfrm_state(struct xfrm_state *x, const struct xt_policy_elem *e,
|
||||
unsigned short family)
|
||||
{
|
||||
#define MATCH_ADDR(x,y,z) (!e->match.x || \
|
||||
(xt_addr_cmp(&e->x, &e->y, z, family) \
|
||||
^ e->invert.x))
|
||||
#define MATCH(x,y) (!e->match.x || ((e->x == (y)) ^ e->invert.x))
|
||||
|
||||
return MATCH_ADDR(saddr, smask, (union xt_policy_addr *)&x->props.saddr) &&
|
||||
MATCH_ADDR(daddr, dmask, (union xt_policy_addr *)&x->id.daddr) &&
|
||||
MATCH(proto, x->id.proto) &&
|
||||
MATCH(mode, x->props.mode) &&
|
||||
MATCH(spi, x->id.spi) &&
|
||||
MATCH(reqid, x->props.reqid);
|
||||
}
|
||||
|
||||
static int
|
||||
match_policy_in(const struct sk_buff *skb, const struct xt_policy_info *info,
|
||||
unsigned short family)
|
||||
{
|
||||
const struct xt_policy_elem *e;
|
||||
struct sec_path *sp = skb->sp;
|
||||
int strict = info->flags & XT_POLICY_MATCH_STRICT;
|
||||
int i, pos;
|
||||
|
||||
if (sp == NULL)
|
||||
return -1;
|
||||
if (strict && info->len != sp->len)
|
||||
return 0;
|
||||
|
||||
for (i = sp->len - 1; i >= 0; i--) {
|
||||
pos = strict ? i - sp->len + 1 : 0;
|
||||
if (pos >= info->len)
|
||||
return 0;
|
||||
e = &info->pol[pos];
|
||||
|
||||
if (match_xfrm_state(sp->xvec[i], e, family)) {
|
||||
if (!strict)
|
||||
return 1;
|
||||
} else if (strict)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strict ? 1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
match_policy_out(const struct sk_buff *skb, const struct xt_policy_info *info,
|
||||
unsigned short family)
|
||||
{
|
||||
const struct xt_policy_elem *e;
|
||||
struct dst_entry *dst = skb->dst;
|
||||
int strict = info->flags & XT_POLICY_MATCH_STRICT;
|
||||
int i, pos;
|
||||
|
||||
if (dst->xfrm == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; dst && dst->xfrm; dst = dst->child, i++) {
|
||||
pos = strict ? i : 0;
|
||||
if (pos >= info->len)
|
||||
return 0;
|
||||
e = &info->pol[pos];
|
||||
|
||||
if (match_xfrm_state(dst->xfrm, e, family)) {
|
||||
if (!strict)
|
||||
return 1;
|
||||
} else if (strict)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strict ? i == info->len : 0;
|
||||
}
|
||||
|
||||
static int match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_policy_info *info = matchinfo;
|
||||
int ret;
|
||||
|
||||
if (info->flags & XT_POLICY_MATCH_IN)
|
||||
ret = match_policy_in(skb, info, match->family);
|
||||
else
|
||||
ret = match_policy_out(skb, info, match->family);
|
||||
|
||||
if (ret < 0)
|
||||
ret = info->flags & XT_POLICY_MATCH_NONE ? 1 : 0;
|
||||
else if (info->flags & XT_POLICY_MATCH_NONE)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int checkentry(const char *tablename, const void *ip_void,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo, unsigned int hook_mask)
|
||||
{
|
||||
struct xt_policy_info *info = matchinfo;
|
||||
|
||||
if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT))) {
|
||||
printk(KERN_ERR "xt_policy: neither incoming nor "
|
||||
"outgoing policy selected\n");
|
||||
return 0;
|
||||
}
|
||||
/* hook values are equal for IPv4 and IPv6 */
|
||||
if (hook_mask & (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN)
|
||||
&& info->flags & XT_POLICY_MATCH_OUT) {
|
||||
printk(KERN_ERR "xt_policy: output policy not valid in "
|
||||
"PRE_ROUTING and INPUT\n");
|
||||
return 0;
|
||||
}
|
||||
if (hook_mask & (1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT)
|
||||
&& info->flags & XT_POLICY_MATCH_IN) {
|
||||
printk(KERN_ERR "xt_policy: input policy not valid in "
|
||||
"POST_ROUTING and OUTPUT\n");
|
||||
return 0;
|
||||
}
|
||||
if (info->len > XT_POLICY_MAX_ELEM) {
|
||||
printk(KERN_ERR "xt_policy: too many policy elements\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_policy_match[] = {
|
||||
{
|
||||
.name = "policy",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_policy_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "policy",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_policy_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return xt_register_matches(xt_policy_match,
|
||||
ARRAY_SIZE(xt_policy_match));
|
||||
}
|
||||
|
||||
static void __exit fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_policy_match, ARRAY_SIZE(xt_policy_match));
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
||||
MODULE_ALIAS("ipt_policy");
|
||||
MODULE_ALIAS("ip6t_policy");
|
||||
85
net/netfilter/xt_quota.c
Normal file
85
net/netfilter/xt_quota.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* netfilter module to enforce network quotas
|
||||
*
|
||||
* Sam Johnston <samj@samj.net>
|
||||
*/
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_quota.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
|
||||
MODULE_ALIAS("ipt_quota");
|
||||
MODULE_ALIAS("ip6t_quota");
|
||||
|
||||
static DEFINE_SPINLOCK(quota_lock);
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const struct xt_match *match, const void *matchinfo,
|
||||
int offset, unsigned int protoff, int *hotdrop)
|
||||
{
|
||||
struct xt_quota_info *q = ((struct xt_quota_info *)matchinfo)->master;
|
||||
int ret = q->flags & XT_QUOTA_INVERT ? 1 : 0;
|
||||
|
||||
spin_lock_bh("a_lock);
|
||||
if (q->quota >= skb->len) {
|
||||
q->quota -= skb->len;
|
||||
ret ^= 1;
|
||||
} else {
|
||||
/* we do not allow even small packets from now on */
|
||||
q->quota = 0;
|
||||
}
|
||||
spin_unlock_bh("a_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename, const void *entry,
|
||||
const struct xt_match *match, void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_quota_info *q = (struct xt_quota_info *)matchinfo;
|
||||
|
||||
if (q->flags & ~XT_QUOTA_MASK)
|
||||
return 0;
|
||||
/* For SMP, we only want to use one set of counters. */
|
||||
q->master = q;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_quota_match[] = {
|
||||
{
|
||||
.name = "quota",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_quota_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "quota",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_quota_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_quota_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_quota_match, ARRAY_SIZE(xt_quota_match));
|
||||
}
|
||||
|
||||
static void __exit xt_quota_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_quota_match, ARRAY_SIZE(xt_quota_match));
|
||||
}
|
||||
|
||||
module_init(xt_quota_init);
|
||||
module_exit(xt_quota_fini);
|
||||
63
net/netfilter/xt_realm.c
Normal file
63
net/netfilter/xt_realm.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/* IP tables module for matching the routing realm
|
||||
*
|
||||
* $Id: xt_realm.c,v 1.1.1.1 2007/06/12 07:27:14 eyryu Exp $
|
||||
*
|
||||
* (C) 2003 by Sampsa Ranta <sampsa@netsonic.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter/xt_realm.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_AUTHOR("Sampsa Ranta <sampsa@netsonic.fi>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("X_tables realm match");
|
||||
MODULE_ALIAS("ipt_realm");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_realm_info *info = matchinfo;
|
||||
struct dst_entry *dst = skb->dst;
|
||||
|
||||
return (info->id == (dst->tclassid & info->mask)) ^ info->invert;
|
||||
}
|
||||
|
||||
static struct xt_match realm_match = {
|
||||
.name = "realm",
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_realm_info),
|
||||
.hooks = (1 << NF_IP_POST_ROUTING) | (1 << NF_IP_FORWARD) |
|
||||
(1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_LOCAL_IN),
|
||||
.family = AF_INET,
|
||||
.me = THIS_MODULE
|
||||
};
|
||||
|
||||
static int __init xt_realm_init(void)
|
||||
{
|
||||
return xt_register_match(&realm_match);
|
||||
}
|
||||
|
||||
static void __exit xt_realm_fini(void)
|
||||
{
|
||||
xt_unregister_match(&realm_match);
|
||||
}
|
||||
|
||||
module_init(xt_realm_init);
|
||||
module_exit(xt_realm_fini);
|
||||
212
net/netfilter/xt_sctp.c
Normal file
212
net/netfilter/xt_sctp.c
Normal file
@@ -0,0 +1,212 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <linux/sctp.h>
|
||||
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_sctp.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kiran Kumar Immidi");
|
||||
MODULE_DESCRIPTION("Match for SCTP protocol packets");
|
||||
MODULE_ALIAS("ipt_sctp");
|
||||
|
||||
#ifdef DEBUG_SCTP
|
||||
#define duprintf(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define duprintf(format, args...)
|
||||
#endif
|
||||
|
||||
#define SCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
|
||||
|| (!!((invflag) & (option)) ^ (cond)))
|
||||
|
||||
static int
|
||||
match_flags(const struct xt_sctp_flag_info *flag_info,
|
||||
const int flag_count,
|
||||
u_int8_t chunktype,
|
||||
u_int8_t chunkflags)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < flag_count; i++) {
|
||||
if (flag_info[i].chunktype == chunktype) {
|
||||
return (chunkflags & flag_info[i].flag_mask) == flag_info[i].flag;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
match_packet(const struct sk_buff *skb,
|
||||
unsigned int offset,
|
||||
const u_int32_t *chunkmap,
|
||||
int chunk_match_type,
|
||||
const struct xt_sctp_flag_info *flag_info,
|
||||
const int flag_count,
|
||||
int *hotdrop)
|
||||
{
|
||||
u_int32_t chunkmapcopy[256 / sizeof (u_int32_t)];
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
|
||||
#ifdef DEBUG_SCTP
|
||||
int i = 0;
|
||||
#endif
|
||||
|
||||
if (chunk_match_type == SCTP_CHUNK_MATCH_ALL) {
|
||||
SCTP_CHUNKMAP_COPY(chunkmapcopy, chunkmap);
|
||||
}
|
||||
|
||||
do {
|
||||
sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch);
|
||||
if (sch == NULL || sch->length == 0) {
|
||||
duprintf("Dropping invalid SCTP packet.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
duprintf("Chunk num: %d\toffset: %d\ttype: %d\tlength: %d\tflags: %x\n",
|
||||
++i, offset, sch->type, htons(sch->length), sch->flags);
|
||||
|
||||
offset += (ntohs(sch->length) + 3) & ~3;
|
||||
|
||||
duprintf("skb->len: %d\toffset: %d\n", skb->len, offset);
|
||||
|
||||
if (SCTP_CHUNKMAP_IS_SET(chunkmap, sch->type)) {
|
||||
switch (chunk_match_type) {
|
||||
case SCTP_CHUNK_MATCH_ANY:
|
||||
if (match_flags(flag_info, flag_count,
|
||||
sch->type, sch->flags)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCTP_CHUNK_MATCH_ALL:
|
||||
if (match_flags(flag_info, flag_count,
|
||||
sch->type, sch->flags)) {
|
||||
SCTP_CHUNKMAP_CLEAR(chunkmapcopy, sch->type);
|
||||
}
|
||||
break;
|
||||
|
||||
case SCTP_CHUNK_MATCH_ONLY:
|
||||
if (!match_flags(flag_info, flag_count,
|
||||
sch->type, sch->flags)) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (chunk_match_type) {
|
||||
case SCTP_CHUNK_MATCH_ONLY:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} while (offset < skb->len);
|
||||
|
||||
switch (chunk_match_type) {
|
||||
case SCTP_CHUNK_MATCH_ALL:
|
||||
return SCTP_CHUNKMAP_IS_CLEAR(chunkmap);
|
||||
case SCTP_CHUNK_MATCH_ANY:
|
||||
return 0;
|
||||
case SCTP_CHUNK_MATCH_ONLY:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This will never be reached, but required to stop compiler whine */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_sctp_info *info = matchinfo;
|
||||
sctp_sctphdr_t _sh, *sh;
|
||||
|
||||
if (offset) {
|
||||
duprintf("Dropping non-first fragment.. FIXME\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sh = skb_header_pointer(skb, protoff, sizeof(_sh), &_sh);
|
||||
if (sh == NULL) {
|
||||
duprintf("Dropping evil TCP offset=0 tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
duprintf("spt: %d\tdpt: %d\n", ntohs(sh->source), ntohs(sh->dest));
|
||||
|
||||
return SCCHECK(((ntohs(sh->source) >= info->spts[0])
|
||||
&& (ntohs(sh->source) <= info->spts[1])),
|
||||
XT_SCTP_SRC_PORTS, info->flags, info->invflags)
|
||||
&& SCCHECK(((ntohs(sh->dest) >= info->dpts[0])
|
||||
&& (ntohs(sh->dest) <= info->dpts[1])),
|
||||
XT_SCTP_DEST_PORTS, info->flags, info->invflags)
|
||||
&& SCCHECK(match_packet(skb, protoff + sizeof (sctp_sctphdr_t),
|
||||
info->chunkmap, info->chunk_match_type,
|
||||
info->flag_info, info->flag_count,
|
||||
hotdrop),
|
||||
XT_SCTP_CHUNK_TYPES, info->flags, info->invflags);
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_sctp_info *info = matchinfo;
|
||||
|
||||
return !(info->flags & ~XT_SCTP_VALID_FLAGS)
|
||||
&& !(info->invflags & ~XT_SCTP_VALID_FLAGS)
|
||||
&& !(info->invflags & ~info->flags)
|
||||
&& ((!(info->flags & XT_SCTP_CHUNK_TYPES)) ||
|
||||
(info->chunk_match_type &
|
||||
(SCTP_CHUNK_MATCH_ALL
|
||||
| SCTP_CHUNK_MATCH_ANY
|
||||
| SCTP_CHUNK_MATCH_ONLY)));
|
||||
}
|
||||
|
||||
static struct xt_match xt_sctp_match[] = {
|
||||
{
|
||||
.name = "sctp",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_sctp_info),
|
||||
.proto = IPPROTO_SCTP,
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "sctp",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_sctp_info),
|
||||
.proto = IPPROTO_SCTP,
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_sctp_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_sctp_match, ARRAY_SIZE(xt_sctp_match));
|
||||
}
|
||||
|
||||
static void __exit xt_sctp_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_sctp_match, ARRAY_SIZE(xt_sctp_match));
|
||||
}
|
||||
|
||||
module_init(xt_sctp_init);
|
||||
module_exit(xt_sctp_fini);
|
||||
99
net/netfilter/xt_state.c
Normal file
99
net/netfilter/xt_state.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/* Kernel module to match connection tracking information. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2005 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/netfilter/nf_conntrack_compat.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_state.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
|
||||
MODULE_DESCRIPTION("ip[6]_tables connection tracking state match module");
|
||||
MODULE_ALIAS("ipt_state");
|
||||
MODULE_ALIAS("ip6t_state");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_state_info *sinfo = matchinfo;
|
||||
enum ip_conntrack_info ctinfo;
|
||||
unsigned int statebit;
|
||||
|
||||
if (nf_ct_is_untracked(skb))
|
||||
statebit = XT_STATE_UNTRACKED;
|
||||
else if (!nf_ct_get_ctinfo(skb, &ctinfo))
|
||||
statebit = XT_STATE_INVALID;
|
||||
else
|
||||
statebit = XT_STATE_BIT(ctinfo);
|
||||
|
||||
return (sinfo->statemask & statebit);
|
||||
}
|
||||
|
||||
static int check(const char *tablename,
|
||||
const void *inf,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
if (nf_ct_l3proto_try_module_get(match->family) < 0) {
|
||||
printk(KERN_WARNING "can't load conntrack support for "
|
||||
"proto=%d\n", match->family);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
nf_ct_l3proto_module_put(match->family);
|
||||
}
|
||||
|
||||
static struct xt_match xt_state_match[] = {
|
||||
{
|
||||
.name = "state",
|
||||
.family = AF_INET,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_state_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "state",
|
||||
.family = AF_INET6,
|
||||
.checkentry = check,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_state_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_state_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_state_match, ARRAY_SIZE(xt_state_match));
|
||||
}
|
||||
|
||||
static void __exit xt_state_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_state_match, ARRAY_SIZE(xt_state_match));
|
||||
}
|
||||
|
||||
module_init(xt_state_init);
|
||||
module_exit(xt_state_fini);
|
||||
101
net/netfilter/xt_statistic.c
Normal file
101
net/netfilter/xt_statistic.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Patrick McHardy <kaber@trash.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Based on ipt_random and ipt_nth by Fabrice MARIE <fabrice@netfilter.org>.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/net.h>
|
||||
|
||||
#include <linux/netfilter/xt_statistic.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
|
||||
MODULE_DESCRIPTION("xtables statistical match module");
|
||||
MODULE_ALIAS("ipt_statistic");
|
||||
MODULE_ALIAS("ip6t_statistic");
|
||||
|
||||
static DEFINE_SPINLOCK(nth_lock);
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
const struct xt_match *match, const void *matchinfo,
|
||||
int offset, unsigned int protoff, int *hotdrop)
|
||||
{
|
||||
struct xt_statistic_info *info = (struct xt_statistic_info *)matchinfo;
|
||||
int ret = info->flags & XT_STATISTIC_INVERT ? 1 : 0;
|
||||
|
||||
switch (info->mode) {
|
||||
case XT_STATISTIC_MODE_RANDOM:
|
||||
if ((net_random() & 0x7FFFFFFF) < info->u.random.probability)
|
||||
ret ^= 1;
|
||||
break;
|
||||
case XT_STATISTIC_MODE_NTH:
|
||||
info = info->master;
|
||||
spin_lock_bh(&nth_lock);
|
||||
if (info->u.nth.count++ == info->u.nth.every) {
|
||||
info->u.nth.count = 0;
|
||||
ret ^= 1;
|
||||
}
|
||||
spin_unlock_bh(&nth_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
checkentry(const char *tablename, const void *entry,
|
||||
const struct xt_match *match, void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_statistic_info *info = (struct xt_statistic_info *)matchinfo;
|
||||
|
||||
if (info->mode > XT_STATISTIC_MODE_MAX ||
|
||||
info->flags & ~XT_STATISTIC_MASK)
|
||||
return 0;
|
||||
info->master = info;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct xt_match xt_statistic_match[] = {
|
||||
{
|
||||
.name = "statistic",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_statistic_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "statistic",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_statistic_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_statistic_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_statistic_match,
|
||||
ARRAY_SIZE(xt_statistic_match));
|
||||
}
|
||||
|
||||
static void __exit xt_statistic_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_statistic_match,
|
||||
ARRAY_SIZE(xt_statistic_match));
|
||||
}
|
||||
|
||||
module_init(xt_statistic_init);
|
||||
module_exit(xt_statistic_fini);
|
||||
108
net/netfilter/xt_string.c
Normal file
108
net/netfilter/xt_string.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/* String matching match for iptables
|
||||
*
|
||||
* (C) 2005 Pablo Neira Ayuso <pablo@eurodev.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_string.h>
|
||||
#include <linux/textsearch.h>
|
||||
|
||||
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@eurodev.net>");
|
||||
MODULE_DESCRIPTION("IP tables string match module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_string");
|
||||
MODULE_ALIAS("ip6t_string");
|
||||
|
||||
static int match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_string_info *conf = matchinfo;
|
||||
struct ts_state state;
|
||||
|
||||
memset(&state, 0, sizeof(struct ts_state));
|
||||
|
||||
return (skb_find_text((struct sk_buff *)skb, conf->from_offset,
|
||||
conf->to_offset, conf->config, &state)
|
||||
!= UINT_MAX) ^ conf->invert;
|
||||
}
|
||||
|
||||
#define STRING_TEXT_PRIV(m) ((struct xt_string_info *) m)
|
||||
|
||||
static int checkentry(const char *tablename,
|
||||
const void *ip,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
struct xt_string_info *conf = matchinfo;
|
||||
struct ts_config *ts_conf;
|
||||
|
||||
/* Damn, can't handle this case properly with iptables... */
|
||||
if (conf->from_offset > conf->to_offset)
|
||||
return 0;
|
||||
if (conf->algo[XT_STRING_MAX_ALGO_NAME_SIZE - 1] != '\0')
|
||||
return 0;
|
||||
if (conf->patlen > XT_STRING_MAX_PATTERN_SIZE)
|
||||
return 0;
|
||||
ts_conf = textsearch_prepare(conf->algo, conf->pattern, conf->patlen,
|
||||
GFP_KERNEL, TS_AUTOLOAD);
|
||||
if (IS_ERR(ts_conf))
|
||||
return 0;
|
||||
|
||||
conf->config = ts_conf;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void destroy(const struct xt_match *match, void *matchinfo)
|
||||
{
|
||||
textsearch_destroy(STRING_TEXT_PRIV(matchinfo)->config);
|
||||
}
|
||||
|
||||
static struct xt_match xt_string_match[] = {
|
||||
{
|
||||
.name = "string",
|
||||
.family = AF_INET,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_string_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
{
|
||||
.name = "string",
|
||||
.family = AF_INET6,
|
||||
.checkentry = checkentry,
|
||||
.match = match,
|
||||
.destroy = destroy,
|
||||
.matchsize = sizeof(struct xt_string_info),
|
||||
.me = THIS_MODULE
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_string_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_string_match, ARRAY_SIZE(xt_string_match));
|
||||
}
|
||||
|
||||
static void __exit xt_string_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_string_match, ARRAY_SIZE(xt_string_match));
|
||||
}
|
||||
|
||||
module_init(xt_string_init);
|
||||
module_exit(xt_string_fini);
|
||||
115
net/netfilter/xt_tcpmss.c
Normal file
115
net/netfilter/xt_tcpmss.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/* Kernel module to match TCP MSS values. */
|
||||
|
||||
/* Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
|
||||
* Portions (C) 2005 by Harald Welte <laforge@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <linux/netfilter/xt_tcpmss.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
|
||||
MODULE_DESCRIPTION("iptables TCP MSS match module");
|
||||
MODULE_ALIAS("ipt_tcpmss");
|
||||
|
||||
static int
|
||||
match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
const struct xt_tcpmss_match_info *info = matchinfo;
|
||||
struct tcphdr _tcph, *th;
|
||||
/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
|
||||
u8 _opt[15 * 4 - sizeof(_tcph)], *op;
|
||||
unsigned int i, optlen;
|
||||
|
||||
/* If we don't have the whole header, drop packet. */
|
||||
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
goto dropit;
|
||||
|
||||
/* Malformed. */
|
||||
if (th->doff*4 < sizeof(*th))
|
||||
goto dropit;
|
||||
|
||||
optlen = th->doff*4 - sizeof(*th);
|
||||
if (!optlen)
|
||||
goto out;
|
||||
|
||||
/* Truncated options. */
|
||||
op = skb_header_pointer(skb, protoff + sizeof(*th), optlen, _opt);
|
||||
if (op == NULL)
|
||||
goto dropit;
|
||||
|
||||
for (i = 0; i < optlen; ) {
|
||||
if (op[i] == TCPOPT_MSS
|
||||
&& (optlen - i) >= TCPOLEN_MSS
|
||||
&& op[i+1] == TCPOLEN_MSS) {
|
||||
u_int16_t mssval;
|
||||
|
||||
mssval = (op[i+2] << 8) | op[i+3];
|
||||
|
||||
return (mssval >= info->mss_min &&
|
||||
mssval <= info->mss_max) ^ info->invert;
|
||||
}
|
||||
if (op[i] < 2)
|
||||
i++;
|
||||
else
|
||||
i += op[i+1] ? : 1;
|
||||
}
|
||||
out:
|
||||
return info->invert;
|
||||
|
||||
dropit:
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xt_match xt_tcpmss_match[] = {
|
||||
{
|
||||
.name = "tcpmss",
|
||||
.family = AF_INET,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_tcpmss_match_info),
|
||||
.proto = IPPROTO_TCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "tcpmss",
|
||||
.family = AF_INET6,
|
||||
.match = match,
|
||||
.matchsize = sizeof(struct xt_tcpmss_match_info),
|
||||
.proto = IPPROTO_TCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_tcpmss_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_tcpmss_match,
|
||||
ARRAY_SIZE(xt_tcpmss_match));
|
||||
}
|
||||
|
||||
static void __exit xt_tcpmss_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_tcpmss_match, ARRAY_SIZE(xt_tcpmss_match));
|
||||
}
|
||||
|
||||
module_init(xt_tcpmss_init);
|
||||
module_exit(xt_tcpmss_fini);
|
||||
269
net/netfilter/xt_tcpudp.c
Normal file
269
net/netfilter/xt_tcpudp.c
Normal file
@@ -0,0 +1,269 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/udp.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/netfilter/xt_tcpudp.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
|
||||
MODULE_DESCRIPTION("x_tables match for TCP and UDP(-Lite), supports IPv4 and IPv6");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("xt_tcp");
|
||||
MODULE_ALIAS("xt_udp");
|
||||
MODULE_ALIAS("ipt_udp");
|
||||
MODULE_ALIAS("ipt_tcp");
|
||||
MODULE_ALIAS("ip6t_udp");
|
||||
MODULE_ALIAS("ip6t_tcp");
|
||||
|
||||
#ifdef DEBUG_IP_FIREWALL_USER
|
||||
#define duprintf(format, args...) printk(format , ## args)
|
||||
#else
|
||||
#define duprintf(format, args...)
|
||||
#endif
|
||||
|
||||
|
||||
/* Returns 1 if the port is matched by the range, 0 otherwise */
|
||||
static inline int
|
||||
port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (port >= min && port <= max) ^ invert;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
tcp_find_option(u_int8_t option,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int protoff,
|
||||
unsigned int optlen,
|
||||
int invert,
|
||||
int *hotdrop)
|
||||
{
|
||||
/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
|
||||
u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
|
||||
unsigned int i;
|
||||
|
||||
duprintf("tcp_match: finding option\n");
|
||||
|
||||
if (!optlen)
|
||||
return invert;
|
||||
|
||||
/* If we don't have the whole header, drop packet. */
|
||||
op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
|
||||
optlen, _opt);
|
||||
if (op == NULL) {
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < optlen; ) {
|
||||
if (op[i] == option) return !invert;
|
||||
if (op[i] < 2) i++;
|
||||
else i += op[i+1]?:1;
|
||||
}
|
||||
|
||||
return invert;
|
||||
}
|
||||
|
||||
static int
|
||||
tcp_match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
struct tcphdr _tcph, *th;
|
||||
const struct xt_tcp *tcpinfo = matchinfo;
|
||||
|
||||
if (offset) {
|
||||
/* To quote Alan:
|
||||
|
||||
Don't allow a fragment of TCP 8 bytes in. Nobody normal
|
||||
causes this. Its a cracker trying to break in by doing a
|
||||
flag overwrite to pass the direction checks.
|
||||
*/
|
||||
if (offset == 1) {
|
||||
duprintf("Dropping evil TCP offset=1 frag.\n");
|
||||
*hotdrop = 1;
|
||||
}
|
||||
/* Must not be a fragment. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
|
||||
|
||||
th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL) {
|
||||
/* We've been asked to examine this packet, and we
|
||||
can't. Hence, no choice but to drop. */
|
||||
duprintf("Dropping evil TCP offset=0 tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
|
||||
ntohs(th->source),
|
||||
!!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
|
||||
return 0;
|
||||
if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
|
||||
ntohs(th->dest),
|
||||
!!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
|
||||
return 0;
|
||||
if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
|
||||
== tcpinfo->flg_cmp,
|
||||
XT_TCP_INV_FLAGS))
|
||||
return 0;
|
||||
if (tcpinfo->option) {
|
||||
if (th->doff * 4 < sizeof(_tcph)) {
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
if (!tcp_find_option(tcpinfo->option, skb, protoff,
|
||||
th->doff*4 - sizeof(_tcph),
|
||||
tcpinfo->invflags & XT_TCP_INV_OPTION,
|
||||
hotdrop))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Called when user tries to insert an entry of this type. */
|
||||
static int
|
||||
tcp_checkentry(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_tcp *tcpinfo = matchinfo;
|
||||
|
||||
/* Must specify no unknown invflags */
|
||||
return !(tcpinfo->invflags & ~XT_TCP_INV_MASK);
|
||||
}
|
||||
|
||||
static int
|
||||
udp_match(const struct sk_buff *skb,
|
||||
const struct net_device *in,
|
||||
const struct net_device *out,
|
||||
const struct xt_match *match,
|
||||
const void *matchinfo,
|
||||
int offset,
|
||||
unsigned int protoff,
|
||||
int *hotdrop)
|
||||
{
|
||||
struct udphdr _udph, *uh;
|
||||
const struct xt_udp *udpinfo = matchinfo;
|
||||
|
||||
/* Must not be a fragment. */
|
||||
if (offset)
|
||||
return 0;
|
||||
|
||||
uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
|
||||
if (uh == NULL) {
|
||||
/* We've been asked to examine this packet, and we
|
||||
can't. Hence, no choice but to drop. */
|
||||
duprintf("Dropping evil UDP tinygram.\n");
|
||||
*hotdrop = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return port_match(udpinfo->spts[0], udpinfo->spts[1],
|
||||
ntohs(uh->source),
|
||||
!!(udpinfo->invflags & XT_UDP_INV_SRCPT))
|
||||
&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
|
||||
ntohs(uh->dest),
|
||||
!!(udpinfo->invflags & XT_UDP_INV_DSTPT));
|
||||
}
|
||||
|
||||
/* Called when user tries to insert an entry of this type. */
|
||||
static int
|
||||
udp_checkentry(const char *tablename,
|
||||
const void *info,
|
||||
const struct xt_match *match,
|
||||
void *matchinfo,
|
||||
unsigned int hook_mask)
|
||||
{
|
||||
const struct xt_tcp *udpinfo = matchinfo;
|
||||
|
||||
/* Must specify no unknown invflags */
|
||||
return !(udpinfo->invflags & ~XT_UDP_INV_MASK);
|
||||
}
|
||||
|
||||
static struct xt_match xt_tcpudp_match[] = {
|
||||
{
|
||||
.name = "tcp",
|
||||
.family = AF_INET,
|
||||
.checkentry = tcp_checkentry,
|
||||
.match = tcp_match,
|
||||
.matchsize = sizeof(struct xt_tcp),
|
||||
.proto = IPPROTO_TCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "tcp",
|
||||
.family = AF_INET6,
|
||||
.checkentry = tcp_checkentry,
|
||||
.match = tcp_match,
|
||||
.matchsize = sizeof(struct xt_tcp),
|
||||
.proto = IPPROTO_TCP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "udp",
|
||||
.family = AF_INET,
|
||||
.checkentry = udp_checkentry,
|
||||
.match = udp_match,
|
||||
.matchsize = sizeof(struct xt_udp),
|
||||
.proto = IPPROTO_UDP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "udp",
|
||||
.family = AF_INET6,
|
||||
.checkentry = udp_checkentry,
|
||||
.match = udp_match,
|
||||
.matchsize = sizeof(struct xt_udp),
|
||||
.proto = IPPROTO_UDP,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "udplite",
|
||||
.family = AF_INET,
|
||||
.checkentry = udp_checkentry,
|
||||
.match = udp_match,
|
||||
.matchsize = sizeof(struct xt_udp),
|
||||
.proto = IPPROTO_UDPLITE,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "udplite",
|
||||
.family = AF_INET6,
|
||||
.checkentry = udp_checkentry,
|
||||
.match = udp_match,
|
||||
.matchsize = sizeof(struct xt_udp),
|
||||
.proto = IPPROTO_UDPLITE,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xt_tcpudp_init(void)
|
||||
{
|
||||
return xt_register_matches(xt_tcpudp_match,
|
||||
ARRAY_SIZE(xt_tcpudp_match));
|
||||
}
|
||||
|
||||
static void __exit xt_tcpudp_fini(void)
|
||||
{
|
||||
xt_unregister_matches(xt_tcpudp_match, ARRAY_SIZE(xt_tcpudp_match));
|
||||
}
|
||||
|
||||
module_init(xt_tcpudp_init);
|
||||
module_exit(xt_tcpudp_fini);
|
||||
Reference in New Issue
Block a user