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#include <unistd.h> 20 21#ifdef DEBUG_CONNTRACK 22#define inline 23#endif 24 25#if !defined(__GLIBC__) || (__GLIBC__ < 2) 26typedef unsigned int socklen_t; 27#endif 28 29#include "libiptc/libiptc.h" 30 31#define IP_VERSION 4 32#define IP_OFFSET 0x1FFF 33 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 39#ifdef NF_IP_DROPPING 40#define HOOK_DROPPING NF_IP_DROPPING 41#endif 42 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 52 53#define STRUCT_TC_HANDLE struct iptc_handle 54#define TC_HANDLE_T iptc_handle_t 55 56#define ENTRY_ITERATE IPT_ENTRY_ITERATE 57#define TABLE_MAXNAMELEN IPT_TABLE_MAXNAMELEN 58#define FUNCTION_MAXNAMELEN IPT_FUNCTION_MAXNAMELEN 59 60#define GET_TARGET ipt_get_target 61 62#define ERROR_TARGET IPT_ERROR_TARGET 63#define NUMHOOKS NF_IP_NUMHOOKS 64 65#define IPT_CHAINLABEL ipt_chainlabel 66 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_COMMIT iptc_commit 95#define TC_STRERROR iptc_strerror 96 97#define TC_AF AF_INET 98#define TC_IPPROTO IPPROTO_IP 99 100#define SO_SET_REPLACE IPT_SO_SET_REPLACE 101#define SO_SET_ADD_COUNTERS IPT_SO_SET_ADD_COUNTERS 102#define SO_GET_INFO IPT_SO_GET_INFO 103#define SO_GET_ENTRIES IPT_SO_GET_ENTRIES 104#define SO_GET_VERSION IPT_SO_GET_VERSION 105 106#define STANDARD_TARGET IPT_STANDARD_TARGET 107#define LABEL_RETURN IPTC_LABEL_RETURN 108#define LABEL_ACCEPT IPTC_LABEL_ACCEPT 109#define LABEL_DROP IPTC_LABEL_DROP 110#define LABEL_QUEUE IPTC_LABEL_QUEUE 111 112#define ALIGN IPT_ALIGN 113#define RETURN IPT_RETURN 114 115#include "libiptc.c" 116 117#define IP_PARTS_NATIVE(n) \ 118(unsigned int)((n)>>24)&0xFF, \ 119(unsigned int)((n)>>16)&0xFF, \ 120(unsigned int)((n)>>8)&0xFF, \ 121(unsigned int)((n)&0xFF) 122 123#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n)) 124 125int 126dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle) 127{ 128 size_t i; 129 STRUCT_ENTRY_TARGET *t; 130 131 printf("Entry %u (%lu):\n", entry2index(handle, e), 132 entry2offset(handle, e)); 133 printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n", 134 IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr)); 135 printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n", 136 IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr)); 137 printf("Interface: `%s'/", e->ip.iniface); 138 for (i = 0; i < IFNAMSIZ; i++) 139 printf("%c", e->ip.iniface_mask[i] ? 'X' : '.'); 140 printf("to `%s'/", e->ip.outiface); 141 for (i = 0; i < IFNAMSIZ; i++) 142 printf("%c", e->ip.outiface_mask[i] ? 'X' : '.'); 143 printf("\nProtocol: %u\n", e->ip.proto); 144 printf("Flags: %02X\n", e->ip.flags); 145 printf("Invflags: %02X\n", e->ip.invflags); 146 printf("Counters: %llu packets, %llu bytes\n", 147 e->counters.pcnt, e->counters.bcnt); 148 printf("Cache: %08X ", e->nfcache); 149 if (e->nfcache & NFC_ALTERED) printf("ALTERED "); 150 if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN "); 151 if (e->nfcache & NFC_IP_SRC) printf("IP_SRC "); 152 if (e->nfcache & NFC_IP_DST) printf("IP_DST "); 153 if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN "); 154 if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT "); 155 if (e->nfcache & NFC_IP_TOS) printf("IP_TOS "); 156 if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO "); 157 if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS "); 158 if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS "); 159 if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT "); 160 if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT "); 161 if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN "); 162 printf("\n"); 163 164 IPT_MATCH_ITERATE(e, print_match); 165 166 t = GET_TARGET(e); 167 printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size); 168 if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) { 169 int pos = *(int *)t->data; 170 if (pos < 0) 171 printf("verdict=%s\n", 172 pos == -NF_ACCEPT-1 ? "NF_ACCEPT" 173 : pos == -NF_DROP-1 ? "NF_DROP" 174 : pos == -NF_QUEUE-1 ? "NF_QUEUE" 175 : pos == RETURN ? "RETURN" 176 : "UNKNOWN"); 177 else 178 printf("verdict=%u\n", pos); 179 } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0) 180 printf("error=`%s'\n", t->data); 181 182 printf("\n"); 183 return 0; 184} 185 186static int 187is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask) 188{ 189 unsigned int i; 190 STRUCT_ENTRY_TARGET *ta, *tb; 191 unsigned char *mptr; 192 193 /* Always compare head structures: ignore mask here. */ 194 if (a->ip.src.s_addr != b->ip.src.s_addr 195 || a->ip.dst.s_addr != b->ip.dst.s_addr 196 || a->ip.smsk.s_addr != b->ip.smsk.s_addr 197 || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr 198 || a->ip.proto != b->ip.proto 199 || a->ip.flags != b->ip.flags 200 || a->ip.invflags != b->ip.invflags) 201 return 0; 202 203 for (i = 0; i < IFNAMSIZ; i++) { 204 if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i]) 205 return 0; 206 if ((a->ip.iniface[i] & a->ip.iniface_mask[i]) 207 != (b->ip.iniface[i] & b->ip.iniface_mask[i])) 208 return 0; 209 if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i]) 210 return 0; 211 if ((a->ip.outiface[i] & a->ip.outiface_mask[i]) 212 != (b->ip.outiface[i] & b->ip.outiface_mask[i])) 213 return 0; 214 } 215 216 if (a->nfcache != b->nfcache 217 || a->target_offset != b->target_offset 218 || a->next_offset != b->next_offset) 219 return 0; 220 221 mptr = matchmask + sizeof(STRUCT_ENTRY); 222 if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr)) 223 return 0; 224 225 ta = GET_TARGET((STRUCT_ENTRY *)a); 226 tb = GET_TARGET((STRUCT_ENTRY *)b); 227 if (ta->u.target_size != tb->u.target_size) 228 return 0; 229 if (strcmp(ta->u.user.name, tb->u.user.name) != 0) 230 return 0; 231 232 mptr += sizeof(*ta); 233 if (target_different(ta->data, tb->data, 234 ta->u.target_size - sizeof(*ta), mptr)) 235 return 0; 236 237 return 1; 238} 239 240/***************************** DEBUGGING ********************************/ 241static inline int 242unconditional(const struct ipt_ip *ip) 243{ 244 unsigned int i; 245 246 for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++) 247 if (((u_int32_t *)ip)[i]) 248 return 0; 249 250 return 1; 251} 252 253static inline int 254check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off) 255{ 256 assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH)); 257 assert(ALIGN(m->u.match_size) == m->u.match_size); 258 259 (*off) += m->u.match_size; 260 return 0; 261} 262 263static inline int 264check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off, 265 unsigned int user_offset, int *was_return, 266 TC_HANDLE_T h) 267{ 268 unsigned int toff; 269 STRUCT_STANDARD_TARGET *t; 270 271 assert(e->target_offset >= sizeof(STRUCT_ENTRY)); 272 assert(e->next_offset >= e->target_offset 273 + sizeof(STRUCT_ENTRY_TARGET)); 274 toff = sizeof(STRUCT_ENTRY); 275 IPT_MATCH_ITERATE(e, check_match, &toff); 276 277 assert(toff == e->target_offset); 278 279 t = (STRUCT_STANDARD_TARGET *) 280 GET_TARGET((STRUCT_ENTRY *)e); 281 /* next_offset will have to be multiple of entry alignment. */ 282 assert(e->next_offset == ALIGN(e->next_offset)); 283 assert(e->target_offset == ALIGN(e->target_offset)); 284 assert(t->target.u.target_size == ALIGN(t->target.u.target_size)); 285 assert(!TC_IS_CHAIN(t->target.u.user.name, h)); 286 287 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) { 288 assert(t->target.u.target_size 289 == ALIGN(sizeof(STRUCT_STANDARD_TARGET))); 290 291 assert(t->verdict == -NF_DROP-1 292 || t->verdict == -NF_ACCEPT-1 293 || t->verdict == RETURN 294 || t->verdict < (int)h->entries.size); 295 296 if (t->verdict >= 0) { 297 STRUCT_ENTRY *te = get_entry(h, t->verdict); 298 int idx; 299 300 idx = entry2index(h, te); 301 assert(strcmp(GET_TARGET(te)->u.user.name, 302 IPT_ERROR_TARGET) 303 != 0); 304 assert(te != e); 305 306 /* Prior node must be error node, or this node. */ 307 assert(t->verdict == entry2offset(h, e)+e->next_offset 308 || strcmp(GET_TARGET(index2entry(h, idx-1)) 309 ->u.user.name, IPT_ERROR_TARGET) 310 == 0); 311 } 312 313 if (t->verdict == RETURN 314 && unconditional(&e->ip) 315 && e->target_offset == sizeof(*e)) 316 *was_return = 1; 317 else 318 *was_return = 0; 319 } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) { 320 assert(t->target.u.target_size 321 == ALIGN(sizeof(struct ipt_error_target))); 322 323 /* If this is in user area, previous must have been return */ 324 if (*off > user_offset) 325 assert(*was_return); 326 327 *was_return = 0; 328 } 329 else *was_return = 0; 330 331 if (*off == user_offset) 332 assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0); 333 334 (*off) += e->next_offset; 335 (*i)++; 336 return 0; 337} 338 339#ifdef IPTC_DEBUG 340/* Do every conceivable sanity check on the handle */ 341static void 342do_check(TC_HANDLE_T h, unsigned int line) 343{ 344 unsigned int i, n; 345 unsigned int user_offset; /* Offset of first user chain */ 346 int was_return; 347 348 assert(h->changed == 0 || h->changed == 1); 349 if (strcmp(h->info.name, "filter") == 0) { 350 assert(h->info.valid_hooks 351 == (1 << NF_IP_LOCAL_IN 352 | 1 << NF_IP_FORWARD 353 | 1 << NF_IP_LOCAL_OUT)); 354 355 /* Hooks should be first three */ 356 assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0); 357 358 n = get_chain_end(h, 0); 359 n += get_entry(h, n)->next_offset; 360 assert(h->info.hook_entry[NF_IP_FORWARD] == n); 361 362 n = get_chain_end(h, n); 363 n += get_entry(h, n)->next_offset; 364 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 365 366 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 367 } else if (strcmp(h->info.name, "nat") == 0) { 368 assert((h->info.valid_hooks 369 == (1 << NF_IP_PRE_ROUTING 370 | 1 << NF_IP_POST_ROUTING 371 | 1 << NF_IP_LOCAL_OUT)) || 372 (h->info.valid_hooks 373 == (1 << NF_IP_PRE_ROUTING 374 | 1 << NF_IP_LOCAL_IN 375 | 1 << NF_IP_POST_ROUTING 376 | 1 << NF_IP_LOCAL_OUT))); 377 378 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); 379 380 n = get_chain_end(h, 0); 381 382 n += get_entry(h, n)->next_offset; 383 assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n); 384 n = get_chain_end(h, n); 385 386 n += get_entry(h, n)->next_offset; 387 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 388 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 389 390 if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) { 391 n = get_chain_end(h, n); 392 n += get_entry(h, n)->next_offset; 393 assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n); 394 user_offset = h->info.hook_entry[NF_IP_LOCAL_IN]; 395 } 396 397 } else if (strcmp(h->info.name, "mangle") == 0) { 398 /* This code is getting ugly because linux < 2.4.18-pre6 had 399 * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks 400 * */ 401 assert((h->info.valid_hooks 402 == (1 << NF_IP_PRE_ROUTING 403 | 1 << NF_IP_LOCAL_OUT)) || 404 (h->info.valid_hooks 405 == (1 << NF_IP_PRE_ROUTING 406 | 1 << NF_IP_LOCAL_IN 407 | 1 << NF_IP_FORWARD 408 | 1 << NF_IP_LOCAL_OUT 409 | 1 << NF_IP_POST_ROUTING))); 410 411 /* Hooks should be first five */ 412 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); 413 414 n = get_chain_end(h, 0); 415 416 if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) { 417 n += get_entry(h, n)->next_offset; 418 assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n); 419 n = get_chain_end(h, n); 420 } 421 422 if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) { 423 n += get_entry(h, n)->next_offset; 424 assert(h->info.hook_entry[NF_IP_FORWARD] == n); 425 n = get_chain_end(h, n); 426 } 427 428 n += get_entry(h, n)->next_offset; 429 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); 430 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; 431 432 if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) { 433 n = get_chain_end(h, n); 434 n += get_entry(h, n)->next_offset; 435 assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n); 436 user_offset = h->info.hook_entry[NF_IP_POST_ROUTING]; 437 } 438 439#ifdef NF_IP_DROPPING 440 } else if (strcmp(h->info.name, "drop") == 0) { 441 assert(h->info.valid_hooks == (1 << NF_IP_DROPPING)); 442 443 /* Hook should be first */ 444 assert(h->info.hook_entry[NF_IP_DROPPING] == 0); 445 user_offset = 0; 446#endif 447 } else { 448 fprintf(stderr, "Unknown table `%s'\n", h->info.name); 449 abort(); 450 } 451 452 /* User chain == end of last builtin + policy entry */ 453 user_offset = get_chain_end(h, user_offset); 454 user_offset += get_entry(h, user_offset)->next_offset; 455 456 /* Overflows should be end of entry chains, and unconditional 457 policy nodes. */ 458 for (i = 0; i < NUMHOOKS; i++) { 459 STRUCT_ENTRY *e; 460 STRUCT_STANDARD_TARGET *t; 461 462 if (!(h->info.valid_hooks & (1 << i))) 463 continue; 464 assert(h->info.underflow[i] 465 == get_chain_end(h, h->info.hook_entry[i])); 466 467 e = get_entry(h, get_chain_end(h, h->info.hook_entry[i])); 468 assert(unconditional(&e->ip)); 469 assert(e->target_offset == sizeof(*e)); 470 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); 471 assert(t->target.u.target_size == ALIGN(sizeof(*t))); 472 assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t))); 473 474 assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0); 475 assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1); 476 477 /* Hooks and underflows must be valid entries */ 478 entry2index(h, get_entry(h, h->info.hook_entry[i])); 479 entry2index(h, get_entry(h, h->info.underflow[i])); 480 } 481 482 assert(h->info.size 483 >= h->info.num_entries * (sizeof(STRUCT_ENTRY) 484 +sizeof(STRUCT_STANDARD_TARGET))); 485 486 assert(h->entries.size 487 >= (h->new_number 488 * (sizeof(STRUCT_ENTRY) 489 + sizeof(STRUCT_STANDARD_TARGET)))); 490 assert(strcmp(h->info.name, h->entries.name) == 0); 491 492 i = 0; n = 0; 493 was_return = 0; 494 /* Check all the entries. */ 495 ENTRY_ITERATE(h->entries.entrytable, h->entries.size, 496 check_entry, &i, &n, user_offset, &was_return, h); 497 498 assert(i == h->new_number); 499 assert(n == h->entries.size); 500 501 /* Final entry must be error node */ 502 assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1)) 503 ->u.user.name, 504 ERROR_TARGET) == 0); 505} 506#endif /*IPTC_DEBUG*/ 507