Annotation of embedaddon/miniupnpd/ipfw/ipfwrdr.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* $Id: ipfwrdr.c,v 1.13 2012/03/05 20:36:19 nanard Exp $ */
1.1       misho       2: /*
                      3:  * MiniUPnP project
                      4:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
                      5:  * (c) 2009 Jardel Weyrich
1.1.1.3 ! misho       6:  * (c) 2011-2012 Thomas Bernard
1.1       misho       7:  * This software is subject to the conditions detailed
                      8:  * in the LICENCE file provided within the distribution
                      9:  */
                     10: 
1.1.1.2   misho      11: #include "../config.h"
                     12: 
1.1       misho      13: #include <sys/param.h>
                     14: #include <sys/types.h>
                     15: #include <sys/file.h>
                     16: 
1.1.1.3 ! misho      17: /*
        !            18: This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
        !            19: Needed here because on some systems <sys/uio.h> gets included by things
        !            20: like <sys/socket.h>
        !            21: */
1.1       misho      22: #ifndef _KERNEL
                     23: #  define ADD_KERNEL
                     24: #  define _KERNEL
                     25: #  define KERNEL
                     26: #endif
                     27: #ifdef __OpenBSD__
                     28: struct file;
                     29: #endif
                     30: #include <sys/uio.h>
                     31: #ifdef ADD_KERNEL
                     32: #  undef _KERNEL
                     33: #  undef KERNEL
                     34: #endif
                     35: 
                     36: #include <sys/time.h>
                     37: #include <sys/socket.h>
                     38: #include <sys/syslog.h>
                     39: #include <sys/ioctl.h>
                     40: #include <net/if.h>
                     41: #if __FreeBSD_version >= 300000
                     42: #  include <net/if_var.h>
                     43: #endif
                     44: #include <netinet/in.h>
                     45: #include <netinet/in_systm.h>
                     46: #include <netinet/ip.h>
                     47: #include <netinet/ip_icmp.h>
                     48: #include <netinet/tcp.h>
                     49: #include <netinet/udp.h>
                     50: #include <arpa/inet.h>
                     51: 
                     52: #include <sys/types.h>
                     53: #include <sys/queue.h>
                     54: #include <sys/socket.h>
                     55: #include <errno.h>
                     56: #include <limits.h>
                     57: #include <netdb.h>
                     58: #include <stdlib.h>
                     59: #include <fcntl.h>
                     60: #include <syslog.h>
                     61: #include <stddef.h>
                     62: #include <stdio.h>
                     63: #include <strings.h>
                     64: #include <string.h>
                     65: #include <unistd.h>
                     66: #include <netinet/ip_fw.h>
                     67: #include "ipfwaux.h"
1.1.1.2   misho      68: #include "ipfwrdr.h"
1.1       misho      69: 
                     70: #include "../upnpglobalvars.h"
                     71: 
1.1.1.2   misho      72: /* init and shutdown functions */
1.1       misho      73: 
                     74: int init_redirect(void) {
1.1.1.2   misho      75:        return ipfw_exec(IP_FW_INIT, NULL, 0);
1.1       misho      76: }
                     77: 
                     78: void shutdown_redirect(void) {
                     79:        ipfw_exec(IP_FW_TERM, NULL, 0);
                     80: }
                     81: 
