/* update the port mapping decription and timestamp */ int update_portmapping_desc_timestamp(const char * ifname, unsigned short eport, int proto, const char * desc, unsigned int timestamp) { unsigned short iport; in_addr_t iaddr; char iaddr_str[16]; char rhost[32]; int r; r = priv_delete_redirect_rule_check_desc(ifname, eport, proto, &iport, &iaddr, rhost, sizeof(rhost), 1, desc); if(r < 0) return -1; if(r == 1) { /* only change timestamp */ remove_timestamp_entry(eport, proto); add_timestamp_entry(eport, proto, timestamp); return 0; } if (priv_delete_filter_rule(ifname, iport, proto, iaddr) < 0) return -1; inet_ntop(AF_INET, &iaddr, iaddr_str, sizeof(iaddr_str)); if(add_redirect_rule2(ifname, rhost, eport, iaddr_str, iport, proto, desc, timestamp) < 0) return -1; if(add_filter_rule2(ifname, rhost, iaddr_str, eport, iport, proto, desc) < 0) return -1; return 0; }
/* add_redirect_rule2() : * create a rdr rule */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { pcr.rule.direction = PF_IN; //pcr.rule.src.addr.type = PF_ADDR_NONE; pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #ifndef PF_NEWSTYLE pcr.rule.action = PF_RDR; #ifndef PF_ENABLE_FILTER_RULES pcr.rule.natpass = 1; #else pcr.rule.natpass = 0; #endif #else #ifndef PF_ENABLE_FILTER_RULES pcr.rule.action = PF_PASS; #else pcr.rule.action = PF_MATCH; #endif #endif pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif pcr.rule.quick = 1; pcr.rule.keep_state = PF_STATE_NORMAL; if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else pcr.rule.rdr.proxy_port[0] = iport; pcr.rule.rdr.proxy_port[1] = iport; inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } if(r == 0 && timestamp > 0) { struct timestamp_entry * tmp; tmp = malloc(sizeof(struct timestamp_entry)); if(tmp) { tmp->next = timestamp_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->protocol = (short)proto; timestamp_list = tmp; } } return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { #ifndef PF_ENABLE_FILTER_RULES return 0; #else int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif pcr.rule.keep_state = 1; strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = eport; a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); TAILQ_INIT(&pcr.rule.rpool.list); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); /* we have any - any port = # keep state label */ /* we want any - iaddr port = # keep state label */ /* memcpy(&pcr.rule.dst, a, sizeof(struct pf_pooladdr)); */ memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } return r; #endif } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) ) { #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(eport, proto); return 0; } } error: return -1; } int delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { int i, n; struct pfioc_rule pr; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) ) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } remove_timestamp_entry(eport, proto); return 0; } } error: return -1; } int delete_filter_rule(const char * ifname, unsigned short eport, int proto) { #ifndef PF_ENABLE_FILTER_RULES return 0; #else int i, n; struct pfioc_rule pr; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_PASS; if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } if( (eport == ntohs(pr.rule.dst.port[0])) && (pr.rule.proto == proto) ) { pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } return 0; } } error: return -1; #endif } int get_redirect_rule_by_index(int index, char * ifname, unsigned short * eport, char * iaddr, int iaddrlen, unsigned short * iport, int * proto, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif if(index < 0) return -1; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; if(index >= n) goto error; pr.nr = index; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } *proto = pr.rule.proto; *eport = ntohs(pr.rule.dst.port[0]); #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(ifname) strlcpy(ifname, pr.rule.ifname, IFNAMSIZ); if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = index; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(*eport, *proto); return 0; error: return -1; } /* return an (malloc'ed) array of "external" port for which there is * a port mapping. number is the size of the array */ unsigned short * get_portmappings_in_range(unsigned short startport, unsigned short endport, int proto, unsigned int * number) { unsigned short * array; unsigned int capacity; int i, n; unsigned short eport; struct pfioc_rule pr; *number = 0; if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return NULL; } capacity = 128; array = calloc(capacity, sizeof(unsigned short)); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : calloc error"); return NULL; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); free(array); return NULL; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); continue; } eport = ntohs(pr.rule.dst.port[0]); if( (eport == ntohs(pr.rule.dst.port[1])) && (pr.rule.proto == proto) && (startport <= eport) && (eport <= endport) ) { if(*number >= capacity) { /* need to increase the capacity of the array */ capacity += 128; array = realloc(array, sizeof(unsigned short)*capacity); if(!array) { syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity); *number = 0; return NULL; } } array[*number] = eport; (*number)++; } } return array; }
/* add_redirect_rule2() : * create a rdr rule */ int add_redirect_rule2(const char * ifname, const char * rhost, unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc, unsigned int timestamp) { int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; struct pf_pooladdr *a; #endif if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { pcr.rule.direction = PF_IN; /*pcr.rule.src.addr.type = PF_ADDR_NONE;*/ pcr.rule.src.addr.type = PF_ADDR_ADDRMASK; pcr.rule.dst.addr.type = PF_ADDR_ADDRMASK; pcr.rule.nat.addr.type = PF_ADDR_NONE; pcr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; #endif #ifdef __APPLE__ pcr.rule.dst.xport.range.op = PF_OP_EQ; pcr.rule.dst.xport.range.port[0] = htons(eport); pcr.rule.dst.xport.range.port[1] = htons(eport); #else pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); #endif #ifndef PF_NEWSTYLE pcr.rule.action = PF_RDR; #ifndef PF_ENABLE_FILTER_RULES pcr.rule.natpass = 1; #else pcr.rule.natpass = 0; #endif #else #ifndef PF_ENABLE_FILTER_RULES pcr.rule.action = PF_PASS; #else pcr.rule.action = PF_MATCH; #endif #endif pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.quick = 1; pcr.rule.keep_state = PF_STATE_NORMAL; if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); a = calloc(1, sizeof(struct pf_pooladdr)); inet_pton(AF_INET, iaddr, &a->addr.v.a.addr.v4.s_addr); a->addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); TAILQ_INSERT_TAIL(&pcr.rule.rpool.list, a, entries); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); r = -1; } else { #else pcr.rule.rdr.proxy_port[0] = iport; pcr.rule.rdr.proxy_port[1] = iport; inet_pton(AF_INET, iaddr, &pcr.rule.rdr.addr.v.a.addr.v4.s_addr); pcr.rule.rdr.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); if(1) { #endif pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } #ifndef PF_NEWSTYLE free(a); #endif } if(r == 0 && timestamp > 0) { struct timestamp_entry * tmp; tmp = malloc(sizeof(struct timestamp_entry)); if(tmp) { tmp->next = timestamp_list; tmp->timestamp = timestamp; tmp->eport = eport; tmp->protocol = (short)proto; timestamp_list = tmp; } } return r; } /* thanks to Seth Mos for this function */ int add_filter_rule2(const char * ifname, const char * rhost, const char * iaddr, unsigned short eport, unsigned short iport, int proto, const char * desc) { #ifndef PF_ENABLE_FILTER_RULES UNUSED(ifname); UNUSED(rhost); UNUSED(iaddr); UNUSED(eport); UNUSED(iport); UNUSED(proto); UNUSED(desc); return 0; #else int r; struct pfioc_rule pcr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif #ifndef USE_IFNAME_IN_RULES UNUSED(ifname); #endif UNUSED(eport); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); r = -1; } else { pcr.pool_ticket = pp.ticket; #else if(1) { #endif pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(iport); pcr.rule.direction = PF_IN; pcr.rule.action = PF_PASS; pcr.rule.af = AF_INET; #ifdef USE_IFNAME_IN_RULES if(ifname) strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); #endif pcr.rule.proto = proto; pcr.rule.quick = (GETFLAG(PFNOQUICKRULESMASK))?0:1; pcr.rule.log = (GETFLAG(LOGPACKETSMASK))?1:0; /*logpackets;*/ /* see the discussion on the forum : * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=638 */ pcr.rule.flags = TH_SYN; pcr.rule.flagset = (TH_SYN|TH_ACK); #ifdef PFRULE_HAS_RTABLEID pcr.rule.rtableid = -1; /* first appeared in OpenBSD 4.0 */ #endif #ifdef PFRULE_HAS_ONRDOMAIN pcr.rule.onrdomain = -1; /* first appeared in OpenBSD 5.0 */ #endif pcr.rule.keep_state = 1; strlcpy(pcr.rule.label, desc, PF_RULE_LABEL_SIZE); if(queue) strlcpy(pcr.rule.qname, queue, PF_QNAME_SIZE); if(tag) strlcpy(pcr.rule.tagname, tag, PF_TAG_NAME_SIZE); if(rhost && rhost[0] != '\0' && rhost[0] != '*') { inet_pton(AF_INET, rhost, &pcr.rule.src.addr.v.a.addr.v4.s_addr); pcr.rule.src.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); } /* we want any - iaddr port = # keep state label */ inet_pton(AF_INET, iaddr, &pcr.rule.dst.addr.v.a.addr.v4.s_addr); pcr.rule.dst.addr.v.a.mask.v4.s_addr = htonl(INADDR_NONE); #ifndef PF_NEWSTYLE pcr.rule.rpool.proxy_port[0] = iport; pcr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pcr.rule.rpool.list); #endif if(1) { pcr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); r = -1; } else { pcr.action = PF_CHANGE_ADD_TAIL; if(ioctl(dev, DIOCCHANGERULE, &pcr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_ADD_TAIL: %m"); r = -1; } } } } return r; #endif } /* get_redirect_rule() * return value : 0 success (found) * -1 = error or rule not found */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport, char * desc, int desclen, char * rhost, int rhostlen, unsigned int * timestamp, u_int64_t * packets, u_int64_t * bytes) { int i, n; struct pfioc_rule pr; #ifndef PF_NEWSTYLE struct pfioc_pooladdr pp; #endif UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } #ifdef __APPLE__ if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) && (eport == ntohs(pr.rule.dst.xport.range.port[1])) #else if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) #endif && (pr.rule.proto == proto) ) { #ifndef PF_NEWSTYLE *iport = pr.rule.rpool.proxy_port[0]; #else *iport = pr.rule.rdr.proxy_port[0]; #endif if(desc) strlcpy(desc, pr.rule.label, desclen); #ifdef PFRULE_INOUT_COUNTS if(packets) *packets = pr.rule.packets[0] + pr.rule.packets[1]; if(bytes) *bytes = pr.rule.bytes[0] + pr.rule.bytes[1]; #else if(packets) *packets = pr.rule.packets; if(bytes) *bytes = pr.rule.bytes; #endif #ifndef PF_NEWSTYLE memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #else inet_ntop(AF_INET, &pr.rule.rdr.addr.v.a.addr.v4.s_addr, iaddr, iaddrlen); #endif if(rhost && rhostlen > 0) { if (pr.rule.src.addr.v.a.addr.v4.s_addr == 0) { rhost[0] = '\0'; /* empty string */ } else { inet_ntop(AF_INET, &pr.rule.src.addr.v.a.addr.v4.s_addr, rhost, rhostlen); } } if(timestamp) *timestamp = get_timestamp(eport, proto); return 0; } } error: return -1; } static int priv_delete_redirect_rule(const char * ifname, unsigned short eport, int proto, unsigned short * iport, in_addr_t * iaddr) { int i, n; struct pfioc_rule pr; UNUSED(ifname); if(dev<0) { syslog(LOG_ERR, "pf device is not open"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); #ifndef PF_NEWSTYLE pr.rule.action = PF_RDR; #endif if(ioctl(dev, DIOCGETRULES, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULES, ...): %m"); goto error; } n = pr.nr; for(i=0; i<n; i++) { pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETRULE): %m"); goto error; } #ifdef __APPLE__ if( (eport == ntohs(pr.rule.dst.xport.range.port[0])) && (eport == ntohs(pr.rule.dst.xport.range.port[1])) #else if( (eport == ntohs(pr.rule.dst.port[0])) && (eport == ntohs(pr.rule.dst.port[1])) #endif && (pr.rule.proto == proto) ) { /* retrieve iport in order to remove filter rule */ #ifndef PF_NEWSTYLE if(iport) *iport = pr.rule.rpool.proxy_port[0]; if(iaddr) { /* retrieve internal address */ struct pfioc_pooladdr pp; memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); pp.r_action = PF_RDR; pp.r_num = i; pp.ticket = pr.ticket; if(ioctl(dev, DIOCGETADDRS, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDRS, ...): %m"); goto error; } if(pp.nr != 1) { syslog(LOG_NOTICE, "No address associated with pf rule"); goto error; } pp.nr = 0; /* first */ if(ioctl(dev, DIOCGETADDR, &pp) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCGETADDR, ...): %m"); goto error; } *iaddr = pp.addr.addr.v.a.addr.v4.s_addr; } #else if(iport) *iport = pr.rule.rdr.proxy_port[0]; if(iaddr) { /* retrieve internal address */ *iaddr = pr.rule.rdr.addr.v.a.addr.v4.s_addr; } #endif pr.action = PF_CHANGE_GET_TICKET; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_GET_TICKET: %m"); goto error; } pr.action = PF_CHANGE_REMOVE; pr.nr = i; if(ioctl(dev, DIOCCHANGERULE, &pr) < 0) { syslog(LOG_ERR, "ioctl(dev, DIOCCHANGERULE, ...) PF_CHANGE_REMOVE: %m"); goto error; } remove_timestamp_entry(eport, proto); return 0; } } error: return -1; }