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

1.1.1.2 ! misho       1: /* $Id: ipfwrdr.c,v 1.11 2011/07/12 19:17:05 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.2 ! misho       6:  * (c) 2011 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: 
                     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: //
                     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;
                    173:        
                    174:        memset(&rule, 0, sizeof(struct ip_fw));
                    175:        rule.version = IP_FW_CURRENT_API_VERSION;
                    176:        //rule.fw_number = 1000; // rule number
1.1.1.2 ! misho     177:        //rule.context = (void *)desc; // The description is kept in a separate list
1.1       misho     178:        rule.fw_prot = proto; // protocol
                    179:        rule.fw_flg |= IP_FW_F_IIFACE; // interfaces to check
                    180:        rule.fw_flg |= IP_FW_F_IIFNAME; // interfaces to check by name
                    181:        rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); // packet direction
                    182:        rule.fw_flg |= IP_FW_F_FWD; // forward action
                    183: #ifdef USE_IFNAME_IN_RULES
                    184:        if (ifname != NULL) {
1.1.1.2 ! misho     185:                strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); // src interface
1.1       misho     186:                rule.fw_in_if.fu_via_if.unit = -1;
                    187:        }
                    188: #endif
                    189:        if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
                    190:                syslog(LOG_ERR, "inet_aton(): %m");
                    191:                return -1;
                    192:        }       
                    193:        memcpy(&rule.fw_dst,  &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
                    194:        memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
1.1.1.2 ! misho     195:        rule.fw_dmsk.s_addr = INADDR_BROADCAST; //TODO check this
1.1       misho     196:        IP_FW_SETNDSTP(&rule, 1); // number of external ports
                    197:        rule.fw_uar.fw_pts[0] = eport; // external port
                    198:        rule.fw_fwd_ip.sin_port = iport; // internal port
1.1.1.2 ! misho     199:        if (rhost && rhost[0] != '\0') {
        !           200:                inet_aton(rhost, &rule.fw_src);
        !           201:                rule.fw_smsk.s_addr = htonl(INADDR_NONE);
        !           202:        }
1.1       misho     203: 
1.1.1.2 ! misho     204:        r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
        !           205:        if(r >= 0)
        !           206:                add_desc_time(eport, proto, desc, timestamp);
        !           207:        return r;
1.1       misho     208: }
                    209: 
                    210: /* get_redirect_rule()
                    211:  * return value : 0 success (found)
                    212:  * -1 = error or rule not found */
                    213: int get_redirect_rule(
                    214:        const char * ifname,
                    215:        unsigned short eport,
                    216:        int proto,
                    217:        char * iaddr, 
                    218:        int iaddrlen, 
                    219:        unsigned short * iport,
                    220:        char * desc, 
                    221:        int desclen,
1.1.1.2 ! misho     222:        char * rhost,
        !           223:        int rhostlen,
        !           224:        unsigned int * timestamp,
1.1       misho     225:        u_int64_t * packets,
                    226:        u_int64_t * bytes)
                    227: {
                    228:        int i, count_rules, total_rules = 0;
                    229:        struct ip_fw * rules = NULL;
                    230:        
                    231:        if (ipfw_validate_protocol(proto) < 0)
                    232:                return -1;
                    233:        if (ipfw_validate_ifname(ifname) < 0)
                    234:                return -1;
1.1.1.2 ! misho     235:        if (timestamp)
        !           236:                *timestamp = 0;
        !           237: 
1.1       misho     238:        do {
                    239:                count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
                    240:                if (count_rules < 0)
                    241:                        goto error;
                    242:        } while (count_rules == 10);
                    243:        
                    244:        for (i=0; i<total_rules-1; i++) {
                    245:                const struct ip_fw const * ptr = &rules[i];
                    246:                if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
                    247:                        if (packets != NULL)
                    248:                                *packets = ptr->fw_pcnt;
                    249:                        if (bytes != NULL)
                    250:                                *bytes = ptr->fw_bcnt;
                    251:                        if (iport != NULL)
                    252:                                *iport = ptr->fw_fwd_ip.sin_port;
                    253:                        if (iaddr != NULL && iaddrlen > 0) {
1.1.1.2 ! misho     254:                                /* looks like fw_out_if.fu_via_ip is zero */
        !           255:                                //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
        !           256:                                if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
        !           257:                                        syslog(LOG_ERR, "inet_ntop(): %m");
        !           258:                                        goto error;
        !           259:                                }                       
        !           260:                        }
        !           261:                        if (rhost != NULL && rhostlen > 0) {
        !           262:                                if (ptr->fw_src.s_addr == 0)
        !           263:                                        rhost[0] = '\0';
        !           264:                                else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
1.1       misho     265:                                        syslog(LOG_ERR, "inet_ntop(): %m");
                    266:                                        goto error;
                    267:                                }                       
                    268:                        }
                    269:                        // And what if we found more than 1 matching rule?
                    270:                        ipfw_free_ruleset(&rules);