1.1.1.2   misho      82: /* ipfw cannot store descriptions and timestamp for port mappings so we keep
                     83:  * our own list in memory */
                     84: struct mapping_desc_time {
                     85:        struct mapping_desc_time * next;
                     86:        unsigned int timestamp;
                     87:        unsigned short eport;
                     88:        short proto;
                     89:        char desc[];
                     90: };
                     91: 
                     92: static struct mapping_desc_time * mappings_list = NULL;
                     93: 
                     94: /* add an element to the port mappings descriptions & timestamp list */
                     95: static void
                     96: add_desc_time(unsigned short eport, int proto,
                     97:               const char * desc, unsigned int timestamp)
                     98: {
                     99:        struct mapping_desc_time * tmp;
                    100:        size_t l;
                    101:        if(!desc)
                    102:                desc = "miniupnpd";
                    103:        l = strlen(desc) + 1;
                    104:        tmp = malloc(sizeof(struct mapping_desc_time) + l);
                    105:        if(tmp) {
                    106:                /* fill the element and insert it as head of the list */
                    107:                tmp->next = mappings_list;
                    108:                tmp->timestamp = timestamp;
                    109:                tmp->eport = eport;
                    110:                tmp->proto = (short)proto;
                    111:                memcpy(tmp->desc, desc, l);
                    112:                mappings_list = tmp;
                    113:        }
                    114: }
                    115: 
                    116: /* remove an element to the port mappings descriptions & timestamp list */
                    117: static void
                    118: del_desc_time(unsigned short eport, int proto)
                    119: {
                    120:        struct mapping_desc_time * e;
                    121:        struct mapping_desc_time * * p;
                    122:        p = &mappings_list;
                    123:        e = *p;
                    124:        while(e) {
                    125:                if(e->eport == eport && e->proto == (short)proto) {
                    126:                        *p = e->next;
                    127:                        free(e);
                    128:                        return;
                    129:                } else {
                    130:                        p = &e->next;
                    131:                        e = *p;
                    132:                }
                    133:        }
                    134: }
                    135: 
                    136: /* go through the list and find the description and timestamp */
                    137: static void
                    138: get_desc_time(unsigned short eport, int proto,
                    139:               char * desc, int desclen,
                    140:               unsigned int * timestamp)
                    141: {
                    142:        struct mapping_desc_time * e;
                    143: 
                    144:        for(e = mappings_list; e; e = e->next) {
                    145:                if(e->eport == eport && e->proto == (short)proto) {
                    146:                        if(desc)
                    147:                                strlcpy(desc, e->desc, desclen);
                    148:                        if(timestamp)
                    149:                                *timestamp = e->timestamp;
                    150:                        return;
                    151:                }
                    152:        }
                    153: }
                    154: 
                    155: /* --- */
1.1       misho     156: int add_redirect_rule2(
                    157:        const char * ifname,
1.1.1.2   misho     158:        const char * rhost,
1.1       misho     159:        unsigned short eport,
                    160:        const char * iaddr,
                    161:        unsigned short iport,
                    162:        int proto,
1.1.1.2   misho     163:        const char * desc,
                    164:        unsigned int timestamp)
