Commit f631723a authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] netfilter: Remove IPCHAINS and IPFWADM compatibility

We've been threatening to do this for ages: remove the backwards compatibility
code.  We can now combine ip_conntrack_core.c and ip_conntrack_standalone.c,
likewise for the NAT code, but that will come later.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6dd1537e
/* Minor modifications to fit on compatibility framework:
Rusty.Russell@rustcorp.com.au
*/
#ifndef __LINUX_FIREWALL_H
#define __LINUX_FIREWALL_H
/*
* Definitions for loadable firewall modules
*/
#define FW_QUEUE 0
#define FW_BLOCK 1
#define FW_ACCEPT 2
#define FW_REJECT (-1)
#define FW_REDIRECT 3
#define FW_MASQUERADE 4
#define FW_SKIP 5
struct firewall_ops
{
struct firewall_ops *next;
int (*fw_forward)(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb);
int (*fw_input)(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb);
int (*fw_output)(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb);
/* These may be NULL. */
int (*fw_acct_in)(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb);
int (*fw_acct_out)(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb);
};
extern int register_firewall(int pf, struct firewall_ops *fw);
extern int unregister_firewall(int pf, struct firewall_ops *fw);
extern int ip_fw_masq_timeouts(void *user, int len);
#endif /* __LINUX_FIREWALL_H */
/*
* This code is heavily based on the code in ip_fw.h; see that file for
* copyrights and attributions. This code is basically GPL.
*
* 15-Feb-1997: Major changes to allow graphs for firewall rules.
* Paul Russell <Paul.Russell@rustcorp.com.au> and
* Michael Neuling <Michael.Neuling@rustcorp.com.au>
* 2-Nov-1997: Changed types to __u16, etc.
* Removed IP_FW_F_TCPACK & IP_FW_F_BIDIR.
* Added inverse flags field.
* Removed multiple port specs.
*/
/*
* Format of an IP firewall descriptor
*
* src, dst, src_mask, dst_mask are always stored in network byte order.
* flags are stored in host byte order (of course).
* Port numbers are stored in HOST byte order.
*/
#ifndef _IP_FWCHAINS_H
#define _IP_FWCHAINS_H
#ifdef __KERNEL__
#include <linux/icmp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#endif /* __KERNEL__ */
#define IP_FW_MAX_LABEL_LENGTH 8
typedef char ip_chainlabel[IP_FW_MAX_LABEL_LENGTH+1];
struct ip_fw
{
struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
__u32 fw_mark; /* ID to stamp on packet */
__u16 fw_proto; /* Protocol, 0 = ANY */
__u16 fw_flg; /* Flags word */
__u16 fw_invflg; /* Inverse flags */
__u16 fw_spts[2]; /* Source port range. */
__u16 fw_dpts[2]; /* Destination port range. */
__u16 fw_redirpt; /* Port to redirect to. */
__u16 fw_outputsize; /* Max amount to output to
NETLINK */
char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
__u8 fw_tosand, fw_tosxor; /* Revised packet priority */
};
struct ip_fwuser
{
struct ip_fw ipfw;
ip_chainlabel label;
};
/* Values for "fw_flg" field . */
#define IP_FW_F_PRN 0x0001 /* Print packet if it matches */
#define IP_FW_F_TCPSYN 0x0002 /* For tcp packets-check SYN only */
#define IP_FW_F_FRAG 0x0004 /* Set if rule is a fragment rule */
#define IP_FW_F_MARKABS 0x0008 /* Set the mark to fw_mark, not add. */
#define IP_FW_F_WILDIF 0x0010 /* Need only match start of interface name. */
#define IP_FW_F_NETLINK 0x0020 /* Redirect to netlink: 2.1.x only */
#define IP_FW_F_MASK 0x003F /* All possible flag bits mask */
/* Values for "fw_invflg" field. */
#define IP_FW_INV_SRCIP 0x0001 /* Invert the sense of fw_src. */
#define IP_FW_INV_DSTIP 0x0002 /* Invert the sense of fw_dst. */
#define IP_FW_INV_PROTO 0x0004 /* Invert the sense of fw_proto. */
#define IP_FW_INV_SRCPT 0x0008 /* Invert the sense of source ports. */
#define IP_FW_INV_DSTPT 0x0010 /* Invert the sense of destination ports. */
#define IP_FW_INV_VIA 0x0020 /* Invert the sense of fw_vianame. */
#define IP_FW_INV_SYN 0x0040 /* Invert the sense of IP_FW_F_TCPSYN. */
#define IP_FW_INV_FRAG 0x0080 /* Invert the sense of IP_FW_F_FRAG. */
/*
* New IP firewall options for [gs]etsockopt at the RAW IP level.
* Unlike BSD Linux inherits IP options so you don't have to use
* a raw socket for this. Instead we check rights in the calls. */
#define IP_FW_BASE_CTL 64 /* base for firewall socket options */
#define IP_FW_APPEND (IP_FW_BASE_CTL) /* Takes ip_fwchange */
#define IP_FW_REPLACE (IP_FW_BASE_CTL+1) /* Takes ip_fwnew */
#define IP_FW_DELETE_NUM (IP_FW_BASE_CTL+2) /* Takes ip_fwdelnum */
#define IP_FW_DELETE (IP_FW_BASE_CTL+3) /* Takes ip_fwchange */
#define IP_FW_INSERT (IP_FW_BASE_CTL+4) /* Takes ip_fwnew */
#define IP_FW_FLUSH (IP_FW_BASE_CTL+5) /* Takes ip_chainlabel */
#define IP_FW_ZERO (IP_FW_BASE_CTL+6) /* Takes ip_chainlabel */
#define IP_FW_CHECK (IP_FW_BASE_CTL+7) /* Takes ip_fwtest */
#define IP_FW_MASQ_TIMEOUTS (IP_FW_BASE_CTL+8) /* Takes 3 ints */
#define IP_FW_CREATECHAIN (IP_FW_BASE_CTL+9) /* Takes ip_chainlabel */
#define IP_FW_DELETECHAIN (IP_FW_BASE_CTL+10) /* Takes ip_chainlabel */
#define IP_FW_POLICY (IP_FW_BASE_CTL+11) /* Takes ip_fwpolicy */
/* Masquerade control, only 1 optname */
#define IP_FW_MASQ_CTL (IP_FW_BASE_CTL+12) /* General ip_masq ctl */
/* Builtin chain labels */
#define IP_FW_LABEL_FORWARD "forward"
#define IP_FW_LABEL_INPUT "input"
#define IP_FW_LABEL_OUTPUT "output"
/* Special targets */
#define IP_FW_LABEL_MASQUERADE "MASQ"
#define IP_FW_LABEL_REDIRECT "REDIRECT"
#define IP_FW_LABEL_ACCEPT "ACCEPT"
#define IP_FW_LABEL_BLOCK "DENY"
#define IP_FW_LABEL_REJECT "REJECT"
#define IP_FW_LABEL_RETURN "RETURN"
#define IP_FW_LABEL_QUEUE "QUEUE"
/* Files in /proc/net */
#define IP_FW_PROC_CHAINS "ip_fwchains"
#define IP_FW_PROC_CHAIN_NAMES "ip_fwnames"
struct ip_fwpkt
{
struct iphdr fwp_iph; /* IP header */
union {
struct tcphdr fwp_tcph; /* TCP header or */
struct udphdr fwp_udph; /* UDP header */
struct icmphdr fwp_icmph; /* ICMP header */
} fwp_protoh;
struct in_addr fwp_via; /* interface address */
char fwp_vianame[IFNAMSIZ]; /* interface name */
};
/* The argument to IP_FW_DELETE and IP_FW_APPEND */
struct ip_fwchange
{
struct ip_fwuser fwc_rule;
ip_chainlabel fwc_label;
};
/* The argument to IP_FW_CHECK. */
struct ip_fwtest
{
struct ip_fwpkt fwt_packet; /* Packet to be tested */
ip_chainlabel fwt_label; /* Block to start test in */
};
/* The argument to IP_FW_DELETE_NUM */
struct ip_fwdelnum
{
__u32 fwd_rulenum;
ip_chainlabel fwd_label;
};
/* The argument to IP_FW_REPLACE and IP_FW_INSERT */
struct ip_fwnew
{
__u32 fwn_rulenum;
struct ip_fwuser fwn_rule;
ip_chainlabel fwn_label;
};
/* The argument to IP_FW_POLICY */
struct ip_fwpolicy
{
ip_chainlabel fwp_policy;
ip_chainlabel fwp_label;
};
/*
* timeouts for ip masquerading
*/
extern int ip_fw_masq_timeouts(void *, int);
/*
* Main firewall chains definitions and global var's definitions.
*/
#ifdef __KERNEL__
#include <linux/config.h>
#include <linux/version.h>
#include <linux/init.h>
extern void ip_fw_init(void) __init;
extern int ip_fw_ctl(int, void *, int);
#ifdef CONFIG_IP_MASQUERADE
extern int ip_masq_uctl(int, char *, int);
#endif
#endif /* KERNEL */
#endif /* _IP_FWCHAINS_H */
#ifndef _IPFWADM_CORE_H
#define _IPFWADM_CORE_H
/* Minor modifications to fit on compatibility framework:
Rusty.Russell@rustcorp.com.au
*/
/*
* IP firewalling code. This is taken from 4.4BSD. Please note the
* copyright message below. As per the GPL it must be maintained
* and the licenses thus do not conflict. While this port is subject
* to the GPL I also place my modifications under the original
* license in recognition of the original copyright.
*
* Ported from BSD to Linux,
* Alan Cox 22/Nov/1994.
* Merged and included the FreeBSD-Current changes at Ugen's request
* (but hey it's a lot cleaner now). Ugen would prefer in some ways
* we waited for his final product but since Linux 1.2.0 is about to
* appear it's not practical - Read: It works, it's not clean but please
* don't consider it to be his standard of finished work.
* Alan.
*
* Fixes:
* Pauline Middelink : Added masquerading.
* Jos Vos : Separate input and output firewall
* chains, new "insert" and "append"
* commands to replace "add" commands,
* add ICMP header to struct ip_fwpkt.
* Jos Vos : Add support for matching device names.
* Willy Konynenberg : Add transparent proxying support.
* Jos Vos : Add options for input/output accounting.
*
* All the real work was done by .....
*/
/*
* Copyright (c) 1993 Daniel Boulet
* Copyright (c) 1994 Ugen J.S.Antsilevich
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
/*
* Format of an IP firewall descriptor
*
* src, dst, src_mask, dst_mask are always stored in network byte order.
* flags and num_*_ports are stored in host byte order (of course).
* Port numbers are stored in HOST byte order.
*/
#ifdef __KERNEL__
#include <linux/icmp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#endif
struct ip_fw
{
struct ip_fw *fw_next; /* Next firewall on chain */
struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
struct in_addr fw_via; /* IP address of interface "via" */
struct net_device *fw_viadev; /* device of interface "via" */
__u16 fw_flg; /* Flags word */
__u16 fw_nsp, fw_ndp; /* N'of src ports and # of dst ports */
/* in ports array (dst ports follow */
/* src ports; max of 10 ports in all; */
/* count of 0 means match all ports) */
#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */
__u16 fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
unsigned long fw_pcnt,fw_bcnt; /* Packet and byte counters */
__u8 fw_tosand, fw_tosxor; /* Revised packet priority */
char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
};
/*
* Values for "flags" field .
*/
#define IP_FW_F_ALL 0x0000 /* This is a universal packet firewall*/
#define IP_FW_F_TCP 0x0001 /* This is a TCP packet firewall */
#define IP_FW_F_UDP 0x0002 /* This is a UDP packet firewall */
#define IP_FW_F_ICMP 0x0003 /* This is a ICMP packet firewall */
#define IP_FW_F_KIND 0x0003 /* Mask to isolate firewall kind */
#define IP_FW_F_ACCEPT 0x0004 /* This is an accept firewall (as *
* opposed to a deny firewall)*
* */
#define IP_FW_F_SRNG 0x0008 /* The first two src ports are a min *
* and max range (stored in host byte *
* order). *
* */
#define IP_FW_F_DRNG 0x0010 /* The first two dst ports are a min *
* and max range (stored in host byte *
* order). *
* (ports[0] <= port <= ports[1]) *
* */
#define IP_FW_F_PRN 0x0020 /* In verbose mode print this firewall*/
#define IP_FW_F_BIDIR 0x0040 /* For bidirectional firewalls */
#define IP_FW_F_TCPSYN 0x0080 /* For tcp packets-check SYN only */
#define IP_FW_F_ICMPRPL 0x0100 /* Send back icmp unreachable packet */
#define IP_FW_F_MASQ 0x0200 /* Masquerading */
#define IP_FW_F_TCPACK 0x0400 /* For tcp-packets match if ACK is set*/
#define IP_FW_F_REDIR 0x0800 /* Redirect to local port fw_pts[n] */
#define IP_FW_F_ACCTIN 0x1000 /* Account incoming packets only. */
#define IP_FW_F_ACCTOUT 0x2000 /* Account outgoing packets only. */
#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */
/*
* New IP firewall options for [gs]etsockopt at the RAW IP level.
* Unlike BSD Linux inherits IP options so you don't have to use
* a raw socket for this. Instead we check rights in the calls.
*/
#define IP_FW_BASE_CTL 64 /* base for firewall socket options */
#define IP_FW_COMMAND 0x00FF /* mask for command without chain */
#define IP_FW_TYPE 0x0300 /* mask for type (chain) */
#define IP_FW_SHIFT 8 /* shift count for type (chain) */
#define IP_FW_FWD 0
#define IP_FW_IN 1
#define IP_FW_OUT 2
#define IP_FW_ACCT 3
#define IP_FW_CHAINS 4 /* total number of ip_fw chains */
#define IP_FW_MASQ 5
#define IP_FW_INSERT (IP_FW_BASE_CTL)
#define IP_FW_APPEND (IP_FW_BASE_CTL+1)
#define IP_FW_DELETE (IP_FW_BASE_CTL+2)
#define IP_FW_FLUSH (IP_FW_BASE_CTL+3)
#define IP_FW_ZERO (IP_FW_BASE_CTL+4)
#define IP_FW_POLICY (IP_FW_BASE_CTL+5)
#define IP_FW_CHECK (IP_FW_BASE_CTL+6)
#define IP_FW_MASQ_TIMEOUTS (IP_FW_BASE_CTL+7)
#define IP_FW_INSERT_FWD (IP_FW_INSERT | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_APPEND_FWD (IP_FW_APPEND | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_DELETE_FWD (IP_FW_DELETE | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_FLUSH_FWD (IP_FW_FLUSH | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_ZERO_FWD (IP_FW_ZERO | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_POLICY_FWD (IP_FW_POLICY | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_CHECK_FWD (IP_FW_CHECK | (IP_FW_FWD << IP_FW_SHIFT))
#define IP_FW_INSERT_IN (IP_FW_INSERT | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_APPEND_IN (IP_FW_APPEND | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_DELETE_IN (IP_FW_DELETE | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_FLUSH_IN (IP_FW_FLUSH | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_ZERO_IN (IP_FW_ZERO | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_POLICY_IN (IP_FW_POLICY | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_CHECK_IN (IP_FW_CHECK | (IP_FW_IN << IP_FW_SHIFT))
#define IP_FW_INSERT_OUT (IP_FW_INSERT | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_APPEND_OUT (IP_FW_APPEND | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_DELETE_OUT (IP_FW_DELETE | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_FLUSH_OUT (IP_FW_FLUSH | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_ZERO_OUT (IP_FW_ZERO | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_POLICY_OUT (IP_FW_POLICY | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_FW_CHECK_OUT (IP_FW_CHECK | (IP_FW_OUT << IP_FW_SHIFT))
#define IP_ACCT_INSERT (IP_FW_INSERT | (IP_FW_ACCT << IP_FW_SHIFT))
#define IP_ACCT_APPEND (IP_FW_APPEND | (IP_FW_ACCT << IP_FW_SHIFT))
#define IP_ACCT_DELETE (IP_FW_DELETE | (IP_FW_ACCT << IP_FW_SHIFT))
#define IP_ACCT_FLUSH (IP_FW_FLUSH | (IP_FW_ACCT << IP_FW_SHIFT))
#define IP_ACCT_ZERO (IP_FW_ZERO | (IP_FW_ACCT << IP_FW_SHIFT))
#define IP_FW_MASQ_INSERT (IP_FW_INSERT | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_ADD (IP_FW_APPEND | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_DEL (IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_FLUSH (IP_FW_FLUSH | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_INSERT (IP_FW_INSERT | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_ADD (IP_FW_APPEND | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_DEL (IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
#define IP_FW_MASQ_FLUSH (IP_FW_FLUSH | (IP_FW_MASQ << IP_FW_SHIFT))
struct ip_fwpkt
{
struct iphdr fwp_iph; /* IP header */
union {
struct tcphdr fwp_tcph; /* TCP header or */
struct udphdr fwp_udph; /* UDP header */
struct icmphdr fwp_icmph; /* ICMP header */
} fwp_protoh;
struct in_addr fwp_via; /* interface address */
char fwp_vianame[IFNAMSIZ]; /* interface name */
};
#define IP_FW_MASQCTL_MAX 256
#define IP_MASQ_MOD_NMAX 32
struct ip_fw_masqctl
{
int mctl_action;
union {
struct {
char name[IP_MASQ_MOD_NMAX];
char data[1];
} mod;
} u;
};
/*
* timeouts for ip masquerading
*/
struct ip_fw_masq;
/*
* Main firewall chains definitions and global var's definitions.
*/
#ifdef __KERNEL__
/* Modes used in the ip_fw_chk() routine. */
#define IP_FW_MODE_FW 0x00 /* kernel firewall check */
#define IP_FW_MODE_ACCT_IN 0x01 /* accounting (incoming) */
#define IP_FW_MODE_ACCT_OUT 0x02 /* accounting (outgoing) */
#define IP_FW_MODE_CHK 0x04 /* check requested by user */
#include <linux/config.h>
#ifdef CONFIG_IP_FIREWALL
extern struct ip_fw *ip_fw_in_chain;
extern struct ip_fw *ip_fw_out_chain;
extern struct ip_fw *ip_fw_fwd_chain;
extern int ip_fw_in_policy;
extern int ip_fw_out_policy;
extern int ip_fw_fwd_policy;
extern int ip_fw_ctl(int, void *, int);
#endif
#ifdef CONFIG_IP_ACCT
extern struct ip_fw *ip_acct_chain;
extern int ip_acct_ctl(int, void *, int);
#endif
#ifdef CONFIG_IP_MASQUERADE
extern int ip_masq_ctl(int, void *, int);
#endif
#ifdef CONFIG_IP_MASQUERADE
extern int ip_masq_ctl(int, void *, int);
#endif
extern int ip_fw_masq_timeouts(void *user, int len);
extern int ip_fw_chk(struct sk_buff **, struct net_device *, __u16 *,
struct ip_fw *, int, int);
#endif /* KERNEL */
#endif /* _IP_FW_H */
......@@ -458,7 +458,7 @@ config IP_NF_NAT
config IP_NF_NAT_NEEDED
bool
depends on IP_NF_CONNTRACK!=y && IP_NF_IPTABLES!=y && (IP_NF_COMPAT_IPCHAINS!=y && IP_NF_COMPAT_IPFWADM || IP_NF_COMPAT_IPCHAINS) || IP_NF_IPTABLES && IP_NF_CONNTRACK && IP_NF_NAT
depends on IP_NF_NAT != n
default y
config IP_NF_TARGET_MASQUERADE
......@@ -693,30 +693,5 @@ config IP_NF_ARP_MANGLE
Allows altering the ARP packet payload: source and destination
hardware and network addresses.
# Backwards compatibility modules: only if you don't build in the others.
config IP_NF_COMPAT_IPCHAINS
tristate "ipchains (2.2-style) support"
depends on IP_NF_CONNTRACK!=y && IP_NF_IPTABLES!=y
help
This option places ipchains (with masquerading and redirection
support) back into the kernel, using the new netfilter
infrastructure. It is not recommended for new installations (see
`Packet filtering'). With this enabled, you should be able to use
the ipchains tool exactly as in 2.2 kernels.
To compile it as a module, choose M here. If unsure, say N.
config IP_NF_COMPAT_IPFWADM
tristate "ipfwadm (2.0-style) support"
depends on IP_NF_CONNTRACK!=y && IP_NF_IPTABLES!=y && IP_NF_COMPAT_IPCHAINS!=y
help
This option places ipfwadm (with masquerading and redirection
support) back into the kernel, using the new netfilter
infrastructure. It is not recommended for new installations (see
`Packet filtering'). With this enabled, you should be able to use
the ipfwadm tool exactly as in 2.0 kernels.
To compile it as a module, choose M here. If unsure, say N.
endmenu
......@@ -2,19 +2,9 @@
# Makefile for the netfilter modules on top of IPv4.
#
# objects for the conntrack and NAT core (used by standalone and backw. compat)
ip_nf_conntrack-objs := ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
ip_nf_nat-objs := ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
# objects for the standalone - connection tracking / NAT
ip_conntrack-objs := ip_conntrack_standalone.o $(ip_nf_conntrack-objs)
iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o $(ip_nf_nat-objs)
# objects for backwards compatibility mode
ip_nf_compat-objs := ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(ip_nf_conntrack-objs) $(ip_nf_nat-objs)
ipfwadm-objs := $(ip_nf_compat-objs) ipfwadm_core.o
ipchains-objs := $(ip_nf_compat-objs) ipchains_core.o
ip_conntrack-objs := ip_conntrack_standalone.o ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
iptable_nat-objs := ip_nat_standalone.o ip_nat_rule.o ip_nat_core.o ip_nat_helper.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
# connection tracking
obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o
......@@ -96,8 +86,4 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o
# just filtering instance of ARP tables for now
obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o
# backwards compatibility
obj-$(CONFIG_IP_NF_COMPAT_IPCHAINS) += ipchains.o
obj-$(CONFIG_IP_NF_COMPAT_IPFWADM) += ipfwadm.o
obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o
/* Compatibility framework for ipchains and ipfwadm support; designed
to look as much like the 2.2 infrastructure as possible. */
/* (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.
*/
struct notifier_block;
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <net/icmp.h>
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include "ip_fw_compat.h"
static struct firewall_ops *fwops;
#ifdef CONFIG_IP_VS
/* From ip_vs_core.c */
extern unsigned int
check_for_ip_vs_out(struct sk_buff **skb_p, int (*okfn)(struct sk_buff *));
#endif
/* They call these; we do what they want. */
int register_firewall(int pf, struct firewall_ops *fw)
{
if (pf != PF_INET) {
printk("Attempt to register non-IP firewall module.\n");
return -EINVAL;
}
if (fwops) {
printk("Attempt to register multiple firewall modules.\n");
return -EBUSY;
}
fwops = fw;
return 0;
}
int unregister_firewall(int pf, struct firewall_ops *fw)
{
fwops = NULL;
return 0;
}
static unsigned int
fw_in(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
int ret = FW_BLOCK;
u_int16_t redirpt;
/* Assume worse case: any hook could change packet */
(*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
if ((*pskb)->ip_summed == CHECKSUM_HW)
if (skb_checksum_help(*pskb, (out == NULL)))
return NF_DROP;
switch (hooknum) {
case NF_IP_PRE_ROUTING:
if (fwops->fw_acct_in)
fwops->fw_acct_in(fwops, PF_INET,
(struct net_device *)in,
&redirpt, pskb);
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb);
if (!*pskb)
return NF_STOLEN;
}
ret = fwops->fw_input(fwops, PF_INET, (struct net_device *)in,
&redirpt, pskb);
break;
case NF_IP_FORWARD:
/* Connection will only be set if it was
demasqueraded: if so, skip forward chain. */
if ((*pskb)->nfct)
ret = FW_ACCEPT;
else ret = fwops->fw_forward(fwops, PF_INET,
(struct net_device *)out,
&redirpt, pskb);
break;
case NF_IP_POST_ROUTING:
ret = fwops->fw_output(fwops, PF_INET,
(struct net_device *)out,
&redirpt, pskb);
if (ret == FW_ACCEPT || ret == FW_SKIP) {
if (fwops->fw_acct_out)
fwops->fw_acct_out(fwops, PF_INET,
(struct net_device *)out,
&redirpt,
pskb);
/* ip_conntrack_confirm return NF_DROP or NF_ACCEPT */
if (ip_conntrack_confirm(*pskb) == NF_DROP)
ret = FW_BLOCK;
}
break;
}
switch (ret) {
case FW_REJECT: {
/* Alexey says:
*
* Generally, routing is THE FIRST thing to make, when
* packet enters IP stack. Before packet is routed you
* cannot call any service routines from IP stack. */
struct iphdr *iph = (*pskb)->nh.iph;
if ((*pskb)->dst != NULL
|| ip_route_input(*pskb, iph->daddr, iph->saddr, iph->tos,
(struct net_device *)in) == 0)
icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH,
0);
return NF_DROP;
}
case FW_ACCEPT:
case FW_SKIP:
if (hooknum == NF_IP_PRE_ROUTING) {
check_for_demasq(pskb);
check_for_redirect(*pskb);
} else if (hooknum == NF_IP_POST_ROUTING) {
check_for_unredirect(*pskb);
/* Handle ICMP errors from client here */
if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP
&& (*pskb)->nfct)
check_for_masq_error(pskb);
}
return NF_ACCEPT;
case FW_MASQUERADE:
if (hooknum == NF_IP_FORWARD) {
#ifdef CONFIG_IP_VS
/* check if it is for ip_vs */
if (check_for_ip_vs_out(pskb, okfn) == NF_STOLEN)
return NF_STOLEN;
#endif
return do_masquerade(pskb, out);
}
else return NF_ACCEPT;
case FW_REDIRECT:
if (hooknum == NF_IP_PRE_ROUTING)
return do_redirect(*pskb, in, redirpt);
else return NF_ACCEPT;
default:
/* FW_BLOCK */
return NF_DROP;
}
}
static unsigned int fw_confirm(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return ip_conntrack_confirm(*pskb);
}
extern int ip_fw_ctl(int optval, void *m, unsigned int len);
static int sock_fn(struct sock *sk, int optval, void __user *user, unsigned int len)
{
/* MAX of:
2.2: sizeof(struct ip_fwtest) (~14x4 + 3x4 = 17x4)
2.2: sizeof(struct ip_fwnew) (~1x4 + 15x4 + 3x4 + 3x4 = 22x4)
2.0: sizeof(struct ip_fw) (~25x4)
We can't include both 2.0 and 2.2 headers, they conflict.
Hence, 200 is a good number. --RR */
char tmp_fw[200];
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (len > sizeof(tmp_fw) || len < 1)
return -EINVAL;
if (copy_from_user(&tmp_fw, user, len))
return -EFAULT;
return -ip_fw_ctl(optval, &tmp_fw, len);
}
static struct nf_hook_ops preroute_ops = {
.hook = fw_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FILTER,
};
static struct nf_hook_ops postroute_ops = {
.hook = fw_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_FILTER,
};
static struct nf_hook_ops forward_ops = {
.hook = fw_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_FORWARD,
.priority = NF_IP_PRI_FILTER,
};
static struct nf_hook_ops local_in_ops = {
.hook = fw_confirm,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_LAST - 1,
};
static struct nf_sockopt_ops sock_ops = {
.pf = PF_INET,
.set_optmin = 64,
.set_optmax = 64 + 1024 + 1,
.set = &sock_fn,
};
extern int ipfw_init_or_cleanup(int init);
static int init_or_cleanup(int init)
{
int ret = 0;
if (!init) goto cleanup;
ret = nf_register_sockopt(&sock_ops);
if (ret < 0)
goto cleanup_nothing;
ret = ipfw_init_or_cleanup(1);
if (ret < 0)
goto cleanup_sockopt;
ret = masq_init();
if (ret < 0)
goto cleanup_ipfw;
nf_register_hook(&preroute_ops);
nf_register_hook(&postroute_ops);
nf_register_hook(&forward_ops);
nf_register_hook(&local_in_ops);
return ret;
cleanup:
nf_unregister_hook(&preroute_ops);
nf_unregister_hook(&postroute_ops);
nf_unregister_hook(&forward_ops);
nf_unregister_hook(&local_in_ops);
masq_cleanup();
cleanup_ipfw:
ipfw_init_or_cleanup(0);
cleanup_sockopt:
nf_unregister_sockopt(&sock_ops);
cleanup_nothing:
return ret;
}
static int __init init(void)
{
return init_or_cleanup(1);
}
static void __exit fini(void)
{
init_or_cleanup(0);
}
module_init(init);
module_exit(fini);
#ifndef _LINUX_IP_FW_COMPAT_H
#define _LINUX_IP_FW_COMPAT_H
/* From ip_fw_compat_redir.c */
extern unsigned int
do_redirect(struct sk_buff *skb,
const struct net_device *dev,
u_int16_t redirpt);
extern void
check_for_redirect(struct sk_buff *skb);
extern void
check_for_unredirect(struct sk_buff *skb);
/* From ip_fw_compat_masq.c */
extern unsigned int
do_masquerade(struct sk_buff **pskb, const struct net_device *dev);
extern void check_for_masq_error(struct sk_buff **pskb);
extern unsigned int
check_for_demasq(struct sk_buff **pskb);
extern int __init masq_init(void);
extern void masq_cleanup(void);
#endif /* _LINUX_IP_FW_COMPAT_H */
/* Masquerading compatibility layer.
Note that there are no restrictions on other programs binding to
ports 61000:65095 (in 2.0 and 2.2 they get EADDRINUSE). Just DON'T
DO IT.
*/
/* (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/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/udp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <net/route.h>
#include <net/ip.h>
#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_conntrack_core.h>
#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
#include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <linux/netfilter_ipv4/listhelp.h>
#include "ip_fw_compat.h"
#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
unsigned int
do_masquerade(struct sk_buff **pskb, const struct net_device *dev)
{
struct ip_nat_info *info;
enum ip_conntrack_info ctinfo;
struct ip_conntrack *ct;
unsigned int ret;
/* Sorry, only ICMP, TCP and UDP. */
if ((*pskb)->nh.iph->protocol != IPPROTO_ICMP
&& (*pskb)->nh.iph->protocol != IPPROTO_TCP
&& (*pskb)->nh.iph->protocol != IPPROTO_UDP)
return NF_DROP;
/* Feed it to connection tracking; in fact we're in NF_IP_FORWARD,
but connection tracking doesn't expect that */
ret = ip_conntrack_in(NF_IP_POST_ROUTING, pskb, dev, NULL, NULL);
if (ret != NF_ACCEPT) {
DEBUGP("ip_conntrack_in returned %u.\n", ret);
return ret;
}
ct = ip_conntrack_get(*pskb, &ctinfo);
if (!ct) {
DEBUGP("ip_conntrack_in set to invalid conntrack.\n");
return NF_DROP;
}
info = &ct->nat.info;
WRITE_LOCK(&ip_nat_lock);
/* Setup the masquerade, if not already */
if (!info->initialized) {
u_int32_t newsrc;
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = (*pskb)->nh.iph->daddr } } };
struct rtable *rt;
struct ip_nat_multi_range range;
/* Pass 0 instead of saddr, since it's going to be changed
anyway. */
if (ip_route_output_key(&rt, &fl) != 0) {
DEBUGP("ipnat_rule_masquerade: Can't reroute.\n");
return NF_DROP;
}
newsrc = inet_select_addr(rt->u.dst.dev, rt->rt_gateway,
RT_SCOPE_UNIVERSE);
ip_rt_put(rt);
range = ((struct ip_nat_multi_range)
{ 1,
{{IP_NAT_RANGE_MAP_IPS|IP_NAT_RANGE_PROTO_SPECIFIED,
newsrc, newsrc,
{ htons(61000) }, { htons(65095) } } } });
ret = ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
if (ret != NF_ACCEPT) {
WRITE_UNLOCK(&ip_nat_lock);
return ret;
}
} else
DEBUGP("Masquerading already done on this conn.\n");
WRITE_UNLOCK(&ip_nat_lock);
return do_bindings(ct, ctinfo, info, NF_IP_POST_ROUTING, pskb);
}
void
check_for_masq_error(struct sk_buff **pskb)
{
enum ip_conntrack_info ctinfo;
struct ip_conntrack *ct;
ct = ip_conntrack_get(*pskb, &ctinfo);
/* Wouldn't be here if not tracked already => masq'ed ICMP
ping or error related to masq'd connection */
IP_NF_ASSERT(ct);
if (ctinfo == IP_CT_RELATED) {
icmp_reply_translation(pskb, ct, NF_IP_PRE_ROUTING,
CTINFO2DIR(ctinfo));
icmp_reply_translation(pskb, ct, NF_IP_POST_ROUTING,
CTINFO2DIR(ctinfo));
}
}
unsigned int
check_for_demasq(struct sk_buff **pskb)
{
struct ip_conntrack_tuple tuple;
struct ip_conntrack_protocol *protocol;
struct ip_conntrack_tuple_hash *h;
enum ip_conntrack_info ctinfo;
struct ip_conntrack *ct;
int ret;
protocol = ip_ct_find_proto((*pskb)->nh.iph->protocol);
/* We don't feed packets to conntrack system unless we know
they're part of an connection already established by an
explicit masq command. */
switch ((*pskb)->nh.iph->protocol) {
case IPPROTO_ICMP:
/* ICMP errors. */
protocol->error(*pskb, &ctinfo, NF_IP_PRE_ROUTING);
ct = (struct ip_conntrack *)(*pskb)->nfct;
if (ct) {
/* We only do SNAT in the compatibility layer.
So we can manipulate ICMP errors from
server here (== DNAT). Do SNAT icmp manips
in POST_ROUTING handling. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
icmp_reply_translation(pskb, ct,
NF_IP_PRE_ROUTING,
CTINFO2DIR(ctinfo));
icmp_reply_translation(pskb, ct,
NF_IP_POST_ROUTING,
CTINFO2DIR(ctinfo));
}
return NF_ACCEPT;
}
/* Fall thru... */
case IPPROTO_TCP:
case IPPROTO_UDP:
IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
if (!ip_ct_get_tuple((*pskb)->nh.iph, *pskb,
(*pskb)->nh.iph->ihl*4, &tuple, protocol)) {
if (net_ratelimit())
printk("ip_fw_compat_masq: Can't get tuple\n");
return NF_ACCEPT;
}
break;
default:
/* Not ours... */
return NF_ACCEPT;
}
h = ip_conntrack_find_get(&tuple, NULL);
/* MUST be found, and MUST be reply. */
if (h && DIRECTION(h) == 1) {
ret = ip_conntrack_in(NF_IP_PRE_ROUTING, pskb,
NULL, NULL, NULL);
/* Put back the reference gained from find_get */
nf_conntrack_put(&h->ctrack->ct_general);
if (ret == NF_ACCEPT) {
struct ip_conntrack *ct;
ct = ip_conntrack_get(*pskb, &ctinfo);
if (ct) {
struct ip_nat_info *info = &ct->nat.info;
do_bindings(ct, ctinfo, info,
NF_IP_PRE_ROUTING,
pskb);
} else
if (net_ratelimit())
printk("ip_fw_compat_masq: conntrack"
" didn't like\n");
}
} else {
if (h)
/* Put back the reference gained from find_get */
nf_conntrack_put(&h->ctrack->ct_general);
ret = NF_ACCEPT;
}
return ret;
}
int ip_fw_masq_timeouts(void *user, int len)
{
printk("Sorry: masquerading timeouts set 5DAYS/2MINS/60SECS\n");
return 0;
}
static const char *masq_proto_name(u_int16_t protonum)
{
switch (protonum) {
case IPPROTO_TCP: return "TCP";
case IPPROTO_UDP: return "UDP";
case IPPROTO_ICMP: return "ICMP";
default: return "MORE-CAFFEINE-FOR-RUSTY";
}
}
static unsigned int
print_masq(char *buffer, const struct ip_conntrack *conntrack)
{
char temp[129];
/* This is for backwards compatibility, but ick!.
We should never export jiffies to userspace.
*/
sprintf(temp,"%s %08X:%04X %08X:%04X %04X %08X %6d %6d %7lu",
masq_proto_name(conntrack->tuplehash[0].tuple.dst.protonum),
ntohl(conntrack->tuplehash[0].tuple.src.ip),
ntohs(conntrack->tuplehash[0].tuple.src.u.all),
ntohl(conntrack->tuplehash[0].tuple.dst.ip),
ntohs(conntrack->tuplehash[0].tuple.dst.u.all),
ntohs(conntrack->tuplehash[1].tuple.dst.u.all),
/* Sorry, no init_seq, delta or previous_delta (yet). */
0, 0, 0,
conntrack->timeout.expires - jiffies);
return sprintf(buffer, "%-127s\n", temp);
}
/* Returns true when finished. */
static int
masq_iterate(const struct ip_conntrack_tuple_hash *hash,
char *buffer, off_t offset, off_t *upto,
unsigned int *len, unsigned int maxlen)
{
unsigned int newlen;
IP_NF_ASSERT(hash->ctrack);
/* Only count originals */
if (DIRECTION(hash))
return 0;
if ((*upto)++ < offset)
return 0;
newlen = print_masq(buffer + *len, hash->ctrack);
if (*len + newlen > maxlen)
return 1;
else *len += newlen;
return 0;
}
/* Everything in the hash is masqueraded. */
static int
masq_procinfo(char *buffer, char **start, off_t offset, int length)
{
unsigned int i;
int len = 0;
off_t upto = 1;
/* Header: first record */
if (offset == 0) {
char temp[128];
sprintf(temp,
"Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=0,0,0)");
len = sprintf(buffer, "%-127s\n", temp);
offset = 1;
}
READ_LOCK(&ip_conntrack_lock);
/* Traverse hash; print originals then reply. */
for (i = 0; i < ip_conntrack_htable_size; i++) {
if (LIST_FIND(&ip_conntrack_hash[i], masq_iterate,
struct ip_conntrack_tuple_hash *,
buffer, offset, &upto, &len, length))
break;
}
READ_UNLOCK(&ip_conntrack_lock);
/* `start' hack - see fs/proc/generic.c line ~165 */
*start = (char *)((unsigned int)upto - offset);
return len;
}
int __init masq_init(void)
{
int ret;
struct proc_dir_entry *proc;
ret = ip_conntrack_init();
if (ret == 0) {
ret = ip_nat_init();
if (ret == 0) {
proc = proc_net_create("ip_masquerade",
0, masq_procinfo);
if (proc)
proc->owner = THIS_MODULE;
else {
ip_nat_cleanup();
ip_conntrack_cleanup();
ret = -ENOMEM;
}
} else
ip_conntrack_cleanup();
}
return ret;
}
void masq_cleanup(void)
{
ip_nat_cleanup();
ip_conntrack_cleanup();
proc_net_remove("ip_masquerade");
}
/* This is a file to handle the "simple" NAT cases (redirect and
masquerade) required for the compatibility layer.
`bind to foreign address' and `getpeername' hacks are not
supported.
FIXME: Timing is overly simplistic. If anyone complains, make it
use conntrack.
*/
/* (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/config.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/in.h>
#include <linux/netfilter_ipv4/lockhelp.h>
/* Very simple timeout pushed back by each packet */
#define REDIR_TIMEOUT (240*HZ)
static DECLARE_LOCK(redir_lock);
#define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
#include <linux/netfilter_ipv4/listhelp.h>
#include "ip_fw_compat.h"
#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
#ifdef CONFIG_NETFILTER_DEBUG
#define IP_NF_ASSERT(x) \
do { \
if (!(x)) \
/* Wooah! I'm tripping my conntrack in a frenzy of \
netplay... */ \
printk("ASSERT: %s:%i(%s)\n", \
__FILE__, __LINE__, __FUNCTION__); \
} while(0)
#else
#define IP_NF_ASSERT(x)
#endif
static u_int16_t
cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
{
u_int32_t diffs[] = { oldvalinv, newval };
return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
oldcheck^0xFFFF));
}
struct redir_core {
u_int32_t orig_srcip, orig_dstip;
u_int16_t orig_sport, orig_dport;
u_int32_t new_dstip;
u_int16_t new_dport;
};
struct redir
{
struct list_head list;
struct redir_core core;
struct timer_list destroyme;
};
static LIST_HEAD(redirs);
static int
redir_cmp(const struct redir *i,
u_int32_t orig_srcip, u_int32_t orig_dstip,
u_int16_t orig_sport, u_int16_t orig_dport)
{
return (i->core.orig_srcip == orig_srcip
&& i->core.orig_dstip == orig_dstip
&& i->core.orig_sport == orig_sport
&& i->core.orig_dport == orig_dport);
}
/* Search for an existing redirection of the TCP packet. */
static struct redir *
find_redir(u_int32_t orig_srcip, u_int32_t orig_dstip,
u_int16_t orig_sport, u_int16_t orig_dport)
{
return LIST_FIND(&redirs, redir_cmp, struct redir *,
orig_srcip, orig_dstip, orig_sport, orig_dport);
}
static void do_tcp_redir(struct sk_buff *skb, struct redir *redir)
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ iph->ihl);
tcph->check = cheat_check(~redir->core.orig_dstip,
redir->core.new_dstip,
cheat_check(redir->core.orig_dport ^ 0xFFFF,
redir->core.new_dport,
tcph->check));
iph->check = cheat_check(~redir->core.orig_dstip,
redir->core.new_dstip, iph->check);
tcph->dest = redir->core.new_dport;
iph->daddr = redir->core.new_dstip;
skb->nfcache |= NFC_ALTERED;
}
static int
unredir_cmp(const struct redir *i,
u_int32_t new_dstip, u_int32_t orig_srcip,
u_int16_t new_dport, u_int16_t orig_sport)
{
return (i->core.orig_srcip == orig_srcip
&& i->core.new_dstip == new_dstip
&& i->core.orig_sport == orig_sport
&& i->core.new_dport == new_dport);
}
/* Match reply packet against redir */
static struct redir *
find_unredir(u_int32_t new_dstip, u_int32_t orig_srcip,
u_int16_t new_dport, u_int16_t orig_sport)
{
return LIST_FIND(&redirs, unredir_cmp, struct redir *,
new_dstip, orig_srcip, new_dport, orig_sport);
}
/* `unredir' a reply packet. */
static void do_tcp_unredir(struct sk_buff *skb, struct redir *redir)
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ iph->ihl);
tcph->check = cheat_check(~redir->core.new_dstip,
redir->core.orig_dstip,
cheat_check(redir->core.new_dport ^ 0xFFFF,
redir->core.orig_dport,
tcph->check));
iph->check = cheat_check(~redir->core.new_dstip,
redir->core.orig_dstip,
iph->check);
tcph->source = redir->core.orig_dport;
iph->saddr = redir->core.orig_dstip;
skb->nfcache |= NFC_ALTERED;
}
static void destroyme(unsigned long me)
{
LOCK_BH(&redir_lock);
LIST_DELETE(&redirs, (struct redir *)me);
UNLOCK_BH(&redir_lock);
kfree((struct redir *)me);
}
/* REDIRECT a packet. */
unsigned int
do_redirect(struct sk_buff *skb,
const struct net_device *dev,
u_int16_t redirpt)
{
struct iphdr *iph = skb->nh.iph;
u_int32_t newdst;
/* Figure out address: not loopback. */
if (!dev)
return NF_DROP;
/* Grab first address on interface. */
newdst = ((struct in_device *)dev->ip_ptr)->ifa_list->ifa_local;
switch (iph->protocol) {
case IPPROTO_UDP: {
/* Simple mangle. */
struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
+ iph->ihl);
/* Must have whole header */
if (skb->len < iph->ihl*4 + sizeof(*udph))
return NF_DROP;
if (udph->check) /* 0 is a special case meaning no checksum */
udph->check = cheat_check(~iph->daddr, newdst,
cheat_check(udph->dest ^ 0xFFFF,
redirpt,
udph->check));
iph->check = cheat_check(~iph->daddr, newdst, iph->check);
udph->dest = redirpt;
iph->daddr = newdst;
skb->nfcache |= NFC_ALTERED;
return NF_ACCEPT;
}
case IPPROTO_TCP: {
/* Mangle, maybe record. */
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ iph->ihl);
struct redir *redir;
int ret;
/* Must have whole header */
if (skb->len < iph->ihl*4 + sizeof(*tcph))
return NF_DROP;
DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
iph->saddr, tcph->source, iph->daddr, tcph->dest,
newdst, redirpt);
LOCK_BH(&redir_lock);
redir = find_redir(iph->saddr, iph->daddr,
tcph->source, tcph->dest);
if (!redir) {
redir = kmalloc(sizeof(struct redir), GFP_ATOMIC);
if (!redir) {
ret = NF_DROP;
goto out;
}
list_prepend(&redirs, redir);
init_timer(&redir->destroyme);
redir->destroyme.function = destroyme;
redir->destroyme.data = (unsigned long)redir;
redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
add_timer(&redir->destroyme);
}
/* In case mangling has changed, rewrite this part. */
redir->core = ((struct redir_core)
{ iph->saddr, iph->daddr,
tcph->source, tcph->dest,
newdst, redirpt });
do_tcp_redir(skb, redir);
ret = NF_ACCEPT;
out:
UNLOCK_BH(&redir_lock);
return ret;
}
default: /* give up if not TCP or UDP. */
return NF_DROP;
}
}
/* Incoming packet: is it a reply to a masqueraded connection, or
part of an already-redirected TCP connection? */
void
check_for_redirect(struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ iph->ihl);
struct redir *redir;
if (iph->protocol != IPPROTO_TCP)
return;
/* Must have whole header */
if (skb->len < iph->ihl*4 + sizeof(*tcph))
return;
LOCK_BH(&redir_lock);
redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
if (redir) {
DEBUGP("Doing tcp redirect again.\n");
do_tcp_redir(skb, redir);
if (del_timer(&redir->destroyme)) {
redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
add_timer(&redir->destroyme);
}
}
UNLOCK_BH(&redir_lock);
}
void
check_for_unredirect(struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ iph->ihl);
struct redir *redir;
if (iph->protocol != IPPROTO_TCP)
return;
/* Must have whole header */
if (skb->len < iph->ihl*4 + sizeof(*tcph))
return;
LOCK_BH(&redir_lock);
redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
if (redir) {
DEBUGP("Doing tcp unredirect.\n");
do_tcp_unredir(skb, redir);
if (del_timer(&redir->destroyme)) {
redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
add_timer(&redir->destroyme);
}
}
UNLOCK_BH(&redir_lock);
}
#warning ipchains is obsolete, and will be removed soon.
/* Minor modifications to fit on compatibility framework:
Rusty.Russell@rustcorp.com.au
*/
/*
* This code is heavily based on the code on the old ip_fw.c code; see below for
* copyrights and attributions of the old code. This code is basically GPL.
*
* 15-Aug-1997: Major changes to allow graphs for firewall rules.
* Paul Russell <Paul.Russell@rustcorp.com.au> and
* Michael Neuling <Michael.Neuling@rustcorp.com.au>
* 24-Aug-1997: Generalised protocol handling (not just TCP/UDP/ICMP).
* Added explicit RETURN from chains.
* Removed TOS mangling (done in ipchains 1.0.1).
* Fixed read & reset bug by reworking proc handling.
* Paul Russell <Paul.Russell@rustcorp.com.au>
* 28-Sep-1997: Added packet marking for net sched code.
* Removed fw_via comparisons: all done on device name now,
* similar to changes in ip_fw.c in DaveM's CVS970924 tree.
* Paul Russell <Paul.Russell@rustcorp.com.au>
* 2-Nov-1997: Moved types across to __u16, etc.
* Added inverse flags.
* Fixed fragment bug (in args to port_match).
* Changed mark to only one flag (MARKABS).
* 21-Nov-1997: Added ability to test ICMP code.
* 19-Jan-1998: Added wildcard interfaces.
* 6-Feb-1998: Merged 2.0 and 2.1 versions.
* Initialised ip_masq for 2.0.x version.
* Added explicit NETLINK option for 2.1.x version.
* Added packet and byte counters for policy matches.
* 26-Feb-1998: Fixed race conditions, added SMP support.
* 18-Mar-1998: Fix SMP, fix race condition fix.
* 1-May-1998: Remove caching of device pointer.
* 12-May-1998: Allow tiny fragment case for TCP/UDP.
* 15-May-1998: Treat short packets as fragments, don't just block.
* 3-Jan-1999: Fixed serious procfs security hole -- users should never
* be allowed to view the chains!
* Marc Santoro <ultima@snicker.emoti.com>
* 29-Jan-1999: Locally generated bogus IPs dealt with, rather than crash
* during dump_packet. --RR.
* 19-May-1999: Star Wars: The Phantom Menace opened. Rule num
* printed in log (modified from Michael Hasenstein's patch).
* Added SYN in log message. --RR
* 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
* John McDonald <jm@dataprotect.com>
* Thomas Lopatic <tl@dataprotect.com>
*/
/*
*
* The origina Linux port was done Alan Cox, with changes/fixes from
* Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
* Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
*
* Copyright from the original FreeBSD version follows:
*
* Copyright (c) 1993 Daniel Boulet
* Copyright (c) 1994 Ugen J.S.Antsilevich
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind. */
#include <linux/config.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/icmp.h>
#include <linux/udp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/ipchains_core.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <net/checksum.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
MODULE_DESCRIPTION("ipchains backwards compatibility layer");
/* Understanding locking in this code: (thanks to Alan Cox for using
* little words to explain this to me). -- PR
*
* In UP, there can be two packets traversing the chains:
* 1) A packet from the current userspace context
* 2) A packet off the bh handlers (timer or net).
*
* For SMP (kernel v2.1+), multiply this by # CPUs.
*
* [Note that this in not correct for 2.2 - because the socket code always
* uses lock_kernel() to serialize, and bottom halves (timers and net_bhs)
* only run on one CPU at a time. This will probably change for 2.3.
* It is still good to use spinlocks because that avoids the global cli()
* for updating the tables, which is rather costly in SMP kernels -AK]
*
* This means counters and backchains can get corrupted if no precautions
* are taken.
*
* To actually alter a chain on UP, we need only do a cli(), as this will
* stop a bh handler firing, as we are in the current userspace context
* (coming from a setsockopt()).
*
* On SMP, we need a write_lock_irqsave(), which is a simple cli() in
* UP.
*
* For backchains and counters, we use an array, indexed by
* [smp_processor_id()*2 + !in_interrupt()]; the array is of
* size [NR_CPUS*2]. For v2.0, NR_CPUS is effectively 1. So,
* confident of uniqueness, we modify counters even though we only
* have a read lock (to read the counters, you need a write lock,
* though). */
/* Why I didn't use straight locking... -- PR
*
* The backchains can be separated out of the ip_chains structure, and
* allocated as needed inside ip_fw_check().
*
* The counters, however, can't. Trying to lock these means blocking
* interrupts every time we want to access them. This would suck HARD
* performance-wise. Not locking them leads to possible corruption,
* made worse on 32-bit machines (counters are 64-bit). */
/*#define DEBUG_IP_FIREWALL*/
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
/*#define DEBUG_IP_FIREWALL_USER*/
/*#define DEBUG_IP_FIREWALL_LOCKING*/
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
static struct sock *ipfwsk;
#endif
#ifdef CONFIG_SMP
#define SLOT_NUMBER() (smp_processor_id()*2 + !in_interrupt())
#else /* !SMP */
#define SLOT_NUMBER() (!in_interrupt())
#endif /* CONFIG_SMP */
#define NUM_SLOTS (NR_CPUS*2)
#define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \
+ NUM_SLOTS*sizeof(struct ip_reent))
#define SIZEOF_STRUCT_IP_FW_KERNEL (sizeof(struct ip_fwkernel) \
+ NUM_SLOTS*sizeof(struct ip_counters))
#ifdef DEBUG_IP_FIREWALL_LOCKING
static unsigned int fwc_rlocks, fwc_wlocks;
#define FWC_DEBUG_LOCK(d) \
do { \
FWC_DONT_HAVE_LOCK(d); \
d |= (1 << SLOT_NUMBER()); \
} while (0)
#define FWC_DEBUG_UNLOCK(d) \
do { \
FWC_HAVE_LOCK(d); \
d &= ~(1 << SLOT_NUMBER()); \
} while (0)
#define FWC_DONT_HAVE_LOCK(d) \
do { \
if ((d) & (1 << SLOT_NUMBER())) \
printk("%s:%i: Got lock on %i already!\n", \
__FILE__, __LINE__, SLOT_NUMBER()); \
} while(0)
#define FWC_HAVE_LOCK(d) \
do { \
if (!((d) & (1 << SLOT_NUMBER()))) \
printk("%s:%i:No lock on %i!\n", \
__FILE__, __LINE__, SLOT_NUMBER()); \
} while (0)
#else
#define FWC_DEBUG_LOCK(d) do { } while(0)
#define FWC_DEBUG_UNLOCK(d) do { } while(0)
#define FWC_DONT_HAVE_LOCK(d) do { } while(0)
#define FWC_HAVE_LOCK(d) do { } while(0)
#endif /*DEBUG_IP_FIRWALL_LOCKING*/
#define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
#define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
struct ip_chain;
struct ip_counters
{
__u64 pcnt, bcnt; /* Packet and byte counters */
};
struct ip_fwkernel
{
struct ip_fw ipfw;
struct ip_fwkernel *next; /* where to go next if current
* rule doesn't match */
struct ip_chain *branch; /* which branch to jump to if
* current rule matches */
int simplebranch; /* Use this if branch == NULL */
struct ip_counters counters[0]; /* Actually several of these */
};
struct ip_reent
{
struct ip_chain *prevchain; /* Pointer to referencing chain */
struct ip_fwkernel *prevrule; /* Pointer to referencing rule */
struct ip_counters counters;
};
struct ip_chain
{
ip_chainlabel label; /* Defines the label for each block */
struct ip_chain *next; /* Pointer to next block */
struct ip_fwkernel *chain; /* Pointer to first rule in block */
__u32 refcount; /* Number of refernces to block */
int policy; /* Default rule for chain. Only *
* used in built in chains */
struct ip_reent reent[0]; /* Actually several of these */
};
/*
* Implement IP packet firewall
*/
#ifdef DEBUG_IP_FIREWALL
#define dprintf(format, args...) printk(format , ## args)
#else
#define dprintf(format, args...)
#endif
#ifdef DEBUG_IP_FIREWALL_USER
#define duprintf(format, args...) printk(format , ## args)
#else
#define duprintf(format, args...)
#endif
/* Lock around ip_fw_chains linked list structure */
rwlock_t ip_fw_lock = RW_LOCK_UNLOCKED;
/* Head of linked list of fw rules */
static struct ip_chain *ip_fw_chains;
#define IP_FW_INPUT_CHAIN ip_fw_chains
#define IP_FW_FORWARD_CHAIN (ip_fw_chains->next)
#define IP_FW_OUTPUT_CHAIN (ip_fw_chains->next->next)
/* Returns 1 if the port is matched by the range, 0 otherwise */
extern inline int port_match(__u16 min, __u16 max, __u16 port,
int frag, int invert)
{
if (frag) /* Fragments fail ANY port test. */
return (min == 0 && max == 0xFFFF);
else return (port >= min && port <= max) ^ invert;
}
/* Returns whether matches rule or not. */
static int ip_rule_match(struct ip_fwkernel *f,
const char *ifname,
struct sk_buff **pskb,
char tcpsyn,
__u16 src_port, __u16 dst_port,
char isfrag)
{
struct iphdr *ip = (*pskb)->nh.iph;
#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg))
/*
* This is a bit simpler as we don't have to walk
* an interface chain as you do in BSD - same logic
* however.
*/
if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr,
IP_FW_INV_SRCIP)
|| FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr,
IP_FW_INV_DSTIP)) {
dprintf("Source or dest mismatch.\n");
dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr,
f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : "");
dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr,
f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : "");
return 0;
}
/*
* Look for a VIA device match
*/
if (f->ipfw.fw_flg & IP_FW_F_WILDIF) {
if (FWINV(strncmp(ifname, f->ipfw.fw_vianame,
strlen(f->ipfw.fw_vianame)) != 0,
IP_FW_INV_VIA)) {
dprintf("Wildcard interface mismatch.%s\n",
f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : "");
return 0; /* Mismatch */
}
}
else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0,
IP_FW_INV_VIA)) {
dprintf("Interface name does not match.%s\n",
f->ipfw.fw_invflg & IP_FW_INV_VIA
? " (INV)" : "");
return 0; /* Mismatch */
}
/*
* Ok the chain addresses match.
*/
/* If we have a fragment rule but the packet is not a fragment
* the we return zero */
if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) {
dprintf("Fragment rule but not fragment.%s\n",
f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : "");
return 0;
}
/* Fragment NEVER passes a SYN test, even an inverted one. */
if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN)
|| (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) {
dprintf("Rule requires SYN and packet has no SYN.%s\n",
f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : "");
return 0;
}
if (f->ipfw.fw_proto) {
/*
* Specific firewall - packet's protocol
* must match firewall's.
*/
if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) {
dprintf("Packet protocol %hi does not match %hi.%s\n",
ip->protocol, f->ipfw.fw_proto,
f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":"");
return 0;
}
/* For non TCP/UDP/ICMP, port range is max anyway. */
if (!port_match(f->ipfw.fw_spts[0],
f->ipfw.fw_spts[1],
src_port, isfrag,
!!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT))
|| !port_match(f->ipfw.fw_dpts[0],
f->ipfw.fw_dpts[1],
dst_port, isfrag,
!!(f->ipfw.fw_invflg
&IP_FW_INV_DSTPT))) {
dprintf("Port match failed.\n");
return 0;
}
}
dprintf("Match succeeded.\n");
return 1;
}
static const char *branchname(struct ip_chain *branch,int simplebranch)
{
if (branch)
return branch->label;
switch (simplebranch)
{
case FW_BLOCK: return IP_FW_LABEL_BLOCK;
case FW_ACCEPT: return IP_FW_LABEL_ACCEPT;
case FW_REJECT: return IP_FW_LABEL_REJECT;
case FW_REDIRECT: return IP_FW_LABEL_REDIRECT;
case FW_MASQUERADE: return IP_FW_LABEL_MASQUERADE;
case FW_SKIP: return "-";
case FW_SKIP+1: return IP_FW_LABEL_RETURN;
default:
return "UNKNOWN";
}
}
/*
* VERY ugly piece of code which actually
* makes kernel printf for matching packets...
*/
static void dump_packet(struct sk_buff **pskb,
const char *ifname,
struct ip_fwkernel *f,
const ip_chainlabel chainlabel,
__u16 src_port,
__u16 dst_port,
unsigned int count,
int syn)
{
__u32 *opt = (__u32 *) ((*pskb)->nh.iph + 1);
int opti;
if (f) {
printk(KERN_INFO "Packet log: %s ",chainlabel);
printk("%s ",branchname(f->branch,f->simplebranch));
if (f->simplebranch==FW_REDIRECT)
printk("%d ",f->ipfw.fw_redirpt);
}
printk("%s PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu"
" L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
ifname, (*pskb)->nh.iph->protocol,
NIPQUAD((*pskb)->nh.iph->saddr),
src_port,
NIPQUAD((*pskb)->nh.iph->daddr),
dst_port,
ntohs((*pskb)->nh.iph->tot_len),
(*pskb)->nh.iph->tos,
ntohs((*pskb)->nh.iph->id),
ntohs((*pskb)->nh.iph->frag_off),
(*pskb)->nh.iph->ttl);
for (opti = 0; opti < ((*pskb)->nh.iph->ihl - sizeof(struct iphdr) / 4); opti++)
printk(" O=0x%8.8X", *opt++);
printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);
}
/* function for checking chain labels for user space. */
static int check_label(ip_chainlabel label)
{
unsigned int i;
/* strlen must be < IP_FW_MAX_LABEL_LENGTH. */
for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++)
if (label[i] == '\0') return 1;
return 0;
}
/* This function returns a pointer to the first chain with a label
* that matches the one given. */
static struct ip_chain *find_label(ip_chainlabel label)
{
struct ip_chain *tmp;
FWC_HAVE_LOCK(fwc_rlocks | fwc_wlocks);
for (tmp = ip_fw_chains; tmp; tmp = tmp->next)
if (strcmp(tmp->label,label) == 0)
break;
return tmp;
}
/* This function returns a boolean which when true sets answer to one
of the FW_*. */
static int find_special(ip_chainlabel label, int *answer)
{
if (label[0] == '\0') {
*answer = FW_SKIP; /* => pass-through rule */
return 1;
} else if (strcmp(label,IP_FW_LABEL_ACCEPT) == 0) {
*answer = FW_ACCEPT;
return 1;
} else if (strcmp(label,IP_FW_LABEL_BLOCK) == 0) {
*answer = FW_BLOCK;
return 1;
} else if (strcmp(label,IP_FW_LABEL_REJECT) == 0) {
*answer = FW_REJECT;
return 1;
} else if (strcmp(label,IP_FW_LABEL_REDIRECT) == 0) {
*answer = FW_REDIRECT;
return 1;
} else if (strcmp(label,IP_FW_LABEL_MASQUERADE) == 0) {
*answer = FW_MASQUERADE;
return 1;
} else if (strcmp(label, IP_FW_LABEL_RETURN) == 0) {
*answer = FW_SKIP+1;
return 1;
} else {
return 0;
}
}
/* This function cleans up the prevchain and prevrule. If the verbose
* flag is set then he names of the chains will be printed as it
* cleans up. */
static void cleanup(struct ip_chain *chain,
const int verbose,
unsigned int slot)
{
struct ip_chain *tmpchain = chain->reent[slot].prevchain;
if (verbose)
printk(KERN_ERR "Chain backtrace: ");
while (tmpchain) {
if (verbose)
printk("%s<-",chain->label);
chain->reent[slot].prevchain = NULL;
chain = tmpchain;
tmpchain = chain->reent[slot].prevchain;
}
if (verbose)
printk("%s\n",chain->label);
}
static inline int
ip_fw_domatch(struct ip_fwkernel *f,
const char *rif,
const ip_chainlabel label,
struct sk_buff **pskb,
unsigned int slot,
__u16 src_port, __u16 dst_port,
unsigned int count,
int tcpsyn,
unsigned char *tos)
{
f->counters[slot].bcnt+=ntohs((*pskb)->nh.iph->tot_len);
f->counters[slot].pcnt++;
if (f->ipfw.fw_flg & IP_FW_F_PRN) {
dump_packet(pskb,rif,f,label,src_port,dst_port,count,tcpsyn);
}
*tos = (*tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
/* This functionality is useless in stock 2.0.x series, but we don't
* discard the mark thing altogether, to avoid breaking ipchains (and,
* more importantly, the ipfwadm wrapper) --PR */
if (f->ipfw.fw_flg & IP_FW_F_MARKABS) {
(*pskb)->nfmark = f->ipfw.fw_mark;
} else {
(*pskb)->nfmark += f->ipfw.fw_mark;
}
if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
size_t len = min_t(unsigned int, f->ipfw.fw_outputsize, ntohs((*pskb)->nh.iph->tot_len))
+ sizeof(__u32) + sizeof((*pskb)->nfmark) + IFNAMSIZ;
struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC);
duprintf("Sending packet out NETLINK (length = %u).\n",
(unsigned int)len);
if (outskb) {
/* Prepend length, mark & interface */
skb_put(outskb, len);
*((__u32 *)outskb->data) = (__u32)len;
*((__u32 *)(outskb->data+sizeof(__u32))) =
(*pskb)->nfmark;
strcpy(outskb->data+sizeof(__u32)*2, rif);
skb_copy_bits(*pskb,
((char *)(*pskb)->nh.iph - (char *)(*pskb)->data),
outskb->data+sizeof(__u32)*2+IFNAMSIZ,
len-(sizeof(__u32)*2+IFNAMSIZ));
netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC);
}
else {
#endif
if (net_ratelimit())
printk(KERN_WARNING "ip_fw: packet drop due to "
"netlink failure\n");
return 0;
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
}
#endif
}
return 1;
}
/*
* Returns one of the generic firewall policies, like FW_ACCEPT.
*
* The testing is either false for normal firewall mode or true for
* user checking mode (counters are not updated, TOS & mark not done).
*/
static int
ip_fw_check(const char *rif,
__u16 *redirport,
struct ip_chain *chain,
struct sk_buff **pskb,
unsigned int slot,
int testing)
{
__u32 src, dst;
__u16 src_port = 0xFFFF, dst_port = 0xFFFF;
char tcpsyn=0;
__u16 offset;
unsigned char tos;
struct ip_fwkernel *f;
int ret = FW_SKIP+2;
unsigned int count;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
offset = ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET;
/*
* 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 && (*pskb)->nh.iph->protocol == IPPROTO_TCP) {
if (!testing && net_ratelimit()) {
printk("Suspect TCP fragment.\n");
dump_packet(pskb,rif,NULL,NULL,0,0,0,0);
}
return FW_BLOCK;
}
/* If we can't investigate ports, treat as fragment. It's
* either a trucated whole packet, or a truncated first
* fragment, or a TCP first fragment of length 8-15, in which
* case the above rule stops reassembly.
*/
if (offset == 0) {
unsigned int size_req;
switch ((*pskb)->nh.iph->protocol) {
case IPPROTO_TCP:
/* Don't care about things past flags word */
size_req = 16;
break;
case IPPROTO_UDP:
case IPPROTO_ICMP:
size_req = 8;
break;
default:
size_req = 0;
}
/* If it is a truncated first fragment then it can be
* used to rewrite port information, and thus should
* be blocked.
*/
if (ntohs((*pskb)->nh.iph->tot_len) <
((*pskb)->nh.iph->ihl<<2)+size_req) {
if (!testing && net_ratelimit()) {
printk("Suspect short first fragment.\n");
dump_packet(pskb,rif,NULL,NULL,0,0,0,0);
}
return FW_BLOCK;
}
}
src = (*pskb)->nh.iph->saddr;
dst = (*pskb)->nh.iph->daddr;
tos = (*pskb)->nh.iph->tos;
/*
* If we got interface from which packet came
* we can use the address directly. Linux 2.1 now uses address
* chains per device too, but unlike BSD we first check if the
* incoming packet matches a device address and the routing
* table before calling the firewall.
*/
dprintf("Packet ");
switch ((*pskb)->nh.iph->protocol) {
case IPPROTO_TCP:
dprintf("TCP ");
if (!offset) {
struct tcphdr _tcph, *th;
th = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_tcph), &_tcph);
if (th == NULL)
return FW_BLOCK;
src_port = ntohs(th->source);
dst_port = ntohs(th->dest);
/* Connection initilisation can only
* be made when the syn bit is set and
* neither of the ack or reset is
* set. */
if (th->syn && !(th->ack || th->rst))
tcpsyn = 1;
}
break;
case IPPROTO_UDP:
dprintf("UDP ");
if (!offset) {
struct udphdr _udph, *uh;
uh = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_udph), &_udph);
if (uh == NULL)
return FW_BLOCK;
src_port = ntohs(uh->source);
dst_port = ntohs(uh->dest);
}
break;
case IPPROTO_ICMP:
if (!offset) {
struct icmphdr _icmph, *ic;
ic = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_icmph),
&_icmph);
if (ic == NULL)
return FW_BLOCK;
src_port = (__u16) ic->type;
dst_port = (__u16) ic->code;
}
dprintf("ICMP ");
break;
default:
dprintf("p=%d ", (*pskb)->nh.iph->protocol);
break;
}
#ifdef DEBUG_IP_FIREWALL
print_ip((*pskb)->nh.iph->saddr);
if (offset)
dprintf(":fragment (%i) ", ((int)offset)<<2);
else if ((*pskb)->nh.iph->protocol == IPPROTO_TCP ||
(*pskb)->nh.iph->protocol == IPPROTO_UDP ||
(*pskb)->nh.iph->protocol == IPPROTO_ICMP)
dprintf(":%hu:%hu", src_port, dst_port);
dprintf("\n");
#endif
if (!testing) FWC_READ_LOCK(&ip_fw_lock);
else FWC_HAVE_LOCK(fwc_rlocks);
f = chain->chain;
do {
count = 0;
for (; f; f = f->next) {
count++;
if (ip_rule_match(f, rif, pskb,
tcpsyn, src_port, dst_port,
offset)) {
if (!testing
&& !ip_fw_domatch(f, rif, chain->label,
pskb, slot,
src_port, dst_port,
count, tcpsyn, &tos)) {
ret = FW_BLOCK;
cleanup(chain, 0, slot);
goto out;
}
break;
}
}
if (f) {
if (f->branch) {
/* Do sanity check to see if we have
* already set prevchain and if so we
* must be in a loop */
if (f->branch->reent[slot].prevchain) {
if (!testing) {
printk(KERN_ERR
"IP firewall: "
"Loop detected "
"at `%s'.\n",
f->branch->label);
cleanup(chain, 1, slot);
ret = FW_BLOCK;
} else {
cleanup(chain, 0, slot);
ret = FW_SKIP+1;
}
}
else {
f->branch->reent[slot].prevchain
= chain;
f->branch->reent[slot].prevrule
= f->next;
chain = f->branch;
f = chain->chain;
}
}
else if (f->simplebranch == FW_SKIP)
f = f->next;
else if (f->simplebranch == FW_SKIP+1) {
/* Just like falling off the chain */
goto fall_off_chain;
} else {
cleanup(chain, 0, slot);
ret = f->simplebranch;
}
} /* f == NULL */
else {
fall_off_chain:
if (chain->reent[slot].prevchain) {
struct ip_chain *tmp = chain;
f = chain->reent[slot].prevrule;
chain = chain->reent[slot].prevchain;
tmp->reent[slot].prevchain = NULL;
}
else {
ret = chain->policy;
if (!testing) {
chain->reent[slot].counters.pcnt++;
chain->reent[slot].counters.bcnt
+= ntohs((*pskb)->nh.iph->tot_len);
}
}
}
} while (ret == FW_SKIP+2);
out:
if (!testing) FWC_READ_UNLOCK(&ip_fw_lock);
/* Recalculate checksum if not going to reject, and TOS changed. */
if ((*pskb)->nh.iph->tos != tos
&& ret != FW_REJECT && ret != FW_BLOCK
&& !testing) {
if (!skb_ip_make_writable(pskb, offsetof(struct iphdr, tos)+1))
ret = FW_BLOCK;
else {
(*pskb)->nh.iph->tos = tos;
ip_send_check((*pskb)->nh.iph);
}
}
if (ret == FW_REDIRECT && redirport) {
if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) {
/* Wildcard redirection.
* Note that redirport will become
* 0xFFFF for non-TCP/UDP packets.
*/
*redirport = htons(dst_port);
}
}
#ifdef DEBUG_ALLOW_ALL
return (testing ? ret : FW_ACCEPT);
#else
return ret;
#endif
}
/* Must have write lock & interrupts off for any of these */
/* This function sets all the byte counters in a chain to zero. The
* input is a pointer to the chain required for zeroing */
static int zero_fw_chain(struct ip_chain *chainptr)
{
struct ip_fwkernel *i;
FWC_HAVE_LOCK(fwc_wlocks);
for (i = chainptr->chain; i; i = i->next)
memset(i->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
return 0;
}
static int clear_fw_chain(struct ip_chain *chainptr)
{
struct ip_fwkernel *i= chainptr->chain;
FWC_HAVE_LOCK(fwc_wlocks);
chainptr->chain=NULL;
while (i) {
struct ip_fwkernel *tmp = i->next;
if (i->branch)
i->branch->refcount--;
kfree(i);
i = tmp;
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
}
return 0;
}
static int replace_in_chain(struct ip_chain *chainptr,
struct ip_fwkernel *frwl,
__u32 position)
{
struct ip_fwkernel *f = chainptr->chain;
FWC_HAVE_LOCK(fwc_wlocks);
while (--position && f != NULL) f = f->next;
if (f == NULL)
return EINVAL;
if (f->branch) f->branch->refcount--;
if (frwl->branch) frwl->branch->refcount++;
frwl->next = f->next;
memcpy(f,frwl,sizeof(struct ip_fwkernel));
kfree(frwl);
return 0;
}
static int append_to_chain(struct ip_chain *chainptr, struct ip_fwkernel *rule)
{
struct ip_fwkernel *i;
FWC_HAVE_LOCK(fwc_wlocks);
/* Are we unloading now? We will block on nf_unregister_sockopt */
if (!try_module_get(THIS_MODULE))
return ENOPROTOOPT;
/* Special case if no rules already present */
if (chainptr->chain == NULL) {
/* If pointer writes are atomic then turning off
* interrupts is not necessary. */
chainptr->chain = rule;
if (rule->branch) rule->branch->refcount++;
goto append_successful;
}
/* Find the rule before the end of the chain */
for (i = chainptr->chain; i->next; i = i->next);
i->next = rule;
if (rule->branch) rule->branch->refcount++;
append_successful:
return 0;
}
/* This function inserts a rule at the position of position in the
* chain refenced by chainptr. If position is 1 then this rule will
* become the new rule one. */
static int insert_in_chain(struct ip_chain *chainptr,
struct ip_fwkernel *frwl,
__u32 position)
{
struct ip_fwkernel *f = chainptr->chain;
FWC_HAVE_LOCK(fwc_wlocks);
/* Are we unloading now? We will block on nf_unregister_sockopt */
if (!try_module_get(THIS_MODULE))
return ENOPROTOOPT;
/* special case if the position is number 1 */
if (position == 1) {
frwl->next = chainptr->chain;
if (frwl->branch) frwl->branch->refcount++;
chainptr->chain = frwl;
goto insert_successful;
}
position--;
while (--position && f != NULL) f = f->next;
if (f == NULL)
return EINVAL;
if (frwl->branch) frwl->branch->refcount++;
frwl->next = f->next;
f->next = frwl;
insert_successful:
return 0;
}
/* This function deletes the a rule from a given rulenum and chain.
* With rulenum = 1 is the first rule is deleted. */
static int del_num_from_chain(struct ip_chain *chainptr, __u32 rulenum)
{
struct ip_fwkernel *i=chainptr->chain,*tmp;
FWC_HAVE_LOCK(fwc_wlocks);
if (!chainptr->chain)
return ENOENT;
/* Need a special case for the first rule */
if (rulenum == 1) {
/* store temp to allow for freeing up of memory */
tmp = chainptr->chain;
if (chainptr->chain->branch) chainptr->chain->branch->refcount--;
chainptr->chain = chainptr->chain->next;
kfree(tmp); /* free memory that is now unused */
} else {
rulenum--;
while (--rulenum && i->next ) i = i->next;
if (!i->next)
return ENOENT;
tmp = i->next;
if (i->next->branch)
i->next->branch->refcount--;
i->next = i->next->next;
kfree(tmp);
}
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
return 0;
}
/* This function deletes the a rule from a given rule and chain.
* The rule that is deleted is the first occursance of that rule. */
static int del_rule_from_chain(struct ip_chain *chainptr,
struct ip_fwkernel *frwl)
{
struct ip_fwkernel *ltmp,*ftmp = chainptr->chain ;
int was_found;
FWC_HAVE_LOCK(fwc_wlocks);
/* Sure, we should compare marks, but since the `ipfwadm'
* script uses it for an unholy hack... well, life is easier
* this way. We also mask it out of the flags word. --PR */
for (ltmp=NULL, was_found=0;
!was_found && ftmp != NULL;
ltmp = ftmp,ftmp = ftmp->next) {
if (ftmp->ipfw.fw_src.s_addr!=frwl->ipfw.fw_src.s_addr
|| ftmp->ipfw.fw_dst.s_addr!=frwl->ipfw.fw_dst.s_addr
|| ftmp->ipfw.fw_smsk.s_addr!=frwl->ipfw.fw_smsk.s_addr
|| ftmp->ipfw.fw_dmsk.s_addr!=frwl->ipfw.fw_dmsk.s_addr
#if 0
|| ftmp->ipfw.fw_flg!=frwl->ipfw.fw_flg
#else
|| ((ftmp->ipfw.fw_flg & ~IP_FW_F_MARKABS)
!= (frwl->ipfw.fw_flg & ~IP_FW_F_MARKABS))
#endif
|| ftmp->ipfw.fw_invflg!=frwl->ipfw.fw_invflg
|| ftmp->ipfw.fw_proto!=frwl->ipfw.fw_proto
#if 0
|| ftmp->ipfw.fw_mark!=frwl->ipfw.fw_mark
#endif
|| ftmp->ipfw.fw_redirpt!=frwl->ipfw.fw_redirpt
|| ftmp->ipfw.fw_spts[0]!=frwl->ipfw.fw_spts[0]
|| ftmp->ipfw.fw_spts[1]!=frwl->ipfw.fw_spts[1]
|| ftmp->ipfw.fw_dpts[0]!=frwl->ipfw.fw_dpts[0]
|| ftmp->ipfw.fw_dpts[1]!=frwl->ipfw.fw_dpts[1]
|| ftmp->ipfw.fw_outputsize!=frwl->ipfw.fw_outputsize) {
duprintf("del_rule_from_chain: mismatch:"
"src:%u/%u dst:%u/%u smsk:%u/%u dmsk:%u/%u "
"flg:%hX/%hX invflg:%hX/%hX proto:%u/%u "
"mark:%u/%u "
"ports:%hu-%hu/%hu-%hu %hu-%hu/%hu-%hu "
"outputsize:%hu-%hu\n",
ftmp->ipfw.fw_src.s_addr,
frwl->ipfw.fw_src.s_addr,
ftmp->ipfw.fw_dst.s_addr,
frwl->ipfw.fw_dst.s_addr,
ftmp->ipfw.fw_smsk.s_addr,
frwl->ipfw.fw_smsk.s_addr,
ftmp->ipfw.fw_dmsk.s_addr,
frwl->ipfw.fw_dmsk.s_addr,
ftmp->ipfw.fw_flg,
frwl->ipfw.fw_flg,
ftmp->ipfw.fw_invflg,
frwl->ipfw.fw_invflg,
ftmp->ipfw.fw_proto,
frwl->ipfw.fw_proto,
ftmp->ipfw.fw_mark,
frwl->ipfw.fw_mark,
ftmp->ipfw.fw_spts[0],
frwl->ipfw.fw_spts[0],
ftmp->ipfw.fw_spts[1],
frwl->ipfw.fw_spts[1],
ftmp->ipfw.fw_dpts[0],
frwl->ipfw.fw_dpts[0],
ftmp->ipfw.fw_dpts[1],
frwl->ipfw.fw_dpts[1],
ftmp->ipfw.fw_outputsize,
frwl->ipfw.fw_outputsize);
continue;
}
if (strncmp(ftmp->ipfw.fw_vianame,
frwl->ipfw.fw_vianame,
IFNAMSIZ)) {
duprintf("del_rule_from_chain: if mismatch: %s/%s\n",
ftmp->ipfw.fw_vianame,
frwl->ipfw.fw_vianame);
continue;
}
if (ftmp->branch != frwl->branch) {
duprintf("del_rule_from_chain: branch mismatch: "
"%s/%s\n",
ftmp->branch?ftmp->branch->label:"(null)",
frwl->branch?frwl->branch->label:"(null)");
continue;
}
if (ftmp->branch == NULL
&& ftmp->simplebranch != frwl->simplebranch) {
duprintf("del_rule_from_chain: simplebranch mismatch: "
"%i/%i\n",
ftmp->simplebranch, frwl->simplebranch);
continue;
}
was_found = 1;
if (ftmp->branch)
ftmp->branch->refcount--;
if (ltmp)
ltmp->next = ftmp->next;
else
chainptr->chain = ftmp->next;
kfree(ftmp);
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
break;
}
if (was_found)
return 0;
else {
duprintf("del_rule_from_chain: no matching rule found\n");
return EINVAL;
}
}
/* This function takes the label of a chain and deletes the first
* chain with that name. No special cases required for the built in
* chains as they have their refcount initilised to 1 so that they are
* never deleted. */
static int del_chain(ip_chainlabel label)
{
struct ip_chain *tmp,*tmp2;
FWC_HAVE_LOCK(fwc_wlocks);
/* Corner case: return EBUSY not ENOENT for first elem ("input") */
if (strcmp(label, ip_fw_chains->label) == 0)
return EBUSY;
for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
if(strcmp(tmp->next->label,label) == 0)
break;
tmp2 = tmp->next;
if (!tmp2)
return ENOENT;
if (tmp2->refcount)
return EBUSY;
if (tmp2->chain)
return ENOTEMPTY;
tmp->next = tmp2->next;
kfree(tmp2);
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
return 0;
}
/* This is a function to initilise a chain. Built in rules start with
* refcount = 1 so that they cannot be deleted. User defined rules
* start with refcount = 0 so they can be deleted. */
static struct ip_chain *ip_init_chain(ip_chainlabel name,
__u32 ref,
int policy)
{
unsigned int i;
struct ip_chain *label
= kmalloc(SIZEOF_STRUCT_IP_CHAIN, GFP_KERNEL);
if (label == NULL)
panic("Can't kmalloc for firewall chains.\n");
strcpy(label->label,name);
label->next = NULL;
label->chain = NULL;
label->refcount = ref;
label->policy = policy;
for (i = 0; i < NUM_SLOTS; i++) {
label->reent[i].counters.pcnt = label->reent[i].counters.bcnt
= 0;
label->reent[i].prevchain = NULL;
label->reent[i].prevrule = NULL;
}
return label;
}
/* This is a function for reating a new chain. The chains is not
* created if a chain of the same name already exists */
static int create_chain(ip_chainlabel label)
{
struct ip_chain *tmp;
if (!check_label(label))
return EINVAL;
FWC_HAVE_LOCK(fwc_wlocks);
for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
if (strcmp(tmp->label,label) == 0)
return EEXIST;
if (strcmp(tmp->label,label) == 0)
return EEXIST;
/* Are we unloading now? We will block on nf_unregister_sockopt */
if (!try_module_get(THIS_MODULE))
return ENOPROTOOPT;
tmp->next = ip_init_chain(label, 0, FW_SKIP); /* refcount is
* zero since this is a
* user defined chain *
* and therefore can be
* deleted */
return 0;
}
/* This function simply changes the policy on one of the built in
* chains. checking must be done before this is call to ensure that
* chainptr is pointing to one of the three possible chains */
static int change_policy(struct ip_chain *chainptr, int policy)
{
FWC_HAVE_LOCK(fwc_wlocks);
chainptr->policy = policy;
return 0;
}
/* This function takes an ip_fwuser and converts it to a ip_fwkernel. It also
* performs some checks in the structure. */
static struct ip_fwkernel *convert_ipfw(struct ip_fwuser *fwuser, int *errno)
{
struct ip_fwkernel *fwkern;
if ( (fwuser->ipfw.fw_flg & ~IP_FW_F_MASK) != 0 ) {
duprintf("convert_ipfw: undefined flag bits set (flags=%x)\n",
fwuser->ipfw.fw_flg);
*errno = EINVAL;
return NULL;
}
#ifdef DEBUG_IP_FIREWALL_USER
/* These are sanity checks that don't really matter.
* We can get rid of these once testing is complete.
*/
if ((fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
&& ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
|| fwuser->ipfw.fw_proto != IPPROTO_TCP)) {
duprintf("convert_ipfw: TCP SYN flag set but proto != TCP!\n");
*errno = EINVAL;
return NULL;
}
if (strcmp(fwuser->label, IP_FW_LABEL_REDIRECT) != 0
&& fwuser->ipfw.fw_redirpt != 0) {
duprintf("convert_ipfw: Target not REDIR but redirpt != 0!\n");
*errno = EINVAL;
return NULL;
}
if ((!(fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
&& (fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG))
|| (!(fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
&& (fwuser->ipfw.fw_invflg & IP_FW_INV_SYN))) {
duprintf("convert_ipfw: Can't have INV flag if flag unset!\n");
*errno = EINVAL;
return NULL;
}
if (((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCPT)
&& fwuser->ipfw.fw_spts[0] == 0
&& fwuser->ipfw.fw_spts[1] == 0xFFFF)
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTPT)
&& fwuser->ipfw.fw_dpts[0] == 0
&& fwuser->ipfw.fw_dpts[1] == 0xFFFF)
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_VIA)
&& (fwuser->ipfw.fw_vianame)[0] == '\0')
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCIP)
&& fwuser->ipfw.fw_smsk.s_addr == 0)
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTIP)
&& fwuser->ipfw.fw_dmsk.s_addr == 0)) {
duprintf("convert_ipfw: INV flag makes rule unmatchable!\n");
*errno = EINVAL;
return NULL;
}
if ((fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
&& !(fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG)
&& (fwuser->ipfw.fw_spts[0] != 0
|| fwuser->ipfw.fw_spts[1] != 0xFFFF
|| fwuser->ipfw.fw_dpts[0] != 0
|| fwuser->ipfw.fw_dpts[1] != 0xFFFF
|| (fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN))) {
duprintf("convert_ipfw: Can't test ports or SYN with frag!\n");
*errno = EINVAL;
return NULL;
}
#endif
if ((fwuser->ipfw.fw_spts[0] != 0
|| fwuser->ipfw.fw_spts[1] != 0xFFFF
|| fwuser->ipfw.fw_dpts[0] != 0
|| fwuser->ipfw.fw_dpts[1] != 0xFFFF)
&& ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
|| (fwuser->ipfw.fw_proto != IPPROTO_TCP
&& fwuser->ipfw.fw_proto != IPPROTO_UDP
&& fwuser->ipfw.fw_proto != IPPROTO_ICMP))) {
duprintf("convert_ipfw: Can only test ports for TCP/UDP/ICMP!\n");
*errno = EINVAL;
return NULL;
}
fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_ATOMIC);
if (!fwkern) {
duprintf("convert_ipfw: kmalloc failed!\n");
*errno = ENOMEM;
return NULL;
}
memcpy(&fwkern->ipfw,&fwuser->ipfw,sizeof(struct ip_fw));
if (!find_special(fwuser->label, &fwkern->simplebranch)) {
fwkern->branch = find_label(fwuser->label);
if (!fwkern->branch) {
duprintf("convert_ipfw: chain doesn't exist `%s'.\n",
fwuser->label);
kfree(fwkern);
*errno = ENOENT;
return NULL;
} else if (fwkern->branch == IP_FW_INPUT_CHAIN
|| fwkern->branch == IP_FW_FORWARD_CHAIN
|| fwkern->branch == IP_FW_OUTPUT_CHAIN) {
duprintf("convert_ipfw: Can't branch to builtin chain `%s'.\n",
fwuser->label);
kfree(fwkern);
*errno = ENOENT;
return NULL;
}
} else
fwkern->branch = NULL;
memset(fwkern->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
/* Handle empty vianame by making it a wildcard */
if ((fwkern->ipfw.fw_vianame)[0] == '\0')
fwkern->ipfw.fw_flg |= IP_FW_F_WILDIF;
fwkern->next = NULL;
return fwkern;
}
int ip_fw_ctl(int cmd, void *m, int len)
{
int ret;
struct ip_chain *chain;
unsigned long flags;
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
switch (cmd) {
case IP_FW_FLUSH:
if (len != sizeof(ip_chainlabel) || !check_label(m))
ret = EINVAL;
else if ((chain = find_label(m)) == NULL)
ret = ENOENT;
else ret = clear_fw_chain(chain);
break;
case IP_FW_ZERO:
if (len != sizeof(ip_chainlabel) || !check_label(m))
ret = EINVAL;
else if ((chain = find_label(m)) == NULL)
ret = ENOENT;
else ret = zero_fw_chain(chain);
break;
case IP_FW_CHECK: {
struct ip_fwtest *new = m;
struct iphdr *ip;
/* Don't need write lock. */
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
if (len != sizeof(struct ip_fwtest) || !check_label(m))
return EINVAL;
/* Need readlock to do find_label */
FWC_READ_LOCK(&ip_fw_lock);
if ((chain = find_label(new->fwt_label)) == NULL)
ret = ENOENT;
else {
struct sk_buff *tmp_skb;
int hdrlen;
hdrlen = sizeof(struct ip_fwpkt) -
sizeof(struct in_addr) -
IFNAMSIZ;
ip = &(new->fwt_packet.fwp_iph);
/* Fix this one up by hand, who knows how many
* tools will break if we start to barf on this.
*/
if (ntohs(ip->tot_len) > hdrlen)
ip->tot_len = htons(hdrlen);
if (ip->ihl != sizeof(struct iphdr) / sizeof(u32)) {
duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n",
ip->ihl,
sizeof(struct iphdr) / sizeof(u32));
ret = EINVAL;
} else if ((tmp_skb = alloc_skb(hdrlen,
GFP_ATOMIC)) == NULL) {
duprintf("ip_fw_ctl: tmp_skb alloc failure\n");
ret = EFAULT;
} else {
skb_reserve(tmp_skb, hdrlen);
skb_push(tmp_skb, hdrlen);
memcpy(tmp_skb->data, ip, hdrlen);
tmp_skb->nh.raw =
(unsigned char *) tmp_skb->data;
ret = ip_fw_check(new->fwt_packet.fwp_vianame,
NULL, chain,
&tmp_skb, SLOT_NUMBER(), 1);
kfree_skb(tmp_skb);
switch (ret) {
case FW_ACCEPT:
ret = 0; break;
case FW_REDIRECT:
ret = ECONNABORTED; break;
case FW_MASQUERADE:
ret = ECONNRESET; break;
case FW_REJECT:
ret = ECONNREFUSED; break;
/* Hack to help diag; these only get
returned when testing. */
case FW_SKIP+1:
ret = ELOOP; break;
case FW_SKIP:
ret = ENFILE; break;
default: /* FW_BLOCK */
ret = ETIMEDOUT; break;
}
}
}
FWC_READ_UNLOCK(&ip_fw_lock);
return ret;
}
case IP_FW_MASQ_TIMEOUTS: {
ret = ip_fw_masq_timeouts(m, len);
}
break;
case IP_FW_REPLACE: {
struct ip_fwkernel *ip_fwkern;
struct ip_fwnew *new = m;
if (len != sizeof(struct ip_fwnew)
|| !check_label(new->fwn_label))
ret = EINVAL;
else if ((chain = find_label(new->fwn_label)) == NULL)
ret = ENOENT;
else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
!= NULL)
ret = replace_in_chain(chain, ip_fwkern,
new->fwn_rulenum);
}
break;
case IP_FW_APPEND: {
struct ip_fwchange *new = m;
struct ip_fwkernel *ip_fwkern;
if (len != sizeof(struct ip_fwchange)
|| !check_label(new->fwc_label))
ret = EINVAL;
else if ((chain = find_label(new->fwc_label)) == NULL)
ret = ENOENT;
else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
!= NULL)
ret = append_to_chain(chain, ip_fwkern);
}
break;
case IP_FW_INSERT: {
struct ip_fwkernel *ip_fwkern;
struct ip_fwnew *new = m;
if (len != sizeof(struct ip_fwnew)
|| !check_label(new->fwn_label))
ret = EINVAL;
else if ((chain = find_label(new->fwn_label)) == NULL)
ret = ENOENT;
else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
!= NULL)
ret = insert_in_chain(chain, ip_fwkern,
new->fwn_rulenum);
}
break;
case IP_FW_DELETE: {
struct ip_fwchange *new = m;
struct ip_fwkernel *ip_fwkern;
if (len != sizeof(struct ip_fwchange)
|| !check_label(new->fwc_label))
ret = EINVAL;
else if ((chain = find_label(new->fwc_label)) == NULL)
ret = ENOENT;
else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
!= NULL) {
ret = del_rule_from_chain(chain, ip_fwkern);
kfree(ip_fwkern);
}
}
break;
case IP_FW_DELETE_NUM: {
struct ip_fwdelnum *new = m;
if (len != sizeof(struct ip_fwdelnum)
|| !check_label(new->fwd_label))
ret = EINVAL;
else if ((chain = find_label(new->fwd_label)) == NULL)
ret = ENOENT;
else ret = del_num_from_chain(chain, new->fwd_rulenum);
}
break;
case IP_FW_CREATECHAIN: {
if (len != sizeof(ip_chainlabel)) {
duprintf("create_chain: bad size %i\n", len);
ret = EINVAL;
}
else ret = create_chain(m);
}
break;
case IP_FW_DELETECHAIN: {
if (len != sizeof(ip_chainlabel)) {
duprintf("delete_chain: bad size %i\n", len);
ret = EINVAL;
}
else ret = del_chain(m);
}
break;
case IP_FW_POLICY: {
struct ip_fwpolicy *new = m;
if (len != sizeof(struct ip_fwpolicy)
|| !check_label(new->fwp_label))
ret = EINVAL;
else if ((chain = find_label(new->fwp_label)) == NULL)
ret = ENOENT;
else if (chain != IP_FW_INPUT_CHAIN
&& chain != IP_FW_FORWARD_CHAIN
&& chain != IP_FW_OUTPUT_CHAIN) {
duprintf("change_policy: can't change policy on user"
" defined chain.\n");
ret = EINVAL;
}
else {
int pol = FW_SKIP;
find_special(new->fwp_policy, &pol);
switch(pol) {
case FW_MASQUERADE:
if (chain != IP_FW_FORWARD_CHAIN) {
ret = EINVAL;
break;
}
/* Fall thru... */
case FW_BLOCK:
case FW_ACCEPT:
case FW_REJECT:
ret = change_policy(chain, pol);
break;
default:
duprintf("change_policy: bad policy `%s'\n",
new->fwp_policy);
ret = EINVAL;
}
}
break;
}
default:
duprintf("ip_fw_ctl: unknown request %d\n",cmd);
ret = ENOPROTOOPT;
}
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
return ret;
}
/* Returns bytes used - doesn't NUL terminate */
static int dump_rule(char *buffer,
const char *chainlabel,
const struct ip_fwkernel *rule)
{
int len;
unsigned int i;
__u64 packets = 0, bytes = 0;
FWC_HAVE_LOCK(fwc_wlocks);
for (i = 0; i < NUM_SLOTS; i++) {
packets += rule->counters[i].pcnt;
bytes += rule->counters[i].bcnt;
}
len=sprintf(buffer,
"%9s " /* Chain name */
"%08X/%08X->%08X/%08X " /* Source & Destination IPs */
"%.16s " /* Interface */
"%X %X " /* fw_flg and fw_invflg fields */
"%u " /* Protocol */
"%-9u %-9u %-9u %-9u " /* Packet & byte counters */
"%u-%u %u-%u " /* Source & Dest port ranges */
"A%02X X%02X " /* TOS and and xor masks */
"%08X " /* Redirection port */
"%u " /* fw_mark field */
"%u " /* output size */
"%9s\n", /* Target */
chainlabel,
ntohl(rule->ipfw.fw_src.s_addr),
ntohl(rule->ipfw.fw_smsk.s_addr),
ntohl(rule->ipfw.fw_dst.s_addr),
ntohl(rule->ipfw.fw_dmsk.s_addr),
(rule->ipfw.fw_vianame)[0] ? rule->ipfw.fw_vianame : "-",
rule->ipfw.fw_flg,
rule->ipfw.fw_invflg,
rule->ipfw.fw_proto,
(__u32)(packets >> 32), (__u32)packets,
(__u32)(bytes >> 32), (__u32)bytes,
rule->ipfw.fw_spts[0], rule->ipfw.fw_spts[1],
rule->ipfw.fw_dpts[0], rule->ipfw.fw_dpts[1],
rule->ipfw.fw_tosand, rule->ipfw.fw_tosxor,
rule->ipfw.fw_redirpt,
rule->ipfw.fw_mark,
rule->ipfw.fw_outputsize,
branchname(rule->branch,rule->simplebranch));
duprintf("dump_rule: %i bytes done.\n", len);
return len;
}
/* File offset is actually in records, not bytes. */
static int ip_chain_procinfo(char *buffer, char **start,
off_t offset, int length)
{
struct ip_chain *i;
struct ip_fwkernel *j = ip_fw_chains->chain;
unsigned long flags;
int len = 0;
int last_len = 0;
off_t upto = 0;
duprintf("Offset starts at %lu\n", offset);
duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
/* Need a write lock to lock out ``readers'' which update counters. */
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
for (i = ip_fw_chains; i; i = i->next) {
for (j = i->chain; j; j = j->next) {
if (upto == offset) break;
duprintf("Skipping rule in chain `%s'\n",
i->label);
upto++;
}
if (upto == offset) break;
}
/* Don't init j first time, or once i = NULL */
for (; i; (void)((i = i->next) && (j = i->chain))) {
duprintf("Dumping chain `%s'\n", i->label);
for (; j; j = j->next, upto++, last_len = len)
{
len += dump_rule(buffer+len, i->label, j);
if (len > length) {
duprintf("Dumped to %i (past %i). "
"Moving back to %i.\n",
len, length, last_len);
len = last_len;
goto outside;
}
}
}
outside:
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
buffer[len] = '\0';
duprintf("ip_chain_procinfo: Length = %i (of %i). Offset = %li.\n",
len, length, upto);
/* `start' hack - see fs/proc/generic.c line ~165 */
*start=(char *)((unsigned int)upto-offset);
return len;
}
static int ip_chain_name_procinfo(char *buffer, char **start,
off_t offset, int length)
{
struct ip_chain *i;
int len = 0,last_len = 0;
off_t pos = 0,begin = 0;
unsigned long flags;
/* Need a write lock to lock out ``readers'' which update counters. */
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
for (i = ip_fw_chains; i; i = i->next)
{
unsigned int j;
__u32 packetsHi = 0, packetsLo = 0, bytesHi = 0, bytesLo = 0;
for (j = 0; j < NUM_SLOTS; j++) {
packetsLo += i->reent[j].counters.pcnt & 0xFFFFFFFF;
packetsHi += ((i->reent[j].counters.pcnt >> 32)
& 0xFFFFFFFF);
bytesLo += i->reent[j].counters.bcnt & 0xFFFFFFFF;
bytesHi += ((i->reent[j].counters.bcnt >> 32)
& 0xFFFFFFFF);
}
/* print the label and the policy */
len+=sprintf(buffer+len,"%s %s %i %u %u %u %u\n",
i->label,branchname(NULL, i->policy),i->refcount,
packetsHi, packetsLo, bytesHi, bytesLo);
pos=begin+len;
if(pos<offset) {
len=0;
begin=pos;
}
else if(pos>offset+length) {
len = last_len;
break;
}
last_len = len;
}
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
*start = buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
len=length;
return len;
}
/*
* Interface to the generic firewall chains.
*/
int ipfw_input_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
return ip_fw_check(dev->name,
arg, IP_FW_INPUT_CHAIN, pskb, SLOT_NUMBER(), 0);
}
int ipfw_output_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
/* Locally generated bogus packets by root. <SIGH>. */
if ((*pskb)->len < sizeof(struct iphdr) ||
(*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
return FW_ACCEPT;
return ip_fw_check(dev->name,
arg, IP_FW_OUTPUT_CHAIN, pskb, SLOT_NUMBER(), 0);
}
int ipfw_forward_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
return ip_fw_check(dev->name,
arg, IP_FW_FORWARD_CHAIN, pskb, SLOT_NUMBER(), 0);
}
struct firewall_ops ipfw_ops = {
.fw_forward = ipfw_forward_check,
.fw_input = ipfw_input_check,
.fw_output = ipfw_output_check,
};
int ipfw_init_or_cleanup(int init)
{
struct proc_dir_entry *proc;
int ret = 0;
unsigned long flags;
if (!init) goto cleanup;
#ifdef DEBUG_IP_FIREWALL_LOCKING
fwc_wlocks = fwc_rlocks = 0;
#endif
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
if (ipfwsk == NULL)
goto cleanup_nothing;
#endif
ret = register_firewall(PF_INET, &ipfw_ops);
if (ret < 0)
goto cleanup_netlink;
proc = proc_net_create(IP_FW_PROC_CHAINS, S_IFREG | S_IRUSR | S_IWUSR,
ip_chain_procinfo);
if (proc) proc->owner = THIS_MODULE;
proc = proc_net_create(IP_FW_PROC_CHAIN_NAMES,
S_IFREG | S_IRUSR | S_IWUSR,
ip_chain_name_procinfo);
if (proc) proc->owner = THIS_MODULE;
IP_FW_INPUT_CHAIN = ip_init_chain(IP_FW_LABEL_INPUT, 1, FW_ACCEPT);
IP_FW_FORWARD_CHAIN = ip_init_chain(IP_FW_LABEL_FORWARD, 1, FW_ACCEPT);
IP_FW_OUTPUT_CHAIN = ip_init_chain(IP_FW_LABEL_OUTPUT, 1, FW_ACCEPT);
return ret;
cleanup:
unregister_firewall(PF_INET, &ipfw_ops);
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
while (ip_fw_chains) {
struct ip_chain *next = ip_fw_chains->next;
clear_fw_chain(ip_fw_chains);
kfree(ip_fw_chains);
ip_fw_chains = next;
}
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
proc_net_remove(IP_FW_PROC_CHAINS);
proc_net_remove(IP_FW_PROC_CHAIN_NAMES);
cleanup_netlink:
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
sock_release(ipfwsk->sk_socket);
cleanup_nothing:
#endif
return ret;
}
#warning ipfwadm is obsolete, and will be removed soon.
/* Minor modifications to fit on compatibility framework:
Rusty.Russell@rustcorp.com.au
*/
#include <linux/config.h>
#define CONFIG_IP_FIREWALL
#define CONFIG_IP_FIREWALL_VERBOSE
#define CONFIG_IP_MASQUERADE
#define CONFIG_IP_ACCT
#define CONFIG_IP_TRANSPARENT_PROXY
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
#define CONFIG_IP_FIREWALL_NETLINK
#endif
/*
* IP firewalling code. This is taken from 4.4BSD. Please note the
* copyright message below. As per the GPL it must be maintained
* and the licenses thus do not conflict. While this port is subject
* to the GPL I also place my modifications under the original
* license in recognition of the original copyright.
* -- Alan Cox.
*
* $Id: ipfwadm_core.c,v 1.11 2002/01/24 15:50:31 davem Exp $
*
* Ported from BSD to Linux,
* Alan Cox 22/Nov/1994.
* Zeroing /proc and other additions
* Jos Vos 4/Feb/1995.
* Merged and included the FreeBSD-Current changes at Ugen's request
* (but hey it's a lot cleaner now). Ugen would prefer in some ways
* we waited for his final product but since Linux 1.2.0 is about to
* appear it's not practical - Read: It works, it's not clean but please
* don't consider it to be his standard of finished work.
* Alan Cox 12/Feb/1995
* Porting bidirectional entries from BSD, fixing accounting issues,
* adding struct ip_fwpkt for checking packets with interface address
* Jos Vos 5/Mar/1995.
* Established connections (ACK check), ACK check on bidirectional rules,
* ICMP type check.
* Wilfred Mollenvanger 7/7/1995.
* TCP attack protection.
* Alan Cox 25/8/95, based on information from bugtraq.
* ICMP type printk, IP_FW_F_APPEND
* Bernd Eckenfels 1996-01-31
* Split blocking chain into input and output chains, add new "insert" and
* "append" commands to replace semi-intelligent "add" command, let "delete".
* only delete the first matching entry, use 0xFFFF (0xFF) as ports (ICMP
* types) when counting packets being 2nd and further fragments.
* Jos Vos <jos@xos.nl> 8/2/1996.
* Add support for matching on device names.
* Jos Vos <jos@xos.nl> 15/2/1996.
* Transparent proxying support.
* Willy Konynenberg <willy@xos.nl> 10/5/96.
* Make separate accounting on incoming and outgoing packets possible.
* Jos Vos <jos@xos.nl> 18/5/1996.
* Added trap out of bad frames.
* Alan Cox <alan@cymru.net> 17/11/1996
*
*
* Masquerading functionality
*
* Copyright (c) 1994 Pauline Middelink
*
* The pieces which added masquerading functionality are totally
* my responsibility and have nothing to with the original authors
* copyright or doing.
*
* Parts distributed under GPL.
*
* Fixes:
* Pauline Middelink : Added masquerading.
* Alan Cox : Fixed an error in the merge.
* Thomas Quinot : Fixed port spoofing.
* Alan Cox : Cleaned up retransmits in spoofing.
* Alan Cox : Cleaned up length setting.
* Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
*
* Juan Jose Ciarlante : Masquerading code moved to ip_masq.c
* Andi Kleen : Print frag_offsets and the ip flags properly.
*
* All the real work was done by .....
*
*/
/*
* Copyright (c) 1993 Daniel Boulet
* Copyright (c) 1994 Ugen J.S.Antsilevich
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/icmp.h>
#include <linux/udp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <linux/netlink.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/netfilter_ipv4/ipfwadm_core.h>
#include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/lockhelp.h>
#include <linux/netfilter_ipv4/ip_nat_core.h>
#include <net/checksum.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("ipfwadm backwards compatibility layer");
/*
* Implement IP packet firewall
*/
#ifdef DEBUG_IP_FIREWALL
#define dprintf1(a) printk(a)
#define dprintf2(a1,a2) printk(a1,a2)
#define dprintf3(a1,a2,a3) printk(a1,a2,a3)
#define dprintf4(a1,a2,a3,a4) printk(a1,a2,a3,a4)
#else
#define dprintf1(a)
#define dprintf2(a1,a2)
#define dprintf3(a1,a2,a3)
#define dprintf4(a1,a2,a3,a4)
#endif
#define print_ip(a) printk("%u.%u.%u.%u", NIPQUAD(a));
#ifdef DEBUG_IP_FIREWALL
#define dprint_ip(a) print_ip(a)
#else
#define dprint_ip(a)
#endif
static DECLARE_RWLOCK(ip_fw_lock);
#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
struct ip_fw *ip_fw_fwd_chain;
struct ip_fw *ip_fw_in_chain;
struct ip_fw *ip_fw_out_chain;
struct ip_fw *ip_acct_chain;
struct ip_fw *ip_masq_chain;
static struct ip_fw **chains[] =
{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain,
&ip_masq_chain
};
#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
#ifdef CONFIG_IP_FIREWALL
int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
int ip_fw_in_policy=IP_FW_F_ACCEPT;
int ip_fw_out_policy=IP_FW_F_ACCEPT;
static int *policies[] =
{&ip_fw_fwd_policy, &ip_fw_in_policy, &ip_fw_out_policy};
#endif
#ifdef CONFIG_IP_FIREWALL_NETLINK
struct sock *ipfwsk;
#endif
/*
* Returns 1 if the port is matched by the vector, 0 otherwise
*/
extern inline int port_match(unsigned short *portptr,int nports,unsigned short port,int range_flag)
{
if (!nports)
return 1;
if ( range_flag )
{
if ( portptr[0] <= port && port <= portptr[1] )
{
return( 1 );
}
nports -= 2;
portptr += 2;
}
while ( nports-- > 0 )
{
if ( *portptr++ == port )
{
return( 1 );
}
}
return(0);
}
#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
#ifdef CONFIG_IP_FIREWALL_VERBOSE
/*
* VERY ugly piece of code which actually makes kernel printf for
* matching packets.
*/
static char *chain_name(struct ip_fw *chain, int mode)
{
switch (mode) {
case IP_FW_MODE_ACCT_IN: return "acct in";
case IP_FW_MODE_ACCT_OUT: return "acct out";
default:
if (chain == ip_fw_fwd_chain)
return "fw-fwd";
else if (chain == ip_fw_in_chain)
return "fw-in";
else
return "fw-out";
}
}
static char *rule_name(struct ip_fw *f, int mode, char *buf)
{
if (mode == IP_FW_MODE_ACCT_IN || mode == IP_FW_MODE_ACCT_OUT)
return "";
if(f->fw_flg&IP_FW_F_ACCEPT) {
if(f->fw_flg&IP_FW_F_REDIR) {
sprintf(buf, "acc/r%d ", f->fw_pts[f->fw_nsp+f->fw_ndp]);
return buf;
} else if(f->fw_flg&IP_FW_F_MASQ)
return "acc/masq ";
else
return "acc ";
} else if(f->fw_flg&IP_FW_F_ICMPRPL) {
return "rej ";
} else {
return "deny ";
}
}
static void print_packet(struct sk_buff **pskb,
u16 src_port, u16 dst_port, u16 icmp_type,
char *chain, char *rule, char *devname)
{
__u32 *opt = (__u32 *) ((*pskb)->nh.iph + 1);
int opti;
__u16 foff = ntohs((*pskb)->nh.iph->frag_off);
int protocol = (*pskb)->nh.iph->protocol;
printk(KERN_INFO "IP %s %s%s", chain, rule, devname);
switch (protocol) {
case IPPROTO_TCP:
printk(" TCP ");
break;
case IPPROTO_UDP:
printk(" UDP ");
break;
case IPPROTO_ICMP:
printk(" ICMP/%d ", icmp_type);
break;
default:
printk(" PROTO=%d ", protocol);
break;
};
print_ip((*pskb)->nh.iph->saddr);
if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
printk(":%hu", src_port);
printk(" ");
print_ip((*pskb)->nh.iph->daddr);
if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
printk(":%hu", dst_port);
printk(" L=%hu S=0x%2.2hX I=%hu FO=0x%4.4hX T=%hu",
ntohs((*pskb)->nh.iph->tot_len),
(*pskb)->nh.iph->tos,
ntohs((*pskb)->nh.iph->id),
foff & IP_OFFSET,
(*pskb)->nh.iph->ttl);
if (foff & IP_DF)
printk(" DF=1");
if (foff & IP_MF)
printk(" MF=1");
for (opti = 0; opti < ((*pskb)->nh.iph->ihl - sizeof(struct iphdr) / 4); opti++)
printk(" O=0x%8.8X", *opt++);
printk("\n");
}
#endif
/*
* Returns one of the generic firewall policies, like FW_ACCEPT.
* Also does accounting so you can feed it the accounting chain.
*
* The modes is either IP_FW_MODE_FW (normal firewall mode),
* IP_FW_MODE_ACCT_IN or IP_FW_MODE_ACCT_OUT (accounting mode,
* steps through the entire chain and handles fragments
* differently), or IP_FW_MODE_CHK (handles user-level check,
* counters are not updated).
*/
int ip_fw_chk(struct sk_buff **pskb,
struct net_device *rif, __u16 *redirport,
struct ip_fw *chain, int policy, int mode)
{
struct ip_fw *f;
__u32 src, dst;
__u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
unsigned short f_prt=0, prt;
char notcpsyn=0, notcpack=0, match;
unsigned short offset;
int answer;
unsigned char tosand, tosxor;
int protocol;
/*
* If the chain is empty follow policy. The BSD one
* accepts anything giving you a time window while
* flushing and rebuilding the tables.
*/
/*
* This way we handle fragmented packets.
* we ignore all fragments but the first one
* so the whole packet can't be reassembled.
* This way we relay on the full info which
* stored only in first packet.
*
* Note that this theoretically allows partial packet
* spoofing. Not very dangerous but paranoid people may
* wish to play with this. It also allows the so called
* "fragment bomb" denial of service attack on some types
* of system.
*/
offset = ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET;
protocol = (*pskb)->nh.iph->protocol;
/*
* 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 && protocol == IPPROTO_TCP)
return FW_BLOCK;
if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) &&
(protocol == IPPROTO_TCP ||
protocol == IPPROTO_UDP ||
protocol == IPPROTO_ICMP))
return FW_ACCEPT;
/*
* Header fragment for TCP is too small to check the bits.
*/
if (protocol == IPPROTO_TCP &&
((*pskb)->nh.iph->ihl<<2)+16 > ntohs((*pskb)->nh.iph->tot_len))
return FW_BLOCK;
/*
* Too short.
*
* But only too short for a packet with ports...
*/
else if ((ntohs((*pskb)->nh.iph->tot_len) <
8 + ((*pskb)->nh.iph->ihl << 2)) &&
(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP))
return FW_BLOCK;
src = (*pskb)->nh.iph->saddr;
dst = (*pskb)->nh.iph->daddr;
/*
* If we got interface from which packet came
* we can use the address directly. This is unlike
* 4.4BSD derived systems that have an address chain
* per device. We have a device per address with dummy
* devices instead.
*/
dprintf1("Packet ");
switch (protocol) {
case IPPROTO_TCP:
dprintf1("TCP ");
/* ports stay 0xFFFF if it is not the first fragment */
if (!offset) {
struct tcphdr _tcph, *th;
th = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_tcph), &_tcph);
if (th == NULL)
return FW_BLOCK;
src_port = ntohs(th->source);
dst_port = ntohs(th->dest);
if(!th->ack && !th->rst)
/* We do NOT have ACK, value TRUE */
notcpack = 1;
if(!th->syn || !notcpack)
/* We do NOT have SYN, value TRUE */
notcpsyn = 1;
}
prt = IP_FW_F_TCP;
break;
case IPPROTO_UDP:
dprintf1("UDP ");
/* ports stay 0xFFFF if it is not the first fragment */
if (!offset) {
struct udphdr _udph, *uh;
uh = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_udph), &_udph);
if (uh == NULL)
return FW_BLOCK;
src_port = ntohs(uh->source);
dst_port = ntohs(uh->dest);
}
prt = IP_FW_F_UDP;
break;
case IPPROTO_ICMP:
/* icmp_type stays 255 if it is not the first fragment */
if (!offset) {
struct icmphdr _icmph, *ic;
ic = skb_header_pointer(*pskb,
(*pskb)->nh.iph->ihl*4,
sizeof(_icmph),
&_icmph);
if (ic == NULL)
return FW_BLOCK;
icmp_type = (__u16) ic->type;
}
dprintf2("ICMP:%d ", icmp_type);
prt = IP_FW_F_ICMP;
break;
default:
dprintf2("p=%d ", protocol);
prt = IP_FW_F_ALL;
break;
}
#ifdef DEBUG_IP_FIREWALL
dprint_ip(src);
if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
/* This will print 65535 when it is not the first fragment! */
dprintf2(":%d ", src_port);
dprint_ip(dst);
if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP)
/* This will print 65535 when it is not the first fragment! */
dprintf2(":%d ", dst_port);
dprintf1("\n");
#endif
if (mode == IP_FW_MODE_CHK)
READ_LOCK(&ip_fw_lock);
else
WRITE_LOCK(&ip_fw_lock);
for (f = chain; f; f = f->fw_next) {
/*
* This is a bit simpler as we don't have to walk
* an interface chain as you do in BSD - same logic
* however.
*/
/*
* Match can become 0x01 (a "normal" match was found),
* 0x02 (a reverse match was found), and 0x03 (the
* IP addresses match in both directions).
* Now we know in which direction(s) we should look
* for a match for the TCP/UDP ports. Both directions
* might match (e.g., when both addresses are on the
* same network for which an address/mask is given), but
* the ports might only match in one direction.
* This was obviously wrong in the original BSD code.
*/
match = 0x00;
if ((src & f->fw_smsk.s_addr) == f->fw_src.s_addr &&
(dst & f->fw_dmsk.s_addr) == f->fw_dst.s_addr)
/* normal direction */
match |= 0x01;
if ((f->fw_flg & IP_FW_F_BIDIR) &&
(dst & f->fw_smsk.s_addr) == f->fw_src.s_addr &&
(src & f->fw_dmsk.s_addr) == f->fw_dst.s_addr)
/* reverse direction */
match |= 0x02;
if (!match)
continue;
/*
* Look for a VIA device match
*/
if (f->fw_viadev) {
if (rif != f->fw_viadev)
continue; /* Mismatch */
}
/* This looks stupid, because we scan almost static
list, searching for static key. However, this way seems
to be only reasonable way of handling fw_via rules
(btw bsd makes the same thing).
It will not affect performance if you will follow
the following simple rules:
- if interface is aliased, ALWAYS specify fw_viadev,
so that previous check will guarantee, that we will
not waste time when packet arrive on another interface.
- avoid using fw_via.s_addr if fw_via.s_addr is owned
by an aliased interface.
--ANK
*/
if (f->fw_via.s_addr && rif) {
struct in_ifaddr *ifa;
if (rif->ip_ptr == NULL)
continue; /* Mismatch */
for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list;
ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_local == f->fw_via.s_addr)
goto ifa_ok;
}
continue; /* Mismatch */
ifa_ok:;
}
/*
* Ok the chain addresses match.
*/
#ifdef CONFIG_IP_ACCT
/*
* See if we're in accounting mode and only want to
* count incoming or outgoing packets.
*/
if (mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT) &&
((mode == IP_FW_MODE_ACCT_IN && f->fw_flg&IP_FW_F_ACCTOUT) ||
(mode == IP_FW_MODE_ACCT_OUT && f->fw_flg&IP_FW_F_ACCTIN)))
continue;
#endif
/*
* For all non-TCP packets and/or non-first fragments,
* notcpsyn and notcpack will always be FALSE,
* so the IP_FW_F_TCPSYN and IP_FW_F_TCPACK flags
* are actually ignored for these packets.
*/
if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
continue;
if((f->fw_flg&IP_FW_F_TCPACK) && notcpack)
continue;
f_prt=f->fw_flg&IP_FW_F_KIND;
if (f_prt != IP_FW_F_ALL) {
/*
* Specific firewall - packet's protocol
* must match firewall's.
*/
if (prt != f_prt)
continue;
if((prt==IP_FW_F_ICMP &&
! port_match(&f->fw_pts[0], f->fw_nsp,
icmp_type,f->fw_flg&IP_FW_F_SRNG)) ||
!(prt==IP_FW_F_ICMP || ((match & 0x01) &&
port_match(&f->fw_pts[0], f->fw_nsp, src_port,
f->fw_flg&IP_FW_F_SRNG) &&
port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port,
f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) &&
port_match(&f->fw_pts[0], f->fw_nsp, dst_port,
f->fw_flg&IP_FW_F_SRNG) &&
port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port,
f->fw_flg&IP_FW_F_DRNG))))
{
continue;
}
}
#ifdef CONFIG_IP_FIREWALL_VERBOSE
if (f->fw_flg & IP_FW_F_PRN)
{
char buf[16];
print_packet(pskb, src_port, dst_port, icmp_type,
chain_name(chain, mode),
rule_name(f, mode, buf),
rif ? rif->name : "-");
}
#endif
if (mode != IP_FW_MODE_CHK) {
f->fw_bcnt += ntohs((*pskb)->nh.iph->tot_len);
f->fw_pcnt++;
}
if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)))
break;
} /* Loop */
if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) {
/*
* We rely on policy defined in the rejecting entry or, if no match
* was found, we rely on the general policy variable for this type
* of firewall.
*/
if (f != NULL) {
policy = f->fw_flg;
tosand = f->fw_tosand;
tosxor = f->fw_tosxor;
} else {
tosand = 0xFF;
tosxor = 0x00;
}
if (policy & IP_FW_F_ACCEPT) {
/* Adjust priority and recompute checksum */
__u8 tos = (*pskb)->nh.iph->tos;
if (((tos & tosand) ^ tosxor) != tos) {
if (!skb_ip_make_writable(pskb,
offsetof(struct iphdr, tos)+1))
goto drop_it;
(*pskb)->nh.iph->tos = (tos & tosand) ^ tosxor;
ip_send_check((*pskb)->nh.iph);
}
#ifdef CONFIG_IP_TRANSPARENT_PROXY
if (policy & IP_FW_F_REDIR) {
if (redirport)
if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) {
/* Wildcard redirection.
* Note that redirport will become
* 0xFFFF for non-TCP/UDP packets.
*/
*redirport = htons(dst_port);
}
answer = FW_REDIRECT;
} else
#endif
#ifdef CONFIG_IP_MASQUERADE
if (policy & IP_FW_F_MASQ)
answer = FW_MASQUERADE;
else
#endif
answer = FW_ACCEPT;
} else if (policy & IP_FW_F_ICMPRPL)
answer = FW_REJECT;
else {
drop_it:
answer = FW_BLOCK;
}
#ifdef CONFIG_IP_FIREWALL_NETLINK
if ((policy & IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
{
struct sk_buff *skb = alloc_skb(128,
(mode == IP_FW_MODE_CHK) ?
GFP_KERNEL : GFP_ATOMIC);
if (skb) {
int len = min_t(unsigned int,
128,
ntohs((*pskb)->nh.iph->tot_len));
skb_put(skb, len);
skb_copy_bits(*pskb,
((char *)(*pskb)->nh.iph -
(char *)(*pskb)->data),
skb->data, len);
if (netlink_post(NETLINK_FIREWALL, skb))
kfree_skb(skb);
}
}
#endif
} else
/* we're doing accounting, always ok */
answer = 0;
if (mode == IP_FW_MODE_CHK)
READ_UNLOCK(&ip_fw_lock);
else
WRITE_UNLOCK(&ip_fw_lock);
return answer;
}
static void zero_fw_chain(struct ip_fw *chainptr)
{
struct ip_fw *ctmp=chainptr;
WRITE_LOCK(&ip_fw_lock);
while(ctmp)
{
ctmp->fw_pcnt=0L;
ctmp->fw_bcnt=0L;
ctmp=ctmp->fw_next;
}
WRITE_UNLOCK(&ip_fw_lock);
}
static void free_fw_chain(struct ip_fw *volatile* chainptr)
{
WRITE_LOCK(&ip_fw_lock);
while ( *chainptr != NULL )
{
struct ip_fw *ftmp;
ftmp = *chainptr;
*chainptr = ftmp->fw_next;
if (ftmp->fw_viadev
&& ftmp->fw_viadev != (struct net_device *)-1)
dev_put(ftmp->fw_viadev);
kfree(ftmp);
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
}
WRITE_UNLOCK(&ip_fw_lock);
}
/* Volatiles to keep some of the compiler versions amused */
static int insert_in_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
{
struct ip_fw *ftmp;
/* Are we unloading now? We will block on nf_unregister_sockopt */
if (!try_module_get(THIS_MODULE))
return ENOPROTOOPT;
ftmp = kmalloc( sizeof(struct ip_fw), GFP_KERNEL );
if ( ftmp == NULL )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: malloc said no\n");
#endif
return( ENOMEM );
}
memcpy(ftmp, frwl, len);
/*
* Allow the more recent "minimise cost" flag to be
* set. [Rob van Nieuwkerk]
*/
ftmp->fw_tosand |= 0x01;
ftmp->fw_tosxor &= 0xFE;
ftmp->fw_pcnt=0L;
ftmp->fw_bcnt=0L;
WRITE_LOCK(&ip_fw_lock);
if ((ftmp->fw_vianame)[0]) {
if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
ftmp->fw_viadev = (struct net_device *) -1;
} else
ftmp->fw_viadev = NULL;
ftmp->fw_next = *chainptr;
*chainptr=ftmp;
WRITE_UNLOCK(&ip_fw_lock);
return(0);
}
static int append_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
{
struct ip_fw *ftmp;
struct ip_fw *chtmp=NULL;
struct ip_fw *volatile chtmp_prev=NULL;
/* Are we unloading now? We will block on nf_unregister_sockopt */
if (!try_module_get(THIS_MODULE))
return ENOPROTOOPT;
ftmp = kmalloc( sizeof(struct ip_fw), GFP_KERNEL );
if ( ftmp == NULL )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: malloc said no\n");
#endif
return( ENOMEM );
}
memcpy(ftmp, frwl, len);
/*
* Allow the more recent "minimise cost" flag to be
* set. [Rob van Nieuwkerk]
*/
ftmp->fw_tosand |= 0x01;
ftmp->fw_tosxor &= 0xFE;
ftmp->fw_pcnt=0L;
ftmp->fw_bcnt=0L;
ftmp->fw_next = NULL;
WRITE_LOCK(&ip_fw_lock);
if ((ftmp->fw_vianame)[0]) {
if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
ftmp->fw_viadev = (struct net_device *) -1;
} else
ftmp->fw_viadev = NULL;
chtmp_prev=NULL;
for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next)
chtmp_prev=chtmp;
if (chtmp_prev)
chtmp_prev->fw_next=ftmp;
else
*chainptr=ftmp;
WRITE_UNLOCK(&ip_fw_lock);
return(0);
}
static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl)
{
struct ip_fw *ftmp,*ltmp;
unsigned short tport1,tport2,tmpnum;
char matches,was_found;
WRITE_LOCK(&ip_fw_lock);
ftmp=*chainptr;
if ( ftmp == NULL )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: chain is empty\n");
#endif
WRITE_UNLOCK(&ip_fw_lock);
return( EINVAL );
}
ltmp=NULL;
was_found=0;
while( !was_found && ftmp != NULL )
{
matches=1;
if (ftmp->fw_src.s_addr!=frwl->fw_src.s_addr
|| ftmp->fw_dst.s_addr!=frwl->fw_dst.s_addr
|| ftmp->fw_smsk.s_addr!=frwl->fw_smsk.s_addr
|| ftmp->fw_dmsk.s_addr!=frwl->fw_dmsk.s_addr
|| ftmp->fw_via.s_addr!=frwl->fw_via.s_addr
|| ftmp->fw_flg!=frwl->fw_flg)
matches=0;
tport1=ftmp->fw_nsp+ftmp->fw_ndp;
tport2=frwl->fw_nsp+frwl->fw_ndp;
if (tport1!=tport2)
matches=0;
else if (tport1!=0)
{
for (tmpnum=0;tmpnum < tport1 && tmpnum < IP_FW_MAX_PORTS;tmpnum++)
if (ftmp->fw_pts[tmpnum]!=frwl->fw_pts[tmpnum])
matches=0;
}
if (strncmp(ftmp->fw_vianame, frwl->fw_vianame, IFNAMSIZ))
matches=0;
if(matches)
{
was_found=1;
if (ftmp->fw_viadev
&& ftmp->fw_viadev != (struct net_device *)-1)
dev_put(ftmp->fw_viadev);
if (ltmp)
{
ltmp->fw_next=ftmp->fw_next;
kfree(ftmp);
ftmp=ltmp->fw_next;
}
else
{
*chainptr=ftmp->fw_next;
kfree(ftmp);
ftmp=*chainptr;
}
}
else
{
ltmp = ftmp;
ftmp = ftmp->fw_next;
}
}
WRITE_UNLOCK(&ip_fw_lock);
if (was_found) {
/* We will block in cleanup's unregister sockopt if unloaded,
so this is safe. */
module_put(THIS_MODULE);
return 0;
} else
return(EINVAL);
}
#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
{
if ( len != sizeof(struct ip_fw) )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
#endif
return(NULL);
}
if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n",
frwl->fw_flg);
#endif
return(NULL);
}
#ifndef CONFIG_IP_TRANSPARENT_PROXY
if (frwl->fw_flg & IP_FW_F_REDIR) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: unsupported flag IP_FW_F_REDIR\n");
#endif
return(NULL);
}
#endif
#ifndef CONFIG_IP_MASQUERADE
if (frwl->fw_flg & IP_FW_F_MASQ) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: unsupported flag IP_FW_F_MASQ\n");
#endif
return(NULL);
}
#endif
if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: src range set but fw_nsp=%d\n",
frwl->fw_nsp);
#endif
return(NULL);
}
if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: dst range set but fw_ndp=%d\n",
frwl->fw_ndp);
#endif
return(NULL);
}
if ( frwl->fw_nsp + frwl->fw_ndp > (frwl->fw_flg & IP_FW_F_REDIR ? IP_FW_MAX_PORTS - 1 : IP_FW_MAX_PORTS) )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: too many ports (%d+%d)\n",
frwl->fw_nsp,frwl->fw_ndp);
#endif
return(NULL);
}
return frwl;
}
#ifdef CONFIG_IP_ACCT
int ip_acct_ctl(int stage, void *m, int len)
{
if ( stage == IP_ACCT_FLUSH )
{
free_fw_chain(&ip_acct_chain);
return(0);
}
if ( stage == IP_ACCT_ZERO )
{
zero_fw_chain(ip_acct_chain);
return(0);
}
if ( stage == IP_ACCT_INSERT || stage == IP_ACCT_APPEND ||
stage == IP_ACCT_DELETE )
{
struct ip_fw *frwl;
if (!(frwl=check_ipfw_struct(m,len)))
return (EINVAL);
switch (stage)
{
case IP_ACCT_INSERT:
return( insert_in_chain(&ip_acct_chain,frwl,len));
case IP_ACCT_APPEND:
return( append_to_chain(&ip_acct_chain,frwl,len));
case IP_ACCT_DELETE:
return( del_from_chain(&ip_acct_chain,frwl));
default:
/*
* Should be panic but... (Why ??? - AC)
*/
#ifdef DEBUG_IP_FIREWALL
printk("ip_acct_ctl: unknown request %d\n",stage);
#endif
return(EINVAL);
}
}
#ifdef DEBUG_IP_FIREWALL
printk("ip_acct_ctl: unknown request %d\n",stage);
#endif
return(EINVAL);
}
#endif
#ifdef CONFIG_IP_FIREWALL
int ip_fw_ctl(int stage, void *m, int len)
{
int cmd, fwtype;
cmd = stage & IP_FW_COMMAND;
fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
if ( cmd == IP_FW_FLUSH )
{
free_fw_chain(chains[fwtype]);
return(0);
}
if ( cmd == IP_FW_ZERO )
{
zero_fw_chain(*chains[fwtype]);
return(0);
}
if ( cmd == IP_FW_POLICY )
{
int *tmp_policy_ptr;
tmp_policy_ptr=(int *)m;
*policies[fwtype] = *tmp_policy_ptr;
return 0;
}
if ( cmd == IP_FW_CHECK )
{
struct sk_buff *tmp_skb;
struct net_device *viadev;
struct ip_fwpkt *ipfwp;
struct iphdr *ip;
int hdrlen, ret;
hdrlen = sizeof(struct ip_fwpkt) -
sizeof(struct in_addr) -
IFNAMSIZ;
if ( len != sizeof(struct ip_fwpkt) )
{
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: length=%d, expected %d\n",
len, sizeof(struct ip_fwpkt));
#endif
return( EINVAL );
}
ipfwp = (struct ip_fwpkt *)m;
ip = &(ipfwp->fwp_iph);
if ( !(viadev = dev_get_by_name(ipfwp->fwp_vianame)) ) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: invalid device \"%s\"\n", ipfwp->fwp_vianame);
#endif
return(EINVAL);
} else if ( ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
sizeof(struct iphdr)/sizeof(int));
#endif
dev_put(viadev);
return(EINVAL);
}
/* Fix this one up by hand, who knows how many
* tools will break if we start to barf on this.
*/
if (ntohs(ip->tot_len) > hdrlen)
ip->tot_len = htons(hdrlen);
if ((tmp_skb = alloc_skb(hdrlen, GFP_ATOMIC)) == NULL) {
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: tmp_skb alloc failure\n");
#endif
dev_put(viadev);
return(EFAULT);
}
skb_reserve(tmp_skb, hdrlen);
skb_push(tmp_skb, hdrlen);
memcpy(tmp_skb->data, ip, hdrlen);
ret = ip_fw_chk(&tmp_skb, viadev, NULL, *chains[fwtype],
*policies[fwtype], IP_FW_MODE_CHK);
kfree_skb(tmp_skb);
dev_put(viadev);
switch (ret) {
case FW_ACCEPT:
return(0);
case FW_REDIRECT:
return(ECONNABORTED);
case FW_MASQUERADE:
return(ECONNRESET);
case FW_REJECT:
return(ECONNREFUSED);
default: /* FW_BLOCK */
return(ETIMEDOUT);
}
}
if ( cmd == IP_FW_MASQ_TIMEOUTS )
return ip_fw_masq_timeouts(m, len);
/*
* Here we really working hard-adding new elements
* to blocking/forwarding chains or deleting 'em
*/
if ( cmd == IP_FW_INSERT || cmd == IP_FW_APPEND || cmd == IP_FW_DELETE )
{
struct ip_fw *frwl;
int fwtype;
frwl=check_ipfw_struct(m,len);
if (frwl==NULL)
return (EINVAL);
fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
switch (cmd)
{
case IP_FW_INSERT:
return(insert_in_chain(chains[fwtype],frwl,len));
case IP_FW_APPEND:
return(append_to_chain(chains[fwtype],frwl,len));
case IP_FW_DELETE:
return(del_from_chain(chains[fwtype],frwl));
default:
/*
* Should be panic but... (Why are BSD people panic obsessed ??)
*/
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: unknown request %d\n",stage);
#endif
return(EINVAL);
}
}
#ifdef DEBUG_IP_FIREWALL
printk("ip_fw_ctl: unknown request %d\n",stage);
#endif
return(ENOPROTOOPT);
}
#endif /* CONFIG_IP_FIREWALL */
#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
static int ip_chain_procinfo(int stage, char *buffer, char **start,
off_t offset, int length)
{
off_t pos=0, begin=0;
struct ip_fw *i;
int len, p;
int last_len = 0;
switch(stage)
{
#ifdef CONFIG_IP_FIREWALL
case IP_FW_IN:
i = ip_fw_in_chain;
len=sprintf(buffer, "IP firewall input rules, default %d\n",
ip_fw_in_policy);
break;
case IP_FW_OUT:
i = ip_fw_out_chain;
len=sprintf(buffer, "IP firewall output rules, default %d\n",
ip_fw_out_policy);
break;
case IP_FW_FWD:
i = ip_fw_fwd_chain;
len=sprintf(buffer, "IP firewall forward rules, default %d\n",
ip_fw_fwd_policy);
break;
#endif
#ifdef CONFIG_IP_ACCT
case IP_FW_ACCT:
i = ip_acct_chain;
len=sprintf(buffer,"IP accounting rules\n");
break;
#endif
default:
/* this should never be reached, but safety first... */
i = NULL;
len=0;
break;
}
READ_LOCK(&ip_fw_lock);
while(i!=NULL)
{
len+=sprintf(buffer+len,"%08X/%08X->%08X/%08X %.16s %08X %X ",
ntohl(i->fw_src.s_addr),ntohl(i->fw_smsk.s_addr),
ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
(i->fw_vianame)[0] ? i->fw_vianame : "-",
ntohl(i->fw_via.s_addr), i->fw_flg);
/* 10 is enough for a 32 bit box but the counters are 64bit on
the Alpha and Ultrapenguin */
len+=sprintf(buffer+len,"%u %u %-20lu %-20lu",
i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
for (p = 0; p < IP_FW_MAX_PORTS; p++)
len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
len+=sprintf(buffer+len, " A%02X X%02X", i->fw_tosand, i->fw_tosxor);
buffer[len++]='\n';
buffer[len]='\0';
pos=begin+len;
if(pos<offset)
{
len=0;
begin=pos;
}
else if(pos>offset+length)
{
len = last_len;
break;
}
last_len = len;
i=i->fw_next;
}
READ_UNLOCK(&ip_fw_lock);
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
len=length;
return len;
}
#endif
#ifdef CONFIG_IP_ACCT
static int ip_acct_procinfo(char *buffer, char **start, off_t offset,
int length)
{
return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length);
}
#endif
#ifdef CONFIG_IP_FIREWALL
static int ip_fw_in_procinfo(char *buffer, char **start, off_t offset,
int length)
{
return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length);
}
static int ip_fw_out_procinfo(char *buffer, char **start, off_t offset,
int length)
{
return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length);
}
static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
int length)
{
return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length);
}
#endif
#ifdef CONFIG_IP_FIREWALL
/*
* Interface to the generic firewall chains.
*/
int ipfw_input_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
return ip_fw_chk(pskb, dev, arg, ip_fw_in_chain, ip_fw_in_policy,
IP_FW_MODE_FW);
}
int ipfw_output_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
return ip_fw_chk(pskb, dev, arg, ip_fw_out_chain, ip_fw_out_policy,
IP_FW_MODE_FW);
}
int ipfw_forward_check(struct firewall_ops *this, int pf,
struct net_device *dev, void *arg,
struct sk_buff **pskb)
{
return ip_fw_chk(pskb, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy,
IP_FW_MODE_FW);
}
#ifdef CONFIG_IP_ACCT
int ipfw_acct_in(struct firewall_ops *this, int pf, struct net_device *dev,
void *arg, struct sk_buff **pskb)
{
return ip_fw_chk(pskb,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
}
int ipfw_acct_out(struct firewall_ops *this, int pf, struct net_device *dev,
void *arg, struct sk_buff **pskb)
{
return ip_fw_chk(pskb,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
}
#endif
struct firewall_ops ipfw_ops = {
.fw_forward = ipfw_forward_check,
.fw_input = ipfw_input_check,
.fw_output = ipfw_output_check,
#ifdef CONFIG_IP_ACCT
.fw_acct_in = ipfw_acct_in,
.fw_acct_out = ipfw_acct_out,
#endif
};
#endif
#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
int ipfw_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev=ptr;
char *devname = dev->name;
struct ip_fw *fw;
int chn;
WRITE_LOCK(&ip_fw_lock);
if (event == NETDEV_UP) {
for (chn = 0; chn < IP_FW_CHAINS; chn++)
for (fw = *chains[chn]; fw; fw = fw->fw_next)
if ((fw->fw_vianame)[0] && !strncmp(devname,
fw->fw_vianame, IFNAMSIZ)) {
dev_hold(dev);
fw->fw_viadev = dev;
}
} else if (event == NETDEV_DOWN) {
for (chn = 0; chn < IP_FW_CHAINS; chn++)
for (fw = *chains[chn]; fw; fw = fw->fw_next)
/* we could compare just the pointers ... */
if ((fw->fw_vianame)[0] && !strncmp(devname,
fw->fw_vianame, IFNAMSIZ)){
if (fw->fw_viadev
&& fw->fw_viadev != (struct net_device *)-1)
dev_put(fw->fw_viadev);
fw->fw_viadev = (struct net_device*)-1;
}
}
WRITE_UNLOCK(&ip_fw_lock);
return NOTIFY_DONE;
}
static struct notifier_block ipfw_dev_notifier = {
.notifier_call = ipfw_device_event,
};
#endif
int ipfw_init_or_cleanup(int init)
{
int ret = 0;
if (!init)
goto cleanup;
ret = register_firewall(PF_INET, &ipfw_ops);
if (ret < 0)
goto cleanup_nothing;
#ifdef CONFIG_IP_ACCT
proc_net_create("ip_acct", S_IFREG | S_IRUGO | S_IWUSR, ip_acct_procinfo);
#endif
proc_net_create("ip_input", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_in_procinfo);
proc_net_create("ip_output", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_out_procinfo);
proc_net_create("ip_forward", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_fwd_procinfo);
/* Register for device up/down reports */
register_netdevice_notifier(&ipfw_dev_notifier);
#ifdef CONFIG_IP_FIREWALL_NETLINK
ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
#endif
return ret;
cleanup:
#ifdef CONFIG_IP_FIREWALL_NETLINK
sock_release(ipfwsk->sk_socket);
#endif
unregister_netdevice_notifier(&ipfw_dev_notifier);
#ifdef CONFIG_IP_ACCT
proc_net_remove("ip_acct");
#endif
proc_net_remove("ip_input");
proc_net_remove("ip_output");
proc_net_remove("ip_forward");
free_fw_chain(chains[IP_FW_FWD]);
free_fw_chain(chains[IP_FW_IN]);
free_fw_chain(chains[IP_FW_OUT]);
free_fw_chain(chains[IP_FW_ACCT]);
unregister_firewall(PF_INET, &ipfw_ops);
cleanup_nothing:
return ret;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment