1/* 2 * Network configuration layer (Linux) 3 * 4 * Copyright (C) 2014, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: netconf_linux.c 358181 2012-09-21 13:59:23Z $ 19 */ 20 21#include <stdio.h> 22#include <stdlib.h> 23#include <ctype.h> 24#include <errno.h> 25#include <error.h> 26#include <string.h> 27#include <time.h> 28#include <unistd.h> 29#include <syslog.h> 30#include <sys/ioctl.h> 31#include <sys/types.h> 32#include <sys/socket.h> 33#include <net/if.h> 34#include <netinet/in.h> 35#include <arpa/inet.h> 36#include <net/if_arp.h> 37 38#include <typedefs.h> 39#include <proto/ethernet.h> 40#include <netconf.h> 41#include <netconf_linux.h> 42 43#ifndef LINUX_2_6_36 44typedef struct ipt_time_info time_info_t; 45#define TIME_INFO_EXTRA_BYTES 8 46#else 47typedef struct xt_time_info time_info_t; 48#define TIME_INFO_EXTRA_BYTES 0 49#define IPT_ALIGN XT_ALIGN 50#endif 51 52/* Loops over each match in the ipt_entry */ 53#define for_each_ipt_match(match, entry) \ 54 for ((match) = (struct ipt_entry_match *) &(entry)->elems[0]; \ 55 (int) (match) < (int) (entry) + (entry)->target_offset; \ 56 (match) = (struct ipt_entry_match *) ((int) (match) + (match)->u.match_size)) 57 58/* Supported ipt table names */ 59static const char *ipt_table_names[] = { "filter", "nat", NULL }; 60 61/* ipt table name appropriate for target (indexed by netconf_fw_t.target) */ 62static const char * ipt_table_name[] = { 63 "filter", "filter", "filter", "filter", 64 "nat", "nat", "nat", "nat" 65}; 66 67/* ipt target name (indexed by netconf_fw_t.target) */ 68static const char * ipt_target_name[] = { 69 "DROP", "ACCEPT", "logdrop", "logaccept", 70 "SNAT", "DNAT", "MASQUERADE", "autofw" 71}; 72 73/* ipt target data size (indexed by netconf_fw_t.target) */ 74#ifndef LINUX_2_6_36 75static const size_t ipt_target_size[] = { 76 sizeof(int), sizeof(int), sizeof(int), sizeof(int), 77 sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_autofw_info) 78}; 79#else /* linux-2.6.36 */ 80/* ipt target data size (indexed by netconf_fw_t.target) */ 81static const size_t ipt_target_size[] = { 82 sizeof(int), sizeof(int), sizeof(int), sizeof(int), 83 sizeof(struct nf_nat_multi_range), sizeof(struct nf_nat_multi_range), sizeof(struct nf_nat_multi_range), sizeof(struct ip_autofw_info) 84}; 85#endif /* linux-2.6.36 */ 86 87/* ipt filter chain name appropriate for direction (indexed by netconf_filter_t.dir) */ 88static const char * ipt_filter_chain_name[] = { 89 "INPUT", "FORWARD", "OUTPUT" 90}; 91 92/* ipt nat chain name appropriate for target (indexed by netconf_nat_t.target) */ 93static const char * ipt_nat_chain_name[] = { 94 NULL, NULL, NULL, NULL, 95 "POSTROUTING", "PREROUTING", "POSTROUTING" 96}; 97 98/* Returns a netconf_dir index */ 99static int 100filter_dir(const char *name) 101{ 102 if (strncmp(name, "INPUT", IPT_FUNCTION_MAXNAMELEN) == 0) 103 return NETCONF_IN; 104 else if (strncmp(name, "FORWARD", IPT_FUNCTION_MAXNAMELEN) == 0) 105 return NETCONF_FORWARD; 106 else if (strncmp(name, "OUTPUT", IPT_FUNCTION_MAXNAMELEN) == 0) 107 return NETCONF_OUT; 108 else 109 return -1; 110} 111 112/* Returns a netconf_target index */ 113static int 114#ifndef LINUX_2_6_36 115target_num(const struct ipt_entry *entry, iptc_handle_t *handle) 116#else /* linux-2.6.36 */ 117target_num(const struct ipt_entry *entry, struct iptc_handle *handle) 118#endif /* linux-2.6.36 */ 119{ 120 const char *name = iptc_get_target(entry, handle); 121 122 if (!name) 123 return -1; 124 125 if (strncmp(name, "DROP", IPT_FUNCTION_MAXNAMELEN) == 0) 126 return NETCONF_DROP; 127 else if (strncmp(name, "ACCEPT", IPT_FUNCTION_MAXNAMELEN) == 0) 128 return NETCONF_ACCEPT; 129 else if (strncmp(name, "logdrop", IPT_FUNCTION_MAXNAMELEN) == 0) 130 return NETCONF_LOG_DROP; 131 else if (strncmp(name, "logaccept", IPT_FUNCTION_MAXNAMELEN) == 0) 132 return NETCONF_LOG_ACCEPT; 133 else if (strncmp(name, "SNAT", IPT_FUNCTION_MAXNAMELEN) == 0) 134 return NETCONF_SNAT; 135 else if (strncmp(name, "DNAT", IPT_FUNCTION_MAXNAMELEN) == 0) 136 return NETCONF_DNAT; 137 else if (strncmp(name, "MASQUERADE", IPT_FUNCTION_MAXNAMELEN) == 0) 138 return NETCONF_MASQ; 139 else if (strncmp(name, "autofw", IPT_FUNCTION_MAXNAMELEN) == 0) 140 return NETCONF_APP; 141 else 142 return -1; 143} 144 145#ifdef LINUX_2_6_36 146/* User: match.day, SUN/0~MON/1 ~ SAT/6 */ 147/* Kernel: MON/1 ~ SAT/6~SUN/7 */ 148/* Need special handle for Sunday */ 149/* Be aware: we steal the flags bit 1 ~ 7 to store the begin day */ 150static void 151get_days(unsigned int *days, time_info_t *time) 152{ 153 int i, j; 154 char weekdays_map[7] = {0}; 155 156 /* Translate from Kernel to User */ 157 for (i = 1; i <= 7; i++) { 158 if (time->weekdays_match & (1 << i)) { 159 if (i == 7) 160 weekdays_map[0] = 1; 161 else 162 weekdays_map[i] = 1; 163 } 164 } 165 166 /* Begin day */ 167 for (i = 1; i <= 7; i++) { 168 if (time->flags & (1 << i)) { 169 if (i == 7) 170 days[0] = 0; 171 else 172 days[0] = i; 173 break; 174 } 175 } 176 177 /* End day */ 178 for (i = days[0], j = 0; j < 7; i = (i + 1) % 7, j++) { 179 if (weekdays_map[i]) 180 days[1] = i; 181 else 182 break; 183 } 184} 185 186/* User: match.day, SUN/0~MON/1 ~ SAT/6 */ 187/* Kernel: MON/1 ~ SAT/6~SUN/7 */ 188/* Need special handle for Sunday */ 189/* Be aware: we steal the flags bit 1 ~ 7 to store the begin day */ 190static void 191set_days(unsigned int *days, time_info_t *time) 192{ 193 int i; 194 195 for (i = days[0]; i != days[1]; i = (i + 1) % 7) { 196 if (!i) 197 time->weekdays_match |= (1 << 7); 198 else 199 time->weekdays_match |= (1 << i); 200 } 201 202 if (!days[1]) 203 time->weekdays_match |= (1 << 7); 204 else 205 time->weekdays_match |= (1 << days[1]); 206 207 /* Use local time */ 208 time->flags = XT_TIME_LOCAL_TZ; 209 210 /* Steal flags to store the begin day */ 211 if (days[0] == 0) 212 time->flags |= (1 << 7); 213 else 214 time->flags |= (1 << days[0]); 215} 216#endif /* LINUX_2_6_36 */ 217 218/* 219 * Get a list of the current firewall entries 220 * @param fw_list list of firewall entries 221 * @return 0 on success and errno on failure 222 */ 223int 224netconf_get_fw(netconf_fw_t *fw_list) 225{ 226 const char **table; 227 const char *chain; 228 const struct ipt_entry *entry; 229#ifndef LINUX_2_6_36 230 iptc_handle_t handle = NULL; 231#else /* linux-2.6.36 */ 232 struct iptc_handle *handle = NULL; 233#endif /* linux-2.6.36 */ 234 235 /* Initialize list */ 236 netconf_list_init(fw_list); 237 238 /* Search all default tables */ 239 for (table = &ipt_table_names[0]; *table; table++) { 240 241 if (strcmp(*table, "filter") && strcmp(*table, "nat")) 242 continue; 243 244 if (!(handle = iptc_init(*table))) { 245 fprintf(stderr, "%s\n", iptc_strerror(errno)); 246 goto err; 247 } 248 249 /* Search all default chains */ 250#ifndef LINUX_2_6_36 251 for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) { 252#else /* linux-2.6.36 */ 253 for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) { 254#endif /* linux-2.6.36 */ 255 if (strcmp(chain, "INPUT") && strcmp(chain, "FORWARD") && strcmp(chain, "OUTPUT") && 256 strcmp(chain, "PREROUTING") && strcmp(chain, "POSTROUTING")) 257 continue; 258 259 /* Search all entries */ 260#ifndef LINUX_2_6_36 261 for (entry = iptc_first_rule(chain, &handle); entry; entry = iptc_next_rule(entry, &handle)) { 262 int num = target_num(entry, &handle); 263#else /* linux-2.6.36 */ 264 for (entry = iptc_first_rule(chain, handle); entry; entry = iptc_next_rule(entry, handle)) { 265 int num = target_num(entry, handle); 266#endif /* linux-2.6.36 */ 267 netconf_fw_t *fw = NULL; 268 netconf_filter_t *filter = NULL; 269 netconf_nat_t *nat = NULL; 270 netconf_app_t *app = NULL; 271 272 const struct ipt_entry_match *match; 273 const struct ipt_entry_target *target; 274 struct ipt_mac_info *mac = NULL; 275 struct ipt_state_info *state = NULL; 276 time_info_t *time = NULL; 277 278 /* Only know about TCP/UDP */ 279 if (!netconf_valid_ipproto(entry->ip.proto)) 280 continue; 281 282 /* Only know about target types in the specified tables */ 283 if (!netconf_valid_target(num) || 284 strncmp(ipt_table_name[num], *table, IPT_FUNCTION_MAXNAMELEN) != 0) 285 continue; 286 287 /* Only know about specified target types */ 288 if (netconf_valid_filter(num)) { 289 filter = calloc(1, sizeof(netconf_filter_t)); 290 fw = (netconf_fw_t *) filter; 291 } 292 else if (netconf_valid_nat(num)) { 293 nat = calloc(1, sizeof(netconf_nat_t)); 294 fw = (netconf_fw_t *) nat; 295 } 296 else if (num == NETCONF_APP) { 297 app = calloc(1, sizeof(netconf_app_t)); 298 fw = (netconf_fw_t *) app; 299 } 300 else 301 continue; 302 303 if (!fw) { 304 perror("calloc"); 305 goto err; 306 } 307 netconf_list_add(fw, fw_list); 308 309 /* Get IP addresses */ 310 fw->match.src.ipaddr.s_addr = entry->ip.src.s_addr; 311 fw->match.src.netmask.s_addr = entry->ip.smsk.s_addr; 312 fw->match.dst.ipaddr.s_addr = entry->ip.dst.s_addr; 313 fw->match.dst.netmask.s_addr = entry->ip.dmsk.s_addr; 314 fw->match.flags |= (entry->ip.invflags & IPT_INV_SRCIP) ? NETCONF_INV_SRCIP : 0; 315 fw->match.flags |= (entry->ip.invflags & IPT_INV_DSTIP) ? NETCONF_INV_DSTIP : 0; 316 317 /* Get interface names */ 318 strncpy(fw->match.in.name, entry->ip.iniface, IFNAMSIZ); 319 strncpy(fw->match.out.name, entry->ip.outiface, IFNAMSIZ); 320 fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_IN) ? NETCONF_INV_IN : 0; 321 fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_OUT) ? NETCONF_INV_OUT : 0; 322 323 /* Get TCP port(s) */ 324 if (entry->ip.proto == IPPROTO_TCP) { 325 struct ipt_tcp *tcp = NULL; 326 327 for_each_ipt_match(match, entry) { 328 if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0) 329 continue; 330 331 tcp = (struct ipt_tcp *) &match->data[0]; 332 break; 333 } 334 335 if (tcp) { 336 /* Match ports stored in host order for some stupid reason */ 337 fw->match.ipproto = IPPROTO_TCP; 338 fw->match.src.ports[0] = htons(tcp->spts[0]); 339 fw->match.src.ports[1] = htons(tcp->spts[1]); 340 fw->match.dst.ports[0] = htons(tcp->dpts[0]); 341 fw->match.dst.ports[1] = htons(tcp->dpts[1]); 342 fw->match.flags |= (tcp->invflags & IPT_TCP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0; 343 fw->match.flags |= (tcp->invflags & IPT_TCP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0; 344 } 345 } 346 347 /* Get UDP port(s) */ 348 else if (entry->ip.proto == IPPROTO_UDP) { 349 struct ipt_udp *udp = NULL; 350 351 for_each_ipt_match(match, entry) { 352 if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0) 353 continue; 354 355 udp = (struct ipt_udp *) &match->data[0]; 356 break; 357 } 358 359 if (udp) { 360 /* Match ports stored in host order for some stupid reason */ 361 fw->match.ipproto = IPPROTO_UDP; 362 fw->match.src.ports[0] = htons(udp->spts[0]); 363 fw->match.src.ports[1] = htons(udp->spts[1]); 364 fw->match.dst.ports[0] = htons(udp->dpts[0]); 365 fw->match.dst.ports[1] = htons(udp->dpts[1]); 366 fw->match.flags |= (udp->invflags & IPT_UDP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0; 367 fw->match.flags |= (udp->invflags & IPT_UDP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0; 368 } 369 } 370 371 /* Get source MAC address */ 372 for_each_ipt_match(match, entry) { 373 if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0) 374 continue; 375 376 mac = (struct ipt_mac_info *) &match->data[0]; 377 break; 378 } 379 if (mac) { 380 memcpy(fw->match.mac.octet, mac->srcaddr, ETHER_ADDR_LEN); 381 fw->match.flags |= mac->invert ? NETCONF_INV_MAC : 0; 382 } 383 384 /* Get packet state */ 385 for_each_ipt_match(match, entry) { 386 if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0) 387 continue; 388 389 state = (struct ipt_state_info *) &match->data[0]; 390 break; 391 } 392 if (state) { 393 fw->match.state |= (state->statemask & IPT_STATE_INVALID) ? NETCONF_INVALID : 0; 394 fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) ? NETCONF_ESTABLISHED : 0; 395 fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_RELATED)) ? NETCONF_RELATED : 0; 396 fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_NEW)) ? NETCONF_NEW : 0; 397 } 398 399 /* Get local time */ 400 for_each_ipt_match(match, entry) { 401 if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0) 402 continue; 403 404 /* We added 8 bytes of day range at the end */ 405 if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) + 406 IPT_ALIGN(sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES))) 407 continue; 408 409 time = (time_info_t *) &match->data[0]; 410 break; 411 } 412 if (time) { 413#ifndef LINUX_2_6_36 414 unsigned int *days = (unsigned int *) &time[1]; 415 416 fw->match.days[0] = days[0]; 417 fw->match.days[1] = days[1]; 418 fw->match.secs[0] = time->time_start; 419 fw->match.secs[1] = time->time_stop; 420#else 421 /* Get days from weekdays_match and flags */ 422 get_days(fw->match.days, time); 423 fw->match.secs[0] = time->daytime_start; 424 fw->match.secs[1] = time->daytime_stop; 425#endif 426 } 427 428 /* Set target type */ 429 fw->target = num; 430 target = (struct ipt_entry_target *) ((int) entry + entry->target_offset); 431 432 /* Get filter target information */ 433 if (filter) { 434 if (!netconf_valid_dir(filter->dir = filter_dir(chain))) 435 goto err; 436 } 437 438 /* Get NAT target information */ 439 else if (nat) { 440#ifndef LINUX_2_6_36 441 struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0]; 442 struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0]; 443#else /* linux-2.6.36 */ 444 struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0]; 445 struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0]; 446 447#endif /* linux-2.6.36 */ 448 /* Get mapped IP address */ 449 nat->ipaddr.s_addr = range->min_ip; 450 451 /* Get mapped TCP port(s) */ 452 if (entry->ip.proto == IPPROTO_TCP) { 453 nat->ports[0] = range->min.tcp.port; 454 nat->ports[1] = range->max.tcp.port; 455 } 456 457 /* Get mapped UDP port(s) */ 458 else if (entry->ip.proto == IPPROTO_UDP) { 459 nat->ports[0] = range->min.udp.port; 460 nat->ports[1] = range->max.udp.port; 461 } 462 } 463 464 /* Get application specific port forward information */ 465 else if (app) { 466 struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0]; 467 468 app->proto = info->proto; 469 app->dport[0] = info->dport[0]; 470 app->dport[1] = info->dport[1]; 471 app->to[0] = info->to[0]; 472 app->to[1] = info->to[1]; 473 } 474 } 475 } 476 477#ifndef LINUX_2_6_36 478 if (!iptc_commit(&handle)) { 479#else /* linux-2.6.36 */ 480 if (!iptc_commit(handle)) { 481#endif /* linux-2.6.36 */ 482#ifdef LINUX_2_6_36 483 iptc_free(handle); 484#endif 485 fprintf(stderr, "%s\n", iptc_strerror(errno)); 486 handle = NULL; 487 goto err; 488 } 489 } 490 491#ifdef LINUX_2_6_36 492 iptc_free(handle); 493#endif 494 return 0; 495 496 err: 497 if (handle) 498#ifndef LINUX_2_6_36 499 iptc_commit(&handle); 500#else /* linux-2.6.36 */ 501 iptc_commit(handle); 502#endif /* linux-2.6.36 */ 503#ifdef LINUX_2_6_36 504 if (handle) 505 iptc_free(handle); 506#endif 507 netconf_list_free(fw_list); 508 return errno; 509} 510 511/* Logical XOR */ 512#define lxor(a, b) (((a) && !(b)) || (!(a) && (b))) 513 514/* 515 * Get the index of a firewall entry 516 * @param fw firewall entry to look for 517 * @return index of firewall entry or <0 if not found or an error occurred 518 */ 519static int 520netconf_fw_index(const netconf_fw_t *fw) 521{ 522 const netconf_filter_t *filter = NULL; 523 const netconf_nat_t *nat = NULL; 524 const netconf_app_t *app = NULL; 525 const char **table; 526 const char *chain; 527 const struct ipt_entry *entry = NULL; 528#ifndef LINUX_2_6_36 529 iptc_handle_t handle = NULL; 530#else /* linux-2.6.36 */ 531 struct iptc_handle *handle = NULL; 532#endif /* linux-2.6.36 */ 533 534 int ret = 0; 535 536 if (!netconf_valid_ipproto(fw->match.ipproto)) { 537 fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto); 538 return -EINVAL; 539 } 540 541 /* Only know about specified target types */ 542 if (netconf_valid_filter(fw->target)) { 543 filter = (netconf_filter_t *) fw; 544 if (!netconf_valid_dir(filter->dir)) { 545 fprintf(stderr, "invalid filter direction %d\n", filter->dir); 546 return -EINVAL; 547 } 548 } 549 else if (netconf_valid_nat(fw->target)) 550 nat = (netconf_nat_t *) fw; 551 else if (fw->target == NETCONF_APP) 552 app = (netconf_app_t *) fw; 553 else { 554 fprintf(stderr, "invalid target type %d\n", fw->target); 555 return -EINVAL; 556 } 557 558 /* Search all default tables */ 559 for (table = &ipt_table_names[0]; *table; table++) { 560 561 /* Only consider specified tables */ 562 if (strncmp(ipt_table_name[fw->target], *table, IPT_FUNCTION_MAXNAMELEN) != 0) 563 continue; 564 565 if (!(handle = iptc_init(*table))) { 566 fprintf(stderr, "%s\n", iptc_strerror(errno)); 567 return -errno; 568 } 569 570 /* Search all default chains */ 571#ifndef LINUX_2_6_36 572 for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) { 573#else /* linux-2.6.36 */ 574 for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) { 575#endif /* linux-2.6.36 */ 576 577 /* Only consider specified chains */ 578 if (filter && strncmp(chain, ipt_filter_chain_name[filter->dir], sizeof(ipt_chainlabel)) != 0) 579 continue; 580 else if (nat && strncmp(chain, ipt_nat_chain_name[nat->target], sizeof(ipt_chainlabel)) != 0) 581 continue; 582 else if (app && strncmp(chain, "PREROUTING", sizeof(ipt_chainlabel)) != 0) 583 continue; 584 585 /* Search all entries */ 586#ifndef LINUX_2_6_36 587 for (ret = 0, entry = iptc_first_rule(chain, &handle); entry; ret++, entry = iptc_next_rule(entry, &handle)) { 588#else /* linux-2.6.36 */ 589 for (ret = 0, entry = iptc_first_rule(chain, handle); entry; ret++, entry = iptc_next_rule(entry, handle)) { 590#endif /* linux-2.6.36 */ 591 const struct ipt_entry_match *match; 592 const struct ipt_entry_target *target; 593 struct ipt_mac_info *mac = NULL; 594 struct ipt_state_info *state = NULL; 595 time_info_t *time = NULL; 596 597 /* Only know about TCP/UDP */ 598 if (entry->ip.proto != fw->match.ipproto) 599 continue; 600 601 /* Compare IP address(es) */ 602 if (entry->ip.src.s_addr != fw->match.src.ipaddr.s_addr || 603 entry->ip.smsk.s_addr != fw->match.src.netmask.s_addr || 604 entry->ip.dst.s_addr != fw->match.dst.ipaddr.s_addr || 605 entry->ip.dmsk.s_addr != fw->match.dst.netmask.s_addr) 606 continue; 607 608 if (lxor(entry->ip.invflags & IPT_INV_SRCIP, fw->match.flags & NETCONF_INV_SRCIP) || 609 lxor(entry->ip.invflags & IPT_INV_DSTIP, fw->match.flags & NETCONF_INV_DSTIP)) 610 continue; 611 612 /* Compare interface names */ 613 if (strncmp(fw->match.in.name, entry->ip.iniface, IFNAMSIZ) != 0 || 614 strncmp(fw->match.out.name, entry->ip.outiface, IFNAMSIZ) != 0) 615 continue; 616 617 /* Compare TCP port(s) */ 618 if (fw->match.ipproto == IPPROTO_TCP) { 619 struct ipt_tcp *tcp = NULL; 620 621 for_each_ipt_match(match, entry) { 622 if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0) 623 continue; 624 625 tcp = (struct ipt_tcp *) &match->data[0]; 626 break; 627 } 628 629 /* Match ports stored in host order for some stupid reason */ 630 if (!tcp || 631 tcp->spts[0] != ntohs(fw->match.src.ports[0]) || 632 tcp->spts[1] != ntohs(fw->match.src.ports[1]) || 633 tcp->dpts[0] != ntohs(fw->match.dst.ports[0]) || 634 tcp->dpts[1] != ntohs(fw->match.dst.ports[1])) 635 continue; 636 637 if (lxor(tcp->invflags & IPT_TCP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) || 638 lxor(tcp->invflags & IPT_TCP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT)) 639 continue; 640 } 641 642 /* Compare UDP port(s) */ 643 else if (fw->match.ipproto == IPPROTO_UDP) { 644 struct ipt_udp *udp = NULL; 645 646 for_each_ipt_match(match, entry) { 647 if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0) 648 continue; 649 650 udp = (struct ipt_udp *) &match->data[0]; 651 break; 652 } 653 654 /* Match ports stored in host order for some stupid reason */ 655 if (!udp || 656 udp->spts[0] != ntohs(fw->match.src.ports[0]) || 657 udp->spts[1] != ntohs(fw->match.src.ports[1]) || 658 udp->dpts[0] != ntohs(fw->match.dst.ports[0]) || 659 udp->dpts[1] != ntohs(fw->match.dst.ports[1])) 660 continue; 661 662 if (lxor(udp->invflags & IPT_UDP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) || 663 lxor(udp->invflags & IPT_UDP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT)) 664 continue; 665 } 666 667 /* Compare source MAC addresses */ 668 if (!ETHER_ISNULLADDR(fw->match.mac.octet)) { 669 for_each_ipt_match(match, entry) { 670 if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0) 671 continue; 672 673 mac = (struct ipt_mac_info *) &match->data[0]; 674 break; 675 } 676 677 if (!mac || 678 memcmp(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN) != 0 || 679 ( mac->invert && !(fw->match.flags & NETCONF_INV_MAC)) || 680 (!mac->invert && (fw->match.flags & NETCONF_INV_MAC))) 681 continue; 682 } 683 684 /* Compare packet states */ 685 if (fw->match.state) { 686 for_each_ipt_match(match, entry) { 687 if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0) 688 continue; 689 690 state = (struct ipt_state_info *) &match->data[0]; 691 break; 692 } 693 694 if (!state || 695 lxor(state->statemask & IPT_STATE_INVALID, fw->match.state & NETCONF_INVALID) || 696 lxor(state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED), fw->match.state & NETCONF_ESTABLISHED) || 697 lxor(state->statemask & IPT_STATE_BIT(IP_CT_RELATED), fw->match.state & NETCONF_RELATED) || 698 lxor(state->statemask & IPT_STATE_BIT(IP_CT_NEW), fw->match.state & NETCONF_NEW)) 699 continue; 700 } 701 702 /* Compare local time */ 703 if (fw->match.secs[0] || fw->match.secs[1]) { 704 for_each_ipt_match(match, entry) { 705 if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0) 706 continue; 707 708 /* We added 8 bytes of day range at the end */ 709 if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) + 710 IPT_ALIGN(sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES))) 711 continue; 712 713 time = (time_info_t *) &match->data[0]; 714 break; 715 } 716 717 if (!time) 718 continue; 719 else { 720 unsigned int time_start, time_stop; 721#ifndef LINUX_2_6_36 722 unsigned int *days = (unsigned int *) &time[1]; 723 724 time_start = time->time_start; 725 time_stop = time->time_stop; 726#else 727 unsigned int days[2]; 728 729 time_start = time->daytime_start; 730 time_stop = time->daytime_stop; 731 get_days(days, time); 732#endif 733 if (fw->match.days[0] != days[0] || 734 fw->match.days[1] != days[1] || 735 fw->match.secs[0] != time_start || 736 fw->match.secs[1] != time_stop) 737 continue; 738 } 739 } 740 741 /* Compare target type */ 742#ifndef LINUX_2_6_36 743 if (fw->target != target_num(entry, &handle)) 744#else 745 if (fw->target != target_num(entry, handle)) 746#endif /* linux-2.6.36 */ 747 continue; 748 target = (struct ipt_entry_target *) ((int) entry + entry->target_offset); 749 750 /* Compare NAT target information */ 751 if (nat) { 752#ifndef LINUX_2_6_36 753 struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0]; 754 struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0]; 755#else 756 struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0]; 757 struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0]; 758#endif /* linux-2.6.36 */ 759 760 /* Compare mapped IP address */ 761 if (range->min_ip != nat->ipaddr.s_addr) 762 continue; 763 764 /* Compare mapped TCP port(s) */ 765 if (fw->match.ipproto == IPPROTO_TCP) { 766 if (range->min.tcp.port != nat->ports[0] || 767 range->max.tcp.port != nat->ports[1]) 768 continue; 769 } 770 771 /* Compare mapped UDP port(s) */ 772 else if (fw->match.ipproto == IPPROTO_UDP) { 773 if (range->min.udp.port != nat->ports[0] || 774 range->max.udp.port != nat->ports[1]) 775 continue; 776 } 777 } 778 779 /* Compare application specific port forward information */ 780 else if (app) { 781 struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0]; 782 783 if (app->proto != info->proto || 784 app->dport[0] != info->dport[0] || 785 app->dport[1] != info->dport[1] || 786 app->to[0] != info->to[0] || 787 app->to[1] != info->to[1]) 788 continue; 789 } 790 791 break; 792 } 793 794 if (entry) 795 break; 796 } 797 798#ifndef LINUX_2_6_36 799 if (!iptc_commit(&handle)) { 800#else 801 if (!iptc_commit(handle)) { 802#endif /* linux-2.6.36 */ 803#ifdef LINUX_2_6_36 804 iptc_free(handle); 805#endif 806 fprintf(stderr, "%s\n", iptc_strerror(errno)); 807 return -errno; 808 } 809 810#ifdef LINUX_2_6_36 811 iptc_free(handle); 812#endif 813 if (entry) 814 break; 815 } 816 817 return (entry ? ret : -ENOENT); 818} 819 820/* 821 * See if a given firewall entry already exists 822 * @param nat NAT entry to look for 823 * @return whether NAT entry exists 824 */ 825int 826netconf_fw_exists(netconf_fw_t *fw) 827{ 828 return (netconf_fw_index(fw) >= 0); 829} 830 831/* 832 * Allocate and append a match structure to an existing ipt_entry 833 * @param pentry pointer to pointer to initialized ipt_entry 834 * @param name name of match 835 * @param match_data_size size of data portion of match structure 836 * @return pointer to newly created match header inside ipt_entry 837 */ 838static struct ipt_entry_match * 839netconf_append_match(struct ipt_entry **pentry, const char *name, size_t match_data_size) 840{ 841 struct ipt_entry *entry; 842 struct ipt_entry_match *match; 843 size_t match_size = 0; 844 845 match_size += IPT_ALIGN(sizeof(struct ipt_entry_match)); 846 match_size += IPT_ALIGN(match_data_size); 847 848 if (!(entry = realloc(*pentry, (*pentry)->next_offset + match_size))) { 849 perror("realloc"); 850 return NULL; 851 } 852 853 match = (struct ipt_entry_match *) ((int) entry + entry->next_offset); 854 entry->next_offset += match_size; 855 entry->target_offset += match_size; 856 memset(match, 0, match_size); 857 858 strncpy(match->u.user.name, name, IPT_FUNCTION_MAXNAMELEN); 859 match->u.match_size = match_size; 860 861 *pentry = entry; 862 return match; 863} 864 865/* 866 * Allocate and append a target structure to an existing ipt_entry 867 * @param pentry pointer to pointer to initialized ipt_entry with matches 868 * @param name name of target 869 * @param target_data_size size of data portion of target structure 870 * @return pointer to newly created target header inside ipt_entry 871 */ 872static struct ipt_entry_target * 873netconf_append_target(struct ipt_entry **pentry, const char *name, size_t target_data_size) 874{ 875 struct ipt_entry *entry; 876 struct ipt_entry_target *target; 877 size_t target_size = 0; 878 879 target_size += IPT_ALIGN(sizeof(struct ipt_entry_target)); 880 target_size += IPT_ALIGN(target_data_size); 881 882 if (!(entry = realloc(*pentry, (*pentry)->next_offset + target_size))) { 883 perror("realloc"); 884 return NULL; 885 } 886 887 target = (struct ipt_entry_target *) ((int) entry + entry->next_offset); 888 entry->next_offset += target_size; 889 memset(target, 0, target_size); 890 891 strncpy(target->u.user.name, name, IPT_FUNCTION_MAXNAMELEN); 892 target->u.target_size = target_size; 893 894 *pentry = entry; 895 return target; 896} 897 898/* 899 * Insert an entry into a reasonable location in the chain 900 * @param chain chain name 901 * @param entry iptables entry 902 * @param handle table handle 903 * @return TRUE on success and 0 on failure 904 */ 905static int 906#ifdef LINUX26 907#ifndef LINUX_2_6_36 908insert_entry(const char *chain, const char *target_name, struct ipt_entry *entry, iptc_handle_t *handle) 909#else 910insert_entry(const char *chain, const char *target_name, struct ipt_entry *entry, struct iptc_handle *handle) 911#endif /* linux-2.6.36 */ 912#else /* LINUX26 */ 913insert_entry(const char *chain, struct ipt_entry *entry, iptc_handle_t *handle) 914#endif /* LINUX26 */ 915{ 916 int i; 917 struct ipt_ip blank; 918 const struct ipt_entry *rule; 919 struct ipt_entry_target *target; 920 921 target = (struct ipt_entry_target *) ((int) entry + entry->target_offset); 922 memset(&blank, 0, sizeof(struct ipt_ip)); 923 924 /* If this is a default policy (no match) insert at the end of the chain */ 925 if (entry->target_offset == sizeof(struct ipt_entry) && 926 !memcmp(&entry->ip, &blank, sizeof(struct ipt_ip))) 927 return iptc_append_entry(chain, entry, handle); 928 929 /* If dropping insert at the beginning of the chain */ 930#ifdef LINUX26 931 if (!strcmp(target_name, "DROP") || 932 !strcmp(target_name, "logdrop")) 933 return iptc_insert_entry(chain, entry, 0, handle); 934 /* If accepting insert after the last drop but before the first default policy */ 935 else if (!strcmp(target_name, "ACCEPT") || 936 !strcmp(target_name, "logaccept")) { 937#else /* LINUX26 */ 938 if (!strcmp(iptc_get_target(entry, handle), "DROP") || 939 !strcmp(iptc_get_target(entry, handle), "logdrop")) 940 return iptc_insert_entry(chain, entry, 0, handle); 941 /* If accepting insert after the last drop but before the first default policy */ 942 else if (!strcmp(iptc_get_target(entry, handle), "ACCEPT") || 943 !strcmp(iptc_get_target(entry, handle), "logaccept")) { 944#endif /* LINUX26 */ 945 for (i = 0, rule = iptc_first_rule(chain, handle); rule; 946 i++, rule = iptc_next_rule(rule, handle)) { 947 if ((strcmp(iptc_get_target(rule, handle), "DROP") && 948 strcmp(iptc_get_target(rule, handle), "logdrop")) || 949 (rule->target_offset == sizeof(struct ipt_entry) && 950 !memcmp(&rule->ip, &blank, sizeof(struct ipt_ip)))) 951 break; 952 } 953 return iptc_insert_entry(chain, entry, i, handle); 954 } 955 956 /* Otherwise insert at the end of the chain */ 957 else 958 return iptc_append_entry(chain, entry, handle); 959} 960 961/* 962 * Add a firewall entry 963 * @param fw firewall entry 964 * @return 0 on success and errno on failure 965 */ 966int 967netconf_add_fw(netconf_fw_t *fw) 968{ 969 netconf_filter_t *filter = NULL; 970 netconf_nat_t *nat = NULL; 971 netconf_app_t *app = NULL; 972 973 struct ipt_entry *entry; 974 struct ipt_entry_match *match; 975 struct ipt_entry_target *target; 976#ifndef LINUX_2_6_36 977 iptc_handle_t handle = NULL; 978#else 979 struct iptc_handle *handle = NULL; 980#endif 981 982 if (!netconf_valid_ipproto(fw->match.ipproto)) { 983 fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto); 984 return -EINVAL; 985 } 986 987 if (!netconf_valid_target(fw->target)) { 988 fprintf(stderr, "invalid target type %d\n", fw->target); 989 return EINVAL; 990 } 991 992 /* Only know about specified target types */ 993 if (netconf_valid_filter(fw->target)) 994 filter = (netconf_filter_t *) fw; 995 else if (netconf_valid_nat(fw->target)) 996 nat = (netconf_nat_t *) fw; 997 else if (fw->target == NETCONF_APP) 998 app = (netconf_app_t *) fw; 999 else 1000 return EINVAL; 1001 1002 /* Allocate entry */ 1003 if (!(entry = calloc(1, sizeof(struct ipt_entry)))) { 1004 perror("calloc"); 1005 return errno; 1006 } 1007 1008 /* Initialize entry parameters */ 1009 entry->nfcache |= NFC_UNKNOWN; 1010 entry->next_offset = entry->target_offset = sizeof(struct ipt_entry); 1011 1012 if (nat && (nat->type == NETCONF_CONE_NAT)) 1013 entry->nfcache |= NETCONF_CONE_NAT; 1014 1015 /* Match by IP address(es) */ 1016 if (fw->match.src.ipaddr.s_addr & fw->match.src.netmask.s_addr) { 1017 entry->ip.src.s_addr = fw->match.src.ipaddr.s_addr; 1018 entry->ip.smsk.s_addr = fw->match.src.netmask.s_addr; 1019 entry->nfcache |= NFC_IP_SRC; 1020 entry->ip.invflags |= (fw->match.flags & NETCONF_INV_SRCIP) ? IPT_INV_SRCIP : 0; 1021 } 1022 if (fw->match.dst.ipaddr.s_addr & fw->match.dst.netmask.s_addr) { 1023 entry->ip.dst.s_addr = fw->match.dst.ipaddr.s_addr; 1024 entry->ip.dmsk.s_addr = fw->match.dst.netmask.s_addr; 1025 entry->nfcache |= NFC_IP_DST; 1026 entry->ip.invflags |= (fw->match.flags & NETCONF_INV_DSTIP) ? IPT_INV_DSTIP : 0; 1027 } 1028 1029 /* Match by inbound or outbound interface name */ 1030 if (strlen(fw->match.in.name) > 0) { 1031 strncpy(entry->ip.iniface, fw->match.in.name, IFNAMSIZ); 1032 memset(&entry->ip.iniface_mask, 0, IFNAMSIZ); 1033 memset(&entry->ip.iniface_mask, 0xff, strlen(fw->match.in.name) + 1); 1034 entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_IN : 0; 1035 entry->nfcache |= NFC_IP_IF_IN; 1036 } 1037 if (strlen(fw->match.out.name) > 0) { 1038 strncpy(entry->ip.outiface, fw->match.out.name, IFNAMSIZ); 1039 memset(&entry->ip.outiface_mask, 0, IFNAMSIZ); 1040 memset(&entry->ip.outiface_mask, 0xff, strlen(fw->match.in.name) + 1); 1041 entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_OUT : 0; 1042 entry->nfcache |= NFC_IP_IF_OUT; 1043 } 1044 1045 /* Match by TCP port(s) */ 1046 if (fw->match.ipproto == IPPROTO_TCP) { 1047 struct ipt_tcp *tcp; 1048 1049 if (!(match = netconf_append_match(&entry, "tcp", sizeof(struct ipt_tcp)))) 1050 goto err; 1051 tcp = (struct ipt_tcp *) &match->data[0]; 1052 1053 entry->ip.proto = IPPROTO_TCP; 1054 entry->nfcache |= NFC_IP_PROTO; 1055 1056 /* Match ports stored in host order for some stupid reason */ 1057 tcp->spts[0] = ntohs(fw->match.src.ports[0]); 1058 tcp->spts[1] = ntohs(fw->match.src.ports[1]); 1059 tcp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_TCP_INV_SRCPT : 0; 1060 entry->nfcache |= (tcp->spts[0] != 0 || tcp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0; 1061 1062 /* Match ports stored in host order for some stupid reason */ 1063 tcp->dpts[0] = ntohs(fw->match.dst.ports[0]); 1064 tcp->dpts[1] = ntohs(fw->match.dst.ports[1]); 1065 tcp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_TCP_INV_DSTPT : 0; 1066 entry->nfcache |= (tcp->dpts[0] != 0 || tcp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0; 1067 } 1068 1069 /* Match by UDP port(s) */ 1070 else if (fw->match.ipproto == IPPROTO_UDP) { 1071 struct ipt_udp *udp; 1072 1073 if (!(match = netconf_append_match(&entry, "udp", sizeof(struct ipt_udp)))) 1074 goto err; 1075 udp = (struct ipt_udp *) &match->data[0]; 1076 1077 entry->ip.proto = IPPROTO_UDP; 1078 entry->nfcache |= NFC_IP_PROTO; 1079 1080 /* Match ports stored in host order for some stupid reason */ 1081 udp->spts[0] = ntohs(fw->match.src.ports[0]); 1082 udp->spts[1] = ntohs(fw->match.src.ports[1]); 1083 udp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_UDP_INV_SRCPT : 0; 1084 entry->nfcache |= (udp->spts[0] != 0 || udp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0; 1085 1086 /* Match ports stored in host order for some stupid reason */ 1087 udp->dpts[0] = ntohs(fw->match.dst.ports[0]); 1088 udp->dpts[1] = ntohs(fw->match.dst.ports[1]); 1089 udp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_UDP_INV_DSTPT : 0; 1090 entry->nfcache |= (udp->dpts[0] != 0 || udp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0; 1091 } 1092 1093 /* Match by source MAC address */ 1094 if (!ETHER_ISNULLADDR(fw->match.mac.octet)) { 1095 struct ipt_mac_info *mac; 1096 1097 if (!(match = netconf_append_match(&entry, "mac", sizeof(struct ipt_mac_info)))) 1098 goto err; 1099 mac = (struct ipt_mac_info *) &match->data[0]; 1100 1101 memcpy(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN); 1102 mac->invert = (fw->match.flags & NETCONF_INV_MAC) ? 1 : 0; 1103 } 1104 1105 /* Match by packet state */ 1106 if (fw->match.state) { 1107 struct ipt_state_info *state; 1108 1109 if (!(match = netconf_append_match(&entry, "state", sizeof(struct ipt_state_info)))) 1110 goto err; 1111 state = (struct ipt_state_info *) &match->data[0]; 1112 1113 state->statemask |= (fw->match.state & NETCONF_INVALID) ? IPT_STATE_INVALID : 0; 1114 state->statemask |= (fw->match.state & NETCONF_ESTABLISHED) ? IPT_STATE_BIT(IP_CT_ESTABLISHED) : 0; 1115 state->statemask |= (fw->match.state & NETCONF_RELATED) ? IPT_STATE_BIT(IP_CT_RELATED) : 0; 1116 state->statemask |= (fw->match.state & NETCONF_NEW) ? IPT_STATE_BIT(IP_CT_NEW) : 0; 1117 } 1118 1119 /* Match by local time */ 1120 if (fw->match.secs[0] || fw->match.secs[1]) { 1121 time_info_t *time; 1122#ifndef LINUX_2_6_36 1123 unsigned int *days; 1124 int i; 1125#endif 1126 if (fw->match.secs[0] >= (24 * 60 * 60) || fw->match.secs[1] >= (24 * 60 * 60) || 1127 fw->match.days[0] >= 7 || fw->match.days[1] >= 7) { 1128 fprintf(stderr, "invalid time %d-%d:%d-%d\n", 1129 fw->match.days[0], fw->match.days[1], 1130 fw->match.secs[0], fw->match.secs[1]); 1131 goto err; 1132 } 1133 1134 if (!(match = netconf_append_match(&entry, "time", sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES))) 1135 goto err; 1136 time = (time_info_t *) &match->data[0]; 1137 1138#ifndef LINUX_2_6_36 1139 days = (unsigned int *) &time[1]; 1140 days[0] = fw->match.days[0]; 1141 days[1] = fw->match.days[1]; 1142 1143 for (i = fw->match.days[0]; i != fw->match.days[1]; i = (i + 1) % 7) 1144 time->days_match |= (1 << i); 1145 time->days_match |= (1 << fw->match.days[1]); 1146 time->time_start = fw->match.secs[0]; 1147 time->time_stop = fw->match.secs[1]; 1148#else 1149 /* We don't need absolute date match */ 1150 time->date_start = 0; 1151 time->date_stop = ~0U; 1152 /* Seconds per day */ 1153 time->daytime_start = fw->match.secs[0]; 1154 time->daytime_stop = fw->match.secs[1]; 1155 /* We don't need month days match */ 1156 time->monthdays_match = XT_TIME_ALL_MONTHDAYS; 1157 /* Week days match */ 1158 set_days(fw->match.days, time); 1159#endif 1160 } 1161 1162 /* Allocate target */ 1163 if (!(target = netconf_append_target(&entry, ipt_target_name[fw->target], ipt_target_size[fw->target]))) 1164 goto err; 1165 1166 if (!(handle = iptc_init(ipt_table_name[fw->target]))) { 1167 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1168 goto err; 1169 } 1170 1171 /* Set filter target information */ 1172 if (filter) { 1173 if (!netconf_valid_dir(filter->dir)) { 1174 fprintf(stderr, "invalid filter direction %d\n", filter->dir); 1175 goto err; 1176 } 1177 1178#ifdef LINUX26 1179#ifndef LINUX_2_6_36 1180 if (!insert_entry(ipt_filter_chain_name[filter->dir], ipt_target_name[fw->target], entry, &handle)) { 1181#else 1182 if (!insert_entry(ipt_filter_chain_name[filter->dir], ipt_target_name[fw->target], entry, handle)) { 1183#endif 1184#else /* LINUX26 */ 1185 if (!insert_entry(ipt_filter_chain_name[filter->dir], entry, &handle)) { 1186#endif /* LINUX26 */ 1187 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1188 goto err; 1189 } 1190 } 1191 1192 /* Set NAT target information */ 1193 else if (nat) { 1194#ifndef LINUX_2_6_36 1195 struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0]; 1196 struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0]; 1197#else 1198 struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0]; 1199 struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0]; 1200#endif /* linux-2.6.36 */ 1201 1202 mr->rangesize = 1; 1203 1204 /* Map to IP address */ 1205 if (nat->ipaddr.s_addr) { 1206 range->min_ip = range->max_ip = nat->ipaddr.s_addr; 1207 range->flags |= IP_NAT_RANGE_MAP_IPS; 1208 } 1209 1210 /* Map to TCP port(s) */ 1211 if (nat->match.ipproto == IPPROTO_TCP) { 1212 range->min.tcp.port = nat->ports[0]; 1213 range->max.tcp.port = nat->ports[1]; 1214 range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 1215 } 1216 1217 /* Map to UDP port(s) */ 1218 else if (nat->match.ipproto == IPPROTO_UDP) { 1219 range->min.udp.port = nat->ports[0]; 1220 range->max.udp.port = nat->ports[1]; 1221 range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 1222 } 1223 1224#ifdef LINUX26 1225#ifndef LINUX_2_6_36 1226 if (!insert_entry(ipt_nat_chain_name[fw->target], ipt_target_name[fw->target], entry, &handle)) { 1227#else 1228 if (!insert_entry(ipt_nat_chain_name[fw->target], ipt_target_name[fw->target], entry, handle)) { 1229#endif /* linux-2.6.36 */ 1230#else /* LINUX26 */ 1231 if (!insert_entry(ipt_nat_chain_name[fw->target], entry, &handle)) { 1232#endif /* LINUX26 */ 1233 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1234 goto err; 1235 } 1236 } 1237 1238 else if (app) { 1239 struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0]; 1240 1241 info->proto = app->proto; 1242 info->dport[0] = app->dport[0]; 1243 info->dport[1] = app->dport[1]; 1244 info->to[0] = app->to[0]; 1245 info->to[1] = app->to[1]; 1246 1247#ifdef LINUX26 1248#ifndef LINUX_2_6_36 1249 if (!insert_entry("PREROUTING", ipt_target_name[fw->target], entry, &handle)) { 1250#else 1251 if (!insert_entry("PREROUTING", ipt_target_name[fw->target], entry, handle)) { 1252#endif /* linux-2.6.36 */ 1253#else /* LINUX26 */ 1254 if (!insert_entry("PREROUTING", entry, &handle)) { 1255#endif /* LINUX26 */ 1256 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1257 goto err; 1258 } 1259 } 1260 1261#ifndef LINUX_2_6_36 1262 if (!iptc_commit(&handle)) { 1263#else 1264 if (!iptc_commit(handle)) { 1265#endif /* linux-2.6.36 */ 1266 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1267 goto err; 1268 } 1269 1270#ifdef LINUX_2_6_36 1271 iptc_free(handle); 1272#endif 1273 free(entry); 1274 return 0; 1275 1276 err: 1277 if (handle) 1278#ifndef LINUX_2_6_36 1279 iptc_commit(&handle); 1280#else 1281 iptc_commit(handle); 1282#endif /* linux-2.6.36 */ 1283#ifdef LINUX_2_6_36 1284 if (handle) 1285 iptc_free(handle); 1286#endif 1287 free(entry); 1288 return errno; 1289} 1290 1291/* 1292 * Delete a firewall entry 1293 * @param fw firewall entry 1294 * @return 0 on success and errno on failure 1295 */ 1296int 1297netconf_del_fw(netconf_fw_t *fw) 1298{ 1299 int num; 1300 const char *chain; 1301#ifndef LINUX_2_6_36 1302 iptc_handle_t handle; 1303#else 1304 struct iptc_handle *handle; 1305#endif /* linux-2.6.36 */ 1306 1307 /* netconf_fw_index() sanity checks fw */ 1308 if ((num = netconf_fw_index(fw)) < 0) 1309 return -num; 1310 1311 /* Pick the right chain name */ 1312 if (netconf_valid_filter(fw->target)) 1313 chain = ipt_filter_chain_name[((netconf_filter_t *) fw)->dir]; 1314 else if (netconf_valid_nat(fw->target)) 1315 chain = ipt_nat_chain_name[fw->target]; 1316 else if (fw->target == NETCONF_APP) 1317 chain = "PREROUTING"; 1318 else 1319 return EINVAL; 1320 1321 /* Commit changes */ 1322 if (!(handle = iptc_init(ipt_table_name[fw->target])) || 1323#ifndef LINUX_2_6_36 1324 !iptc_delete_num_entry(chain, num, &handle) || 1325 !iptc_commit(&handle)) { 1326#else 1327 !iptc_delete_num_entry(chain, num, handle) || 1328 !iptc_commit(handle)) { 1329#endif /* linux-2.6.36 */ 1330#ifdef LINUX_2_6_36 1331 if (handle) 1332 iptc_free(handle); 1333#endif 1334 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1335 return errno; 1336 } 1337 1338#ifdef LINUX_2_6_36 1339 iptc_free(handle); 1340#endif 1341 return 0; 1342} 1343 1344/* 1345 * Add or delete a firewall entry or list of firewall entries 1346 * @param fw_list firewall entry or list of firewall entries 1347 * @bool del whether to delete or add 1348 * @return 0 on success and errno on failure 1349 */ 1350static int 1351netconf_manip_fw(netconf_fw_t *fw_list, bool del) 1352{ 1353 netconf_fw_t *fw; 1354 int ret; 1355 1356 /* Single firewall entry */ 1357 if (netconf_list_empty(fw_list) || !fw_list->next) 1358 return (del ? netconf_del_fw(fw_list) : netconf_add_fw(fw_list)); 1359 1360 /* List of firewall entries */ 1361 netconf_list_for_each(fw, fw_list) { 1362 if ((ret = del ? netconf_del_fw(fw) : netconf_add_fw(fw))) 1363 return ret; 1364 } 1365 1366 return 0; 1367} 1368 1369/* 1370 * Add a NAT entry or list of NAT entries 1371 * @param nat_list NAT entry or list of NAT entries 1372 * @return 0 on success and errno on failure 1373 */ 1374int 1375netconf_add_nat(netconf_nat_t *nat_list) 1376{ 1377 return netconf_manip_fw((netconf_fw_t *) nat_list, 0); 1378} 1379 1380/* 1381 * Delete a NAT entry or list of NAT entries 1382 * @param nat_list NAT entry or list of NAT entries 1383 * @return 0 on success and errno on failure 1384 */ 1385int 1386netconf_del_nat(netconf_nat_t *nat_list) 1387{ 1388 return netconf_manip_fw((netconf_fw_t *) nat_list, 1); 1389} 1390 1391/* 1392 * Get an array of the current NAT entries 1393 * @param nat_array array of NAT entries 1394 * @param space Pointer to size of nat_array in bytes 1395 * @return 0 on success and errno on failure 1396 */ 1397int 1398netconf_get_nat(netconf_nat_t *nat_array, int *space) 1399{ 1400 netconf_fw_t *fw, fw_list; 1401 int ret; 1402 int found = 0; 1403 1404 if ((ret = netconf_get_fw(&fw_list))) 1405 return ret; 1406 1407 netconf_list_for_each(fw, &fw_list) { 1408 if (netconf_valid_nat(fw->target)) { 1409 found++; 1410 if (*space && *space >= (found * sizeof(netconf_nat_t))) 1411 memcpy(&nat_array[found - 1], (netconf_nat_t *) fw, sizeof(netconf_nat_t)); 1412 } 1413 } 1414 1415 if (!*space) 1416 *space = found * sizeof(netconf_nat_t); 1417 1418 netconf_list_free(&fw_list); 1419 return 0; 1420} 1421 1422/* 1423 * Add a filter entry or list of filter entries 1424 * @param filter_list filter entry or list of filter entries 1425 * @return 0 on success and errno on failure 1426 */ 1427int 1428netconf_add_filter(netconf_filter_t *filter_list) 1429{ 1430 return netconf_manip_fw((netconf_fw_t *) filter_list, 0); 1431} 1432 1433/* 1434 * Delete a filter entry or list of filter entries 1435 * @param filter_list filter entry or list of filter entries 1436 * @return 0 on success and errno on failure 1437 */ 1438int 1439netconf_del_filter(netconf_filter_t *filter_list) 1440{ 1441 return netconf_manip_fw((netconf_fw_t *) filter_list, 1); 1442} 1443 1444/* 1445 * Get an array of the current filter entries 1446 * @param filter_array array of filter entries 1447 * @param space Pointer to size of filter_array in bytes 1448 * @return 0 on success and errno on failure 1449 */ 1450int 1451netconf_get_filter(netconf_filter_t *filter_array, int *space) 1452{ 1453 netconf_fw_t *fw, fw_list; 1454 int ret; 1455 int found = 0; 1456 1457 if ((ret = netconf_get_fw(&fw_list))) 1458 return ret; 1459 1460 netconf_list_for_each(fw, &fw_list) { 1461 if (netconf_valid_filter(fw->target)) { 1462 found++; 1463 if (*space && *space >= (found * sizeof(netconf_filter_t))) 1464 memcpy(&filter_array[found - 1], (netconf_filter_t *) fw, sizeof(netconf_filter_t)); 1465 } 1466 } 1467 1468 if (!*space) 1469 *space = found * sizeof(netconf_filter_t); 1470 1471 netconf_list_free(&fw_list); 1472 return 0; 1473} 1474 1475/* 1476 * Generates an ipt_entry with an optional match and one target 1477 * @param match_name match name 1478 * @param match_data match data 1479 * @param match_data_size match data size 1480 * @param target_name target name 1481 * @param target_data target data 1482 * @param target_data_size target data size 1483 * @return newly allocated and initialized ipt_entry 1484 */ 1485static struct ipt_entry * 1486netconf_generate_entry(const char *match_name, const void *match_data, size_t match_data_size, 1487 const char *target_name, const void *target_data, size_t target_data_size) 1488{ 1489 struct ipt_entry *entry; 1490 struct ipt_entry_match *match; 1491 struct ipt_entry_target *target; 1492 1493 /* Allocate entry */ 1494 if (!(entry = calloc(1, sizeof(struct ipt_entry)))) { 1495 perror("calloc"); 1496 return NULL; 1497 } 1498 1499 /* Initialize entry parameters */ 1500 entry->next_offset = entry->target_offset = sizeof(struct ipt_entry); 1501 1502 /* Allocate space for and copy match data */ 1503 if (match_data) { 1504 if (!(match = netconf_append_match(&entry, match_name, match_data_size))) 1505 goto err; 1506 memcpy(&match->data[0], match_data, match_data_size); 1507 } 1508 1509 /* Allocate space for and copy target data */ 1510 if (!(target = netconf_append_target(&entry, target_name, target_data_size))) 1511 goto err; 1512 memcpy(&target->data[0], target_data, target_data_size); 1513 1514 return entry; 1515 1516 err: 1517 free(entry); 1518 return NULL; 1519} 1520 1521static int 1522netconf_reset_chain(char *table, char *chain) 1523{ 1524#ifndef LINUX_2_6_36 1525 iptc_handle_t handle = NULL; 1526#else 1527 struct iptc_handle *handle = NULL; 1528#endif /* linux-2.6.36 */ 1529 1530 /* Get handle to table */ 1531 if (!(handle = iptc_init(table))) 1532 goto err; 1533 1534 /* Create chain if necessary */ 1535 if (!iptc_is_chain(chain, handle)) 1536#ifndef LINUX_2_6_36 1537 if (!iptc_create_chain(chain, &handle)) 1538#else 1539 if (!iptc_create_chain(chain, handle)) 1540#endif /* linux-2.6.36 */ 1541 goto err; 1542 1543 /* Flush entries and commit */ 1544#ifndef LINUX_2_6_36 1545 if (!iptc_flush_entries(chain, &handle) || 1546 !iptc_commit(&handle)) 1547#else 1548 if (!iptc_flush_entries(chain, handle) || 1549 !iptc_commit(handle)) 1550#endif /* linux-2.6.36 */ 1551 goto err; 1552 1553#ifdef LINUX_2_6_36 1554 iptc_free(handle); 1555#endif 1556 return 0; 1557 1558 err: 1559 if (handle) 1560#ifndef LINUX_2_6_36 1561 iptc_commit(&handle); 1562#else 1563 iptc_commit(handle); 1564#endif /* linux-2.6.36 */ 1565#ifdef LINUX_2_6_36 1566 if (handle) 1567 iptc_free(handle); 1568#endif 1569 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1570 return errno; 1571} 1572 1573/* 1574 * Reset the firewall to a sane state 1575 * @return 0 on success and errno on failure 1576 */ 1577int 1578netconf_reset_fw(void) 1579{ 1580#ifndef LINUX_2_6_36 1581 iptc_handle_t handle = NULL; 1582#else 1583 struct iptc_handle *handle = NULL; 1584#endif /* linux-2.6.36 */ 1585 struct ipt_entry *entry = NULL; 1586 struct ipt_state_info state; 1587 struct ipt_log_info log; 1588 int ret, unused; 1589 1590 /* Reset default chains */ 1591 if ((ret = netconf_reset_chain("filter", "INPUT")) || 1592 (ret = netconf_reset_chain("filter", "FORWARD")) || 1593 (ret = netconf_reset_chain("filter", "OUTPUT")) || 1594 (ret = netconf_reset_chain("nat", "PREROUTING")) || 1595 (ret = netconf_reset_chain("nat", "POSTROUTING")) || 1596 (ret = netconf_reset_chain("nat", "OUTPUT"))) 1597 return ret; 1598 1599 /* Reset custom chains */ 1600 if ((ret = netconf_reset_chain("filter", "logdrop")) || 1601 (ret = netconf_reset_chain("filter", "logaccept"))) 1602 goto err; 1603 1604 /* Log only when a connection is attempted */ 1605 memset(&state, 0, sizeof(state)); 1606 state.statemask = IPT_STATE_BIT(IP_CT_NEW); 1607 1608 /* Set miscellaneous log parameters */ 1609 memset(&log, 0, sizeof(log)); 1610 log.level = LOG_WARNING; 1611 log.logflags = 0xf; 1612 1613 /* Log packet */ 1614 strncpy(log.prefix, "DROP ", sizeof(log.prefix)); 1615 if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log)))) 1616 return ENOMEM; 1617 entry->nfcache |= NFC_UNKNOWN; 1618 if (!(handle = iptc_init("filter")) || 1619#ifndef LINUX_2_6_36 1620 !iptc_insert_entry("logdrop", entry, 0, &handle) || 1621 !iptc_commit(&handle)) 1622#else 1623 !iptc_insert_entry("logdrop", entry, 0, handle) || 1624 !iptc_commit(handle)) 1625#endif /* linux-2.6.36 */ 1626 goto err; 1627#ifdef LINUX_2_6_36 1628 iptc_free(handle); 1629#endif 1630 free(entry); 1631 1632 /* Drop packet */ 1633 if (!(entry = netconf_generate_entry(NULL, NULL, 0, "DROP", &unused, sizeof(unused)))) 1634 return ENOMEM; 1635 entry->nfcache |= NFC_UNKNOWN; 1636 if (!(handle = iptc_init("filter")) || 1637#ifndef LINUX_2_6_36 1638 !iptc_insert_entry("logdrop", entry, 1, &handle) || 1639 !iptc_commit(&handle)) 1640#else 1641 !iptc_insert_entry("logdrop", entry, 1, handle) || 1642 !iptc_commit(handle)) 1643#endif /* linux-2.6.36 */ 1644 goto err; 1645#ifdef LINUX_2_6_36 1646 iptc_free(handle); 1647#endif 1648 free(entry); 1649 1650 /* Log packet */ 1651 strncpy(log.prefix, "ACCEPT ", sizeof(log.prefix)); 1652 if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log)))) 1653 return ENOMEM; 1654 entry->nfcache |= NFC_UNKNOWN; 1655 if (!(handle = iptc_init("filter")) || 1656#ifndef LINUX_2_6_36 1657 !iptc_insert_entry("logaccept", entry, 0, &handle) || 1658 !iptc_commit(&handle)) 1659#else 1660 !iptc_insert_entry("logaccept", entry, 0, handle) || 1661 !iptc_commit(handle)) 1662#endif /* linux-2.6.36 */ 1663 goto err; 1664#ifdef LINUX_2_6_36 1665 iptc_free(handle); 1666#endif 1667 free(entry); 1668 1669 /* Accept packet */ 1670 if (!(entry = netconf_generate_entry(NULL, NULL, 0, "ACCEPT", &unused, sizeof(unused)))) 1671 return ENOMEM; 1672 entry->nfcache |= NFC_UNKNOWN; 1673 if (!(handle = iptc_init("filter")) || 1674#ifndef LINUX_2_6_36 1675 !iptc_insert_entry("logaccept", entry, 1, &handle) || 1676 !iptc_commit(&handle)) 1677#else 1678 !iptc_insert_entry("logaccept", entry, 1, handle) || 1679 !iptc_commit(handle)) 1680#endif /* linux-2.6.36 */ 1681 goto err; 1682#ifdef LINUX_2_6_36 1683 iptc_free(handle); 1684#endif 1685 free(entry); 1686 1687 return 0; 1688 1689 err: 1690#ifdef LINUX_2_6_36 1691 if (handle) 1692 iptc_free(handle); 1693#endif 1694 if (entry) 1695 free(entry); 1696 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1697 return errno; 1698} 1699 1700/* 1701 * Below are miscellaneous functions that do not fit into the grand 1702 * scheme of netconf 1703 */ 1704 1705/* 1706 * Clamp TCP MSS value to PMTU of interface (for masquerading through PPPoE) 1707 * @return 0 on success and errno on failure 1708 */ 1709int 1710netconf_clamp_mss_to_pmtu(void) 1711{ 1712 struct ipt_entry *entry; 1713#ifndef LINUX_2_6_36 1714 iptc_handle_t handle; 1715#else 1716 struct iptc_handle *handle = NULL; 1717#endif /* linux-2.6.36 */ 1718 struct ipt_tcp tcp; 1719 struct ipt_tcpmss_info tcpmss; 1720 1721 /* Match on SYN=1 RST=0 */ 1722 memset(&tcp, 0, sizeof(tcp)); 1723 tcp.spts[1] = tcp.dpts[1] = 0xffff; 1724 tcp.flg_mask = TH_SYN | TH_RST; 1725 tcp.flg_cmp = TH_SYN; 1726 1727 /* Clamp TCP MSS to PMTU */ 1728 memset(&tcpmss, 0, sizeof(tcpmss)); 1729 tcpmss.mss = IPT_TCPMSS_CLAMP_PMTU; 1730 1731 /* Generate and complete the entry */ 1732 if (!(entry = netconf_generate_entry("tcp", &tcp, sizeof(tcp), "TCPMSS", &tcpmss, sizeof(tcpmss)))) 1733 return ENOMEM; 1734 entry->ip.proto = IPPROTO_TCP; 1735 entry->nfcache |= NFC_IP_PROTO | NFC_IP_TCPFLAGS; 1736 1737 /* Do it */ 1738 if (!(handle = iptc_init("filter")) || 1739#ifndef LINUX_2_6_36 1740 !iptc_insert_entry("FORWARD", entry, 0, &handle) || 1741 !iptc_commit(&handle)) { 1742#else 1743 !iptc_insert_entry("FORWARD", entry, 0, handle) || 1744 !iptc_commit(handle)) { 1745#endif /* linux-2.6.36 */ 1746#ifdef LINUX_2_6_36 1747 if (handle) 1748 iptc_free(handle); 1749#endif 1750 fprintf(stderr, "%s\n", iptc_strerror(errno)); 1751 free(entry); 1752 return errno; 1753 } 1754 1755#ifdef LINUX_2_6_36 1756 if (handle) 1757 iptc_free(handle); 1758#endif 1759 free(entry); 1760 return 0; 1761} 1762