1.1       misho     165: {
                    166:        struct ip_fw rule;
1.1.1.2   misho     167:        int r;
1.1       misho     168: 
                    169:        if (ipfw_validate_protocol(proto) < 0)
                    170:                return -1;
                    171:        if (ipfw_validate_ifname(ifname) < 0)
                    172:                return -1;
1.1.1.3 ! misho     173: 
1.1       misho     174:        memset(&rule, 0, sizeof(struct ip_fw));
                    175:        rule.version = IP_FW_CURRENT_API_VERSION;
1.1.1.3 ! misho     176: #if 0
        !           177:        rule.fw_number = 1000; /* rule number */
        !           178:        rule.context = (void *)desc; /* The description is kept in a separate list */
        !           179: #endif
        !           180:        rule.fw_prot = proto; /* protocol */
        !           181:        rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */
        !           182:        rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */
        !           183:        rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */
        !           184:        rule.fw_flg |= IP_FW_F_FWD; /* forward action */
1.1       misho     185: #ifdef USE_IFNAME_IN_RULES
                    186:        if (ifname != NULL) {
1.1.1.3 ! misho     187:                strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */
1.1       misho     188:                rule.fw_in_if.fu_via_if.unit = -1;
                    189:        }
                    190: #endif
                    191:        if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
                    192:                syslog(LOG_ERR, "inet_aton(): %m");
                    193:                return -1;
1.1.1.3 ! misho     194:        }
1.1       misho     195:        memcpy(&rule.fw_dst,  &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
                    196:        memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
1.1.1.3 ! misho     197:        rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */
        !           198:        IP_FW_SETNDSTP(&rule, 1); /* number of external ports */
        !           199:        rule.fw_uar.fw_pts[0] = eport; /* external port */
        !           200:        rule.fw_fwd_ip.sin_port = iport; /* internal port */
1.1.1.2   misho     201:        if (rhost && rhost[0] != '\0') {
                    202:                inet_aton(rhost, &rule.fw_src);
                    203:                rule.fw_smsk.s_addr = htonl(INADDR_NONE);
                    204:        }
1.1       misho     205: 
1.1.1.2   misho     206:        r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
                    207:        if(r >= 0)
                    208:                add_desc_time(eport, proto, desc, timestamp);
                    209:        return r;
1.1       misho     210: }
                    211: 
                    212: /* get_redirect_rule()
                    213:  * return value : 0 success (found)
                    214:  * -1 = error or rule not found */
                    215: int get_redirect_rule(
                    216:        const char * ifname,
                    217:        unsigned short eport,
                    218:        int proto,
1.1.1.3 ! misho     219:        char * iaddr,
        !           220:        int iaddrlen,
1.1       misho     221:        unsigned short * iport,
1.1.1.3 ! misho     222:        char * desc,
1.1       misho     223:        int desclen,
1.1.1.2   misho     224:        char * rhost,
                    225:        int rhostlen,
                    226:        unsigned int * timestamp,
1.1       misho     227:        u_int64_t * packets,
                    228:        u_int64_t * bytes)
                    229: {
                    230:        int i, count_rules, total_rules = 0;
                    231:        struct ip_fw * rules = NULL;
1.1.1.3 ! misho     232: 
1.1       misho     233:        if (ipfw_validate_protocol(proto) < 0)
                    234:                return -1;
                    235:        if (ipfw_validate_ifname(ifname) < 0)
                    236:                return -1;
1.1.1.2   misho     237:        if (timestamp)
                    238:                *timestamp = 0;
                    239: 
1.1       misho     240:        do {
                    241:                count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
                    242:                if (count_rules < 0)
                    243:                        goto error;
                    244:        } while (count_rules == 10);
1.1.1.3 ! misho     245: 
1.1       misho     246:        for (i=0; i<total_rules-1; i++) {
                    247:                const struct ip_fw const * ptr = &rules[i];
                    248:                if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
                    249:                        if (packets != NULL)
                    250:                                *packets = ptr->fw_pcnt;
                    251:                        if (bytes != NULL)
                    252:                                *bytes = ptr->fw_bcnt;
                    253:                        if (iport != NULL)
                    254:                                *iport = ptr->fw_fwd_ip.sin_port;
                    255:                        if (iaddr != NULL && iaddrlen > 0) {
1.1.1.2   misho     256:                                /* looks like fw_out_if.fu_via_ip is zero */
1.1.1.3 ! misho     257:                                /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
1.1.1.2   misho     258:                                if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
                    259:                                        syslog(LOG_ERR, "inet_ntop(): %m");
                    260:                                        goto error;
1.1.1.3 ! misho     261:                                }
1.1.1.2   misho     262:                        }
                    263:                        if (rhost != NULL && rhostlen > 0) {
                    264:                                if (ptr->fw_src.s_addr == 0)
                    265:                                        rhost[0] = '\0';
                    266:                                else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
1.1       misho     267:                                        syslog(LOG_ERR, "inet_ntop(): %m");
                    268:                                        goto error;
1.1.1.3 ! misho     269:                                }
1.1       misho     270:                        }
1.1.1.3 ! misho     271:                        /* And what if we found more than 1 matching rule? */
1.1       misho     272:                        ipfw_free_ruleset(&rules);
1.1.1.2   misho     273:                        get_desc_time(eport, proto, desc, desclen, timestamp);
1.1       misho     274:                        return 0;
                    275:                }
                    276:        }
                    277: 
                    278: error:
                    279:        if (rules != NULL)
