/* $Id: obsdrdr.c.html,v 1.1 2006/05/31 21:21:41 nanard Exp $ */ /* miniupnp project : http://miniupnp.free.fr/ * * Copyright (c) 2006 Thomas Bernard * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include <sys/types.h> #include <sys/socket.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/pfvar.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <string.h> #include <syslog.h> #include <stdio.h> #include <stdlib.h> #include "obsdrdr.h" static const char anchor_name[] = "miniupnpd"; void add_redirect_rule(const char * ifname, unsigned short eport, const char * iaddr, unsigned short iport) { int dev; struct pfioc_trans io; struct pfioc_trans_e ioe; struct pfioc_pooladdr pp; struct pfioc_rule pr; struct pf_pooladdr *a; dev = open("/dev/pf", O_RDWR); if(dev<0) { syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); } else { /* * DIOCXBEGIN * DIOCBEGINADDRS * DIOCADDADDR ??? * DIOCADDRULE * DIOCXCOMMIT rdr on $ext_if proto tcp from any to any port 2022 -> 192.168.0.55 port 22 rdr on ep0 inet proto tcp from any to any port = 2022 -> 192.168.0.55 port 22 */ memset(&ioe, 0, sizeof(ioe)); io.size = 1; io.esize = sizeof(ioe); io.array = &ioe; ioe.rs_num = PF_RULESET_RDR; strlcpy(ioe.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCXBEGIN, &io) < 0) syslog(LOG_ERR, "ioctl(dev, DIOCXBEGIN, ...): %m"); memset(&pp, 0, sizeof(pp)); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCBEGINADDRS, &pp) < 0) syslog(LOG_ERR, "ioctl(dev, DIOCBEGINADDRS, ...): %m"); memset(&pr, 0, sizeof(pr)); pr.ticket = ioe.ticket; pr.pool_ticket = pp.ticket; pr.rule.dst.port_op = PF_OP_EQ; pr.rule.dst.port[0] = htons(eport); pr.rule.dst.port[1] = htons(eport); pr.rule.action = PF_RDR; pr.rule.af = AF_INET; strlcpy(pr.rule.ifname, ifname, IFNAMSIZ); pr.rule.proto = IPPROTO_TCP; pr.rule.rpool.proxy_port[0] = iport; pr.rule.rpool.proxy_port[1] = iport; TAILQ_INIT(&pr.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(&pr.rule.rpool.list, a, entries); strlcpy(pp.anchor, anchor_name, MAXPATHLEN); memcpy(&pp.addr, a, sizeof(struct pf_pooladdr)); if(ioctl(dev, DIOCADDADDR, &pp) < 0) syslog(LOG_ERR, "ioctl(dev, DIOCADDADDR, ...): %m"); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); if(ioctl(dev, DIOCADDRULE, &pr) < 0) syslog(LOG_ERR, "ioctl(dev, DIOCADDRULE, ...): %m"); else { if(ioctl(dev, DIOCXCOMMIT, &io) < 0) syslog(LOG_ERR, "ioctl(dev, DIOCXCOMMIT, ...): %m"); } free(a); close(dev); } } int add_redirect_rule2(const char * ifname, unsigned short eport, const char * iaddr, unsigned short iport, int proto) { int dev; int r; struct pfioc_rule pcr; struct pfioc_pooladdr pp; struct pf_pooladdr *a; dev = open("/dev/pf", O_RDWR); if(dev<0) { syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); return -1; } r = 0; memset(&pcr, 0, sizeof(pcr)); strlcpy(pcr.anchor, anchor_name, MAXPATHLEN); 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; pcr.rule.dst.port_op = PF_OP_EQ; pcr.rule.dst.port[0] = htons(eport); pcr.rule.dst.port[1] = htons(eport); pcr.rule.action = PF_RDR; pcr.rule.af = AF_INET; strlcpy(pcr.rule.ifname, ifname, IFNAMSIZ); pcr.rule.proto = proto; 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 { 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; } } } free(a); } close(dev); return r; } /* get_redirect_rule() * return value : 0 success (found) * -1 = error */ int get_redirect_rule(const char * ifname, unsigned short eport, int proto, char * iaddr, int iaddrlen, unsigned short * iport) { int dev; int i, n; struct pfioc_rule pr; struct pfioc_pooladdr pp; dev = open("/dev/pf", O_RDWR); if(dev<0) { syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_RDR; 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) ) { *iport = pr.rule.rpool.proxy_port[0]; 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; // 1er 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); close(dev); return 0; } } error: close(dev); return -1; } int delete_redirect_rule(const char * ifname, unsigned short eport, int proto) { int dev; int i, n; struct pfioc_rule pr; dev = open("/dev/pf", O_RDWR); if(dev<0) { syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); return -1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_RDR; 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; } close(dev); return 0; } } error: close(dev); return -1; } /* this function is only for testing */ void list_rules(void) { char buf[32]; int dev; int i, n; struct pfioc_rule pr; struct pfioc_pooladdr pp; dev = open("/dev/pf", O_RDWR); if(dev<0) { perror("open(\"/dev/pf\")"); syslog(LOG_ERR, "open(\"/dev/pf\"): %m"); return ;//-1; } memset(&pr, 0, sizeof(pr)); strlcpy(pr.anchor, anchor_name, MAXPATHLEN); pr.rule.action = PF_RDR; if(ioctl(dev, DIOCGETRULES, &pr) < 0) perror("DIOCGETRULES"); printf("ticket = %d, nr = %d\n", pr.ticket, pr.nr); n = pr.nr; for(i=0; i<n; i++) { printf("-- rule %d --\n", i); pr.nr = i; if(ioctl(dev, DIOCGETRULE, &pr) < 0) perror("DIOCGETRULE"); printf(" %d:%d -> %d:%d proto %d\n", (int)ntohs(pr.rule.dst.port[0]), (int)ntohs(pr.rule.dst.port[1]), (int)pr.rule.rpool.proxy_port[0], (int)pr.rule.rpool.proxy_port[1], (int)pr.rule.proto); 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) perror("DIOCGETADDRS"); printf(" nb pool addr = %d ticket=%d\n", pp.nr, pp.ticket); //if(ioctl(dev, DIOCGETRULE, &pr) < 0) // perror("DIOCGETRULE"); pp.nr = 0; // 1er if(ioctl(dev, DIOCGETADDR, &pp) < 0) perror("DIOCGETADDR"); // addr.v.a.addr.v4.s_addr printf(" %s\n", inet_ntop(AF_INET, &pp.addr.addr.v.a.addr.v4.s_addr, buf, 32)); } close(dev); }