1.1.1.2 ! misho     271:                        get_desc_time(eport, proto, desc, desclen, timestamp);
1.1       misho     272:                        return 0;
                    273:                }
                    274:        }
                    275: 
                    276: error:
                    277:        if (rules != NULL)
                    278:                ipfw_free_ruleset(&rules);      
                    279:        return -1;
                    280: }
                    281: 
                    282: int delete_redirect_rule(
                    283:        const char * ifname,
                    284:        unsigned short eport,
                    285:        int proto) 
                    286: {
                    287:        int i, count_rules, total_rules = 0;
                    288:        struct ip_fw * rules = NULL;
                    289:        
                    290:        if (ipfw_validate_protocol(proto) < 0)
                    291:                return -1;
                    292:        if (ipfw_validate_ifname(ifname) < 0)
                    293:                return -1;
                    294:        
                    295:        do {
                    296:                count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
                    297:                if (count_rules < 0)
                    298:                        goto error;
                    299:        } while (count_rules == 10);
                    300:        
                    301:        for (i=0; i<total_rules-1; i++) {
                    302:                const struct ip_fw const * ptr = &rules[i];
                    303:                if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
                    304:                        if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
                    305:                                goto error;
                    306:                        // And what if we found more than 1 matching rule?
                    307:                        ipfw_free_ruleset(&rules);
1.1.1.2 ! misho     308:                        del_desc_time(eport, proto);
1.1       misho     309:                        return 0;
                    310:                }
                    311:        }
                    312:        
                    313: error:
                    314:        if (rules != NULL)
                    315:                ipfw_free_ruleset(&rules);      
                    316:        return -1;
                    317: }
                    318: 
                    319: int add_filter_rule2(
                    320:        const char * ifname, 
1.1.1.2 ! misho     321:        const char * rhost,
1.1       misho     322:        const char * iaddr,
                    323:        unsigned short eport, 
                    324:        unsigned short iport,
                    325:        int proto, 
                    326:        const char * desc)
                    327: {
1.1.1.2 ! misho     328:        //return -1;
        !           329:        return 0; /* nothing to do, always success */
1.1       misho     330: }
                    331: 
                    332: int delete_filter_rule(
                    333:        const char * ifname, 
                    334:        unsigned short eport, 
                    335:        int proto) 
                    336: {
1.1.1.2 ! misho     337:        //return -1;
        !           338:        return 0; /* nothing to do, always success */
1.1       misho     339: }
                    340: 
                    341: int get_redirect_rule_by_index(
                    342:        int index,
                    343:        char * ifname, 
                    344:        unsigned short * eport,
                    345:        char * iaddr, 
                    346:        int iaddrlen, 
                    347:        unsigned short * iport,
                    348:        int * proto, 
                    349:        char * desc, 
                    350:        int desclen,
1.1.1.2 ! misho     351:        char * rhost,
        !           352:        int rhostlen,
        !           353:        unsigned int * timestamp,
1.1       misho     354:        u_int64_t * packets, 
                    355:        u_int64_t * bytes)
                    356: {
                    357:        int total_rules = 0;
                    358:        struct ip_fw * rules = NULL;
                    359: 
                    360:        if (index < 0) // TODO shouldn't we also validate the maximum?
                    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.2 ! misho     370:                if (ptr->fw_prot == 0)  // invalid rule
        !           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 */
        !           388:                        //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
        !           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;
        !           392:                        }                       
        !           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;
                    400:                        }                       
                    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)
                    409:                ipfw_free_ruleset(&rules);      
                    410:        return -1;      
                    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;
        !           426:        
        !           427:        if (ipfw_validate_protocol(proto) < 0)
        !           428:                return NULL;
        !           429:        
        !           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);
        !           435:        
        !           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)
        !           464:                ipfw_free_ruleset(&rules);      
        !           465:        return array;
        !           466: }
        !           467: 

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