1.1.1.3 ! misho     280:                ipfw_free_ruleset(&rules);
1.1       misho     281:        return -1;
                    282: }
                    283: 
                    284: int delete_redirect_rule(
                    285:        const char * ifname,
                    286:        unsigned short eport,
1.1.1.3 ! misho     287:        int proto)
1.1       misho     288: {
                    289:        int i, count_rules, total_rules = 0;
                    290:        struct ip_fw * rules = NULL;
1.1.1.3 ! misho     291: 
1.1       misho     292:        if (ipfw_validate_protocol(proto) < 0)
                    293:                return -1;
                    294:        if (ipfw_validate_ifname(ifname) < 0)
                    295:                return -1;
1.1.1.3 ! misho     296: 
1.1       misho     297:        do {
                    298:                count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
                    299:                if (count_rules < 0)
                    300:                        goto error;
                    301:        } while (count_rules == 10);
1.1.1.3 ! misho     302: 
1.1       misho     303:        for (i=0; i<total_rules-1; i++) {
                    304:                const struct ip_fw const * ptr = &rules[i];
                    305:                if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
                    306:                        if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
                    307:                                goto error;
1.1.1.3 ! misho     308:                        /* And what if we found more than 1 matching rule? */
1.1       misho     309:                        ipfw_free_ruleset(&rules);
1.1.1.2   misho     310:                        del_desc_time(eport, proto);
1.1       misho     311:                        return 0;
                    312:                }
                    313:        }
1.1.1.3 ! misho     314: 
1.1       misho     315: error:
                    316:        if (rules != NULL)
1.1.1.3 ! misho     317:                ipfw_free_ruleset(&rules);
1.1       misho     318:        return -1;
                    319: }
                    320: 
                    321: int add_filter_rule2(
1.1.1.3 ! misho     322:        const char * ifname,
1.1.1.2   misho     323:        const char * rhost,
1.1       misho     324:        const char * iaddr,
1.1.1.3 ! misho     325:        unsigned short eport,
1.1       misho     326:        unsigned short iport,
1.1.1.3 ! misho     327:        int proto,
1.1       misho     328:        const char * desc)
                    329: {
1.1.1.2   misho     330:        return 0; /* nothing to do, always success */
1.1       misho     331: }
                    332: 
                    333: int delete_filter_rule(
1.1.1.3 ! misho     334:        const char * ifname,
        !           335:        unsigned short eport,
        !           336:        int proto)
1.1       misho     337: {
1.1.1.2   misho     338:        return 0; /* nothing to do, always success */
1.1       misho     339: }
                    340: 
                    341: int get_redirect_rule_by_index(
                    342:        int index,
1.1.1.3 ! misho     343:        char * ifname,
1.1       misho     344:        unsigned short * eport,
1.1.1.3 ! misho     345:        char * iaddr,
        !           346:        int iaddrlen,
1.1       misho     347:        unsigned short * iport,
1.1.1.3 ! misho     348:        int * proto,
        !           349:        char * desc,
1.1       misho     350:        int desclen,
1.1.1.2   misho     351:        char * rhost,
                    352:        int rhostlen,
                    353:        unsigned int * timestamp,
1.1.1.3 ! misho     354:        u_int64_t * packets,
1.1       misho     355:        u_int64_t * bytes)
                    356: {
                    357:        int total_rules = 0;
                    358:        struct ip_fw * rules = NULL;
                    359: 
1.1.1.3 ! misho     360:        if (index < 0) /* TODO shouldn't we also validate the maximum? */
1.1       misho     361:                return -1;
                    362: 
1.1.1.2   misho     363:        if(timestamp)
                    364:                *timestamp = 0;
                    365: 
1.1       misho     366:        ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
                    367: 
1.1.1.2   misho     368:        if (total_rules > index) {
1.1       misho     369:                const struct ip_fw const * ptr = &rules[index];
1.1.1.3 ! misho     370:                if (ptr->fw_prot == 0)  /* invalid rule */
1.1.1.2   misho     371:                        goto error;
1.1       misho     372:                if (proto != NULL)
                    373:                        *proto = ptr->fw_prot;
                    374:                if (eport != NULL)
                    375:                        *eport = ptr->fw_uar.fw_pts[0];
                    376:                if (iport != NULL)
                    377:                        *iport = ptr->fw_fwd_ip.sin_port;
                    378:                if (ifname != NULL)
                    379:                        strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
                    380:                if (packets != NULL)
                    381:                        *packets = ptr->fw_pcnt;
                    382:                if (bytes != NULL)
                    383:                        *bytes = ptr->fw_bcnt;
                    384:                if (iport != NULL)
                    385:                        *iport = ptr->fw_fwd_ip.sin_port;
                    386:                if (iaddr != NULL && iaddrlen > 0) {
1.1.1.2   misho     387:                        /* looks like fw_out_if.fu_via_ip is zero */
1.1.1.3 ! misho     388:                        /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
1.1.1.2   misho     389:                        if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
                    390:                                syslog(LOG_ERR, "inet_ntop(): %m");
                    391:                                goto error;
1.1.1.3 ! misho     392:                        }
1.1.1.2   misho     393:                }
                    394:                if (rhost != NULL && rhostlen > 0) {
                    395:                        if (ptr->fw_src.s_addr == 0)
                    396:                                rhost[0] = '\0';
                    397:                        else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
1.1       misho     398:                                syslog(LOG_ERR, "inet_ntop(): %m");
                    399:                                goto error;
1.1.1.3 ! misho     400:                        }
1.1       misho     401:                }
                    402:                ipfw_free_ruleset(&rules);
