1 |
1275 |
phoenix |
/* Minor modifications to fit on compatibility framework:
|
2 |
|
|
Rusty.Russell@rustcorp.com.au
|
3 |
|
|
*/
|
4 |
|
|
|
5 |
|
|
/*
|
6 |
|
|
* This code is heavily based on the code on the old ip_fw.c code; see below for
|
7 |
|
|
* copyrights and attributions of the old code. This code is basically GPL.
|
8 |
|
|
*
|
9 |
|
|
* 15-Aug-1997: Major changes to allow graphs for firewall rules.
|
10 |
|
|
* Paul Russell <Paul.Russell@rustcorp.com.au> and
|
11 |
|
|
* Michael Neuling <Michael.Neuling@rustcorp.com.au>
|
12 |
|
|
* 24-Aug-1997: Generalised protocol handling (not just TCP/UDP/ICMP).
|
13 |
|
|
* Added explicit RETURN from chains.
|
14 |
|
|
* Removed TOS mangling (done in ipchains 1.0.1).
|
15 |
|
|
* Fixed read & reset bug by reworking proc handling.
|
16 |
|
|
* Paul Russell <Paul.Russell@rustcorp.com.au>
|
17 |
|
|
* 28-Sep-1997: Added packet marking for net sched code.
|
18 |
|
|
* Removed fw_via comparisons: all done on device name now,
|
19 |
|
|
* similar to changes in ip_fw.c in DaveM's CVS970924 tree.
|
20 |
|
|
* Paul Russell <Paul.Russell@rustcorp.com.au>
|
21 |
|
|
* 2-Nov-1997: Moved types across to __u16, etc.
|
22 |
|
|
* Added inverse flags.
|
23 |
|
|
* Fixed fragment bug (in args to port_match).
|
24 |
|
|
* Changed mark to only one flag (MARKABS).
|
25 |
|
|
* 21-Nov-1997: Added ability to test ICMP code.
|
26 |
|
|
* 19-Jan-1998: Added wildcard interfaces.
|
27 |
|
|
* 6-Feb-1998: Merged 2.0 and 2.1 versions.
|
28 |
|
|
* Initialised ip_masq for 2.0.x version.
|
29 |
|
|
* Added explicit NETLINK option for 2.1.x version.
|
30 |
|
|
* Added packet and byte counters for policy matches.
|
31 |
|
|
* 26-Feb-1998: Fixed race conditions, added SMP support.
|
32 |
|
|
* 18-Mar-1998: Fix SMP, fix race condition fix.
|
33 |
|
|
* 1-May-1998: Remove caching of device pointer.
|
34 |
|
|
* 12-May-1998: Allow tiny fragment case for TCP/UDP.
|
35 |
|
|
* 15-May-1998: Treat short packets as fragments, don't just block.
|
36 |
|
|
* 3-Jan-1999: Fixed serious procfs security hole -- users should never
|
37 |
|
|
* be allowed to view the chains!
|
38 |
|
|
* Marc Santoro <ultima@snicker.emoti.com>
|
39 |
|
|
* 29-Jan-1999: Locally generated bogus IPs dealt with, rather than crash
|
40 |
|
|
* during dump_packet. --RR.
|
41 |
|
|
* 19-May-1999: Star Wars: The Phantom Menace opened. Rule num
|
42 |
|
|
* printed in log (modified from Michael Hasenstein's patch).
|
43 |
|
|
* Added SYN in log message. --RR
|
44 |
|
|
* 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
|
45 |
|
|
* John McDonald <jm@dataprotect.com>
|
46 |
|
|
* Thomas Lopatic <tl@dataprotect.com>
|
47 |
|
|
*/
|
48 |
|
|
|
49 |
|
|
/*
|
50 |
|
|
*
|
51 |
|
|
* The origina Linux port was done Alan Cox, with changes/fixes from
|
52 |
|
|
* Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
|
53 |
|
|
* Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
|
54 |
|
|
*
|
55 |
|
|
* Copyright from the original FreeBSD version follows:
|
56 |
|
|
*
|
57 |
|
|
* Copyright (c) 1993 Daniel Boulet
|
58 |
|
|
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
59 |
|
|
*
|
60 |
|
|
* Redistribution and use in source forms, with and without modification,
|
61 |
|
|
* are permitted provided that this entire comment appears intact.
|
62 |
|
|
*
|
63 |
|
|
* Redistribution in binary form may occur without any restrictions.
|
64 |
|
|
* Obviously, it would be nice if you gave credit where credit is due
|
65 |
|
|
* but requiring it would be too onerous.
|
66 |
|
|
*
|
67 |
|
|
* This software is provided ``AS IS'' without any warranties of any kind. */
|
68 |
|
|
|
69 |
|
|
#include <linux/config.h>
|
70 |
|
|
|
71 |
|
|
#include <asm/uaccess.h>
|
72 |
|
|
#include <asm/system.h>
|
73 |
|
|
#include <linux/types.h>
|
74 |
|
|
#include <linux/sched.h>
|
75 |
|
|
#include <linux/string.h>
|
76 |
|
|
#include <linux/errno.h>
|
77 |
|
|
#include <linux/module.h>
|
78 |
|
|
|
79 |
|
|
#include <linux/socket.h>
|
80 |
|
|
#include <linux/sockios.h>
|
81 |
|
|
#include <linux/in.h>
|
82 |
|
|
#include <linux/inet.h>
|
83 |
|
|
#include <linux/netdevice.h>
|
84 |
|
|
#include <linux/icmp.h>
|
85 |
|
|
#include <linux/udp.h>
|
86 |
|
|
#include <net/ip.h>
|
87 |
|
|
#include <net/protocol.h>
|
88 |
|
|
#include <net/route.h>
|
89 |
|
|
#include <net/tcp.h>
|
90 |
|
|
#include <net/udp.h>
|
91 |
|
|
#include <net/sock.h>
|
92 |
|
|
#include <net/icmp.h>
|
93 |
|
|
#include <linux/netlink.h>
|
94 |
|
|
#include <linux/netfilter.h>
|
95 |
|
|
#include <linux/netfilter_ipv4/compat_firewall.h>
|
96 |
|
|
#include <linux/netfilter_ipv4/ipchains_core.h>
|
97 |
|
|
|
98 |
|
|
#include <net/checksum.h>
|
99 |
|
|
#include <linux/proc_fs.h>
|
100 |
|
|
#include <linux/stat.h>
|
101 |
|
|
|
102 |
|
|
/* Understanding locking in this code: (thanks to Alan Cox for using
|
103 |
|
|
* little words to explain this to me). -- PR
|
104 |
|
|
*
|
105 |
|
|
* In UP, there can be two packets traversing the chains:
|
106 |
|
|
* 1) A packet from the current userspace context
|
107 |
|
|
* 2) A packet off the bh handlers (timer or net).
|
108 |
|
|
*
|
109 |
|
|
* For SMP (kernel v2.1+), multiply this by # CPUs.
|
110 |
|
|
*
|
111 |
|
|
* [Note that this in not correct for 2.2 - because the socket code always
|
112 |
|
|
* uses lock_kernel() to serialize, and bottom halves (timers and net_bhs)
|
113 |
|
|
* only run on one CPU at a time. This will probably change for 2.3.
|
114 |
|
|
* It is still good to use spinlocks because that avoids the global cli()
|
115 |
|
|
* for updating the tables, which is rather costly in SMP kernels -AK]
|
116 |
|
|
*
|
117 |
|
|
* This means counters and backchains can get corrupted if no precautions
|
118 |
|
|
* are taken.
|
119 |
|
|
*
|
120 |
|
|
* To actually alter a chain on UP, we need only do a cli(), as this will
|
121 |
|
|
* stop a bh handler firing, as we are in the current userspace context
|
122 |
|
|
* (coming from a setsockopt()).
|
123 |
|
|
*
|
124 |
|
|
* On SMP, we need a write_lock_irqsave(), which is a simple cli() in
|
125 |
|
|
* UP.
|
126 |
|
|
*
|
127 |
|
|
* For backchains and counters, we use an array, indexed by
|
128 |
|
|
* [cpu_number_map[smp_processor_id()]*2 + !in_interrupt()]; the array is of
|
129 |
|
|
* size [smp_num_cpus*2]. For v2.0, smp_num_cpus is effectively 1. So,
|
130 |
|
|
* confident of uniqueness, we modify counters even though we only
|
131 |
|
|
* have a read lock (to read the counters, you need a write lock,
|
132 |
|
|
* though). */
|
133 |
|
|
|
134 |
|
|
/* Why I didn't use straight locking... -- PR
|
135 |
|
|
*
|
136 |
|
|
* The backchains can be separated out of the ip_chains structure, and
|
137 |
|
|
* allocated as needed inside ip_fw_check().
|
138 |
|
|
*
|
139 |
|
|
* The counters, however, can't. Trying to lock these means blocking
|
140 |
|
|
* interrupts every time we want to access them. This would suck HARD
|
141 |
|
|
* performance-wise. Not locking them leads to possible corruption,
|
142 |
|
|
* made worse on 32-bit machines (counters are 64-bit). */
|
143 |
|
|
|
144 |
|
|
/*#define DEBUG_IP_FIREWALL*/
|
145 |
|
|
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
|
146 |
|
|
/*#define DEBUG_IP_FIREWALL_USER*/
|
147 |
|
|
/*#define DEBUG_IP_FIREWALL_LOCKING*/
|
148 |
|
|
|
149 |
|
|
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
|
150 |
|
|
static struct sock *ipfwsk;
|
151 |
|
|
#endif
|
152 |
|
|
|
153 |
|
|
#ifdef CONFIG_SMP
|
154 |
|
|
#define SLOT_NUMBER() (cpu_number_map(smp_processor_id())*2 + !in_interrupt())
|
155 |
|
|
#else /* !SMP */
|
156 |
|
|
#define SLOT_NUMBER() (!in_interrupt())
|
157 |
|
|
#endif /* CONFIG_SMP */
|
158 |
|
|
#define NUM_SLOTS (smp_num_cpus*2)
|
159 |
|
|
|
160 |
|
|
#define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \
|
161 |
|
|
+ NUM_SLOTS*sizeof(struct ip_reent))
|
162 |
|
|
#define SIZEOF_STRUCT_IP_FW_KERNEL (sizeof(struct ip_fwkernel) \
|
163 |
|
|
+ NUM_SLOTS*sizeof(struct ip_counters))
|
164 |
|
|
|
165 |
|
|
#ifdef DEBUG_IP_FIREWALL_LOCKING
|
166 |
|
|
static unsigned int fwc_rlocks, fwc_wlocks;
|
167 |
|
|
#define FWC_DEBUG_LOCK(d) \
|
168 |
|
|
do { \
|
169 |
|
|
FWC_DONT_HAVE_LOCK(d); \
|
170 |
|
|
d |= (1 << SLOT_NUMBER()); \
|
171 |
|
|
} while (0)
|
172 |
|
|
|
173 |
|
|
#define FWC_DEBUG_UNLOCK(d) \
|
174 |
|
|
do { \
|
175 |
|
|
FWC_HAVE_LOCK(d); \
|
176 |
|
|
d &= ~(1 << SLOT_NUMBER()); \
|
177 |
|
|
} while (0)
|
178 |
|
|
|
179 |
|
|
#define FWC_DONT_HAVE_LOCK(d) \
|
180 |
|
|
do { \
|
181 |
|
|
if ((d) & (1 << SLOT_NUMBER())) \
|
182 |
|
|
printk("%s:%i: Got lock on %i already!\n", \
|
183 |
|
|
__FILE__, __LINE__, SLOT_NUMBER()); \
|
184 |
|
|
} while(0)
|
185 |
|
|
|
186 |
|
|
#define FWC_HAVE_LOCK(d) \
|
187 |
|
|
do { \
|
188 |
|
|
if (!((d) & (1 << SLOT_NUMBER()))) \
|
189 |
|
|
printk("%s:%i:No lock on %i!\n", \
|
190 |
|
|
__FILE__, __LINE__, SLOT_NUMBER()); \
|
191 |
|
|
} while (0)
|
192 |
|
|
|
193 |
|
|
#else
|
194 |
|
|
#define FWC_DEBUG_LOCK(d) do { } while(0)
|
195 |
|
|
#define FWC_DEBUG_UNLOCK(d) do { } while(0)
|
196 |
|
|
#define FWC_DONT_HAVE_LOCK(d) do { } while(0)
|
197 |
|
|
#define FWC_HAVE_LOCK(d) do { } while(0)
|
198 |
|
|
#endif /*DEBUG_IP_FIRWALL_LOCKING*/
|
199 |
|
|
|
200 |
|
|
#define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
|
201 |
|
|
#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
|
202 |
|
|
#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
|
203 |
|
|
#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
|
204 |
|
|
#define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
|
205 |
|
|
#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
|
206 |
|
|
#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
|
207 |
|
|
#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
|
208 |
|
|
|
209 |
|
|
struct ip_chain;
|
210 |
|
|
|
211 |
|
|
struct ip_counters
|
212 |
|
|
{
|
213 |
|
|
__u64 pcnt, bcnt; /* Packet and byte counters */
|
214 |
|
|
};
|
215 |
|
|
|
216 |
|
|
struct ip_fwkernel
|
217 |
|
|
{
|
218 |
|
|
struct ip_fw ipfw;
|
219 |
|
|
struct ip_fwkernel *next; /* where to go next if current
|
220 |
|
|
* rule doesn't match */
|
221 |
|
|
struct ip_chain *branch; /* which branch to jump to if
|
222 |
|
|
* current rule matches */
|
223 |
|
|
int simplebranch; /* Use this if branch == NULL */
|
224 |
|
|
struct ip_counters counters[0]; /* Actually several of these */
|
225 |
|
|
};
|
226 |
|
|
|
227 |
|
|
struct ip_reent
|
228 |
|
|
{
|
229 |
|
|
struct ip_chain *prevchain; /* Pointer to referencing chain */
|
230 |
|
|
struct ip_fwkernel *prevrule; /* Pointer to referencing rule */
|
231 |
|
|
struct ip_counters counters;
|
232 |
|
|
};
|
233 |
|
|
|
234 |
|
|
struct ip_chain
|
235 |
|
|
{
|
236 |
|
|
ip_chainlabel label; /* Defines the label for each block */
|
237 |
|
|
struct ip_chain *next; /* Pointer to next block */
|
238 |
|
|
struct ip_fwkernel *chain; /* Pointer to first rule in block */
|
239 |
|
|
__u32 refcount; /* Number of refernces to block */
|
240 |
|
|
int policy; /* Default rule for chain. Only *
|
241 |
|
|
* used in built in chains */
|
242 |
|
|
struct ip_reent reent[0]; /* Actually several of these */
|
243 |
|
|
};
|
244 |
|
|
|
245 |
|
|
/*
|
246 |
|
|
* Implement IP packet firewall
|
247 |
|
|
*/
|
248 |
|
|
|
249 |
|
|
#ifdef DEBUG_IP_FIREWALL
|
250 |
|
|
#define dprintf(format, args...) printk(format , ## args)
|
251 |
|
|
#else
|
252 |
|
|
#define dprintf(format, args...)
|
253 |
|
|
#endif
|
254 |
|
|
|
255 |
|
|
#ifdef DEBUG_IP_FIREWALL_USER
|
256 |
|
|
#define duprintf(format, args...) printk(format , ## args)
|
257 |
|
|
#else
|
258 |
|
|
#define duprintf(format, args...)
|
259 |
|
|
#endif
|
260 |
|
|
|
261 |
|
|
/* Lock around ip_fw_chains linked list structure */
|
262 |
|
|
rwlock_t ip_fw_lock = RW_LOCK_UNLOCKED;
|
263 |
|
|
|
264 |
|
|
/* Head of linked list of fw rules */
|
265 |
|
|
static struct ip_chain *ip_fw_chains;
|
266 |
|
|
|
267 |
|
|
#define IP_FW_INPUT_CHAIN ip_fw_chains
|
268 |
|
|
#define IP_FW_FORWARD_CHAIN (ip_fw_chains->next)
|
269 |
|
|
#define IP_FW_OUTPUT_CHAIN (ip_fw_chains->next->next)
|
270 |
|
|
|
271 |
|
|
/* Returns 1 if the port is matched by the range, 0 otherwise */
|
272 |
|
|
extern inline int port_match(__u16 min, __u16 max, __u16 port,
|
273 |
|
|
int frag, int invert)
|
274 |
|
|
{
|
275 |
|
|
if (frag) /* Fragments fail ANY port test. */
|
276 |
|
|
return (min == 0 && max == 0xFFFF);
|
277 |
|
|
else return (port >= min && port <= max) ^ invert;
|
278 |
|
|
}
|
279 |
|
|
|
280 |
|
|
/* Returns whether matches rule or not. */
|
281 |
|
|
static int ip_rule_match(struct ip_fwkernel *f,
|
282 |
|
|
const char *ifname,
|
283 |
|
|
struct iphdr *ip,
|
284 |
|
|
char tcpsyn,
|
285 |
|
|
__u16 src_port, __u16 dst_port,
|
286 |
|
|
char isfrag)
|
287 |
|
|
{
|
288 |
|
|
#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg))
|
289 |
|
|
/*
|
290 |
|
|
* This is a bit simpler as we don't have to walk
|
291 |
|
|
* an interface chain as you do in BSD - same logic
|
292 |
|
|
* however.
|
293 |
|
|
*/
|
294 |
|
|
|
295 |
|
|
if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr,
|
296 |
|
|
IP_FW_INV_SRCIP)
|
297 |
|
|
|| FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr,
|
298 |
|
|
IP_FW_INV_DSTIP)) {
|
299 |
|
|
dprintf("Source or dest mismatch.\n");
|
300 |
|
|
|
301 |
|
|
dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
|
302 |
|
|
f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr,
|
303 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : "");
|
304 |
|
|
dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
|
305 |
|
|
f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr,
|
306 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : "");
|
307 |
|
|
return 0;
|
308 |
|
|
}
|
309 |
|
|
|
310 |
|
|
/*
|
311 |
|
|
* Look for a VIA device match
|
312 |
|
|
*/
|
313 |
|
|
if (f->ipfw.fw_flg & IP_FW_F_WILDIF) {
|
314 |
|
|
if (FWINV(strncmp(ifname, f->ipfw.fw_vianame,
|
315 |
|
|
strlen(f->ipfw.fw_vianame)) != 0,
|
316 |
|
|
IP_FW_INV_VIA)) {
|
317 |
|
|
dprintf("Wildcard interface mismatch.%s\n",
|
318 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : "");
|
319 |
|
|
return 0; /* Mismatch */
|
320 |
|
|
}
|
321 |
|
|
}
|
322 |
|
|
else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0,
|
323 |
|
|
IP_FW_INV_VIA)) {
|
324 |
|
|
dprintf("Interface name does not match.%s\n",
|
325 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_VIA
|
326 |
|
|
? " (INV)" : "");
|
327 |
|
|
return 0; /* Mismatch */
|
328 |
|
|
}
|
329 |
|
|
|
330 |
|
|
/*
|
331 |
|
|
* Ok the chain addresses match.
|
332 |
|
|
*/
|
333 |
|
|
|
334 |
|
|
/* If we have a fragment rule but the packet is not a fragment
|
335 |
|
|
* the we return zero */
|
336 |
|
|
if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) {
|
337 |
|
|
dprintf("Fragment rule but not fragment.%s\n",
|
338 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : "");
|
339 |
|
|
return 0;
|
340 |
|
|
}
|
341 |
|
|
|
342 |
|
|
/* Fragment NEVER passes a SYN test, even an inverted one. */
|
343 |
|
|
if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN)
|
344 |
|
|
|| (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) {
|
345 |
|
|
dprintf("Rule requires SYN and packet has no SYN.%s\n",
|
346 |
|
|
f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : "");
|
347 |
|
|
return 0;
|
348 |
|
|
}
|
349 |
|
|
|
350 |
|
|
if (f->ipfw.fw_proto) {
|
351 |
|
|
/*
|
352 |
|
|
* Specific firewall - packet's protocol
|
353 |
|
|
* must match firewall's.
|
354 |
|
|
*/
|
355 |
|
|
|
356 |
|
|
if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) {
|
357 |
|
|
dprintf("Packet protocol %hi does not match %hi.%s\n",
|
358 |
|
|
ip->protocol, f->ipfw.fw_proto,
|
359 |
|
|
f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":"");
|
360 |
|
|
return 0;
|
361 |
|
|
}
|
362 |
|
|
|
363 |
|
|
/* For non TCP/UDP/ICMP, port range is max anyway. */
|
364 |
|
|
if (!port_match(f->ipfw.fw_spts[0],
|
365 |
|
|
f->ipfw.fw_spts[1],
|
366 |
|
|
src_port, isfrag,
|
367 |
|
|
!!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT))
|
368 |
|
|
|| !port_match(f->ipfw.fw_dpts[0],
|
369 |
|
|
f->ipfw.fw_dpts[1],
|
370 |
|
|
dst_port, isfrag,
|
371 |
|
|
!!(f->ipfw.fw_invflg
|
372 |
|
|
&IP_FW_INV_DSTPT))) {
|
373 |
|
|
dprintf("Port match failed.\n");
|
374 |
|
|
return 0;
|
375 |
|
|
}
|
376 |
|
|
}
|
377 |
|
|
|
378 |
|
|
dprintf("Match succeeded.\n");
|
379 |
|
|
return 1;
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
static const char *branchname(struct ip_chain *branch,int simplebranch)
|
383 |
|
|
{
|
384 |
|
|
if (branch)
|
385 |
|
|
return branch->label;
|
386 |
|
|
switch (simplebranch)
|
387 |
|
|
{
|
388 |
|
|
case FW_BLOCK: return IP_FW_LABEL_BLOCK;
|
389 |
|
|
case FW_ACCEPT: return IP_FW_LABEL_ACCEPT;
|
390 |
|
|
case FW_REJECT: return IP_FW_LABEL_REJECT;
|
391 |
|
|
case FW_REDIRECT: return IP_FW_LABEL_REDIRECT;
|
392 |
|
|
case FW_MASQUERADE: return IP_FW_LABEL_MASQUERADE;
|
393 |
|
|
case FW_SKIP: return "-";
|
394 |
|
|
case FW_SKIP+1: return IP_FW_LABEL_RETURN;
|
395 |
|
|
default:
|
396 |
|
|
return "UNKNOWN";
|
397 |
|
|
}
|
398 |
|
|
}
|
399 |
|
|
|
400 |
|
|
/*
|
401 |
|
|
* VERY ugly piece of code which actually
|
402 |
|
|
* makes kernel printf for matching packets...
|
403 |
|
|
*/
|
404 |
|
|
static void dump_packet(const struct iphdr *ip,
|
405 |
|
|
const char *ifname,
|
406 |
|
|
struct ip_fwkernel *f,
|
407 |
|
|
const ip_chainlabel chainlabel,
|
408 |
|
|
__u16 src_port,
|
409 |
|
|
__u16 dst_port,
|
410 |
|
|
unsigned int count,
|
411 |
|
|
int syn)
|
412 |
|
|
{
|
413 |
|
|
__u32 *opt = (__u32 *) (ip + 1);
|
414 |
|
|
int opti;
|
415 |
|
|
|
416 |
|
|
if (f) {
|
417 |
|
|
printk(KERN_INFO "Packet log: %s ",chainlabel);
|
418 |
|
|
printk("%s ",branchname(f->branch,f->simplebranch));
|
419 |
|
|
if (f->simplebranch==FW_REDIRECT)
|
420 |
|
|
printk("%d ",f->ipfw.fw_redirpt);
|
421 |
|
|
}
|
422 |
|
|
|
423 |
|
|
printk("%s PROTO=%d %u.%u.%u.%u:%hu %u.%u.%u.%u:%hu"
|
424 |
|
|
" L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
|
425 |
|
|
ifname, ip->protocol, NIPQUAD(ip->saddr),
|
426 |
|
|
src_port, NIPQUAD(ip->daddr),
|
427 |
|
|
dst_port,
|
428 |
|
|
ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
|
429 |
|
|
ntohs(ip->frag_off), ip->ttl);
|
430 |
|
|
|
431 |
|
|
for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
|
432 |
|
|
printk(" O=0x%8.8X", *opt++);
|
433 |
|
|
printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);
|
434 |
|
|
}
|
435 |
|
|
|
436 |
|
|
/* function for checking chain labels for user space. */
|
437 |
|
|
static int check_label(ip_chainlabel label)
|
438 |
|
|
{
|
439 |
|
|
unsigned int i;
|
440 |
|
|
/* strlen must be < IP_FW_MAX_LABEL_LENGTH. */
|
441 |
|
|
for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++)
|
442 |
|
|
if (label[i] == '\0') return 1;
|
443 |
|
|
|
444 |
|
|
return 0;
|
445 |
|
|
}
|
446 |
|
|
|
447 |
|
|
/* This function returns a pointer to the first chain with a label
|
448 |
|
|
* that matches the one given. */
|
449 |
|
|
static struct ip_chain *find_label(ip_chainlabel label)
|
450 |
|
|
{
|
451 |
|
|
struct ip_chain *tmp;
|
452 |
|
|
FWC_HAVE_LOCK(fwc_rlocks | fwc_wlocks);
|
453 |
|
|
for (tmp = ip_fw_chains; tmp; tmp = tmp->next)
|
454 |
|
|
if (strcmp(tmp->label,label) == 0)
|
455 |
|
|
break;
|
456 |
|
|
return tmp;
|
457 |
|
|
}
|
458 |
|
|
|
459 |
|
|
/* This function returns a boolean which when true sets answer to one
|
460 |
|
|
of the FW_*. */
|
461 |
|
|
static int find_special(ip_chainlabel label, int *answer)
|
462 |
|
|
{
|
463 |
|
|
if (label[0] == '\0') {
|
464 |
|
|
*answer = FW_SKIP; /* => pass-through rule */
|
465 |
|
|
return 1;
|
466 |
|
|
} else if (strcmp(label,IP_FW_LABEL_ACCEPT) == 0) {
|
467 |
|
|
*answer = FW_ACCEPT;
|
468 |
|
|
return 1;
|
469 |
|
|
} else if (strcmp(label,IP_FW_LABEL_BLOCK) == 0) {
|
470 |
|
|
*answer = FW_BLOCK;
|
471 |
|
|
return 1;
|
472 |
|
|
} else if (strcmp(label,IP_FW_LABEL_REJECT) == 0) {
|
473 |
|
|
*answer = FW_REJECT;
|
474 |
|
|
return 1;
|
475 |
|
|
} else if (strcmp(label,IP_FW_LABEL_REDIRECT) == 0) {
|
476 |
|
|
*answer = FW_REDIRECT;
|
477 |
|
|
return 1;
|
478 |
|
|
} else if (strcmp(label,IP_FW_LABEL_MASQUERADE) == 0) {
|
479 |
|
|
*answer = FW_MASQUERADE;
|
480 |
|
|
return 1;
|
481 |
|
|
} else if (strcmp(label, IP_FW_LABEL_RETURN) == 0) {
|
482 |
|
|
*answer = FW_SKIP+1;
|
483 |
|
|
return 1;
|
484 |
|
|
} else {
|
485 |
|
|
return 0;
|
486 |
|
|
}
|
487 |
|
|
}
|
488 |
|
|
|
489 |
|
|
/* This function cleans up the prevchain and prevrule. If the verbose
|
490 |
|
|
* flag is set then he names of the chains will be printed as it
|
491 |
|
|
* cleans up. */
|
492 |
|
|
static void cleanup(struct ip_chain *chain,
|
493 |
|
|
const int verbose,
|
494 |
|
|
unsigned int slot)
|
495 |
|
|
{
|
496 |
|
|
struct ip_chain *tmpchain = chain->reent[slot].prevchain;
|
497 |
|
|
if (verbose)
|
498 |
|
|
printk(KERN_ERR "Chain backtrace: ");
|
499 |
|
|
while (tmpchain) {
|
500 |
|
|
if (verbose)
|
501 |
|
|
printk("%s<-",chain->label);
|
502 |
|
|
chain->reent[slot].prevchain = NULL;
|
503 |
|
|
chain = tmpchain;
|
504 |
|
|
tmpchain = chain->reent[slot].prevchain;
|
505 |
|
|
}
|
506 |
|
|
if (verbose)
|
507 |
|
|
printk("%s\n",chain->label);
|
508 |
|
|
}
|
509 |
|
|
|
510 |
|
|
static inline int
|
511 |
|
|
ip_fw_domatch(struct ip_fwkernel *f,
|
512 |
|
|
struct iphdr *ip,
|
513 |
|
|
const char *rif,
|
514 |
|
|
const ip_chainlabel label,
|
515 |
|
|
struct sk_buff *skb,
|
516 |
|
|
unsigned int slot,
|
517 |
|
|
__u16 src_port, __u16 dst_port,
|
518 |
|
|
unsigned int count,
|
519 |
|
|
int tcpsyn)
|
520 |
|
|
{
|
521 |
|
|
f->counters[slot].bcnt+=ntohs(ip->tot_len);
|
522 |
|
|
f->counters[slot].pcnt++;
|
523 |
|
|
if (f->ipfw.fw_flg & IP_FW_F_PRN) {
|
524 |
|
|
dump_packet(ip,rif,f,label,src_port,dst_port,count,tcpsyn);
|
525 |
|
|
}
|
526 |
|
|
ip->tos = (ip->tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
|
527 |
|
|
|
528 |
|
|
/* This functionality is useless in stock 2.0.x series, but we don't
|
529 |
|
|
* discard the mark thing altogether, to avoid breaking ipchains (and,
|
530 |
|
|
* more importantly, the ipfwadm wrapper) --PR */
|
531 |
|
|
if (f->ipfw.fw_flg & IP_FW_F_MARKABS) {
|
532 |
|
|
skb->nfmark = f->ipfw.fw_mark;
|
533 |
|
|
} else {
|
534 |
|
|
skb->nfmark += f->ipfw.fw_mark;
|
535 |
|
|
}
|
536 |
|
|
if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {
|
537 |
|
|
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
|
538 |
|
|
size_t len = min_t(unsigned int, f->ipfw.fw_outputsize, ntohs(ip->tot_len))
|
539 |
|
|
+ sizeof(__u32) + sizeof(skb->nfmark) + IFNAMSIZ;
|
540 |
|
|
struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC);
|
541 |
|
|
|
542 |
|
|
duprintf("Sending packet out NETLINK (length = %u).\n",
|
543 |
|
|
(unsigned int)len);
|
544 |
|
|
if (outskb) {
|
545 |
|
|
/* Prepend length, mark & interface */
|
546 |
|
|
skb_put(outskb, len);
|
547 |
|
|
*((__u32 *)outskb->data) = (__u32)len;
|
548 |
|
|
*((__u32 *)(outskb->data+sizeof(__u32))) = skb->nfmark;
|
549 |
|
|
strcpy(outskb->data+sizeof(__u32)*2, rif);
|
550 |
|
|
memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip,
|
551 |
|
|
len-(sizeof(__u32)*2+IFNAMSIZ));
|
552 |
|
|
netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_ATOMIC);
|
553 |
|
|
}
|
554 |
|
|
else {
|
555 |
|
|
#endif
|
556 |
|
|
if (net_ratelimit())
|
557 |
|
|
printk(KERN_WARNING "ip_fw: packet drop due to "
|
558 |
|
|
"netlink failure\n");
|
559 |
|
|
return 0;
|
560 |
|
|
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
|
561 |
|
|
}
|
562 |
|
|
#endif
|
563 |
|
|
}
|
564 |
|
|
return 1;
|
565 |
|
|
}
|
566 |
|
|
|
567 |
|
|
/*
|
568 |
|
|
* Returns one of the generic firewall policies, like FW_ACCEPT.
|
569 |
|
|
*
|
570 |
|
|
* The testing is either false for normal firewall mode or true for
|
571 |
|
|
* user checking mode (counters are not updated, TOS & mark not done).
|
572 |
|
|
*/
|
573 |
|
|
static int
|
574 |
|
|
ip_fw_check(struct iphdr *ip,
|
575 |
|
|
const char *rif,
|
576 |
|
|
__u16 *redirport,
|
577 |
|
|
struct ip_chain *chain,
|
578 |
|
|
struct sk_buff *skb,
|
579 |
|
|
unsigned int slot,
|
580 |
|
|
int testing)
|
581 |
|
|
{
|
582 |
|
|
struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
|
583 |
|
|
struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
|
584 |
|
|
struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
|
585 |
|
|
__u32 src, dst;
|
586 |
|
|
__u16 src_port = 0xFFFF, dst_port = 0xFFFF;
|
587 |
|
|
char tcpsyn=0;
|
588 |
|
|
__u16 offset;
|
589 |
|
|
unsigned char oldtos;
|
590 |
|
|
struct ip_fwkernel *f;
|
591 |
|
|
int ret = FW_SKIP+2;
|
592 |
|
|
unsigned int count;
|
593 |
|
|
|
594 |
|
|
/* We handle fragments by dealing with the first fragment as
|
595 |
|
|
* if it was a normal packet. All other fragments are treated
|
596 |
|
|
* normally, except that they will NEVER match rules that ask
|
597 |
|
|
* things we don't know, ie. tcp syn flag or ports). If the
|
598 |
|
|
* rule is also a fragment-specific rule, non-fragments won't
|
599 |
|
|
* match it. */
|
600 |
|
|
|
601 |
|
|
offset = ntohs(ip->frag_off) & IP_OFFSET;
|
602 |
|
|
|
603 |
|
|
/*
|
604 |
|
|
* Don't allow a fragment of TCP 8 bytes in. Nobody
|
605 |
|
|
* normal causes this. Its a cracker trying to break
|
606 |
|
|
* in by doing a flag overwrite to pass the direction
|
607 |
|
|
* checks.
|
608 |
|
|
*/
|
609 |
|
|
if (offset == 1 && ip->protocol == IPPROTO_TCP) {
|
610 |
|
|
if (!testing && net_ratelimit()) {
|
611 |
|
|
printk("Suspect TCP fragment.\n");
|
612 |
|
|
dump_packet(ip,rif,NULL,NULL,0,0,0,0);
|
613 |
|
|
}
|
614 |
|
|
return FW_BLOCK;
|
615 |
|
|
}
|
616 |
|
|
|
617 |
|
|
/* If we can't investigate ports, treat as fragment. It's
|
618 |
|
|
* either a trucated whole packet, or a truncated first
|
619 |
|
|
* fragment, or a TCP first fragment of length 8-15, in which
|
620 |
|
|
* case the above rule stops reassembly.
|
621 |
|
|
*/
|
622 |
|
|
if (offset == 0) {
|
623 |
|
|
unsigned int size_req;
|
624 |
|
|
switch (ip->protocol) {
|
625 |
|
|
case IPPROTO_TCP:
|
626 |
|
|
/* Don't care about things past flags word */
|
627 |
|
|
size_req = 16;
|
628 |
|
|
break;
|
629 |
|
|
|
630 |
|
|
case IPPROTO_UDP:
|
631 |
|
|
case IPPROTO_ICMP:
|
632 |
|
|
size_req = 8;
|
633 |
|
|
break;
|
634 |
|
|
|
635 |
|
|
default:
|
636 |
|
|
size_req = 0;
|
637 |
|
|
}
|
638 |
|
|
|
639 |
|
|
/* If it is a truncated first fragment then it can be
|
640 |
|
|
* used to rewrite port information, and thus should
|
641 |
|
|
* be blocked.
|
642 |
|
|
*/
|
643 |
|
|
if (ntohs(ip->tot_len) < (ip->ihl<<2)+size_req) {
|
644 |
|
|
if (!testing && net_ratelimit()) {
|
645 |
|
|
printk("Suspect short first fragment.\n");
|
646 |
|
|
dump_packet(ip,rif,NULL,NULL,0,0,0,0);
|
647 |
|
|
}
|
648 |
|
|
return FW_BLOCK;
|
649 |
|
|
}
|
650 |
|
|
}
|
651 |
|
|
|
652 |
|
|
src = ip->saddr;
|
653 |
|
|
dst = ip->daddr;
|
654 |
|
|
oldtos = ip->tos;
|
655 |
|
|
|
656 |
|
|
/*
|
657 |
|
|
* If we got interface from which packet came
|
658 |
|
|
* we can use the address directly. Linux 2.1 now uses address
|
659 |
|
|
* chains per device too, but unlike BSD we first check if the
|
660 |
|
|
* incoming packet matches a device address and the routing
|
661 |
|
|
* table before calling the firewall.
|
662 |
|
|
*/
|
663 |
|
|
|
664 |
|
|
dprintf("Packet ");
|
665 |
|
|
switch(ip->protocol)
|
666 |
|
|
{
|
667 |
|
|
case IPPROTO_TCP:
|
668 |
|
|
dprintf("TCP ");
|
669 |
|
|
if (!offset) {
|
670 |
|
|
src_port=ntohs(tcp->source);
|
671 |
|
|
dst_port=ntohs(tcp->dest);
|
672 |
|
|
|
673 |
|
|
/* Connection initilisation can only
|
674 |
|
|
* be made when the syn bit is set and
|
675 |
|
|
* neither of the ack or reset is
|
676 |
|
|
* set. */
|
677 |
|
|
if(tcp->syn && !(tcp->ack || tcp->rst))
|
678 |
|
|
tcpsyn=1;
|
679 |
|
|
}
|
680 |
|
|
break;
|
681 |
|
|
case IPPROTO_UDP:
|
682 |
|
|
dprintf("UDP ");
|
683 |
|
|
if (!offset) {
|
684 |
|
|
src_port=ntohs(udp->source);
|
685 |
|
|
dst_port=ntohs(udp->dest);
|
686 |
|
|
}
|
687 |
|
|
break;
|
688 |
|
|
case IPPROTO_ICMP:
|
689 |
|
|
if (!offset) {
|
690 |
|
|
src_port=(__u16)icmp->type;
|
691 |
|
|
dst_port=(__u16)icmp->code;
|
692 |
|
|
}
|
693 |
|
|
dprintf("ICMP ");
|
694 |
|
|
break;
|
695 |
|
|
default:
|
696 |
|
|
dprintf("p=%d ",ip->protocol);
|
697 |
|
|
break;
|
698 |
|
|
}
|
699 |
|
|
#ifdef DEBUG_IP_FIREWALL
|
700 |
|
|
print_ip(ip->saddr);
|
701 |
|
|
|
702 |
|
|
if (offset)
|
703 |
|
|
dprintf(":fragment (%i) ", ((int)offset)<<2);
|
704 |
|
|
else if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP
|
705 |
|
|
|| ip->protocol==IPPROTO_ICMP)
|
706 |
|
|
dprintf(":%hu:%hu", src_port, dst_port);
|
707 |
|
|
dprintf("\n");
|
708 |
|
|
#endif
|
709 |
|
|
|
710 |
|
|
if (!testing) FWC_READ_LOCK(&ip_fw_lock);
|
711 |
|
|
else FWC_HAVE_LOCK(fwc_rlocks);
|
712 |
|
|
|
713 |
|
|
f = chain->chain;
|
714 |
|
|
do {
|
715 |
|
|
count = 0;
|
716 |
|
|
for (; f; f = f->next) {
|
717 |
|
|
count++;
|
718 |
|
|
if (ip_rule_match(f,rif,ip,
|
719 |
|
|
tcpsyn,src_port,dst_port,offset)) {
|
720 |
|
|
if (!testing
|
721 |
|
|
&& !ip_fw_domatch(f, ip, rif, chain->label,
|
722 |
|
|
skb, slot,
|
723 |
|
|
src_port, dst_port,
|
724 |
|
|
count, tcpsyn)) {
|
725 |
|
|
ret = FW_BLOCK;
|
726 |
|
|
cleanup(chain, 0, slot);
|
727 |
|
|
goto out;
|
728 |
|
|
}
|
729 |
|
|
break;
|
730 |
|
|
}
|
731 |
|
|
}
|
732 |
|
|
if (f) {
|
733 |
|
|
if (f->branch) {
|
734 |
|
|
/* Do sanity check to see if we have
|
735 |
|
|
* already set prevchain and if so we
|
736 |
|
|
* must be in a loop */
|
737 |
|
|
if (f->branch->reent[slot].prevchain) {
|
738 |
|
|
if (!testing) {
|
739 |
|
|
printk(KERN_ERR
|
740 |
|
|
"IP firewall: "
|
741 |
|
|
"Loop detected "
|
742 |
|
|
"at `%s'.\n",
|
743 |
|
|
f->branch->label);
|
744 |
|
|
cleanup(chain, 1, slot);
|
745 |
|
|
ret = FW_BLOCK;
|
746 |
|
|
} else {
|
747 |
|
|
cleanup(chain, 0, slot);
|
748 |
|
|
ret = FW_SKIP+1;
|
749 |
|
|
}
|
750 |
|
|
}
|
751 |
|
|
else {
|
752 |
|
|
f->branch->reent[slot].prevchain
|
753 |
|
|
= chain;
|
754 |
|
|
f->branch->reent[slot].prevrule
|
755 |
|
|
= f->next;
|
756 |
|
|
chain = f->branch;
|
757 |
|
|
f = chain->chain;
|
758 |
|
|
}
|
759 |
|
|
}
|
760 |
|
|
else if (f->simplebranch == FW_SKIP)
|
761 |
|
|
f = f->next;
|
762 |
|
|
else if (f->simplebranch == FW_SKIP+1) {
|
763 |
|
|
/* Just like falling off the chain */
|
764 |
|
|
goto fall_off_chain;
|
765 |
|
|
} else {
|
766 |
|
|
cleanup(chain, 0, slot);
|
767 |
|
|
ret = f->simplebranch;
|
768 |
|
|
}
|
769 |
|
|
} /* f == NULL */
|
770 |
|
|
else {
|
771 |
|
|
fall_off_chain:
|
772 |
|
|
if (chain->reent[slot].prevchain) {
|
773 |
|
|
struct ip_chain *tmp = chain;
|
774 |
|
|
f = chain->reent[slot].prevrule;
|
775 |
|
|
chain = chain->reent[slot].prevchain;
|
776 |
|
|
tmp->reent[slot].prevchain = NULL;
|
777 |
|
|
}
|
778 |
|
|
else {
|
779 |
|
|
ret = chain->policy;
|
780 |
|
|
if (!testing) {
|
781 |
|
|
chain->reent[slot].counters.pcnt++;
|
782 |
|
|
chain->reent[slot].counters.bcnt
|
783 |
|
|
+= ntohs(ip->tot_len);
|
784 |
|
|
}
|
785 |
|
|
}
|
786 |
|
|
}
|
787 |
|
|
} while (ret == FW_SKIP+2);
|
788 |
|
|
|
789 |
|
|
out:
|
790 |
|
|
if (!testing) FWC_READ_UNLOCK(&ip_fw_lock);
|
791 |
|
|
|
792 |
|
|
/* Recalculate checksum if not going to reject, and TOS changed. */
|
793 |
|
|
if (ip->tos != oldtos
|
794 |
|
|
&& ret != FW_REJECT && ret != FW_BLOCK
|
795 |
|
|
&& !testing)
|
796 |
|
|
ip_send_check(ip);
|
797 |
|
|
|
798 |
|
|
if (ret == FW_REDIRECT && redirport) {
|
799 |
|
|
if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) {
|
800 |
|
|
/* Wildcard redirection.
|
801 |
|
|
* Note that redirport will become
|
802 |
|
|
* 0xFFFF for non-TCP/UDP packets.
|
803 |
|
|
*/
|
804 |
|
|
*redirport = htons(dst_port);
|
805 |
|
|
}
|
806 |
|
|
}
|
807 |
|
|
|
808 |
|
|
#ifdef DEBUG_ALLOW_ALL
|
809 |
|
|
return (testing ? ret : FW_ACCEPT);
|
810 |
|
|
#else
|
811 |
|
|
return ret;
|
812 |
|
|
#endif
|
813 |
|
|
}
|
814 |
|
|
|
815 |
|
|
/* Must have write lock & interrupts off for any of these */
|
816 |
|
|
|
817 |
|
|
/* This function sets all the byte counters in a chain to zero. The
|
818 |
|
|
* input is a pointer to the chain required for zeroing */
|
819 |
|
|
static int zero_fw_chain(struct ip_chain *chainptr)
|
820 |
|
|
{
|
821 |
|
|
struct ip_fwkernel *i;
|
822 |
|
|
|
823 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
824 |
|
|
for (i = chainptr->chain; i; i = i->next)
|
825 |
|
|
memset(i->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
|
826 |
|
|
return 0;
|
827 |
|
|
}
|
828 |
|
|
|
829 |
|
|
static int clear_fw_chain(struct ip_chain *chainptr)
|
830 |
|
|
{
|
831 |
|
|
struct ip_fwkernel *i= chainptr->chain;
|
832 |
|
|
|
833 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
834 |
|
|
chainptr->chain=NULL;
|
835 |
|
|
|
836 |
|
|
while (i) {
|
837 |
|
|
struct ip_fwkernel *tmp = i->next;
|
838 |
|
|
if (i->branch)
|
839 |
|
|
i->branch->refcount--;
|
840 |
|
|
kfree(i);
|
841 |
|
|
i = tmp;
|
842 |
|
|
MOD_DEC_USE_COUNT;
|
843 |
|
|
}
|
844 |
|
|
return 0;
|
845 |
|
|
}
|
846 |
|
|
|
847 |
|
|
static int replace_in_chain(struct ip_chain *chainptr,
|
848 |
|
|
struct ip_fwkernel *frwl,
|
849 |
|
|
__u32 position)
|
850 |
|
|
{
|
851 |
|
|
struct ip_fwkernel *f = chainptr->chain;
|
852 |
|
|
|
853 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
854 |
|
|
|
855 |
|
|
while (--position && f != NULL) f = f->next;
|
856 |
|
|
if (f == NULL)
|
857 |
|
|
return EINVAL;
|
858 |
|
|
|
859 |
|
|
if (f->branch) f->branch->refcount--;
|
860 |
|
|
if (frwl->branch) frwl->branch->refcount++;
|
861 |
|
|
|
862 |
|
|
frwl->next = f->next;
|
863 |
|
|
memcpy(f,frwl,sizeof(struct ip_fwkernel));
|
864 |
|
|
kfree(frwl);
|
865 |
|
|
return 0;
|
866 |
|
|
}
|
867 |
|
|
|
868 |
|
|
static int append_to_chain(struct ip_chain *chainptr, struct ip_fwkernel *rule)
|
869 |
|
|
{
|
870 |
|
|
struct ip_fwkernel *i;
|
871 |
|
|
|
872 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
873 |
|
|
/* Special case if no rules already present */
|
874 |
|
|
if (chainptr->chain == NULL) {
|
875 |
|
|
|
876 |
|
|
/* If pointer writes are atomic then turning off
|
877 |
|
|
* interrupts is not necessary. */
|
878 |
|
|
chainptr->chain = rule;
|
879 |
|
|
if (rule->branch) rule->branch->refcount++;
|
880 |
|
|
goto append_successful;
|
881 |
|
|
}
|
882 |
|
|
|
883 |
|
|
/* Find the rule before the end of the chain */
|
884 |
|
|
for (i = chainptr->chain; i->next; i = i->next);
|
885 |
|
|
i->next = rule;
|
886 |
|
|
if (rule->branch) rule->branch->refcount++;
|
887 |
|
|
|
888 |
|
|
append_successful:
|
889 |
|
|
MOD_INC_USE_COUNT;
|
890 |
|
|
return 0;
|
891 |
|
|
}
|
892 |
|
|
|
893 |
|
|
/* This function inserts a rule at the position of position in the
|
894 |
|
|
* chain refenced by chainptr. If position is 1 then this rule will
|
895 |
|
|
* become the new rule one. */
|
896 |
|
|
static int insert_in_chain(struct ip_chain *chainptr,
|
897 |
|
|
struct ip_fwkernel *frwl,
|
898 |
|
|
__u32 position)
|
899 |
|
|
{
|
900 |
|
|
struct ip_fwkernel *f = chainptr->chain;
|
901 |
|
|
|
902 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
903 |
|
|
/* special case if the position is number 1 */
|
904 |
|
|
if (position == 1) {
|
905 |
|
|
frwl->next = chainptr->chain;
|
906 |
|
|
if (frwl->branch) frwl->branch->refcount++;
|
907 |
|
|
chainptr->chain = frwl;
|
908 |
|
|
goto insert_successful;
|
909 |
|
|
}
|
910 |
|
|
position--;
|
911 |
|
|
while (--position && f != NULL) f = f->next;
|
912 |
|
|
if (f == NULL)
|
913 |
|
|
return EINVAL;
|
914 |
|
|
if (frwl->branch) frwl->branch->refcount++;
|
915 |
|
|
frwl->next = f->next;
|
916 |
|
|
|
917 |
|
|
f->next = frwl;
|
918 |
|
|
|
919 |
|
|
insert_successful:
|
920 |
|
|
MOD_INC_USE_COUNT;
|
921 |
|
|
return 0;
|
922 |
|
|
}
|
923 |
|
|
|
924 |
|
|
/* This function deletes the a rule from a given rulenum and chain.
|
925 |
|
|
* With rulenum = 1 is the first rule is deleted. */
|
926 |
|
|
|
927 |
|
|
static int del_num_from_chain(struct ip_chain *chainptr, __u32 rulenum)
|
928 |
|
|
{
|
929 |
|
|
struct ip_fwkernel *i=chainptr->chain,*tmp;
|
930 |
|
|
|
931 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
932 |
|
|
|
933 |
|
|
if (!chainptr->chain)
|
934 |
|
|
return ENOENT;
|
935 |
|
|
|
936 |
|
|
/* Need a special case for the first rule */
|
937 |
|
|
if (rulenum == 1) {
|
938 |
|
|
/* store temp to allow for freeing up of memory */
|
939 |
|
|
tmp = chainptr->chain;
|
940 |
|
|
if (chainptr->chain->branch) chainptr->chain->branch->refcount--;
|
941 |
|
|
chainptr->chain = chainptr->chain->next;
|
942 |
|
|
kfree(tmp); /* free memory that is now unused */
|
943 |
|
|
} else {
|
944 |
|
|
rulenum--;
|
945 |
|
|
while (--rulenum && i->next ) i = i->next;
|
946 |
|
|
if (!i->next)
|
947 |
|
|
return ENOENT;
|
948 |
|
|
tmp = i->next;
|
949 |
|
|
if (i->next->branch)
|
950 |
|
|
i->next->branch->refcount--;
|
951 |
|
|
i->next = i->next->next;
|
952 |
|
|
kfree(tmp);
|
953 |
|
|
}
|
954 |
|
|
|
955 |
|
|
MOD_DEC_USE_COUNT;
|
956 |
|
|
return 0;
|
957 |
|
|
}
|
958 |
|
|
|
959 |
|
|
|
960 |
|
|
/* This function deletes the a rule from a given rule and chain.
|
961 |
|
|
* The rule that is deleted is the first occursance of that rule. */
|
962 |
|
|
static int del_rule_from_chain(struct ip_chain *chainptr,
|
963 |
|
|
struct ip_fwkernel *frwl)
|
964 |
|
|
{
|
965 |
|
|
struct ip_fwkernel *ltmp,*ftmp = chainptr->chain ;
|
966 |
|
|
int was_found;
|
967 |
|
|
|
968 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
969 |
|
|
|
970 |
|
|
/* Sure, we should compare marks, but since the `ipfwadm'
|
971 |
|
|
* script uses it for an unholy hack... well, life is easier
|
972 |
|
|
* this way. We also mask it out of the flags word. --PR */
|
973 |
|
|
for (ltmp=NULL, was_found=0;
|
974 |
|
|
!was_found && ftmp != NULL;
|
975 |
|
|
ltmp = ftmp,ftmp = ftmp->next) {
|
976 |
|
|
if (ftmp->ipfw.fw_src.s_addr!=frwl->ipfw.fw_src.s_addr
|
977 |
|
|
|| ftmp->ipfw.fw_dst.s_addr!=frwl->ipfw.fw_dst.s_addr
|
978 |
|
|
|| ftmp->ipfw.fw_smsk.s_addr!=frwl->ipfw.fw_smsk.s_addr
|
979 |
|
|
|| ftmp->ipfw.fw_dmsk.s_addr!=frwl->ipfw.fw_dmsk.s_addr
|
980 |
|
|
#if 0
|
981 |
|
|
|| ftmp->ipfw.fw_flg!=frwl->ipfw.fw_flg
|
982 |
|
|
#else
|
983 |
|
|
|| ((ftmp->ipfw.fw_flg & ~IP_FW_F_MARKABS)
|
984 |
|
|
!= (frwl->ipfw.fw_flg & ~IP_FW_F_MARKABS))
|
985 |
|
|
#endif
|
986 |
|
|
|| ftmp->ipfw.fw_invflg!=frwl->ipfw.fw_invflg
|
987 |
|
|
|| ftmp->ipfw.fw_proto!=frwl->ipfw.fw_proto
|
988 |
|
|
#if 0
|
989 |
|
|
|| ftmp->ipfw.fw_mark!=frwl->ipfw.fw_mark
|
990 |
|
|
#endif
|
991 |
|
|
|| ftmp->ipfw.fw_redirpt!=frwl->ipfw.fw_redirpt
|
992 |
|
|
|| ftmp->ipfw.fw_spts[0]!=frwl->ipfw.fw_spts[0]
|
993 |
|
|
|| ftmp->ipfw.fw_spts[1]!=frwl->ipfw.fw_spts[1]
|
994 |
|
|
|| ftmp->ipfw.fw_dpts[0]!=frwl->ipfw.fw_dpts[0]
|
995 |
|
|
|| ftmp->ipfw.fw_dpts[1]!=frwl->ipfw.fw_dpts[1]
|
996 |
|
|
|| ftmp->ipfw.fw_outputsize!=frwl->ipfw.fw_outputsize) {
|
997 |
|
|
duprintf("del_rule_from_chain: mismatch:"
|
998 |
|
|
"src:%u/%u dst:%u/%u smsk:%u/%u dmsk:%u/%u "
|
999 |
|
|
"flg:%hX/%hX invflg:%hX/%hX proto:%u/%u "
|
1000 |
|
|
"mark:%u/%u "
|
1001 |
|
|
"ports:%hu-%hu/%hu-%hu %hu-%hu/%hu-%hu "
|
1002 |
|
|
"outputsize:%hu-%hu\n",
|
1003 |
|
|
ftmp->ipfw.fw_src.s_addr,
|
1004 |
|
|
frwl->ipfw.fw_src.s_addr,
|
1005 |
|
|
ftmp->ipfw.fw_dst.s_addr,
|
1006 |
|
|
frwl->ipfw.fw_dst.s_addr,
|
1007 |
|
|
ftmp->ipfw.fw_smsk.s_addr,
|
1008 |
|
|
frwl->ipfw.fw_smsk.s_addr,
|
1009 |
|
|
ftmp->ipfw.fw_dmsk.s_addr,
|
1010 |
|
|
frwl->ipfw.fw_dmsk.s_addr,
|
1011 |
|
|
ftmp->ipfw.fw_flg,
|
1012 |
|
|
frwl->ipfw.fw_flg,
|
1013 |
|
|
ftmp->ipfw.fw_invflg,
|
1014 |
|
|
frwl->ipfw.fw_invflg,
|
1015 |
|
|
ftmp->ipfw.fw_proto,
|
1016 |
|
|
frwl->ipfw.fw_proto,
|
1017 |
|
|
ftmp->ipfw.fw_mark,
|
1018 |
|
|
frwl->ipfw.fw_mark,
|
1019 |
|
|
ftmp->ipfw.fw_spts[0],
|
1020 |
|
|
frwl->ipfw.fw_spts[0],
|
1021 |
|
|
ftmp->ipfw.fw_spts[1],
|
1022 |
|
|
frwl->ipfw.fw_spts[1],
|
1023 |
|
|
ftmp->ipfw.fw_dpts[0],
|
1024 |
|
|
frwl->ipfw.fw_dpts[0],
|
1025 |
|
|
ftmp->ipfw.fw_dpts[1],
|
1026 |
|
|
frwl->ipfw.fw_dpts[1],
|
1027 |
|
|
ftmp->ipfw.fw_outputsize,
|
1028 |
|
|
frwl->ipfw.fw_outputsize);
|
1029 |
|
|
continue;
|
1030 |
|
|
}
|
1031 |
|
|
|
1032 |
|
|
if (strncmp(ftmp->ipfw.fw_vianame,
|
1033 |
|
|
frwl->ipfw.fw_vianame,
|
1034 |
|
|
IFNAMSIZ)) {
|
1035 |
|
|
duprintf("del_rule_from_chain: if mismatch: %s/%s\n",
|
1036 |
|
|
ftmp->ipfw.fw_vianame,
|
1037 |
|
|
frwl->ipfw.fw_vianame);
|
1038 |
|
|
continue;
|
1039 |
|
|
}
|
1040 |
|
|
if (ftmp->branch != frwl->branch) {
|
1041 |
|
|
duprintf("del_rule_from_chain: branch mismatch: "
|
1042 |
|
|
"%s/%s\n",
|
1043 |
|
|
ftmp->branch?ftmp->branch->label:"(null)",
|
1044 |
|
|
frwl->branch?frwl->branch->label:"(null)");
|
1045 |
|
|
continue;
|
1046 |
|
|
}
|
1047 |
|
|
if (ftmp->branch == NULL
|
1048 |
|
|
&& ftmp->simplebranch != frwl->simplebranch) {
|
1049 |
|
|
duprintf("del_rule_from_chain: simplebranch mismatch: "
|
1050 |
|
|
"%i/%i\n",
|
1051 |
|
|
ftmp->simplebranch, frwl->simplebranch);
|
1052 |
|
|
continue;
|
1053 |
|
|
}
|
1054 |
|
|
was_found = 1;
|
1055 |
|
|
if (ftmp->branch)
|
1056 |
|
|
ftmp->branch->refcount--;
|
1057 |
|
|
if (ltmp)
|
1058 |
|
|
ltmp->next = ftmp->next;
|
1059 |
|
|
else
|
1060 |
|
|
chainptr->chain = ftmp->next;
|
1061 |
|
|
kfree(ftmp);
|
1062 |
|
|
MOD_DEC_USE_COUNT;
|
1063 |
|
|
break;
|
1064 |
|
|
}
|
1065 |
|
|
|
1066 |
|
|
if (was_found)
|
1067 |
|
|
return 0;
|
1068 |
|
|
else {
|
1069 |
|
|
duprintf("del_rule_from_chain: no matching rule found\n");
|
1070 |
|
|
return EINVAL;
|
1071 |
|
|
}
|
1072 |
|
|
}
|
1073 |
|
|
|
1074 |
|
|
/* This function takes the label of a chain and deletes the first
|
1075 |
|
|
* chain with that name. No special cases required for the built in
|
1076 |
|
|
* chains as they have their refcount initilised to 1 so that they are
|
1077 |
|
|
* never deleted. */
|
1078 |
|
|
static int del_chain(ip_chainlabel label)
|
1079 |
|
|
{
|
1080 |
|
|
struct ip_chain *tmp,*tmp2;
|
1081 |
|
|
|
1082 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
1083 |
|
|
/* Corner case: return EBUSY not ENOENT for first elem ("input") */
|
1084 |
|
|
if (strcmp(label, ip_fw_chains->label) == 0)
|
1085 |
|
|
return EBUSY;
|
1086 |
|
|
|
1087 |
|
|
for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
|
1088 |
|
|
if(strcmp(tmp->next->label,label) == 0)
|
1089 |
|
|
break;
|
1090 |
|
|
|
1091 |
|
|
tmp2 = tmp->next;
|
1092 |
|
|
if (!tmp2)
|
1093 |
|
|
return ENOENT;
|
1094 |
|
|
|
1095 |
|
|
if (tmp2->refcount)
|
1096 |
|
|
return EBUSY;
|
1097 |
|
|
|
1098 |
|
|
if (tmp2->chain)
|
1099 |
|
|
return ENOTEMPTY;
|
1100 |
|
|
|
1101 |
|
|
tmp->next = tmp2->next;
|
1102 |
|
|
kfree(tmp2);
|
1103 |
|
|
|
1104 |
|
|
MOD_DEC_USE_COUNT;
|
1105 |
|
|
return 0;
|
1106 |
|
|
}
|
1107 |
|
|
|
1108 |
|
|
/* This is a function to initilise a chain. Built in rules start with
|
1109 |
|
|
* refcount = 1 so that they cannot be deleted. User defined rules
|
1110 |
|
|
* start with refcount = 0 so they can be deleted. */
|
1111 |
|
|
static struct ip_chain *ip_init_chain(ip_chainlabel name,
|
1112 |
|
|
__u32 ref,
|
1113 |
|
|
int policy)
|
1114 |
|
|
{
|
1115 |
|
|
unsigned int i;
|
1116 |
|
|
struct ip_chain *label
|
1117 |
|
|
= kmalloc(SIZEOF_STRUCT_IP_CHAIN, GFP_KERNEL);
|
1118 |
|
|
if (label == NULL)
|
1119 |
|
|
panic("Can't kmalloc for firewall chains.\n");
|
1120 |
|
|
strcpy(label->label,name);
|
1121 |
|
|
label->next = NULL;
|
1122 |
|
|
label->chain = NULL;
|
1123 |
|
|
label->refcount = ref;
|
1124 |
|
|
label->policy = policy;
|
1125 |
|
|
for (i = 0; i < smp_num_cpus*2; i++) {
|
1126 |
|
|
label->reent[i].counters.pcnt = label->reent[i].counters.bcnt
|
1127 |
|
|
= 0;
|
1128 |
|
|
label->reent[i].prevchain = NULL;
|
1129 |
|
|
label->reent[i].prevrule = NULL;
|
1130 |
|
|
}
|
1131 |
|
|
|
1132 |
|
|
return label;
|
1133 |
|
|
}
|
1134 |
|
|
|
1135 |
|
|
/* This is a function for reating a new chain. The chains is not
|
1136 |
|
|
* created if a chain of the same name already exists */
|
1137 |
|
|
static int create_chain(ip_chainlabel label)
|
1138 |
|
|
{
|
1139 |
|
|
struct ip_chain *tmp;
|
1140 |
|
|
|
1141 |
|
|
if (!check_label(label))
|
1142 |
|
|
return EINVAL;
|
1143 |
|
|
|
1144 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
1145 |
|
|
for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
|
1146 |
|
|
if (strcmp(tmp->label,label) == 0)
|
1147 |
|
|
return EEXIST;
|
1148 |
|
|
|
1149 |
|
|
if (strcmp(tmp->label,label) == 0)
|
1150 |
|
|
return EEXIST;
|
1151 |
|
|
|
1152 |
|
|
tmp->next = ip_init_chain(label, 0, FW_SKIP); /* refcount is
|
1153 |
|
|
* zero since this is a
|
1154 |
|
|
* user defined chain *
|
1155 |
|
|
* and therefore can be
|
1156 |
|
|
* deleted */
|
1157 |
|
|
MOD_INC_USE_COUNT;
|
1158 |
|
|
return 0;
|
1159 |
|
|
}
|
1160 |
|
|
|
1161 |
|
|
/* This function simply changes the policy on one of the built in
|
1162 |
|
|
* chains. checking must be done before this is call to ensure that
|
1163 |
|
|
* chainptr is pointing to one of the three possible chains */
|
1164 |
|
|
static int change_policy(struct ip_chain *chainptr, int policy)
|
1165 |
|
|
{
|
1166 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
1167 |
|
|
chainptr->policy = policy;
|
1168 |
|
|
return 0;
|
1169 |
|
|
}
|
1170 |
|
|
|
1171 |
|
|
/* This function takes an ip_fwuser and converts it to a ip_fwkernel. It also
|
1172 |
|
|
* performs some checks in the structure. */
|
1173 |
|
|
static struct ip_fwkernel *convert_ipfw(struct ip_fwuser *fwuser, int *errno)
|
1174 |
|
|
{
|
1175 |
|
|
struct ip_fwkernel *fwkern;
|
1176 |
|
|
|
1177 |
|
|
if ( (fwuser->ipfw.fw_flg & ~IP_FW_F_MASK) != 0 ) {
|
1178 |
|
|
duprintf("convert_ipfw: undefined flag bits set (flags=%x)\n",
|
1179 |
|
|
fwuser->ipfw.fw_flg);
|
1180 |
|
|
*errno = EINVAL;
|
1181 |
|
|
return NULL;
|
1182 |
|
|
}
|
1183 |
|
|
|
1184 |
|
|
#ifdef DEBUG_IP_FIREWALL_USER
|
1185 |
|
|
/* These are sanity checks that don't really matter.
|
1186 |
|
|
* We can get rid of these once testing is complete.
|
1187 |
|
|
*/
|
1188 |
|
|
if ((fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
|
1189 |
|
|
&& ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
|
1190 |
|
|
|| fwuser->ipfw.fw_proto != IPPROTO_TCP)) {
|
1191 |
|
|
duprintf("convert_ipfw: TCP SYN flag set but proto != TCP!\n");
|
1192 |
|
|
*errno = EINVAL;
|
1193 |
|
|
return NULL;
|
1194 |
|
|
}
|
1195 |
|
|
|
1196 |
|
|
if (strcmp(fwuser->label, IP_FW_LABEL_REDIRECT) != 0
|
1197 |
|
|
&& fwuser->ipfw.fw_redirpt != 0) {
|
1198 |
|
|
duprintf("convert_ipfw: Target not REDIR but redirpt != 0!\n");
|
1199 |
|
|
*errno = EINVAL;
|
1200 |
|
|
return NULL;
|
1201 |
|
|
}
|
1202 |
|
|
|
1203 |
|
|
if ((!(fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
|
1204 |
|
|
&& (fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG))
|
1205 |
|
|
|| (!(fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
|
1206 |
|
|
&& (fwuser->ipfw.fw_invflg & IP_FW_INV_SYN))) {
|
1207 |
|
|
duprintf("convert_ipfw: Can't have INV flag if flag unset!\n");
|
1208 |
|
|
*errno = EINVAL;
|
1209 |
|
|
return NULL;
|
1210 |
|
|
}
|
1211 |
|
|
|
1212 |
|
|
if (((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCPT)
|
1213 |
|
|
&& fwuser->ipfw.fw_spts[0] == 0
|
1214 |
|
|
&& fwuser->ipfw.fw_spts[1] == 0xFFFF)
|
1215 |
|
|
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTPT)
|
1216 |
|
|
&& fwuser->ipfw.fw_dpts[0] == 0
|
1217 |
|
|
&& fwuser->ipfw.fw_dpts[1] == 0xFFFF)
|
1218 |
|
|
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_VIA)
|
1219 |
|
|
&& (fwuser->ipfw.fw_vianame)[0] == '\0')
|
1220 |
|
|
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCIP)
|
1221 |
|
|
&& fwuser->ipfw.fw_smsk.s_addr == 0)
|
1222 |
|
|
|| ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTIP)
|
1223 |
|
|
&& fwuser->ipfw.fw_dmsk.s_addr == 0)) {
|
1224 |
|
|
duprintf("convert_ipfw: INV flag makes rule unmatchable!\n");
|
1225 |
|
|
*errno = EINVAL;
|
1226 |
|
|
return NULL;
|
1227 |
|
|
}
|
1228 |
|
|
|
1229 |
|
|
if ((fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
|
1230 |
|
|
&& !(fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG)
|
1231 |
|
|
&& (fwuser->ipfw.fw_spts[0] != 0
|
1232 |
|
|
|| fwuser->ipfw.fw_spts[1] != 0xFFFF
|
1233 |
|
|
|| fwuser->ipfw.fw_dpts[0] != 0
|
1234 |
|
|
|| fwuser->ipfw.fw_dpts[1] != 0xFFFF
|
1235 |
|
|
|| (fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN))) {
|
1236 |
|
|
duprintf("convert_ipfw: Can't test ports or SYN with frag!\n");
|
1237 |
|
|
*errno = EINVAL;
|
1238 |
|
|
return NULL;
|
1239 |
|
|
}
|
1240 |
|
|
#endif
|
1241 |
|
|
|
1242 |
|
|
if ((fwuser->ipfw.fw_spts[0] != 0
|
1243 |
|
|
|| fwuser->ipfw.fw_spts[1] != 0xFFFF
|
1244 |
|
|
|| fwuser->ipfw.fw_dpts[0] != 0
|
1245 |
|
|
|| fwuser->ipfw.fw_dpts[1] != 0xFFFF)
|
1246 |
|
|
&& ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
|
1247 |
|
|
|| (fwuser->ipfw.fw_proto != IPPROTO_TCP
|
1248 |
|
|
&& fwuser->ipfw.fw_proto != IPPROTO_UDP
|
1249 |
|
|
&& fwuser->ipfw.fw_proto != IPPROTO_ICMP))) {
|
1250 |
|
|
duprintf("convert_ipfw: Can only test ports for TCP/UDP/ICMP!\n");
|
1251 |
|
|
*errno = EINVAL;
|
1252 |
|
|
return NULL;
|
1253 |
|
|
}
|
1254 |
|
|
|
1255 |
|
|
fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_ATOMIC);
|
1256 |
|
|
if (!fwkern) {
|
1257 |
|
|
duprintf("convert_ipfw: kmalloc failed!\n");
|
1258 |
|
|
*errno = ENOMEM;
|
1259 |
|
|
return NULL;
|
1260 |
|
|
}
|
1261 |
|
|
memcpy(&fwkern->ipfw,&fwuser->ipfw,sizeof(struct ip_fw));
|
1262 |
|
|
|
1263 |
|
|
if (!find_special(fwuser->label, &fwkern->simplebranch)) {
|
1264 |
|
|
fwkern->branch = find_label(fwuser->label);
|
1265 |
|
|
if (!fwkern->branch) {
|
1266 |
|
|
duprintf("convert_ipfw: chain doesn't exist `%s'.\n",
|
1267 |
|
|
fwuser->label);
|
1268 |
|
|
kfree(fwkern);
|
1269 |
|
|
*errno = ENOENT;
|
1270 |
|
|
return NULL;
|
1271 |
|
|
} else if (fwkern->branch == IP_FW_INPUT_CHAIN
|
1272 |
|
|
|| fwkern->branch == IP_FW_FORWARD_CHAIN
|
1273 |
|
|
|| fwkern->branch == IP_FW_OUTPUT_CHAIN) {
|
1274 |
|
|
duprintf("convert_ipfw: Can't branch to builtin chain `%s'.\n",
|
1275 |
|
|
fwuser->label);
|
1276 |
|
|
kfree(fwkern);
|
1277 |
|
|
*errno = ENOENT;
|
1278 |
|
|
return NULL;
|
1279 |
|
|
}
|
1280 |
|
|
} else
|
1281 |
|
|
fwkern->branch = NULL;
|
1282 |
|
|
memset(fwkern->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
|
1283 |
|
|
|
1284 |
|
|
/* Handle empty vianame by making it a wildcard */
|
1285 |
|
|
if ((fwkern->ipfw.fw_vianame)[0] == '\0')
|
1286 |
|
|
fwkern->ipfw.fw_flg |= IP_FW_F_WILDIF;
|
1287 |
|
|
|
1288 |
|
|
fwkern->next = NULL;
|
1289 |
|
|
return fwkern;
|
1290 |
|
|
}
|
1291 |
|
|
|
1292 |
|
|
int ip_fw_ctl(int cmd, void *m, int len)
|
1293 |
|
|
{
|
1294 |
|
|
int ret;
|
1295 |
|
|
struct ip_chain *chain;
|
1296 |
|
|
unsigned long flags;
|
1297 |
|
|
|
1298 |
|
|
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
|
1299 |
|
|
|
1300 |
|
|
switch (cmd) {
|
1301 |
|
|
case IP_FW_FLUSH:
|
1302 |
|
|
if (len != sizeof(ip_chainlabel) || !check_label(m))
|
1303 |
|
|
ret = EINVAL;
|
1304 |
|
|
else if ((chain = find_label(m)) == NULL)
|
1305 |
|
|
ret = ENOENT;
|
1306 |
|
|
else ret = clear_fw_chain(chain);
|
1307 |
|
|
break;
|
1308 |
|
|
|
1309 |
|
|
case IP_FW_ZERO:
|
1310 |
|
|
if (len != sizeof(ip_chainlabel) || !check_label(m))
|
1311 |
|
|
ret = EINVAL;
|
1312 |
|
|
else if ((chain = find_label(m)) == NULL)
|
1313 |
|
|
ret = ENOENT;
|
1314 |
|
|
else ret = zero_fw_chain(chain);
|
1315 |
|
|
break;
|
1316 |
|
|
|
1317 |
|
|
case IP_FW_CHECK: {
|
1318 |
|
|
struct ip_fwtest *new = m;
|
1319 |
|
|
struct iphdr *ip;
|
1320 |
|
|
|
1321 |
|
|
/* Don't need write lock. */
|
1322 |
|
|
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
|
1323 |
|
|
|
1324 |
|
|
if (len != sizeof(struct ip_fwtest) || !check_label(m))
|
1325 |
|
|
return EINVAL;
|
1326 |
|
|
|
1327 |
|
|
/* Need readlock to do find_label */
|
1328 |
|
|
FWC_READ_LOCK(&ip_fw_lock);
|
1329 |
|
|
|
1330 |
|
|
if ((chain = find_label(new->fwt_label)) == NULL)
|
1331 |
|
|
ret = ENOENT;
|
1332 |
|
|
else {
|
1333 |
|
|
ip = &(new->fwt_packet.fwp_iph);
|
1334 |
|
|
|
1335 |
|
|
if (ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
|
1336 |
|
|
duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n",
|
1337 |
|
|
ip->ihl,
|
1338 |
|
|
sizeof(struct iphdr) / sizeof(int));
|
1339 |
|
|
ret = EINVAL;
|
1340 |
|
|
}
|
1341 |
|
|
else {
|
1342 |
|
|
ret = ip_fw_check(ip, new->fwt_packet.fwp_vianame,
|
1343 |
|
|
NULL, chain,
|
1344 |
|
|
NULL, SLOT_NUMBER(), 1);
|
1345 |
|
|
switch (ret) {
|
1346 |
|
|
case FW_ACCEPT:
|
1347 |
|
|
ret = 0; break;
|
1348 |
|
|
case FW_REDIRECT:
|
1349 |
|
|
ret = ECONNABORTED; break;
|
1350 |
|
|
case FW_MASQUERADE:
|
1351 |
|
|
ret = ECONNRESET; break;
|
1352 |
|
|
case FW_REJECT:
|
1353 |
|
|
ret = ECONNREFUSED; break;
|
1354 |
|
|
/* Hack to help diag; these only get
|
1355 |
|
|
returned when testing. */
|
1356 |
|
|
case FW_SKIP+1:
|
1357 |
|
|
ret = ELOOP; break;
|
1358 |
|
|
case FW_SKIP:
|
1359 |
|
|
ret = ENFILE; break;
|
1360 |
|
|
default: /* FW_BLOCK */
|
1361 |
|
|
ret = ETIMEDOUT; break;
|
1362 |
|
|
}
|
1363 |
|
|
}
|
1364 |
|
|
}
|
1365 |
|
|
FWC_READ_UNLOCK(&ip_fw_lock);
|
1366 |
|
|
return ret;
|
1367 |
|
|
}
|
1368 |
|
|
|
1369 |
|
|
case IP_FW_MASQ_TIMEOUTS: {
|
1370 |
|
|
ret = ip_fw_masq_timeouts(m, len);
|
1371 |
|
|
}
|
1372 |
|
|
break;
|
1373 |
|
|
|
1374 |
|
|
case IP_FW_REPLACE: {
|
1375 |
|
|
struct ip_fwkernel *ip_fwkern;
|
1376 |
|
|
struct ip_fwnew *new = m;
|
1377 |
|
|
|
1378 |
|
|
if (len != sizeof(struct ip_fwnew)
|
1379 |
|
|
|| !check_label(new->fwn_label))
|
1380 |
|
|
ret = EINVAL;
|
1381 |
|
|
else if ((chain = find_label(new->fwn_label)) == NULL)
|
1382 |
|
|
ret = ENOENT;
|
1383 |
|
|
else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
|
1384 |
|
|
!= NULL)
|
1385 |
|
|
ret = replace_in_chain(chain, ip_fwkern,
|
1386 |
|
|
new->fwn_rulenum);
|
1387 |
|
|
}
|
1388 |
|
|
break;
|
1389 |
|
|
|
1390 |
|
|
case IP_FW_APPEND: {
|
1391 |
|
|
struct ip_fwchange *new = m;
|
1392 |
|
|
struct ip_fwkernel *ip_fwkern;
|
1393 |
|
|
|
1394 |
|
|
if (len != sizeof(struct ip_fwchange)
|
1395 |
|
|
|| !check_label(new->fwc_label))
|
1396 |
|
|
ret = EINVAL;
|
1397 |
|
|
else if ((chain = find_label(new->fwc_label)) == NULL)
|
1398 |
|
|
ret = ENOENT;
|
1399 |
|
|
else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
|
1400 |
|
|
!= NULL)
|
1401 |
|
|
ret = append_to_chain(chain, ip_fwkern);
|
1402 |
|
|
}
|
1403 |
|
|
break;
|
1404 |
|
|
|
1405 |
|
|
case IP_FW_INSERT: {
|
1406 |
|
|
struct ip_fwkernel *ip_fwkern;
|
1407 |
|
|
struct ip_fwnew *new = m;
|
1408 |
|
|
|
1409 |
|
|
if (len != sizeof(struct ip_fwnew)
|
1410 |
|
|
|| !check_label(new->fwn_label))
|
1411 |
|
|
ret = EINVAL;
|
1412 |
|
|
else if ((chain = find_label(new->fwn_label)) == NULL)
|
1413 |
|
|
ret = ENOENT;
|
1414 |
|
|
else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
|
1415 |
|
|
!= NULL)
|
1416 |
|
|
ret = insert_in_chain(chain, ip_fwkern,
|
1417 |
|
|
new->fwn_rulenum);
|
1418 |
|
|
}
|
1419 |
|
|
break;
|
1420 |
|
|
|
1421 |
|
|
case IP_FW_DELETE: {
|
1422 |
|
|
struct ip_fwchange *new = m;
|
1423 |
|
|
struct ip_fwkernel *ip_fwkern;
|
1424 |
|
|
|
1425 |
|
|
if (len != sizeof(struct ip_fwchange)
|
1426 |
|
|
|| !check_label(new->fwc_label))
|
1427 |
|
|
ret = EINVAL;
|
1428 |
|
|
else if ((chain = find_label(new->fwc_label)) == NULL)
|
1429 |
|
|
ret = ENOENT;
|
1430 |
|
|
else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
|
1431 |
|
|
!= NULL) {
|
1432 |
|
|
ret = del_rule_from_chain(chain, ip_fwkern);
|
1433 |
|
|
kfree(ip_fwkern);
|
1434 |
|
|
}
|
1435 |
|
|
}
|
1436 |
|
|
break;
|
1437 |
|
|
|
1438 |
|
|
case IP_FW_DELETE_NUM: {
|
1439 |
|
|
struct ip_fwdelnum *new = m;
|
1440 |
|
|
|
1441 |
|
|
if (len != sizeof(struct ip_fwdelnum)
|
1442 |
|
|
|| !check_label(new->fwd_label))
|
1443 |
|
|
ret = EINVAL;
|
1444 |
|
|
else if ((chain = find_label(new->fwd_label)) == NULL)
|
1445 |
|
|
ret = ENOENT;
|
1446 |
|
|
else ret = del_num_from_chain(chain, new->fwd_rulenum);
|
1447 |
|
|
}
|
1448 |
|
|
break;
|
1449 |
|
|
|
1450 |
|
|
case IP_FW_CREATECHAIN: {
|
1451 |
|
|
if (len != sizeof(ip_chainlabel)) {
|
1452 |
|
|
duprintf("create_chain: bad size %i\n", len);
|
1453 |
|
|
ret = EINVAL;
|
1454 |
|
|
}
|
1455 |
|
|
else ret = create_chain(m);
|
1456 |
|
|
}
|
1457 |
|
|
break;
|
1458 |
|
|
|
1459 |
|
|
case IP_FW_DELETECHAIN: {
|
1460 |
|
|
if (len != sizeof(ip_chainlabel)) {
|
1461 |
|
|
duprintf("delete_chain: bad size %i\n", len);
|
1462 |
|
|
ret = EINVAL;
|
1463 |
|
|
}
|
1464 |
|
|
else ret = del_chain(m);
|
1465 |
|
|
}
|
1466 |
|
|
break;
|
1467 |
|
|
|
1468 |
|
|
case IP_FW_POLICY: {
|
1469 |
|
|
struct ip_fwpolicy *new = m;
|
1470 |
|
|
|
1471 |
|
|
if (len != sizeof(struct ip_fwpolicy)
|
1472 |
|
|
|| !check_label(new->fwp_label))
|
1473 |
|
|
ret = EINVAL;
|
1474 |
|
|
else if ((chain = find_label(new->fwp_label)) == NULL)
|
1475 |
|
|
ret = ENOENT;
|
1476 |
|
|
else if (chain != IP_FW_INPUT_CHAIN
|
1477 |
|
|
&& chain != IP_FW_FORWARD_CHAIN
|
1478 |
|
|
&& chain != IP_FW_OUTPUT_CHAIN) {
|
1479 |
|
|
duprintf("change_policy: can't change policy on user"
|
1480 |
|
|
" defined chain.\n");
|
1481 |
|
|
ret = EINVAL;
|
1482 |
|
|
}
|
1483 |
|
|
else {
|
1484 |
|
|
int pol = FW_SKIP;
|
1485 |
|
|
find_special(new->fwp_policy, &pol);
|
1486 |
|
|
|
1487 |
|
|
switch(pol) {
|
1488 |
|
|
case FW_MASQUERADE:
|
1489 |
|
|
if (chain != IP_FW_FORWARD_CHAIN) {
|
1490 |
|
|
ret = EINVAL;
|
1491 |
|
|
break;
|
1492 |
|
|
}
|
1493 |
|
|
/* Fall thru... */
|
1494 |
|
|
case FW_BLOCK:
|
1495 |
|
|
case FW_ACCEPT:
|
1496 |
|
|
case FW_REJECT:
|
1497 |
|
|
ret = change_policy(chain, pol);
|
1498 |
|
|
break;
|
1499 |
|
|
default:
|
1500 |
|
|
duprintf("change_policy: bad policy `%s'\n",
|
1501 |
|
|
new->fwp_policy);
|
1502 |
|
|
ret = EINVAL;
|
1503 |
|
|
}
|
1504 |
|
|
}
|
1505 |
|
|
break;
|
1506 |
|
|
}
|
1507 |
|
|
default:
|
1508 |
|
|
duprintf("ip_fw_ctl: unknown request %d\n",cmd);
|
1509 |
|
|
ret = ENOPROTOOPT;
|
1510 |
|
|
}
|
1511 |
|
|
|
1512 |
|
|
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
|
1513 |
|
|
return ret;
|
1514 |
|
|
}
|
1515 |
|
|
|
1516 |
|
|
/* Returns bytes used - doesn't NUL terminate */
|
1517 |
|
|
static int dump_rule(char *buffer,
|
1518 |
|
|
const char *chainlabel,
|
1519 |
|
|
const struct ip_fwkernel *rule)
|
1520 |
|
|
{
|
1521 |
|
|
int len;
|
1522 |
|
|
unsigned int i;
|
1523 |
|
|
__u64 packets = 0, bytes = 0;
|
1524 |
|
|
|
1525 |
|
|
FWC_HAVE_LOCK(fwc_wlocks);
|
1526 |
|
|
for (i = 0; i < NUM_SLOTS; i++) {
|
1527 |
|
|
packets += rule->counters[i].pcnt;
|
1528 |
|
|
bytes += rule->counters[i].bcnt;
|
1529 |
|
|
}
|
1530 |
|
|
|
1531 |
|
|
len=sprintf(buffer,
|
1532 |
|
|
"%9s " /* Chain name */
|
1533 |
|
|
"%08X/%08X->%08X/%08X " /* Source & Destination IPs */
|
1534 |
|
|
"%.16s " /* Interface */
|
1535 |
|
|
"%X %X " /* fw_flg and fw_invflg fields */
|
1536 |
|
|
"%u " /* Protocol */
|
1537 |
|
|
"%-9u %-9u %-9u %-9u " /* Packet & byte counters */
|
1538 |
|
|
"%u-%u %u-%u " /* Source & Dest port ranges */
|
1539 |
|
|
"A%02X X%02X " /* TOS and and xor masks */
|
1540 |
|
|
"%08X " /* Redirection port */
|
1541 |
|
|
"%u " /* fw_mark field */
|
1542 |
|
|
"%u " /* output size */
|
1543 |
|
|
"%9s\n", /* Target */
|
1544 |
|
|
chainlabel,
|
1545 |
|
|
ntohl(rule->ipfw.fw_src.s_addr),
|
1546 |
|
|
ntohl(rule->ipfw.fw_smsk.s_addr),
|
1547 |
|
|
ntohl(rule->ipfw.fw_dst.s_addr),
|
1548 |
|
|
ntohl(rule->ipfw.fw_dmsk.s_addr),
|
1549 |
|
|
(rule->ipfw.fw_vianame)[0] ? rule->ipfw.fw_vianame : "-",
|
1550 |
|
|
rule->ipfw.fw_flg,
|
1551 |
|
|
rule->ipfw.fw_invflg,
|
1552 |
|
|
rule->ipfw.fw_proto,
|
1553 |
|
|
(__u32)(packets >> 32), (__u32)packets,
|
1554 |
|
|
(__u32)(bytes >> 32), (__u32)bytes,
|
1555 |
|
|
rule->ipfw.fw_spts[0], rule->ipfw.fw_spts[1],
|
1556 |
|
|
rule->ipfw.fw_dpts[0], rule->ipfw.fw_dpts[1],
|
1557 |
|
|
rule->ipfw.fw_tosand, rule->ipfw.fw_tosxor,
|
1558 |
|
|
rule->ipfw.fw_redirpt,
|
1559 |
|
|
rule->ipfw.fw_mark,
|
1560 |
|
|
rule->ipfw.fw_outputsize,
|
1561 |
|
|
branchname(rule->branch,rule->simplebranch));
|
1562 |
|
|
|
1563 |
|
|
duprintf("dump_rule: %i bytes done.\n", len);
|
1564 |
|
|
return len;
|
1565 |
|
|
}
|
1566 |
|
|
|
1567 |
|
|
/* File offset is actually in records, not bytes. */
|
1568 |
|
|
static int ip_chain_procinfo(char *buffer, char **start,
|
1569 |
|
|
off_t offset, int length)
|
1570 |
|
|
{
|
1571 |
|
|
struct ip_chain *i;
|
1572 |
|
|
struct ip_fwkernel *j = ip_fw_chains->chain;
|
1573 |
|
|
unsigned long flags;
|
1574 |
|
|
int len = 0;
|
1575 |
|
|
int last_len = 0;
|
1576 |
|
|
off_t upto = 0;
|
1577 |
|
|
|
1578 |
|
|
duprintf("Offset starts at %lu\n", offset);
|
1579 |
|
|
duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
|
1580 |
|
|
|
1581 |
|
|
/* Need a write lock to lock out ``readers'' which update counters. */
|
1582 |
|
|
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
|
1583 |
|
|
|
1584 |
|
|
for (i = ip_fw_chains; i; i = i->next) {
|
1585 |
|
|
for (j = i->chain; j; j = j->next) {
|
1586 |
|
|
if (upto == offset) break;
|
1587 |
|
|
duprintf("Skipping rule in chain `%s'\n",
|
1588 |
|
|
i->label);
|
1589 |
|
|
upto++;
|
1590 |
|
|
}
|
1591 |
|
|
if (upto == offset) break;
|
1592 |
|
|
}
|
1593 |
|
|
|
1594 |
|
|
/* Don't init j first time, or once i = NULL */
|
1595 |
|
|
for (; i; (void)((i = i->next) && (j = i->chain))) {
|
1596 |
|
|
duprintf("Dumping chain `%s'\n", i->label);
|
1597 |
|
|
for (; j; j = j->next, upto++, last_len = len)
|
1598 |
|
|
{
|
1599 |
|
|
len += dump_rule(buffer+len, i->label, j);
|
1600 |
|
|
if (len > length) {
|
1601 |
|
|
duprintf("Dumped to %i (past %i). "
|
1602 |
|
|
"Moving back to %i.\n",
|
1603 |
|
|
len, length, last_len);
|
1604 |
|
|
len = last_len;
|
1605 |
|
|
goto outside;
|
1606 |
|
|
}
|
1607 |
|
|
}
|
1608 |
|
|
}
|
1609 |
|
|
outside:
|
1610 |
|
|
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
|
1611 |
|
|
buffer[len] = '\0';
|
1612 |
|
|
|
1613 |
|
|
duprintf("ip_chain_procinfo: Length = %i (of %i). Offset = %li.\n",
|
1614 |
|
|
len, length, upto);
|
1615 |
|
|
/* `start' hack - see fs/proc/generic.c line ~165 */
|
1616 |
|
|
*start=(char *)((unsigned int)upto-offset);
|
1617 |
|
|
return len;
|
1618 |
|
|
}
|
1619 |
|
|
|
1620 |
|
|
static int ip_chain_name_procinfo(char *buffer, char **start,
|
1621 |
|
|
off_t offset, int length)
|
1622 |
|
|
{
|
1623 |
|
|
struct ip_chain *i;
|
1624 |
|
|
int len = 0,last_len = 0;
|
1625 |
|
|
off_t pos = 0,begin = 0;
|
1626 |
|
|
unsigned long flags;
|
1627 |
|
|
|
1628 |
|
|
/* Need a write lock to lock out ``readers'' which update counters. */
|
1629 |
|
|
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
|
1630 |
|
|
|
1631 |
|
|
for (i = ip_fw_chains; i; i = i->next)
|
1632 |
|
|
{
|
1633 |
|
|
unsigned int j;
|
1634 |
|
|
__u32 packetsHi = 0, packetsLo = 0, bytesHi = 0, bytesLo = 0;
|
1635 |
|
|
|
1636 |
|
|
for (j = 0; j < NUM_SLOTS; j++) {
|
1637 |
|
|
packetsLo += i->reent[j].counters.pcnt & 0xFFFFFFFF;
|
1638 |
|
|
packetsHi += ((i->reent[j].counters.pcnt >> 32)
|
1639 |
|
|
& 0xFFFFFFFF);
|
1640 |
|
|
bytesLo += i->reent[j].counters.bcnt & 0xFFFFFFFF;
|
1641 |
|
|
bytesHi += ((i->reent[j].counters.bcnt >> 32)
|
1642 |
|
|
& 0xFFFFFFFF);
|
1643 |
|
|
}
|
1644 |
|
|
|
1645 |
|
|
/* print the label and the policy */
|
1646 |
|
|
len+=sprintf(buffer+len,"%s %s %i %u %u %u %u\n",
|
1647 |
|
|
i->label,branchname(NULL, i->policy),i->refcount,
|
1648 |
|
|
packetsHi, packetsLo, bytesHi, bytesLo);
|
1649 |
|
|
pos=begin+len;
|
1650 |
|
|
if(pos<offset) {
|
1651 |
|
|
len=0;
|
1652 |
|
|
begin=pos;
|
1653 |
|
|
}
|
1654 |
|
|
else if(pos>offset+length) {
|
1655 |
|
|
len = last_len;
|
1656 |
|
|
break;
|
1657 |
|
|
}
|
1658 |
|
|
|
1659 |
|
|
last_len = len;
|
1660 |
|
|
}
|
1661 |
|
|
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
|
1662 |
|
|
|
1663 |
|
|
*start = buffer+(offset-begin);
|
1664 |
|
|
len-=(offset-begin);
|
1665 |
|
|
if(len>length)
|
1666 |
|
|
len=length;
|
1667 |
|
|
return len;
|
1668 |
|
|
}
|
1669 |
|
|
|
1670 |
|
|
/*
|
1671 |
|
|
* Interface to the generic firewall chains.
|
1672 |
|
|
*/
|
1673 |
|
|
int ipfw_input_check(struct firewall_ops *this, int pf,
|
1674 |
|
|
struct net_device *dev, void *phdr, void *arg,
|
1675 |
|
|
struct sk_buff **pskb)
|
1676 |
|
|
{
|
1677 |
|
|
return ip_fw_check(phdr, dev->name,
|
1678 |
|
|
arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
|
1679 |
|
|
}
|
1680 |
|
|
|
1681 |
|
|
int ipfw_output_check(struct firewall_ops *this, int pf,
|
1682 |
|
|
struct net_device *dev, void *phdr, void *arg,
|
1683 |
|
|
struct sk_buff **pskb)
|
1684 |
|
|
{
|
1685 |
|
|
/* Locally generated bogus packets by root. <SIGH>. */
|
1686 |
|
|
if (((struct iphdr *)phdr)->ihl * 4 < sizeof(struct iphdr)
|
1687 |
|
|
|| (*pskb)->len < sizeof(struct iphdr))
|
1688 |
|
|
return FW_ACCEPT;
|
1689 |
|
|
return ip_fw_check(phdr, dev->name,
|
1690 |
|
|
arg, IP_FW_OUTPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
|
1691 |
|
|
}
|
1692 |
|
|
|
1693 |
|
|
int ipfw_forward_check(struct firewall_ops *this, int pf,
|
1694 |
|
|
struct net_device *dev, void *phdr, void *arg,
|
1695 |
|
|
struct sk_buff **pskb)
|
1696 |
|
|
{
|
1697 |
|
|
return ip_fw_check(phdr, dev->name,
|
1698 |
|
|
arg, IP_FW_FORWARD_CHAIN, *pskb, SLOT_NUMBER(), 0);
|
1699 |
|
|
}
|
1700 |
|
|
|
1701 |
|
|
struct firewall_ops ipfw_ops=
|
1702 |
|
|
{
|
1703 |
|
|
NULL,
|
1704 |
|
|
ipfw_forward_check,
|
1705 |
|
|
ipfw_input_check,
|
1706 |
|
|
ipfw_output_check,
|
1707 |
|
|
NULL,
|
1708 |
|
|
NULL
|
1709 |
|
|
};
|
1710 |
|
|
|
1711 |
|
|
int ipfw_init_or_cleanup(int init)
|
1712 |
|
|
{
|
1713 |
|
|
struct proc_dir_entry *proc;
|
1714 |
|
|
int ret = 0;
|
1715 |
|
|
unsigned long flags;
|
1716 |
|
|
|
1717 |
|
|
if (!init) goto cleanup;
|
1718 |
|
|
|
1719 |
|
|
#ifdef DEBUG_IP_FIREWALL_LOCKING
|
1720 |
|
|
fwc_wlocks = fwc_rlocks = 0;
|
1721 |
|
|
#endif
|
1722 |
|
|
|
1723 |
|
|
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
|
1724 |
|
|
ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
|
1725 |
|
|
if (ipfwsk == NULL)
|
1726 |
|
|
goto cleanup_nothing;
|
1727 |
|
|
#endif
|
1728 |
|
|
|
1729 |
|
|
ret = register_firewall(PF_INET, &ipfw_ops);
|
1730 |
|
|
if (ret < 0)
|
1731 |
|
|
goto cleanup_netlink;
|
1732 |
|
|
|
1733 |
|
|
proc = proc_net_create(IP_FW_PROC_CHAINS, S_IFREG | S_IRUSR | S_IWUSR,
|
1734 |
|
|
ip_chain_procinfo);
|
1735 |
|
|
if (proc) proc->owner = THIS_MODULE;
|
1736 |
|
|
proc = proc_net_create(IP_FW_PROC_CHAIN_NAMES,
|
1737 |
|
|
S_IFREG | S_IRUSR | S_IWUSR,
|
1738 |
|
|
ip_chain_name_procinfo);
|
1739 |
|
|
if (proc) proc->owner = THIS_MODULE;
|
1740 |
|
|
|
1741 |
|
|
IP_FW_INPUT_CHAIN = ip_init_chain(IP_FW_LABEL_INPUT, 1, FW_ACCEPT);
|
1742 |
|
|
IP_FW_FORWARD_CHAIN = ip_init_chain(IP_FW_LABEL_FORWARD, 1, FW_ACCEPT);
|
1743 |
|
|
IP_FW_OUTPUT_CHAIN = ip_init_chain(IP_FW_LABEL_OUTPUT, 1, FW_ACCEPT);
|
1744 |
|
|
|
1745 |
|
|
return ret;
|
1746 |
|
|
|
1747 |
|
|
cleanup:
|
1748 |
|
|
unregister_firewall(PF_INET, &ipfw_ops);
|
1749 |
|
|
|
1750 |
|
|
FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
|
1751 |
|
|
while (ip_fw_chains) {
|
1752 |
|
|
struct ip_chain *next = ip_fw_chains->next;
|
1753 |
|
|
|
1754 |
|
|
clear_fw_chain(ip_fw_chains);
|
1755 |
|
|
kfree(ip_fw_chains);
|
1756 |
|
|
ip_fw_chains = next;
|
1757 |
|
|
}
|
1758 |
|
|
FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
|
1759 |
|
|
|
1760 |
|
|
proc_net_remove(IP_FW_PROC_CHAINS);
|
1761 |
|
|
proc_net_remove(IP_FW_PROC_CHAIN_NAMES);
|
1762 |
|
|
|
1763 |
|
|
cleanup_netlink:
|
1764 |
|
|
#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
|
1765 |
|
|
sock_release(ipfwsk->socket);
|
1766 |
|
|
|
1767 |
|
|
cleanup_nothing:
|
1768 |
|
|
#endif
|
1769 |
|
|
return ret;
|
1770 |
|
|
}
|
1771 |
|
|
MODULE_LICENSE("Dual BSD/GPL");
|