[BACK]Return to ip_fw_compat.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / net / ipv4 / netfilter

File: [Development] / linux-2.6-xfs / net / ipv4 / netfilter / Attic / ip_fw_compat.c (download)

Revision 1.3, Thu Jun 17 03:20:52 2004 UTC (13 years, 4 months ago) by nathans
Branch: MAIN
Changes since 1.2: +3 -2 lines

Merge up to 2.6.7, and upgrade kdb at the same time.

/* 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);