libip4tc.c revision 10758b743d6aa076ebe2c3e8f855e73826841e71
1/* Library which manipulates firewall rules. Version 0.1. */ 2 3/* Architecture of firewall rules is as follows: 4 * 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. 9 */ 10 11/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See 12 COPYING for details). */ 13 14#include <assert.h> 15#include <string.h> 16#include <errno.h> 17#include <stdlib.h> 18#include <stdio.h> 19 20#ifdef DEBUG_CONNTRACK 21#define inline 22#endif 23 24#if !defined(__GLIBC__) || (__GLIBC__ < 2) 25typedef unsigned int socklen_t; 26#endif 27 28#include "libiptc/libiptc.h" 29 30#define IP_VERSION 4 31#define IP_OFFSET 0x1FFF 32 33#define HOOK_PRE_ROUTING NF_IP_PRE_ROUTING 34#define HOOK_LOCAL_IN NF_IP_LOCAL_IN 35#define HOOK_FORWARD NF_IP_FORWARD 36#define HOOK_LOCAL_OUT NF_IP_LOCAL_OUT 37#define HOOK_POST_ROUTING NF_IP_POST_ROUTING 38#ifdef NF_IP_DROPPING 39#define HOOK_DROPPING NF_IP_DROPPING 40#endif 41 42#define STRUCT_ENTRY_TARGET struct ipt_entry_target 43#define STRUCT_ENTRY struct ipt_entry 44#define STRUCT_ENTRY_MATCH struct ipt_entry_match 45#define STRUCT_GETINFO struct ipt_getinfo 46#define STRUCT_GET_ENTRIES struct ipt_get_entries 47#define STRUCT_COUNTERS struct ipt_counters 48#define STRUCT_COUNTERS_INFO struct ipt_counters_info 49#define STRUCT_STANDARD_TARGET struct ipt_standard_target 50#define STRUCT_REPLACE struct ipt_replace 51 52#define STRUCT_TC_HANDLE struct iptc_handle 53#define TC_HANDLE_T iptc_handle_t 54 55#define ENTRY_ITERATE IPT_ENTRY_ITERATE 56#define TABLE_MAXNAMELEN IPT_TABLE_MAXNAMELEN 57#define FUNCTION_MAXNAMELEN IPT_FUNCTION_MAXNAMELEN 58 59#define GET_TARGET ipt_get_target 60 61#define ERROR_TARGET IPT_ERROR_TARGET 62#define NUMHOOKS NF_IP_NUMHOOKS 63 64#define IPT_CHAINLABEL ipt_chainlabel 65 66#define TC_DUMP_ENTRIES dump_entries 67#define TC_IS_CHAIN iptc_is_chain 68#define TC_FIRST_CHAIN iptc_first_chain 69#define TC_NEXT_CHAIN iptc_next_chain 70#define TC_FIRST_RULE iptc_first_rule 71#define TC_NEXT_RULE iptc_next_rule 72#define TC_GET_TARGET iptc_get_target 73#define TC_BUILTIN iptc_builtin 74#define TC_GET_POLICY iptc_get_policy 75#define TC_INSERT_ENTRY iptc_insert_entry 76#define TC_REPLACE_ENTRY iptc_replace_entry 77#define TC_APPEND_ENTRY iptc_append_entry 78#define TC_DELETE_ENTRY iptc_delete_entry 79#define TC_DELETE_NUM_ENTRY iptc_delete_num_entry 80#define TC_CHECK_PACKET iptc_check_packet 81#define TC_FLUSH_ENTRIES iptc_flush_entries 82#define TC_ZERO_ENTRIES iptc_zero_entries 83#define TC_CREATE_CHAIN iptc_create_chain 84#define TC_GET_REFERENCES iptc_get_references 85#define TC_DELETE_CHAIN iptc_delete_chain 86#define TC_RENAME_CHAIN iptc_rename_chain 87#define TC_SET_POLICY iptc_set_policy 88#define TC_GET_RAW_SOCKET iptc_get_raw_socket 89#define TC_INIT iptc_init 90#define TC_COMMIT iptc_commit 91#define TC_STRERROR iptc_strerror 92 93#define TC_AF AF_INET 94#define TC_IPPROTO IPPROTO_IP 95 96#define SO_SET_REPLACE IPT_SO_SET_REPLACE 97#define SO_SET_ADD_COUNTERS IPT_SO_SET_ADD_COUNTERS 98#define SO_GET_INFO IPT_SO_GET_INFO 99#define SO_GET_ENTRIES IPT_SO_GET_ENTRIES 100#define SO_GET_VERSION IPT_SO_GET_VERSION 101 102#define STANDARD_TARGET IPT_STANDARD_TARGET 103#define LABEL_RETURN IPTC_LABEL_RETURN 104#define LABEL_ACCEPT IPTC_LABEL_ACCEPT 105#define LABEL_DROP IPTC_LABEL_DROP 106#define LABEL_QUEUE IPTC_LABEL_QUEUE 107 108#define ALIGN IPT_ALIGN 109#define RETURN IPT_RETURN 110 111#include "libiptc.c" 112 113#define IP_PARTS_NATIVE(n) \ 114(unsigned int)((n)>>24)&0xFF, \ 115(unsigned int)((n)>>16)&0xFF, \ 116(unsigned int)((n)>>8)&0xFF, \ 117(unsigned int)((n)&0xFF) 118 119#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n)) 120 121int 122dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle) 123{ 124 size_t i; 125 STRUCT_ENTRY_TARGET *t; 126 127 printf("Entry %u (%lu):\n", entry2index(handle, e), 128 entry2offset(handle, e)); 129 printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n", 130 IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr)); 131 printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n", 132 IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr)); 133 printf("Interface: `%s'/", e->ip.iniface); 134 for (i = 0; i < IFNAMSIZ; i++) 135 printf("%c", e->ip.iniface_mask[i] ? 'X' : '.'); 136 printf("to `%s'/", e->ip.outiface); 137 for (i = 0; i < IFNAMSIZ; i++) 138 printf("%c", e->ip.outiface_mask[i] ? 'X' : '.'); 139 printf("\nProtocol: %u\n", e->ip.proto); 140 printf("Flags: %02X\n", e->ip.flags); 141 printf("Invflags: %02X\n", e->ip.invflags); 142 printf("Counters: %llu packets, %llu bytes\n", 143 e->counters.pcnt, e->counters.bcnt); 144 printf("Cache: %08X ", e->nfcache); 145 if (e->nfcache & NFC_ALTERED) printf("ALTERED "); 146 if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN "); 147 if (e->nfcache & NFC_IP_SRC) printf("IP_SRC "); 148 if (e->nfcache & NFC_IP_DST) printf("IP_DST "); 149 if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN "); 150 if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT "); 151 if (e->nfcache & NFC_IP_TOS) printf("IP_TOS "); 152 if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO "); 153 if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS "); 154 if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS "); 155 if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT "); 156 if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT "); 157 if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN "); 158 printf("\n"); 159 160 IPT_MATCH_ITERATE(e, print_match); 161 162 t = GET_TARGET(e); 163 printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size); 164 if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) { 165 int pos = *(int *)t->data; 166 if (pos < 0) 167 printf("verdict=%s\n", 168 pos == -NF_ACCEPT-1 ? "NF_ACCEPT" 169 : pos == -NF_DROP-1 ? "NF_DROP" 170 : pos == -NF_QUEUE-1 ? "NF_QUEUE" 171 : pos == RETURN ? "RETURN" 172 : "UNKNOWN"); 173 else 174 printf("verdict=%u\n", pos); 175 } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0) 176 printf("error=`%s'\n", t->data); 177 178 printf("\n"); 179 return 0; 180} 181 182static int 183is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask) 184{ 185 unsigned int i; 186 STRUCT_ENTRY_TARGET *ta, *tb; 187 unsigned char *mptr; 188 189 /* Always compare head structures: ignore mask here. */ 190 if (a->ip.src.s_addr != b->ip.src.s_addr 191 || a->ip.dst.s_addr != b->ip.dst.s_addr 192 || a->ip.smsk.s_addr != b->ip.smsk.s_addr 193 || a->ip.smsk.s_addr != b->ip.smsk.s_addr 194 || a->ip.proto != b->ip.proto 195 || a->ip.flags != b->ip.flags 196 || a->ip.invflags != b->ip.invflags) 197 return 0; 198 199 for (i = 0; i < IFNAMSIZ; i++) { 200 if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i]) 201 return 0; 202 if ((a->ip.iniface[i] & a->ip.iniface_mask[i]) 203 != (b->ip.iniface[i] & b->ip.iniface_mask[i])) 204 return 0; 205 if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i]) 206 return 0; 207 if ((a->ip.outiface[i] & a->ip.outiface_mask[i]) 208 != (b->ip.outiface[i] & b->ip.outiface_mask[i])) 209 return 0; 210 } 211 212 if (a->nfcache != b->nfcache 213 || a->target_offset != b->target_offset 214 || a->next_offset != b->next_offset) 215 return 0; 216 217 mptr = matchmask + sizeof(STRUCT_ENTRY); 218 if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr)) 219 return 0; 220 221 ta = GET_TARGET((STRUCT_ENTRY *)a); 222 tb = GET_TARGET((STRUCT_ENTRY *)b); 223 if (ta->u.target_size != tb->u.target_size) 224 return 0; 225 if (strcmp(ta->u.user.name, tb->u.user.name) != 0) 226 return 0; 227 228 mptr += sizeof(*ta); 229 if (target_different(ta->data, tb->data, 230 ta->u.target_size - sizeof(*ta), mptr)) 231 return 0; 232 233 return 1; 234} 235 236/***************************** DEBUGGING ********************************/ 237static inline int 238unconditional(const struct ipt_ip *ip) 239{ 240 unsigned int i; 241 242 for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++) 243 if (((u_int32_t *)ip)[i]) 244 return 0; 245 246 return 1; 247} 248 249static inline int 250check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off) 251{ 252 assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH)); 253 assert(ALIGN(m->u.match_size) == m->u.match_size); 254 255 (*off) += m->u.match_size; 256 return 0; 257} 258 259static inline int 260check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off, 261 unsigned int user_offset, int *was_return, 262 TC_HANDLE_T h) 263{ 264 unsigned int toff; 265 STRUCT_STANDARD_TARGET *t; 266 267 assert(e->target_offset >= sizeof(STRUCT_ENTRY)); 268 assert(e->next_offset >= e->target_offset 269 + sizeof(STRUCT_ENTRY_TARGET)); 270 toff = sizeof(STRUCT_ENTRY); 271 IPT_MATCH_ITERATE(e, check_match, &toff); 272 273 assert(toff == e->target_offset); 274 275 t = (STRUCT_STANDARD_TARGET *) 276 GET_TARGET((STRUCT_ENTRY *)e); 277 /* next_offset will have to be multiple of entry alignment. */ 278 assert(e->next_offset == ALIGN(e->next_offset)); 279 assert(e->target_offset == ALIGN(e->target_offset)); 280 assert(t->target.u.target_size == ALIGN(t->target.u.target_size)); 281 assert(!TC_IS_CHAIN(t->target.u.user.name, h)); 282 283 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) { 284 assert(t->target.u.target_size 285 == ALIGN(sizeof(STRUCT_STANDARD_TARGET))); 286 287 assert(t->verdict == -NF_DROP-1 288 || t->verdict == -NF_ACCEPT-1 289 || t->verdict == RETURN 290 || t->verdict < (int)h->entries.size); 291 292 if (t->verdict >= 0) { 293 STRUCT_ENTRY *te = get_entry(h, t->verdict); 294 int idx; 295 296 idx = entry2index(h, te); 297 assert(strcmp(GET_TARGET(te)->u.user.name, 298 IPT_ERROR_TARGET) 299 != 0); 300 assert(te != e); 301 302 /* Prior node must be error node, or this node. */ 303 assert(t->verdict == entry2offset(h, e)+e->next_offset 304 || strcmp(GET_TARGET(index2entry(h, idx-1)) 305 ->u.user.name, IPT_ERROR_TARGET) 306 == 0); 307 } 308 309 if (t->verdict == RETURN 310 && unconditional(&e->ip) 311 && e->target_offset == sizeof(*e)) 312 *was_return = 1; 313 else 314 *was_return = 0; 315 } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) { 316 assert(t->target.u.target_size 317 == ALIGN(sizeof(struct ipt_error_target))); 318 319 /* If this is in user area, previous must have been return */ 320 if (*off > user_offset) 321 assert(*was_return); 322 323 *was_return = 0; 324 } 325 else *was_return = 0; 326 327 if (*off == user_offset) 328 assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0); 329 330 (*off) += e->next_offset; 331 (*i)++; 332 return 0; 333} 334 335#ifndef NDEBUG 336/* Do every conceivable sanity check on the handle */ 337static void 338do_check(TC_HANDLE_T h, unsigned int line) 339{ 340 unsigned int i, n; 341 unsigned int user_offset; /* Offset of first user chain */ 342 int was_return; 343 344 assert(h->changed == 0 || h->changed == 1); 345 if (strcmp(h->info.name, "filter") == 0) { 346 assert(h->info.valid_hooks 347 == (1 << NF_IP_LOCAL_IN 348 | 1 << NF_IP_FORWARD 349 | 1 << NF_IP_LOCAL_OUT)); 350 351 /* Hooks should be first three */ 352 assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0); 353 354 n = get_chain_end(h, 0); 355 n += get_entry(h, n)->next_offset; 356 assert(h->info.hook_entry[NF_IP_FORWARD] == n); 357 358 n = get_chain_end(h, n); 359 n += get_entry(h, n)->next_offset; 360 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 361 362 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 363 } else if (strcmp(h->info.name, "nat") == 0) { 364 assert(h->info.valid_hooks 365 == (1 << NF_IP_PRE_ROUTING 366 | 1 << NF_IP_POST_ROUTING 367 | 1 << NF_IP_LOCAL_OUT)); 368 369 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); 370 371 n = get_chain_end(h, 0); 372 n += get_entry(h, n)->next_offset; 373 assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n); 374 375 n = get_chain_end(h, n); 376 n += get_entry(h, n)->next_offset; 377 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 378 379 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 380 } else if (strcmp(h->info.name, "mangle") == 0) { 381 assert(h->info.valid_hooks 382 == (1 << NF_IP_PRE_ROUTING 383 | 1 << NF_IP_LOCAL_OUT)); 384 385 /* Hooks should be first three */ 386 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); 387 388 n = get_chain_end(h, 0); 389 n += get_entry(h, n)->next_offset; 390 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 391 392 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 393 } else 394 abort(); 395 396 /* User chain == end of last builtin + policy entry */ 397 user_offset = get_chain_end(h, user_offset); 398 user_offset += get_entry(h, user_offset)->next_offset; 399 400 /* Overflows should be end of entry chains, and unconditional 401 policy nodes. */ 402 for (i = 0; i < NUMHOOKS; i++) { 403 STRUCT_ENTRY *e; 404 STRUCT_STANDARD_TARGET *t; 405 406 if (!(h->info.valid_hooks & (1 << i))) 407 continue; 408 assert(h->info.underflow[i] 409 == get_chain_end(h, h->info.hook_entry[i])); 410 411 e = get_entry(h, get_chain_end(h, h->info.hook_entry[i])); 412 assert(unconditional(&e->ip)); 413 assert(e->target_offset == sizeof(*e)); 414 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); 415 assert(t->target.u.target_size == IPT_ALIGN(sizeof(*t))); 416 assert(e->next_offset == sizeof(*e) + IPT_ALIGN(sizeof(*t))); 417 418 assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0); 419 assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1); 420 421 /* Hooks and underflows must be valid entries */ 422 entry2index(h, get_entry(h, h->info.hook_entry[i])); 423 entry2index(h, get_entry(h, h->info.underflow[i])); 424 } 425 426 assert(h->info.size 427 >= h->info.num_entries * (sizeof(STRUCT_ENTRY) 428 +sizeof(STRUCT_STANDARD_TARGET))); 429 430 assert(h->entries.size 431 >= (h->new_number 432 * (sizeof(STRUCT_ENTRY) 433 + sizeof(STRUCT_STANDARD_TARGET)))); 434 assert(strcmp(h->info.name, h->entries.name) == 0); 435 436 i = 0; n = 0; 437 was_return = 0; 438 /* Check all the entries. */ 439 ENTRY_ITERATE(h->entries.entrytable, h->entries.size, 440 check_entry, &i, &n, user_offset, &was_return, h); 441 442 assert(i == h->new_number); 443 assert(n == h->entries.size); 444 445 /* Final entry must be error node */ 446 assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1)) 447 ->u.user.name, 448 IPT_ERROR_TARGET) == 0); 449} 450#endif /*NDEBUG*/ 451