1/* $Id: obsdrdr.c,v 1.84 2015/02/08 08:55:55 nanard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2015 Thomas Bernard 5 * This software is subject to the conditions detailed 6 * in the LICENCE file provided within the distribution */ 7 8/* 9 * pf rules created (with ext_if = xl1) 10 * - OpenBSD up to version 4.6 : 11 * rdr pass on xl1 inet proto udp from any to any port = 54321 \ 12 * keep state label "test label" -> 192.168.0.42 port 12345 13 * or a rdr rule + a pass rule : 14 * rdr quick on xl1 inet proto udp from any to any port = 54321 \ 15 * keep state label "test label" -> 192.168.0.42 port 12345 16 * pass in quick on xl1 inet proto udp from any to 192.168.0.42 port = 12345 \ 17 * flags S/SA keep state label "test label" 18 * 19 * - OpenBSD starting from version 4.7 20 * match in on xl1 inet proto udp from any to any port 54321 \ 21 * label "test label" rdr-to 192.168.0.42 port 12345 22 * or 23 * pass in quick on xl1 inet proto udp from any to any port 54321 \ 24 * label "test label" rdr-to 192.168.0.42 port 12345 25 * 26 * 27 * 28 * Macros/#defines : 29 * - PF_ENABLE_FILTER_RULES 30 * If set, two rules are created : rdr + pass. Else a rdr/pass rule 31 * is created. 32 * - USE_IFNAME_IN_RULES 33 * If set the interface name is set in the rule. 34 * - PFRULE_INOUT_COUNTS 35 * Must be set with OpenBSD version 3.8 and up. 36 * - PFRULE_HAS_RTABLEID 37 * Must be set with OpenBSD version 4.0 and up. 38 * - PF_NEWSSTYLE 39 * Must be set with OpenBSD version 4.7 and up. 40 */ 41 42#include <sys/types.h> 43#include <sys/socket.h> 44#include <sys/param.h> 45#include <net/if.h> 46#include <netinet/in.h> 47#include <netinet/tcp.h> 48#include <arpa/inet.h> 49#ifdef __DragonFly__ 50#include <net/pf/pfvar.h> 51#else 52#ifdef __APPLE__ 53#define PRIVATE 1 54#endif 55#include <net/pfvar.h> 56#endif 57#include <fcntl.h> 58#include <sys/ioctl.h> 59#include <unistd.h> 60#include <string.h> 61#include <syslog.h> 62#include <stdio.h> 63#include <stdlib.h> 64 65#include "../macros.h" 66#include "../config.h" 67#include "obsdrdr.h" 68#include "../upnpglobalvars.h" 69 70#ifndef USE_PF 71#error "USE_PF macro is undefined, check consistency between config.h and Makefile" 72#else 73 74/* list too keep timestamps for port mappings having a lease duration */ 75struct timestamp_entry { 76 struct timestamp_entry * next; 77 unsigned int timestamp; 78 unsigned short eport; 79 short protocol; 80}; 81 82static struct timestamp_entry * timestamp_list = NULL; 83 84static unsigned int 85get_timestamp(unsigned short eport, int proto) 86{ 87 struct timestamp_entry * e; 88 e = timestamp_list; 89 while(e) { 90 if(e->eport == eport && e->protocol == (short)proto) 91 return e->timestamp; 92 e = e->next; 93 } 94 return 0; 95} 96 97static void 98remove_timestamp_entry(unsigned short eport, int proto) 99{ 100 struct timestamp_entry * e; 101 struct timestamp_entry * * p; 102 p = ×tamp_list; 103 e = *p; 104 while(e) { 105 if(e->eport == eport && e->protocol == (short)proto) { 106 /* remove the entry */ 107 *p = e->next; 108 free(e); 109 return; 110 } 111 p = &(e->next); 112 e = *p; 113 } 114} 115 116/* /dev/pf when opened */ 117int dev = -1; 118 119/* shutdown_redirect() : 120 * close the /dev/pf device */ 121void 122shutdown_redirect(void) 123{ 124 if(close(dev)<0) 125 syslog(LOG_ERR, "close(\"/dev/pf\"): %m"); 126 dev = -1; 127} 128 129/* open the device */ 130int 131init_redirect(void) 132{ 133 struct pf_status status; 134 if(dev>=0) 135 shutdown_redirect(); 136 dev = open("/dev/pf", O_RDWR); 137 if(dev<0) { 138 syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); 139 return -1; 140 } 141 if(ioctl(dev, DIOCGETSTATUS, &status)<0) { 142 syslog(LOG_ERR, "DIOCGETSTATUS: %m"); 143 return -1; 144 } 145 if(!status.running) { 146 syslog(LOG_ERR, "pf is disabled"); 147 return -1; 148 } 149 return 0; 150} 151 152#if TEST 153/* for debug */ 154int 155clear_redirect_rules(void) 156{ 157 struct pfioc_trans io; 158 struct pfioc_trans_e ioe; 159 if(dev<0) { 160 syslog(LOG_ERR, "pf device is not open"); 161 return -1; 162 } 163 memset(&ioe, 0, sizeof(ioe)); 164 io.size = 1; 165 io.esize = sizeof(ioe); 166 io.array = &ioe; 167#ifndef PF_NEWSTYLE 168 ioe.rs_num = PF_RULESET_RDR; 169#else 170 ioe.type = PF_TRANS_RULESET; 171#endif 172 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN); 173 if(ioctl(dev, DIOCXBEGIN, &io) < 0) 174 { 175 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m"); 176 goto error; 177 } 178 if(ioctl(dev, DIOCXCOMMIT, &io) < 0) 179 { 180 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m"); 181 goto error; 182 } 183 return 0; 184error: 185 return -1; 186} 187 188int 189clear_filter_rules(void) 190{ 191#ifndef PF_ENABLE_FILTER_RULES 192 return 0; 193#else 194 struct pfioc_trans io; 195 struct pfioc_trans_e ioe; 196 if(dev<0) { 197 syslog(LOG_ERR, "pf device is not open"); 198 return -1; 199 } 200 memset(&ioe, 0, sizeof(ioe)); 201 io.size = 1; 202 io.esize = sizeof(ioe); 203 io.array = &ioe; 204#ifndef PF_NEWSTYLE 205 ioe.rs_num = PF_RULESET_FILTER; 206#else 207 /* ? */ 208 ioe.type = PF_TRANS_RULESET; 209#endif 210 strlcpy(ioe.anchor, anchor_name, MAXPATHLEN); 211 if(ioctl(dev, DIOCXBEGIN, &io) < 0) 212 { 213 syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m"); 214 goto error; 215 } 216 if(ioctl(dev, DIOCXCOMMIT, &io) < 0) 217 { 218 syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m"); 219 goto error; 220 } 221 return 0; 222error: 223 return -1; 224#endif 225} 226#endif 227 228/* add_redirect_rule2() : 229 * create a rdr rule */ 230int 231add_redirect_rule2(const char * ifname, 232 const char * rhost, unsigned short eport, 233 const char * iaddr, unsigned short iport, int proto, 234 const char * desc, unsigned int timestamp) 235{ 236 int r; 237 struct pfioc_rule pcr; 238#ifndef PF_NEWSTYLE 239 struct pfioc_pooladdr pp; 240 struct pf_pooladdr *a; 241#endif 242 if(dev<0) { 243 syslog(LOG_ERR, "pf device is not open"); 244 return -1; 245 } 246 r = 0; 247 memset(&pcr, 0, sizeof(pcr)); 248 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); 249 250#ifndef PF_NEWSTYLE 251 memset(&pp, 0, sizeof(pp)); 252 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 253 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) 254 { 255 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); 256 r = -1; 257 } 258 else 259 { 260 pcr.pool_ticket = pp.ticket; 261#else 262 if(1) 263 { 264 pcr.rule.direction = PF_IN; 265 /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/ 266 pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; 267 pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 268 pcr.rule.nat.addr.type = PF_ADDR_NONE; 269 pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; 270#endif 271 272#ifdef __APPLE__ 273 pcr.rule.dst.xport.range.op = PF_OP_EQ; 274 pcr.rule.dst.xport.range.port[0] = htons(eport); 275 pcr.rule.dst.xport.range.port[1] = htons(eport); 276#else 277 pcr.rule.dst.port_op = PF_OP_EQ; 278 pcr.rule.dst.port[0] = htons(eport); 279 pcr.rule.dst.port[1] = htons(eport); 280#endif 281#ifndef PF_NEWSTYLE 282 pcr.rule.action = PF_RDR; 283#ifndef PF_ENABLE_FILTER_RULES 284 pcr.rule.natpass = 1; 285#else 286 pcr.rule.natpass = 0; 287#endif 288#else 289#ifndef PF_ENABLE_FILTER_RULES 290 pcr.rule.action = PF_PASS; 291#else 292 pcr.rule.action = PF_MATCH; 293#endif 294#endif 295 pcr.rule.af = AF_INET; 296#ifdef USE_IFNAME_IN_RULES 297 if(ifname) 298 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); 299#endif 300 pcr.rule.proto = proto; 301 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ 302#ifdef PFRULE_HAS_RTABLEID 303 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ 304#endif 305#ifdef PFRULE_HAS_ONRDOMAIN 306 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ 307#endif 308 pcr.rule.quick = 1; 309 pcr.rule.keep_state = PF_STATE_NORMAL; 310 if(tag) 311 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); 312 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); 313 if(rhost && rhost[0] != '\0' && rhost[0] != '*') 314 { 315 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); 316 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); 317 } 318#ifndef PF_NEWSTYLE 319 pcr.rule.rpool.proxy_port[0] = iport; 320 pcr.rule.rpool.proxy_port[1] = iport; 321 TAILQ_INIT(&pcr.rule.rpool.list); 322 a = calloc(1, sizeof(struct pf_pooladdr)); 323 inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); 324 a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); 325 TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); 326 327 memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); 328 if(ioctl(dev, DIOCADDADDR, &pp) < 0) 329 { 330 syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); 331 r = -1; 332 } 333 else 334 { 335#else 336 pcr.rule.rdr.proxy_port[0] = iport; 337 pcr.rule.rdr.proxy_port[1] = iport; 338 inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); 339 pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); 340 if(1) 341 { 342#endif 343 pcr.action = PF_CHANGE_GET_TICKET; 344 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) 345 { 346 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); 347 r = -1; 348 } 349 else 350 { 351 pcr.action = PF_CHANGE_ADD_TAIL; 352 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) 353 { 354 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); 355 r = -1; 356 } 357 } 358 } 359#ifndef PF_NEWSTYLE 360 free(a); 361#endif 362 } 363 if(r == 0 && timestamp > 0) 364 { 365 struct timestamp_entry * tmp; 366 tmp = malloc(sizeof(struct timestamp_entry)); 367 if(tmp) 368 { 369 tmp->next = timestamp_list; 370 tmp->timestamp = timestamp; 371 tmp->eport = eport; 372 tmp->protocol = (short)proto; 373 timestamp_list = tmp; 374 } 375 } 376 return r; 377} 378 379/* thanks to Seth Mos for this function */ 380int 381add_filter_rule2(const char * ifname, 382 const char * rhost, const char * iaddr, 383 unsigned short eport, unsigned short iport, 384 int proto, const char * desc) 385{ 386#ifndef PF_ENABLE_FILTER_RULES 387 UNUSED(ifname); 388 UNUSED(rhost); UNUSED(iaddr); 389 UNUSED(eport); UNUSED(iport); 390 UNUSED(proto); UNUSED(desc); 391 return 0; 392#else 393 int r; 394 struct pfioc_rule pcr; 395#ifndef PF_NEWSTYLE 396 struct pfioc_pooladdr pp; 397#endif 398#ifndef USE_IFNAME_IN_RULES 399 UNUSED(ifname); 400#endif 401 UNUSED(eport); 402 if(dev<0) { 403 syslog(LOG_ERR, "pf device is not open"); 404 return -1; 405 } 406 r = 0; 407 memset(&pcr, 0, sizeof(pcr)); 408 strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); 409 410#ifndef PF_NEWSTYLE 411 memset(&pp, 0, sizeof(pp)); 412 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 413 if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) 414 { 415 syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); 416 r = -1; 417 } 418 else 419 { 420 pcr.pool_ticket = pp.ticket; 421#else 422 if(1) 423 { 424#endif 425 pcr.rule.dst.port_op = PF_OP_EQ; 426 pcr.rule.dst.port[0] = htons(iport); 427 pcr.rule.direction = PF_IN; 428 pcr.rule.action = PF_PASS; 429 pcr.rule.af = AF_INET; 430#ifdef USE_IFNAME_IN_RULES 431 if(ifname) 432 strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); 433#endif 434 pcr.rule.proto = proto; 435 pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; 436 pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ 437/* see the discussion on the forum : 438 * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ 439 pcr.rule.flags = TH_SYN; 440 pcr.rule.flagset = (TH_SYN|TH_ACK); 441#ifdef PFRULE_HAS_RTABLEID 442 pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ 443#endif 444#ifdef PFRULE_HAS_ONRDOMAIN 445 pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ 446#endif 447 pcr.rule.keep_state = 1; 448 strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); 449 if(queue) 450 strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); 451 if(tag) 452 strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); 453 454 if(rhost && rhost[0] != '\0' && rhost[0] != '*') 455 { 456 inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); 457 pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); 458 } 459 /* we want any - iaddr port = # keep state label */ 460 inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr); 461 pcr.rule.dst.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); 462#ifndef PF_NEWSTYLE 463 pcr.rule.rpool.proxy_port[0] = iport; 464 pcr.rule.rpool.proxy_port[1] = iport; 465 TAILQ_INIT(&pcr.rule.rpool.list); 466#endif 467 if(1) 468 { 469 pcr.action = PF_CHANGE_GET_TICKET; 470 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) 471 { 472 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); 473 r = -1; 474 } 475 else 476 { 477 pcr.action = PF_CHANGE_ADD_TAIL; 478 if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) 479 { 480 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); 481 r = -1; 482 } 483 } 484 } 485 } 486 return r; 487#endif 488} 489 490/* get_redirect_rule() 491 * return value : 0 success (found) 492 * -1 = error or rule not found */ 493int 494get_redirect_rule(const char * ifname, unsigned short eport, int proto, 495 char * iaddr, int iaddrlen, unsigned short * iport, 496 char * desc, int desclen, 497 char * rhost, int rhostlen, 498 unsigned int * timestamp, 499 u_int64_t * packets, u_int64_t * bytes) 500{ 501 int i, n; 502 struct pfioc_rule pr; 503#ifndef PF_NEWSTYLE 504 struct pfioc_pooladdr pp; 505#endif 506 UNUSED(ifname); 507 508 if(dev<0) { 509 syslog(LOG_ERR, "pf device is not open"); 510 return -1; 511 } 512 memset(&pr, 0, sizeof(pr)); 513 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 514#ifndef PF_NEWSTYLE 515 pr.rule.action = PF_RDR; 516#endif 517 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 518 { 519 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); 520 goto error; 521 } 522 n = pr.nr; 523 for(i=0; i<n; i++) 524 { 525 pr.nr = i; 526 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 527 { 528 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); 529 goto error; 530 } 531#ifdef __APPLE__ 532 if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) 533 && (eport == ntohs(pr.rule.dst.xport.range.port[1])) 534#else 535 if( (eport == ntohs(pr.rule.dst.port[0])) 536 && (eport == ntohs(pr.rule.dst.port[1])) 537#endif 538 && (pr.rule.proto == proto) ) 539 { 540#ifndef PF_NEWSTYLE 541 *iport = pr.rule.rpool.proxy_port[0]; 542#else 543 *iport = pr.rule.rdr.proxy_port[0]; 544#endif 545 if(desc) 546 strlcpy(desc, pr.rule.label, desclen); 547#ifdef PFRULE_INOUT_COUNTS 548 if(packets) 549 *packets = pr.rule.packets[0] + pr.rule.packets[1]; 550 if(bytes) 551 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; 552#else 553 if(packets) 554 *packets = pr.rule.packets; 555 if(bytes) 556 *bytes = pr.rule.bytes; 557#endif 558#ifndef PF_NEWSTYLE 559 memset(&pp, 0, sizeof(pp)); 560 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 561 pp.r_action = PF_RDR; 562 pp.r_num = i; 563 pp.ticket = pr.ticket; 564 if(ioctl(dev, DIOCGETADDRS, &pp) < 0) 565 { 566 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); 567 goto error; 568 } 569 if(pp.nr != 1) 570 { 571 syslog(LOG_NOTICE, "No address associated with pf rule"); 572 goto error; 573 } 574 pp.nr = 0; /* first */ 575 if(ioctl(dev, DIOCGETADDR, &pp) < 0) 576 { 577 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); 578 goto error; 579 } 580 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, 581 iaddr, iaddrlen); 582#else 583 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, 584 iaddr, iaddrlen); 585#endif 586 if(rhost && rhostlen > 0) 587 { 588 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) 589 { 590 rhost[0] = '\0'; /* empty string */ 591 } 592 else 593 { 594 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, 595 rhost, rhostlen); 596 } 597 } 598 if(timestamp) 599 *timestamp = get_timestamp(eport, proto); 600 return 0; 601 } 602 } 603error: 604 return -1; 605} 606 607static int 608priv_delete_redirect_rule(const char * ifname, unsigned short eport, 609 int proto, unsigned short * iport, 610 in_addr_t * iaddr) 611{ 612 int i, n; 613 struct pfioc_rule pr; 614 UNUSED(ifname); 615 616 if(dev<0) { 617 syslog(LOG_ERR, "pf device is not open"); 618 return -1; 619 } 620 memset(&pr, 0, sizeof(pr)); 621 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 622#ifndef PF_NEWSTYLE 623 pr.rule.action = PF_RDR; 624#endif 625 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 626 { 627 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); 628 goto error; 629 } 630 n = pr.nr; 631 for(i=0; i<n; i++) 632 { 633 pr.nr = i; 634 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 635 { 636 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); 637 goto error; 638 } 639#ifdef __APPLE__ 640 if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) 641 && (eport == ntohs(pr.rule.dst.xport.range.port[1])) 642#else 643 if( (eport == ntohs(pr.rule.dst.port[0])) 644 && (eport == ntohs(pr.rule.dst.port[1])) 645#endif 646 && (pr.rule.proto == proto) ) 647 { 648 /* retrieve iport in order to remove filter rule */ 649#ifndef PF_NEWSTYLE 650 if(iport) *iport = pr.rule.rpool.proxy_port[0]; 651 if(iaddr) 652 { 653 /* retrieve internal address */ 654 struct pfioc_pooladdr pp; 655 memset(&pp, 0, sizeof(pp)); 656 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 657 pp.r_action = PF_RDR; 658 pp.r_num = i; 659 pp.ticket = pr.ticket; 660 if(ioctl(dev, DIOCGETADDRS, &pp) < 0) 661 { 662 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); 663 goto error; 664 } 665 if(pp.nr != 1) 666 { 667 syslog(LOG_NOTICE, "No address associated with pf rule"); 668 goto error; 669 } 670 pp.nr = 0; /* first */ 671 if(ioctl(dev, DIOCGETADDR, &pp) < 0) 672 { 673 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); 674 goto error; 675 } 676 *iaddr = pp.addr.addr.v.a.addr.v4.s_addr; 677 } 678#else 679 if(iport) *iport = pr.rule.rdr.proxy_port[0]; 680 if(iaddr) 681 { 682 /* retrieve internal address */ 683 *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr; 684 } 685#endif 686 pr.action = PF_CHANGE_GET_TICKET; 687 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) 688 { 689 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); 690 goto error; 691 } 692 pr.action = PF_CHANGE_REMOVE; 693 pr.nr = i; 694 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) 695 { 696 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); 697 goto error; 698 } 699 remove_timestamp_entry(eport, proto); 700 return 0; 701 } 702 } 703error: 704 return -1; 705} 706 707int 708delete_redirect_rule(const char * ifname, unsigned short eport, 709 int proto) 710{ 711 return priv_delete_redirect_rule(ifname, eport, proto, NULL, NULL); 712} 713 714static int 715priv_delete_filter_rule(const char * ifname, unsigned short iport, 716 int proto, in_addr_t iaddr) 717{ 718#ifndef PF_ENABLE_FILTER_RULES 719 UNUSED(ifname); UNUSED(iport); UNUSED(proto); UNUSED(iaddr); 720 return 0; 721#else 722 int i, n; 723 struct pfioc_rule pr; 724 UNUSED(ifname); 725 if(dev<0) { 726 syslog(LOG_ERR, "pf device is not open"); 727 return -1; 728 } 729 memset(&pr, 0, sizeof(pr)); 730 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 731 pr.rule.action = PF_PASS; 732 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 733 { 734 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); 735 goto error; 736 } 737 n = pr.nr; 738 for(i=0; i<n; i++) 739 { 740 pr.nr = i; 741 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 742 { 743 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); 744 goto error; 745 } 746#ifdef TEST 747syslog(LOG_DEBUG, "%2d port=%hu proto=%d addr=%8x", 748 i, ntohs(pr.rule.dst.port[0]), pr.rule.proto, 749 pr.rule.dst.addr.v.a.addr.v4.s_addr); 750/*pr.rule.dst.addr.v.a.mask.v4.s_addr*/ 751#endif 752 if( (iport == ntohs(pr.rule.dst.port[0])) 753 && (pr.rule.proto == proto) && 754 (iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr) 755 ) 756 { 757 pr.action = PF_CHANGE_GET_TICKET; 758 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) 759 { 760 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); 761 goto error; 762 } 763 pr.action = PF_CHANGE_REMOVE; 764 pr.nr = i; 765 if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) 766 { 767 syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); 768 goto error; 769 } 770 return 0; 771 } 772 } 773error: 774 return -1; 775#endif 776} 777 778int 779delete_redirect_and_filter_rules(const char * ifname, unsigned short eport, 780 int proto) 781{ 782 int r; 783 unsigned short iport; 784 in_addr_t iaddr; 785 r = priv_delete_redirect_rule(ifname, eport, proto, &iport, &iaddr); 786 if(r == 0) 787 { 788 r = priv_delete_filter_rule(ifname, iport, proto, iaddr); 789 } 790 return r; 791} 792 793int 794get_redirect_rule_by_index(int index, 795 char * ifname, unsigned short * eport, 796 char * iaddr, int iaddrlen, unsigned short * iport, 797 int * proto, char * desc, int desclen, 798 char * rhost, int rhostlen, 799 unsigned int * timestamp, 800 u_int64_t * packets, u_int64_t * bytes) 801{ 802 int n; 803 struct pfioc_rule pr; 804#ifndef PF_NEWSTYLE 805 struct pfioc_pooladdr pp; 806#endif 807 if(index < 0) 808 return -1; 809 if(dev<0) { 810 syslog(LOG_ERR, "pf device is not open"); 811 return -1; 812 } 813 memset(&pr, 0, sizeof(pr)); 814 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 815#ifndef PF_NEWSTYLE 816 pr.rule.action = PF_RDR; 817#endif 818 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 819 { 820 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); 821 goto error; 822 } 823 n = pr.nr; 824 if(index >= n) 825 goto error; 826 pr.nr = index; 827 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 828 { 829 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); 830 goto error; 831 } 832 *proto = pr.rule.proto; 833#ifdef __APPLE__ 834 *eport = ntohs(pr.rule.dst.xport.range.port[0]); 835#else 836 *eport = ntohs(pr.rule.dst.port[0]); 837#endif 838#ifndef PF_NEWSTYLE 839 *iport = pr.rule.rpool.proxy_port[0]; 840#else 841 *iport = pr.rule.rdr.proxy_port[0]; 842#endif 843 if(ifname) 844 strlcpy(ifname, pr.rule.ifname, IFNAMSIZ); 845 if(desc) 846 strlcpy(desc, pr.rule.label, desclen); 847#ifdef PFRULE_INOUT_COUNTS 848 if(packets) 849 *packets = pr.rule.packets[0] + pr.rule.packets[1]; 850 if(bytes) 851 *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; 852#else 853 if(packets) 854 *packets = pr.rule.packets; 855 if(bytes) 856 *bytes = pr.rule.bytes; 857#endif 858#ifndef PF_NEWSTYLE 859 memset(&pp, 0, sizeof(pp)); 860 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 861 pp.r_action = PF_RDR; 862 pp.r_num = index; 863 pp.ticket = pr.ticket; 864 if(ioctl(dev, DIOCGETADDRS, &pp) < 0) 865 { 866 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); 867 goto error; 868 } 869 if(pp.nr != 1) 870 { 871 syslog(LOG_NOTICE, "No address associated with pf rule"); 872 goto error; 873 } 874 pp.nr = 0; /* first */ 875 if(ioctl(dev, DIOCGETADDR, &pp) < 0) 876 { 877 syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); 878 goto error; 879 } 880 inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, 881 iaddr, iaddrlen); 882#else 883 inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, 884 iaddr, iaddrlen); 885#endif 886 if(rhost && rhostlen > 0) 887 { 888 if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) 889 { 890 rhost[0] = '\0'; /* empty string */ 891 } 892 else 893 { 894 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, 895 rhost, rhostlen); 896 } 897 } 898 if(timestamp) 899 *timestamp = get_timestamp(*eport, *proto); 900 return 0; 901error: 902 return -1; 903} 904 905/* return an (malloc'ed) array of "external" port for which there is 906 * a port mapping. number is the size of the array */ 907unsigned short * 908get_portmappings_in_range(unsigned short startport, unsigned short endport, 909 int proto, unsigned int * number) 910{ 911 unsigned short * array; 912 unsigned int capacity; 913 int i, n; 914 unsigned short eport; 915 struct pfioc_rule pr; 916 917 *number = 0; 918 if(dev<0) { 919 syslog(LOG_ERR, "pf device is not open"); 920 return NULL; 921 } 922 capacity = 128; 923 array = calloc(capacity, sizeof(unsigned short)); 924 if(!array) 925 { 926 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); 927 return NULL; 928 } 929 memset(&pr, 0, sizeof(pr)); 930 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 931#ifndef PF_NEWSTYLE 932 pr.rule.action = PF_RDR; 933#endif 934 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 935 { 936 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); 937 free(array); 938 return NULL; 939 } 940 n = pr.nr; 941 for(i=0; i<n; i++) 942 { 943 pr.nr = i; 944 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 945 { 946 syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); 947 continue; 948 } 949#ifdef __APPLE__ 950 eport = ntohs(pr.rule.dst.xport.range.port[0]); 951 if( (eport == ntohs(pr.rule.dst.xport.range.port[1])) 952#else 953 eport = ntohs(pr.rule.dst.port[0]); 954 if( (eport == ntohs(pr.rule.dst.port[1])) 955#endif 956 && (pr.rule.proto == proto) 957 && (startport <= eport) && (eport <= endport) ) 958 { 959 if(*number >= capacity) 960 { 961 /* need to increase the capacity of the array */ 962 unsigned short * tmp; 963 capacity += 128; 964 tmp = realloc(array, sizeof(unsigned short)*capacity); 965 if(!tmp) 966 { 967 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); 968 *number = 0; 969 free(array); 970 return NULL; 971 } 972 array = tmp; 973 } 974 array[*number] = eport; 975 (*number)++; 976 } 977 } 978 return array; 979} 980 981/* this function is only for testing */ 982#if TEST 983void 984list_rules(void) 985{ 986 char buf[32]; 987 int i, n; 988 struct pfioc_rule pr; 989#ifndef PF_NEWSTYLE 990 struct pfioc_pooladdr pp; 991#endif 992 993 if(dev<0) 994 { 995 perror("pf dev not open"); 996 return ; 997 } 998 memset(&pr, 0, sizeof(pr)); 999 strlcpy(pr.anchor, anchor_name, MAXPATHLEN); 1000 pr.rule.action = PF_RDR; 1001 if(ioctl(dev, DIOCGETRULES, &pr) < 0) 1002 perror("DIOCGETRULES"); 1003 printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr); 1004 n = pr.nr; 1005 for(i=0; i<n; i++) 1006 { 1007 printf("-- rule %d --\n", i); 1008 pr.nr = i; 1009 if(ioctl(dev, DIOCGETRULE, &pr) < 0) 1010 perror("DIOCGETRULE"); 1011 printf(" %s %s %d:%d -> %d:%d proto %d keep_state=%d action=%d\n", 1012 pr.rule.ifname, 1013 inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, buf, 32), 1014 (int)ntohs(pr.rule.dst.port[0]), 1015 (int)ntohs(pr.rule.dst.port[1]), 1016#ifndef PF_NEWSTYLE 1017 (int)pr.rule.rpool.proxy_port[0], 1018 (int)pr.rule.rpool.proxy_port[1], 1019#else 1020 (int)pr.rule.rdr.proxy_port[0], 1021 (int)pr.rule.rdr.proxy_port[1], 1022#endif 1023 (int)pr.rule.proto, 1024 (int)pr.rule.keep_state, 1025 (int)pr.rule.action); 1026 printf(" description: \"%s\"\n", pr.rule.label); 1027#ifndef PF_NEWSTYLE 1028 memset(&pp, 0, sizeof(pp)); 1029 strlcpy(pp.anchor, anchor_name, MAXPATHLEN); 1030 pp.r_action = PF_RDR; 1031 pp.r_num = i; 1032 pp.ticket = pr.ticket; 1033 if(ioctl(dev, DIOCGETADDRS, &pp) < 0) 1034 perror("DIOCGETADDRS"); 1035 printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket); 1036 /*if(ioctl(dev, DIOCGETRULE, &pr) < 0) 1037 perror("DIOCGETRULE"); */ 1038 pp.nr = 0; /* first */ 1039 if(ioctl(dev, DIOCGETADDR, &pp) < 0) 1040 perror("DIOCGETADDR"); 1041 /* addr.v.a.addr.v4.s_addr */ 1042 printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32)); 1043#else 1044 printf(" rule_flag=%08x action=%d direction=%d log=%d logif=%d " 1045 "quick=%d ifnot=%d af=%d type=%d code=%d rdr.port_op=%d rdr.opts=%d\n", 1046 pr.rule.rule_flag, pr.rule.action, pr.rule.direction, 1047 pr.rule.log, pr.rule.logif, pr.rule.quick, pr.rule.ifnot, 1048 pr.rule.af, pr.rule.type, pr.rule.code, 1049 pr.rule.rdr.port_op, pr.rule.rdr.opts); 1050 printf(" %s\n", inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, buf, 32)); 1051#endif 1052 } 1053} 1054#endif /* TEST */ 1055 1056#endif /* USE_PF */ 1057