1 /* Library which manipulates firewall rules. Version 0.1. */
3 /* Architecture of firewall rules is as follows:
5 * Chains go INPUT, FORWARD, OUTPUT then user chains.
6 * Each user chain starts with an ERROR node.
7 * Every chain ends with an unconditional jump: a RETURN for user chains,
8 * and a POLICY for built-ins.
11 /* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12 COPYING for details). */
21 #ifdef DEBUG_CONNTRACK
25 #if !defined(__GLIBC__) || (__GLIBC__ < 2)
26 typedef unsigned int socklen_t
;
29 #include "libiptc/libiptc.h"
32 #define IP_OFFSET 0x1FFF
34 #define HOOK_PRE_ROUTING NF_IP_PRE_ROUTING
35 #define HOOK_LOCAL_IN NF_IP_LOCAL_IN
36 #define HOOK_FORWARD NF_IP_FORWARD
37 #define HOOK_LOCAL_OUT NF_IP_LOCAL_OUT
38 #define HOOK_POST_ROUTING NF_IP_POST_ROUTING
40 #define HOOK_DROPPING NF_IP_DROPPING
43 #define STRUCT_ENTRY_TARGET struct ipt_entry_target
44 #define STRUCT_ENTRY struct ipt_entry
45 #define STRUCT_ENTRY_MATCH struct ipt_entry_match
46 #define STRUCT_GETINFO struct ipt_getinfo
47 #define STRUCT_GET_ENTRIES struct ipt_get_entries
48 #define STRUCT_COUNTERS struct ipt_counters
49 #define STRUCT_COUNTERS_INFO struct ipt_counters_info
50 #define STRUCT_STANDARD_TARGET struct ipt_standard_target
51 #define STRUCT_REPLACE struct ipt_replace
53 #define STRUCT_TC_HANDLE struct iptc_handle
54 #define TC_HANDLE_T iptc_handle_t
56 #define ENTRY_ITERATE IPT_ENTRY_ITERATE
57 #define TABLE_MAXNAMELEN IPT_TABLE_MAXNAMELEN
58 #define FUNCTION_MAXNAMELEN IPT_FUNCTION_MAXNAMELEN
60 #define GET_TARGET ipt_get_target
62 #define ERROR_TARGET IPT_ERROR_TARGET
63 #define NUMHOOKS NF_IP_NUMHOOKS
65 #define IPT_CHAINLABEL ipt_chainlabel
67 #define TC_DUMP_ENTRIES dump_entries
68 #define TC_IS_CHAIN iptc_is_chain
69 #define TC_FIRST_CHAIN iptc_first_chain
70 #define TC_NEXT_CHAIN iptc_next_chain
71 #define TC_FIRST_RULE iptc_first_rule
72 #define TC_NEXT_RULE iptc_next_rule
73 #define TC_GET_TARGET iptc_get_target
74 #define TC_BUILTIN iptc_builtin
75 #define TC_GET_POLICY iptc_get_policy
76 #define TC_INSERT_ENTRY iptc_insert_entry
77 #define TC_REPLACE_ENTRY iptc_replace_entry
78 #define TC_APPEND_ENTRY iptc_append_entry
79 #define TC_DELETE_ENTRY iptc_delete_entry
80 #define TC_DELETE_NUM_ENTRY iptc_delete_num_entry
81 #define TC_CHECK_PACKET iptc_check_packet
82 #define TC_FLUSH_ENTRIES iptc_flush_entries
83 #define TC_ZERO_ENTRIES iptc_zero_entries
84 #define TC_READ_COUNTER iptc_read_counter
85 #define TC_ZERO_COUNTER iptc_zero_counter
86 #define TC_SET_COUNTER iptc_set_counter
87 #define TC_CREATE_CHAIN iptc_create_chain
88 #define TC_GET_REFERENCES iptc_get_references
89 #define TC_DELETE_CHAIN iptc_delete_chain
90 #define TC_RENAME_CHAIN iptc_rename_chain
91 #define TC_SET_POLICY iptc_set_policy
92 #define TC_GET_RAW_SOCKET iptc_get_raw_socket
93 #define TC_INIT iptc_init
94 #define TC_FREE iptc_free
95 #define TC_COMMIT iptc_commit
96 #define TC_STRERROR iptc_strerror
97 #define TC_NUM_RULES iptc_num_rules
98 #define TC_GET_RULE iptc_get_rule
100 #define TC_AF AF_INET
101 #define TC_IPPROTO IPPROTO_IP
103 #define SO_SET_REPLACE IPT_SO_SET_REPLACE
104 #define SO_SET_ADD_COUNTERS IPT_SO_SET_ADD_COUNTERS
105 #define SO_GET_INFO IPT_SO_GET_INFO
106 #define SO_GET_ENTRIES IPT_SO_GET_ENTRIES
107 #define SO_GET_VERSION IPT_SO_GET_VERSION
109 #define STANDARD_TARGET IPT_STANDARD_TARGET
110 #define LABEL_RETURN IPTC_LABEL_RETURN
111 #define LABEL_ACCEPT IPTC_LABEL_ACCEPT
112 #define LABEL_DROP IPTC_LABEL_DROP
113 #define LABEL_QUEUE IPTC_LABEL_QUEUE
115 #define ALIGN IPT_ALIGN
116 #define RETURN IPT_RETURN
120 #define IP_PARTS_NATIVE(n) \
121 (unsigned int)((n)>>24)&0xFF, \
122 (unsigned int)((n)>>16)&0xFF, \
123 (unsigned int)((n)>>8)&0xFF, \
124 (unsigned int)((n)&0xFF)
126 #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
130 dump_entry(STRUCT_ENTRY
*e
, const TC_HANDLE_T handle
)
133 STRUCT_ENTRY_TARGET
*t
;
135 printf("Entry %u (%lu):\n", iptcb_entry2index(handle
, e
),
136 iptcb_entry2offset(handle
, e
));
137 printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
138 IP_PARTS(e
->ip
.src
.s_addr
),IP_PARTS(e
->ip
.smsk
.s_addr
));
139 printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
140 IP_PARTS(e
->ip
.dst
.s_addr
),IP_PARTS(e
->ip
.dmsk
.s_addr
));
141 printf("Interface: `%s'/", e
->ip
.iniface
);
142 for (i
= 0; i
< IFNAMSIZ
; i
++)
143 printf("%c", e
->ip
.iniface_mask
[i
] ? 'X' : '.');
144 printf("to `%s'/", e
->ip
.outiface
);
145 for (i
= 0; i
< IFNAMSIZ
; i
++)
146 printf("%c", e
->ip
.outiface_mask
[i
] ? 'X' : '.');
147 printf("\nProtocol: %u\n", e
->ip
.proto
);
148 printf("Flags: %02X\n", e
->ip
.flags
);
149 printf("Invflags: %02X\n", e
->ip
.invflags
);
150 printf("Counters: %llu packets, %llu bytes\n",
151 (unsigned long long)e
->counters
.pcnt
, (unsigned long long)e
->counters
.bcnt
);
152 printf("Cache: %08X ", e
->nfcache
);
153 if (e
->nfcache
& NFC_ALTERED
) printf("ALTERED ");
154 if (e
->nfcache
& NFC_UNKNOWN
) printf("UNKNOWN ");
157 IPT_MATCH_ITERATE(e
, print_match
);
160 printf("Target name: `%s' [%u]\n", t
->u
.user
.name
, t
->u
.target_size
);
161 if (strcmp(t
->u
.user
.name
, STANDARD_TARGET
) == 0) {
162 int pos
= *(int *)t
->data
;
164 printf("verdict=%s\n",
165 pos
== -NF_ACCEPT
-1 ? "NF_ACCEPT"
166 : pos
== -NF_DROP
-1 ? "NF_DROP"
167 : pos
== -NF_QUEUE
-1 ? "NF_QUEUE"
168 : pos
== RETURN
? "RETURN"
171 printf("verdict=%u\n", pos
);
172 } else if (strcmp(t
->u
.user
.name
, IPT_ERROR_TARGET
) == 0)
173 printf("error=`%s'\n", t
->data
);
180 static unsigned char *
181 is_same(const STRUCT_ENTRY
*a
, const STRUCT_ENTRY
*b
, unsigned char *matchmask
)
186 /* Always compare head structures: ignore mask here. */
187 if (a
->ip
.src
.s_addr
!= b
->ip
.src
.s_addr
188 || a
->ip
.dst
.s_addr
!= b
->ip
.dst
.s_addr
189 || a
->ip
.smsk
.s_addr
!= b
->ip
.smsk
.s_addr
190 || a
->ip
.dmsk
.s_addr
!= b
->ip
.dmsk
.s_addr
191 || a
->ip
.proto
!= b
->ip
.proto
192 || (a
->ip
.flags
& ~IPT_F_NO_DEF_MATCH
) != (b
->ip
.flags
& ~IPT_F_NO_DEF_MATCH
)
193 || a
->ip
.invflags
!= b
->ip
.invflags
)
196 for (i
= 0; i
< IFNAMSIZ
; i
++) {
197 if (a
->ip
.iniface_mask
[i
] != b
->ip
.iniface_mask
[i
])
199 if ((a
->ip
.iniface
[i
] & a
->ip
.iniface_mask
[i
])
200 != (b
->ip
.iniface
[i
] & b
->ip
.iniface_mask
[i
]))
202 if (a
->ip
.outiface_mask
[i
] != b
->ip
.outiface_mask
[i
])
204 if ((a
->ip
.outiface
[i
] & a
->ip
.outiface_mask
[i
])
205 != (b
->ip
.outiface
[i
] & b
->ip
.outiface_mask
[i
]))
209 if (a
->nfcache
!= b
->nfcache
210 || a
->target_offset
!= b
->target_offset
211 || a
->next_offset
!= b
->next_offset
)
214 mptr
= matchmask
+ sizeof(STRUCT_ENTRY
);
215 if (IPT_MATCH_ITERATE(a
, match_different
, a
->elems
, b
->elems
, &mptr
))
217 mptr
+= IPT_ALIGN(sizeof(struct ipt_entry_target
));
223 /***************************** DEBUGGING ********************************/
225 unconditional(const struct ipt_ip
*ip
)
229 for (i
= 0; i
< sizeof(*ip
)/sizeof(u_int32_t
); i
++)
230 if (((u_int32_t
*)ip
)[i
])
237 check_match(const STRUCT_ENTRY_MATCH
*m
, unsigned int *off
)
239 assert(m
->u
.match_size
>= sizeof(STRUCT_ENTRY_MATCH
));
240 assert(ALIGN(m
->u
.match_size
) == m
->u
.match_size
);
242 (*off
) += m
->u
.match_size
;
247 check_entry(const STRUCT_ENTRY
*e
, unsigned int *i
, unsigned int *off
,
248 unsigned int user_offset
, int *was_return
,
252 STRUCT_STANDARD_TARGET
*t
;
254 assert(e
->target_offset
>= sizeof(STRUCT_ENTRY
));
255 assert(e
->next_offset
>= e
->target_offset
256 + sizeof(STRUCT_ENTRY_TARGET
));
257 toff
= sizeof(STRUCT_ENTRY
);
258 IPT_MATCH_ITERATE(e
, check_match
, &toff
);
260 assert(toff
== e
->target_offset
);
262 t
= (STRUCT_STANDARD_TARGET
*)
263 GET_TARGET((STRUCT_ENTRY
*)e
);
264 /* next_offset will have to be multiple of entry alignment. */
265 assert(e
->next_offset
== ALIGN(e
->next_offset
));
266 assert(e
->target_offset
== ALIGN(e
->target_offset
));
267 assert(t
->target
.u
.target_size
== ALIGN(t
->target
.u
.target_size
));
268 assert(!TC_IS_CHAIN(t
->target
.u
.user
.name
, h
));
270 if (strcmp(t
->target
.u
.user
.name
, STANDARD_TARGET
) == 0) {
271 assert(t
->target
.u
.target_size
272 == ALIGN(sizeof(STRUCT_STANDARD_TARGET
)));
274 assert(t
->verdict
== -NF_DROP
-1
275 || t
->verdict
== -NF_ACCEPT
-1
276 || t
->verdict
== RETURN
277 || t
->verdict
< (int)h
->entries
->size
);
279 if (t
->verdict
>= 0) {
280 STRUCT_ENTRY
*te
= get_entry(h
, t
->verdict
);
283 idx
= iptcb_entry2index(h
, te
);
284 assert(strcmp(GET_TARGET(te
)->u
.user
.name
,
289 /* Prior node must be error node, or this node. */
290 assert(t
->verdict
== iptcb_entry2offset(h
, e
)+e
->next_offset
291 || strcmp(GET_TARGET(index2entry(h
, idx
-1))
292 ->u
.user
.name
, IPT_ERROR_TARGET
)
296 if (t
->verdict
== RETURN
297 && unconditional(&e
->ip
)
298 && e
->target_offset
== sizeof(*e
))
302 } else if (strcmp(t
->target
.u
.user
.name
, IPT_ERROR_TARGET
) == 0) {
303 assert(t
->target
.u
.target_size
304 == ALIGN(sizeof(struct ipt_error_target
)));
306 /* If this is in user area, previous must have been return */
307 if (*off
> user_offset
)
312 else *was_return
= 0;
314 if (*off
== user_offset
)
315 assert(strcmp(t
->target
.u
.user
.name
, IPT_ERROR_TARGET
) == 0);
317 (*off
) += e
->next_offset
;
323 /* Do every conceivable sanity check on the handle */
325 do_check(TC_HANDLE_T h
, unsigned int line
)
328 unsigned int user_offset
; /* Offset of first user chain */
331 assert(h
->changed
== 0 || h
->changed
== 1);
332 if (strcmp(h
->info
.name
, "filter") == 0) {
333 assert(h
->info
.valid_hooks
334 == (1 << NF_IP_LOCAL_IN
336 | 1 << NF_IP_LOCAL_OUT
));
338 /* Hooks should be first three */
339 assert(h
->info
.hook_entry
[NF_IP_LOCAL_IN
] == 0);
341 n
= get_chain_end(h
, 0);
342 n
+= get_entry(h
, n
)->next_offset
;
343 assert(h
->info
.hook_entry
[NF_IP_FORWARD
] == n
);
345 n
= get_chain_end(h
, n
);
346 n
+= get_entry(h
, n
)->next_offset
;
347 assert(h
->info
.hook_entry
[NF_IP_LOCAL_OUT
] == n
);
349 user_offset
= h
->info
.hook_entry
[NF_IP_LOCAL_OUT
];
350 } else if (strcmp(h
->info
.name
, "nat") == 0) {
351 assert((h
->info
.valid_hooks
352 == (1 << NF_IP_PRE_ROUTING
353 | 1 << NF_IP_POST_ROUTING
354 | 1 << NF_IP_LOCAL_OUT
)) ||
356 == (1 << NF_IP_PRE_ROUTING
357 | 1 << NF_IP_LOCAL_IN
358 | 1 << NF_IP_POST_ROUTING
359 | 1 << NF_IP_LOCAL_OUT
)));
361 assert(h
->info
.hook_entry
[NF_IP_PRE_ROUTING
] == 0);
363 n
= get_chain_end(h
, 0);
365 n
+= get_entry(h
, n
)->next_offset
;
366 assert(h
->info
.hook_entry
[NF_IP_POST_ROUTING
] == n
);
367 n
= get_chain_end(h
, n
);
369 n
+= get_entry(h
, n
)->next_offset
;
370 assert(h
->info
.hook_entry
[NF_IP_LOCAL_OUT
] == n
);
371 user_offset
= h
->info
.hook_entry
[NF_IP_LOCAL_OUT
];
373 if (h
->info
.valid_hooks
& (1 << NF_IP_LOCAL_IN
)) {
374 n
= get_chain_end(h
, n
);
375 n
+= get_entry(h
, n
)->next_offset
;
376 assert(h
->info
.hook_entry
[NF_IP_LOCAL_IN
] == n
);
377 user_offset
= h
->info
.hook_entry
[NF_IP_LOCAL_IN
];
380 } else if (strcmp(h
->info
.name
, "mangle") == 0) {
381 /* This code is getting ugly because linux < 2.4.18-pre6 had
382 * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
384 assert((h
->info
.valid_hooks
385 == (1 << NF_IP_PRE_ROUTING
386 | 1 << NF_IP_LOCAL_OUT
)) ||
388 == (1 << NF_IP_PRE_ROUTING
389 | 1 << NF_IP_LOCAL_IN
391 | 1 << NF_IP_LOCAL_OUT
392 | 1 << NF_IP_POST_ROUTING
)));
394 /* Hooks should be first five */
395 assert(h
->info
.hook_entry
[NF_IP_PRE_ROUTING
] == 0);
397 n
= get_chain_end(h
, 0);
399 if (h
->info
.valid_hooks
& (1 << NF_IP_LOCAL_IN
)) {
400 n
+= get_entry(h
, n
)->next_offset
;
401 assert(h
->info
.hook_entry
[NF_IP_LOCAL_IN
] == n
);
402 n
= get_chain_end(h
, n
);
405 if (h
->info
.valid_hooks
& (1 << NF_IP_FORWARD
)) {
406 n
+= get_entry(h
, n
)->next_offset
;
407 assert(h
->info
.hook_entry
[NF_IP_FORWARD
] == n
);
408 n
= get_chain_end(h
, n
);
411 n
+= get_entry(h
, n
)->next_offset
;
412 assert(h
->info
.hook_entry
[NF_IP_LOCAL_OUT
] == n
);
413 user_offset
= h
->info
.hook_entry
[NF_IP_LOCAL_OUT
];
415 if (h
->info
.valid_hooks
& (1 << NF_IP_POST_ROUTING
)) {
416 n
= get_chain_end(h
, n
);
417 n
+= get_entry(h
, n
)->next_offset
;
418 assert(h
->info
.hook_entry
[NF_IP_POST_ROUTING
] == n
);
419 user_offset
= h
->info
.hook_entry
[NF_IP_POST_ROUTING
];
421 } else if (strcmp(h
->info
.name
, "raw") == 0) {
422 assert(h
->info
.valid_hooks
423 == (1 << NF_IP_PRE_ROUTING
424 | 1 << NF_IP_LOCAL_OUT
));
426 /* Hooks should be first three */
427 assert(h
->info
.hook_entry
[NF_IP_PRE_ROUTING
] == 0);
429 n
= get_chain_end(h
, n
);
430 n
+= get_entry(h
, n
)->next_offset
;
431 assert(h
->info
.hook_entry
[NF_IP_LOCAL_OUT
] == n
);
433 user_offset
= h
->info
.hook_entry
[NF_IP_LOCAL_OUT
];
435 #ifdef NF_IP_DROPPING
436 } else if (strcmp(h
->info
.name
, "drop") == 0) {
437 assert(h
->info
.valid_hooks
== (1 << NF_IP_DROPPING
));
439 /* Hook should be first */
440 assert(h
->info
.hook_entry
[NF_IP_DROPPING
] == 0);
444 fprintf(stderr
, "Unknown table `%s'\n", h
->info
.name
);
448 /* User chain == end of last builtin + policy entry */
449 user_offset
= get_chain_end(h
, user_offset
);
450 user_offset
+= get_entry(h
, user_offset
)->next_offset
;
452 /* Overflows should be end of entry chains, and unconditional
454 for (i
= 0; i
< NUMHOOKS
; i
++) {
456 STRUCT_STANDARD_TARGET
*t
;
458 if (!(h
->info
.valid_hooks
& (1 << i
)))
460 assert(h
->info
.underflow
[i
]
461 == get_chain_end(h
, h
->info
.hook_entry
[i
]));
463 e
= get_entry(h
, get_chain_end(h
, h
->info
.hook_entry
[i
]));
464 assert(unconditional(&e
->ip
));
465 assert(e
->target_offset
== sizeof(*e
));
466 t
= (STRUCT_STANDARD_TARGET
*)GET_TARGET(e
);
467 assert(t
->target
.u
.target_size
== ALIGN(sizeof(*t
)));
468 assert(e
->next_offset
== sizeof(*e
) + ALIGN(sizeof(*t
)));
470 assert(strcmp(t
->target
.u
.user
.name
, STANDARD_TARGET
)==0);
471 assert(t
->verdict
== -NF_DROP
-1 || t
->verdict
== -NF_ACCEPT
-1);
473 /* Hooks and underflows must be valid entries */
474 entry2index(h
, get_entry(h
, h
->info
.hook_entry
[i
]));
475 entry2index(h
, get_entry(h
, h
->info
.underflow
[i
]));
479 >= h
->info
.num_entries
* (sizeof(STRUCT_ENTRY
)
480 +sizeof(STRUCT_STANDARD_TARGET
)));
482 assert(h
->entries
.size
484 * (sizeof(STRUCT_ENTRY
)
485 + sizeof(STRUCT_STANDARD_TARGET
))));
486 assert(strcmp(h
->info
.name
, h
->entries
.name
) == 0);
490 /* Check all the entries. */
491 ENTRY_ITERATE(h
->entries
.entrytable
, h
->entries
.size
,
492 check_entry
, &i
, &n
, user_offset
, &was_return
, h
);
494 assert(i
== h
->new_number
);
495 assert(n
== h
->entries
.size
);
497 /* Final entry must be error node */
498 assert(strcmp(GET_TARGET(index2entry(h
, h
->new_number
-1))
502 #endif /*IPTC_DEBUG*/