1.1.1.2   misho     403:                get_desc_time(*eport, *proto, desc, desclen, timestamp);
1.1       misho     404:                return 0;
                    405:        }
                    406: 
                    407: error:
                    408:        if (rules != NULL)
1.1.1.3 ! misho     409:                ipfw_free_ruleset(&rules);
        !           410:        return -1;
1.1       misho     411: }
1.1.1.2   misho     412: 
                    413: /* upnp_get_portmappings_in_range()
                    414:  * return a list of all "external" ports for which a port
                    415:  * mapping exists */
                    416: unsigned short *
                    417: get_portmappings_in_range(unsigned short startport,
                    418:                           unsigned short endport,
                    419:                           int proto,
                    420:                           unsigned int * number)
                    421: {
                    422:        unsigned short * array = NULL;
                    423:        unsigned int capacity = 128;
                    424:        int i, count_rules, total_rules = 0;
                    425:        struct ip_fw * rules = NULL;
1.1.1.3 ! misho     426: 
1.1.1.2   misho     427:        if (ipfw_validate_protocol(proto) < 0)
                    428:                return NULL;
1.1.1.3 ! misho     429: 
1.1.1.2   misho     430:        do {
                    431:                count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
                    432:                if (count_rules < 0)
                    433:                        goto error;
                    434:        } while (count_rules == 10);
1.1.1.3 ! misho     435: 
1.1.1.2   misho     436:        array = calloc(capacity, sizeof(unsigned short));
                    437:        if(!array) {
                    438:                syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
                    439:                 goto error;
                    440:        }
                    441:        *number = 0;
                    442: 
                    443:        for (i=0; i<total_rules-1; i++) {
                    444:                const struct ip_fw const * ptr = &rules[i];
                    445:                unsigned short eport = ptr->fw_uar.fw_pts[0];
                    446:                if (proto == ptr->fw_prot
                    447:                    && startport <= eport
                    448:                    && eport <= endport) {
                    449:                        if(*number >= capacity) {
                    450:                                capacity += 128;
                    451:                                array = realloc(array, sizeof(unsigned short)*capacity);
                    452:                                if(!array) {
                    453:                                        syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
                    454:                                        *number = 0;
                    455:                                        goto error;
                    456:                                }
                    457:                        }
                    458:                        array[*number] = eport;
                    459:                        (*number)++;
                    460:                }
                    461:        }
                    462: error:
                    463:        if (rules != NULL)
1.1.1.3 ! misho     464:                ipfw_free_ruleset(&rules);
1.1.1.2   misho     465:        return array;
                    466: }
                    467: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>