finally in sync with archive
[bcm963xx.git] / kernel / linux / net / ipv4 / netfilter / ip_conntrack_proto_esp.c
1 /*
2 <:copyright-gpl
3  Copyright 2002 Broadcom Corp. All Rights Reserved.
4
5  This program is free software; you can distribute it and/or modify it
6  under the terms of the GNU General Public License (Version 2) as
7  published by the Free Software Foundation.
8
9  This program is distributed in the hope it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  for more details.
13
14  You should have received a copy of the GNU General Public License along
15  with this program; if not, write to the Free Software Foundation, Inc.,
16  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
17 :>
18 */
19 /******************************************************************************
20 //
21 //  Filename:       ip_conntrack_proto_esp.c
22 //  Author:         Pavan Kumar
23 //  Creation Date:  05/27/04
24 //
25 //  Description:
26 //      Implements the ESP ALG connectiontracking.
27 //
28 *****************************************************************************/
29 #include <linux/types.h>
30 #include <linux/sched.h>
31 #include <linux/timer.h>
32 #include <linux/netfilter.h>
33 #include <linux/in.h>
34 #ifdef CONFIG_MIPS_BRCM
35 #include <linux/ip.h>
36 #endif
37 #include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
38 #include <linux/netfilter_ipv4/ip_conntrack_esp.h>
39
40 #define ESP_TIMEOUT (30*HZ)
41 #define ESP_STREAM_TIMEOUT (180*HZ)
42
43 #ifdef CONFIG_MIPS_BRCM
44 #define ESP_UNREPLIEDDNS_TIMEOUT (1*HZ)
45 #endif
46
47 #define IPSEC_FREE     0
48 #define IPSEC_INUSE    1
49 #define MAX_PORTS      64
50 #define TEMP_SPI_START 1500
51
52 struct _esp_table {
53         u_int32_t l_spi;
54         u_int32_t r_spi;
55         u_int32_t l_ip;
56         u_int32_t r_ip;
57         u_int32_t timeout;
58         u_int16_t tspi;
59         int       inuse;
60 };
61
62 static struct _esp_table esp_table[MAX_PORTS];
63
64 #if 0
65 #define DEBUGP(format, args...) printk(__FILE__ ":" __FUNCTION__ ": " \
66                                        format, ## args)
67 #else
68 #define DEBUGP(format, args...)
69 #endif
70
71 static u_int16_t cur_spi = 0;
72
73 /*
74  * Allocate a free IPSEC table entry.
75  */
76 struct _esp_table *alloc_esp_entry ( void )
77 {
78         int idx = 0;
79         struct _esp_table *esp_entry = esp_table;
80
81         for ( ; idx < MAX_PORTS; idx++ ) {
82                 if ( esp_entry->inuse == IPSEC_FREE ) {
83                         esp_entry->tspi  = cur_spi = TEMP_SPI_START + idx;
84                         esp_entry->inuse = IPSEC_INUSE;
85                         //printk ( KERN_DEBUG "%s:%s New esp_entry at idx %d entry %p"
86                         //         " tspi %u cspi %u\n", __FILE__, __FUNCTION__, idx,
87                         //         esp_entry, esp_entry->tspi, cur_spi );
88                         return esp_entry;
89                 }
90                 esp_entry++;
91         }
92         return NULL;
93 }
94
95 /*
96  * Search an ESP table entry by the Security Parameter Identifier (SPI).
97  */
98 struct _esp_table *search_esp_entry_by_spi ( const struct esphdr *esph,
99                                              u_int32_t daddr )
100 {
101         int idx = 0;
102         struct _esp_table *esp_entry = esp_table;
103
104         //printk ( KERN_DEBUG "%s:%s (0x%x) %u.%u.%u.%u\n", __FILE__, __FUNCTION__,
105         //         ntohl(esph->spi), NIPQUAD(daddr));
106         for ( ; idx < MAX_PORTS; idx++, esp_entry++ ) {
107                 if ( esp_entry->inuse == IPSEC_FREE ) {
108                         continue;
109                 }
110                 /* If we have seen traffic both ways */
111                 if ( esp_entry->l_spi != 0 && esp_entry->r_spi != 0 ) {
112                         if ( esp_entry->l_spi == ntohl(esph->spi) ||
113                              esp_entry->r_spi == ntohl(esph->spi) ) {
114                                 return esp_entry;
115                         }
116                         continue;
117                 }
118                 /* If we have seen traffic only one way */
119                 if ( esp_entry->l_spi == 0 || esp_entry->r_spi == 0 ) {
120                         /* We have traffic from local */
121                         if ( esp_entry->l_spi ) {
122                                 if ( ntohl(esph->spi) == esp_entry->l_spi ) {
123                                         return esp_entry;
124                                 }
125                                 /* This must be the first packet from remote */
126                                 esp_entry->r_spi = ntohl(esph->spi);
127                                 esp_entry->r_ip = ntohl(daddr);
128                                 return esp_entry;
129                         /* We have seen traffic only from remote */
130                         } else if ( esp_entry->r_spi ) {
131                                 if ( ntohl(esph->spi) == esp_entry->r_spi ) {
132                                         return esp_entry;
133                                 }
134                                 /* This must be the first packet from local */
135                                 esp_entry->l_spi = ntohl(esph->spi);
136                                 return esp_entry;
137                         }
138                 }
139         }
140         return NULL;
141 }
142
143 static int esp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
144                             struct ip_conntrack_tuple *tuple)
145 {
146         struct esphdr esph;
147         struct _esp_table *esp_entry;
148
149         if (skb_copy_bits(skb, dataoff, &esph, sizeof(esph)) != 0)
150                 return 0;
151         //printk ( KERN_DEBUG "%s:%s (0x%x) IP Pkt Hdr %u.%u.%u.%u <-> %u.%u.%u.%u\n",
152         //         __FILE__, __FUNCTION__, ntohl(esph.spi),
153         //         NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
154         //printk ( KERN_DEBUG "%s:%s (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u\n", 
155         //         __FILE__, __FUNCTION__, ntohl(esph.spi),
156         //         NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip) );
157
158         if ( (esp_entry = search_esp_entry_by_spi ( &esph, tuple->dst.ip ) ) == NULL ) {
159                 esp_entry = alloc_esp_entry();
160                 if ( esp_entry == NULL ) {
161                         return 0;
162                 }
163                 esp_entry->l_spi = ntohl(esph.spi);
164                 esp_entry->l_ip  = ntohl(tuple->src.ip);
165         }
166         //printk ( KERN_DEBUG "%s:%s tspi %u cspi %u spi 0x%x seq 0x%x"
167         //         " sip %u.%u.%u.%u dip %u.%u.%u.%u\n", __FILE__,
168         //         __FUNCTION__, esp_entry->tspi, cur_spi,
169         //         ntohl(esph.spi), ntohl(esph.seq),
170         //         NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip) );
171         tuple->dst.u.esp.spi = esp_entry->tspi;
172         tuple->src.u.esp.spi = esp_entry->tspi;
173         return 1;
174 }
175
176 static int esp_invert_tuple(struct ip_conntrack_tuple *tuple,
177                             const struct ip_conntrack_tuple *orig)
178 {
179         //printk ( KERN_DEBUG "%s:%s cspi 0x%x dspi 0x%x sspi 0x%x"
180         //         " %u.%u.%u.%u <-> %u.%u.%u.%u\n",
181         //         __FILE__, __FUNCTION__, cur_spi, orig->dst.u.esp.spi,
182         //         orig->src.u.esp.spi, NIPQUAD(tuple->src.ip),
183         //         NIPQUAD(tuple->dst.ip) );
184         tuple->src.u.esp.spi = orig->dst.u.esp.spi;
185         tuple->dst.u.esp.spi = orig->src.u.esp.spi;
186         return 1;
187 }
188
189 /* Print out the per-protocol part of the tuple. */
190 static unsigned int esp_print_tuple(char *buffer,
191                                     const struct ip_conntrack_tuple *tuple)
192 {
193         return sprintf(buffer, "sport=%u dport=%u ",
194                        ntohs(tuple->src.u.esp.spi), ntohs(tuple->dst.u.esp.spi));
195 }
196
197 /* Print out the private part of the conntrack. */
198 static unsigned int esp_print_conntrack(char *buffer,
199                                         const struct ip_conntrack *conntrack)
200 {
201         return 0;
202 }
203
204 /* Returns verdict for packet, and may modify conntracktype */
205 static int esp_packet(struct ip_conntrack *conntrack,
206                       const struct sk_buff *skb,
207                       enum ip_conntrack_info conntrackinfo)
208 {
209         const struct iphdr *iph = skb->nh.iph;
210         const struct esphdr *esph = (void *)iph + iph->ihl*4;
211         struct _esp_table *esp_entry;
212
213         //printk ( KERN_DEBUG "%s:%s (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u %s\n",
214         //         __FILE__, __FUNCTION__, ntohl(esph->spi), 
215         //         NIPQUAD(iph->saddr), NIPQUAD(iph->daddr),
216         //         (conntrackinfo == IP_CT_NEW ) ? "CT_NEW" : "SEEN_REPLY" );
217         /*
218          * This should not happen. We get into this routine only if there is
219          * an existing stream.
220          */
221         if (conntrackinfo == IP_CT_NEW ) {
222                 //printk ( KERN_DEBUG "%s:%s IP_CT_NEW (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u\n",
223                 //         __FILE__, __FUNCTION__, esph->spi, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
224                 if ( (esp_entry = search_esp_entry_by_spi ( esph, 
225                                         iph->daddr ) ) == NULL ) {
226                         esp_entry = alloc_esp_entry ();
227                         if ( esp_entry == NULL ) {
228                                 /* All entries are currently in use */
229                                 //printk ( KERN_DEBUG "%s:%s All connections in use\n",
230                                 //         __FILE__, __FUNCTION__);
231                                 return NF_DROP;
232                         }
233                         esp_entry->l_spi = ntohl(esph->spi);
234                         esp_entry->l_ip  = ntohl(iph->saddr);
235                         esp_entry->r_spi = 0;
236                 }
237         }
238         /* If we've seen traffic both ways, this is some kind of UDP
239            stream.  Extend timeout. */
240         if (conntrack->status & IPS_SEEN_REPLY) {
241                 ip_ct_refresh(conntrack, ESP_STREAM_TIMEOUT);
242                 /* Also, more likely to be important, and not a probe */
243                 set_bit(IPS_ASSURED_BIT, &conntrack->status);
244         } else {
245                 ip_ct_refresh(conntrack, ESP_TIMEOUT);
246         }
247         //esp_entry = search_esp_entry_by_spi ( esph, iph->daddr );
248         if ( esp_entry != NULL ) {
249                 //printk ( KERN_DEBUG "%s:%s can modify this %u.%u.%u.%u"
250                 //         " with %u.%u.%u.%u\n",
251                 //         __FILE__, __FUNCTION__,
252                 //         NIPQUAD(conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip),
253                 //         NIPQUAD(esp_entry->l_ip) );
254         }
255
256         return NF_ACCEPT;
257 }
258
259 /* Called when a new connection for this protocol found. */
260 static int esp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
261 {
262         const struct iphdr *iph = skb->nh.iph;
263         const struct esphdr *esph = (void *)iph + iph->ihl*4;
264         struct _esp_table *esp_entry;
265         //printk ( KERN_DEBUG "%s:%s (0x%x) %u.%u.%u.%u <-> %u.%u.%u.%u\n",
266         //         __FILE__, __FUNCTION__, ntohl(esph->spi),
267         //         NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
268         if ( (esp_entry = search_esp_entry_by_spi ( esph, iph->daddr ) ) == NULL ) {
269                 /*
270                  * Check if this is the same LAN client creating another session.
271                  * If this is true, then the LAN IP address will be the same with
272                  * a new SPI value. This would indicate that the entire transaction
273                  * using the previous value of SPI is now not required.
274                  */
275                 esp_entry = alloc_esp_entry ();
276                 if ( esp_entry == NULL ) {
277                         /* All entries are currently in use */
278                         //printk ( KERN_DEBUG "%s:%s All connections in use\n",
279                         //         __FILE__, __FUNCTION__);
280                         return NF_DROP;
281                 }
282                 esp_entry->l_spi = ntohl(esph->spi);
283                 esp_entry->l_ip  = ntohl(iph->saddr);
284                 esp_entry->r_spi = 0;
285         }
286         return 1;
287 }
288
289 struct ip_conntrack_protocol ip_conntrack_protocol_esp
290 = { { NULL, NULL }, IPPROTO_ESP, "esp",
291     esp_pkt_to_tuple, esp_invert_tuple, esp_print_tuple, esp_print_conntrack,
292     esp_packet, esp_new, NULL };