summaryrefslogtreecommitdiff
path: root/ip_wccp.c
blob: 1a89db80710d159d351aa11e5808f2a24a944fdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 *      $Id$
 *
 * Glenn Chisholm <glenn@ircache.net>
 *
 * Change log:
 *   2003-10-20 Henrik Nordstrom <hno@squid-cache.org>
 *   		Dropped support for old kernels. Linux-2.4 or later required
 *   		Play well with Netfilter
 *
 *   2002-04-16 francis a. vidal <francisv@dagupan.com>
 *		Module license tag
 *
 *   2002-04-13 Henrik Nordstrom <hno@squid-cache.org>
 *   		Updated to Linux-2.4
 *		- there no longer is a len argument to ip_wccp_recv
 *		- deal with fragmented skb packets
 *		- incremental checksumming to allow detection of corrupted
 *		  packets
 *
 *   1999-09-30 Glenn Chisholm <glenn@ircache.net>
 *              Original release
 */


#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/if_arp.h>
#include <linux/init.h>
#include <linux/inetdevice.h>
#include <linux/netfilter_ipv4.h>
#include <net/ip.h>
#include <net/inet_ecn.h>

#include <net/ip.h>

#define WCCP_PROTOCOL_TYPE 	0x883E
#define WCCP_GRE_LEN		sizeof(u32)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
/* New License scheme */
#ifdef MODULE_LICENSE
MODULE_AUTHOR("Glenn Chisholm");
MODULE_DESCRIPTION("WCCP module");
MODULE_LICENSE("GPL");
#endif
#endif

static inline void ip_wccp_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb)
{
	struct iphdr *inner_iph = skb->nh.iph;

	if (INET_ECN_is_ce(outer_iph->tos) &&
	    INET_ECN_is_not_ce(inner_iph->tos))
		IP_ECN_set_ce(inner_iph);
}


int ip_wccp_rcv(struct sk_buff *skb)
{
	u32  *gre_hdr;
	u8   *h;
	struct iphdr *iph;

	if (!pskb_may_pull(skb, 16))
		goto drop;

	iph = skb->nh.iph;
	gre_hdr = (u32 *)skb->h.raw;
	h = skb->data;
	if(*gre_hdr != htonl(WCCP_PROTOCOL_TYPE)) 
		goto drop;

	skb->mac.raw = skb->nh.raw;
	skb->nh.raw = pskb_pull(skb, WCCP_GRE_LEN);
	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
	skb->protocol = htons(ETH_P_IP);
	skb->pkt_type = PACKET_HOST;

	dst_release(skb->dst);
	skb->dst = NULL;
#ifdef CONFIG_NETFILTER
	nf_conntrack_put(skb->nfct);
	skb->nfct = NULL;
#ifdef CONFIG_NETFILTER_DEBUG
	skb->nf_debug = 0;
#endif
#endif
	ip_wccp_ecn_decapsulate(iph, skb);
	netif_rx(skb);
	return(0);

drop:
	kfree_skb(skb);
	return(0);
}

static struct inet_protocol ipgre_protocol = {
  ip_wccp_rcv,     
  NULL,           
  0,            
  IPPROTO_GRE, 
  0,          
  NULL,      
  "GRE"     
};

int __init ip_wccp_init(void)
{
	printk(KERN_INFO "WCCP IPv4/GRE driver\n");
	inet_add_protocol(&ipgre_protocol);
	return 0;
}

static void __exit ip_wccp_fini(void)
{
	if ( inet_del_protocol(&ipgre_protocol) < 0 )
		printk(KERN_INFO "ipgre close: can't remove protocol\n");
	else
		printk(KERN_INFO "WCCP IPv4/GRE driver unloaded\n");
}

#ifdef MODULE
module_init(ip_wccp_init);
#endif
module_exit(ip_wccp_fini);