Annotation of embedaddon/miniupnpd/netfilter/iptcrdr.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* $Id: iptcrdr.c,v 1.50 2012/10/03 14:49:08 nanard Exp $ */
1.1       misho       2: /* MiniUPnP project
                      3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
1.1.1.2   misho       4:  * (c) 2006-2011 Thomas Bernard
1.1       misho       5:  * This software is subject to the conditions detailed
                      6:  * in the LICENCE file provided within the distribution */
                      7: #include <stdio.h>
                      8: #include <stdlib.h>
                      9: #include <string.h>
                     10: #include <syslog.h>
                     11: #include <sys/errno.h>
                     12: #include <sys/socket.h>
                     13: #include <netinet/in.h>
                     14: #include <arpa/inet.h>
                     15: #include <dlfcn.h>
1.1.1.3 ! misho      16: #include <xtables.h>
1.1       misho      17: #include <libiptc/libiptc.h>
                     18: 
                     19: #include <linux/version.h>
                     20: 
                     21: #if IPTABLES_143
                     22: /* IPTABLES API version >= 1.4.3 */
1.1.1.2   misho      23: 
                     24: /* added in order to compile on gentoo :
                     25:  * http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=2183 */
                     26: #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
                     27: #define __must_be_array(a) \
                     28:        BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
                     29: #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
                     30: #define LIST_POISON2  ((void *) 0x00200200 )
                     31: 
1.1.1.3 ! misho      32: #if 0
        !            33: #include <linux/netfilter/nf_nat.h>
        !            34: #else
        !            35: #include "tiny_nf_nat.h"
        !            36: #endif
1.1       misho      37: #define ip_nat_multi_range     nf_nat_multi_range
                     38: #define ip_nat_range           nf_nat_range
                     39: #define IPTC_HANDLE            struct iptc_handle *
                     40: #else
                     41: /* IPTABLES API version < 1.4.3 */
                     42: #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
                     43: #include <linux/netfilter_ipv4/ip_nat.h>
                     44: #else
1.1.1.3 ! misho      45: #if 0
1.1       misho      46: #include <linux/netfilter/nf_nat.h>
1.1.1.3 ! misho      47: #else
        !            48: #include "tiny_nf_nat.h"
        !            49: #endif
1.1       misho      50: #endif
                     51: #define IPTC_HANDLE            iptc_handle_t
                     52: #endif
                     53: 
1.1.1.2   misho      54: /* IPT_ALIGN was renamed XT_ALIGN in iptables-1.4.11 */
                     55: #ifndef IPT_ALIGN
                     56: #define IPT_ALIGN XT_ALIGN
                     57: #endif
                     58: 
1.1.1.3 ! misho      59: #include "../macros.h"
        !            60: #include "../config.h"
1.1       misho      61: #include "iptcrdr.h"
                     62: #include "../upnpglobalvars.h"
                     63: 
1.1.1.2   misho      64: /* local functions declarations */
                     65: static int
                     66: addnatrule(int proto, unsigned short eport,
                     67:            const char * iaddr, unsigned short iport,
                     68:            const char * rhost);
                     69: 
                     70: static int
                     71: add_filter_rule(int proto, const char * rhost,
                     72:                 const char * iaddr, unsigned short iport);
                     73: 
1.1       misho      74: /* dummy init and shutdown functions */
                     75: int init_redirect(void)
                     76: {
                     77:        return 0;
                     78: }
                     79: 
                     80: void shutdown_redirect(void)
                     81: {
                     82:        return;
                     83: }
                     84: 
                     85: /* convert an ip address to string */
                     86: static int snprintip(char * dst, size_t size, uint32_t ip)
                     87: {
                     88:        return snprintf(dst, size,
                     89:               "%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
                     90:               (ip >> 8) & 0xff, ip & 0xff);
                     91: }
                     92: 
                     93: /* netfilter cannot store redirection descriptions, so we use our
                     94:  * own structure to store them */
                     95: struct rdr_desc {
                     96:        struct rdr_desc * next;
1.1.1.2   misho      97:        unsigned int timestamp;
1.1       misho      98:        unsigned short eport;
                     99:        short proto;
                    100:        char str[];
                    101: };
                    102: 
                    103: /* pointer to the chained list where descriptions are stored */
                    104: static struct rdr_desc * rdr_desc_list = 0;
                    105: 
1.1.1.2   misho     106: /* add a description to the list of redirection descriptions */
1.1       misho     107: static void
1.1.1.2   misho     108: add_redirect_desc(unsigned short eport, int proto,
                    109:                   const char * desc, unsigned int timestamp)
1.1       misho     110: {
                    111:        struct rdr_desc * p;
                    112:        size_t l;
1.1.1.2   misho     113:        /* set a default description if none given */
                    114:        if(!desc)
                    115:                desc = "miniupnpd";
                    116:        l = strlen(desc) + 1;
                    117:        p = malloc(sizeof(struct rdr_desc) + l);
                    118:        if(p)
                    119:        {
                    120:                p->next = rdr_desc_list;
                    121:                p->timestamp = timestamp;
                    122:                p->eport = eport;
                    123:                p->proto = (short)proto;
                    124:                memcpy(p->str, desc, l);
                    125:                rdr_desc_list = p;
                    126:        }
1.1       misho     127: }
                    128: 
1.1.1.2   misho     129: /* delete a description from the list */
1.1       misho     130: static void
                    131: del_redirect_desc(unsigned short eport, int proto)
                    132: {
                    133:        struct rdr_desc * p, * last;
                    134:        p = rdr_desc_list;
                    135:        last = 0;
                    136:        while(p)
                    137:        {
                    138:                if(p->eport == eport && p->proto == proto)
                    139:                {
                    140:                        if(!last)
                    141:                                rdr_desc_list = p->next;
                    142:                        else
                    143:                                last->next = p->next;
                    144:                        free(p);
                    145:                        return;
                    146:                }
                    147:                last = p;
                    148:                p = p->next;
                    149:        }
                    150: }
                    151: 
1.1.1.2   misho     152: /* go through the list to find the description */
1.1       misho     153: static void
                    154: get_redirect_desc(unsigned short eport, int proto,
1.1.1.2   misho     155:                   char * desc, int desclen,
                    156:                   unsigned int * timestamp)
1.1       misho     157: {
                    158:        struct rdr_desc * p;
                    159:        for(p = rdr_desc_list; p; p = p->next)
                    160:        {
                    161:                if(p->eport == eport && p->proto == (short)proto)
                    162:                {
1.1.1.2   misho     163:                        if(desc)
                    164:                                strncpy(desc, p->str, desclen);
                    165:                        if(timestamp)
                    166:                                *timestamp = p->timestamp;
1.1       misho     167:                        return;
                    168:                }
                    169:        }
                    170:        /* if no description was found, return miniupnpd as default */
1.1.1.2   misho     171:        if(desc)
                    172:                strncpy(desc, "miniupnpd", desclen);
                    173:        if(timestamp)
                    174:                *timestamp = 0;
1.1       misho     175: }
                    176: 
1.1.1.2   misho     177: #if USE_INDEX_FROM_DESC_LIST
                    178: static int
1.1       misho     179: get_redirect_desc_by_index(int index, unsigned short * eport, int * proto,
1.1.1.2   misho     180:                   char * desc, int desclen, unsigned int * timestamp)
1.1       misho     181: {
                    182:        int i = 0;
                    183:        struct rdr_desc * p;
                    184:        if(!desc || (desclen == 0))
                    185:                return -1;
                    186:        for(p = rdr_desc_list; p; p = p->next, i++)
                    187:        {
                    188:                if(i == index)
                    189:                {
                    190:                        *eport = p->eport;
                    191:                        *proto = (int)p->proto;
                    192:                        strncpy(desc, p->str, desclen);
1.1.1.2   misho     193:                        if(timestamp)
                    194:                                *timestamp = p->timestamp;
1.1       misho     195:                        return 0;
                    196:                }
                    197:        }
                    198:        return -1;
                    199: }
1.1.1.2   misho     200: #endif
1.1       misho     201: 
                    202: /* add_redirect_rule2() */
                    203: int
1.1.1.2   misho     204: add_redirect_rule2(const char * ifname,
                    205:                    const char * rhost, unsigned short eport,
1.1       misho     206:                    const char * iaddr, unsigned short iport, int proto,
1.1.1.2   misho     207:                                   const char * desc, unsigned int timestamp)
1.1       misho     208: {
1.1.1.3 ! misho     209:        int r;
        !           210:        UNUSED(ifname);
        !           211: 
        !           212:        r = addnatrule(proto, eport, iaddr, iport, rhost);
1.1       misho     213:        if(r >= 0)
1.1.1.2   misho     214:                add_redirect_desc(eport, proto, desc, timestamp);
1.1       misho     215:        return r;
                    216: }
                    217: 
                    218: int
1.1.1.2   misho     219: add_filter_rule2(const char * ifname,
                    220:                  const char * rhost, const char * iaddr,
1.1       misho     221:                  unsigned short eport, unsigned short iport,
                    222:                  int proto, const char * desc)
                    223: {
1.1.1.3 ! misho     224:        UNUSED(ifname);
        !           225:        UNUSED(eport);
        !           226:        UNUSED(desc);
        !           227: 
1.1.1.2   misho     228:        return add_filter_rule(proto, rhost, iaddr, iport);
1.1       misho     229: }
                    230: 
1.1.1.3 ! misho     231: /* get_redirect_rule()
1.1       misho     232:  * returns -1 if the rule is not found */
                    233: int
                    234: get_redirect_rule(const char * ifname, unsigned short eport, int proto,
                    235:                   char * iaddr, int iaddrlen, unsigned short * iport,
                    236:                   char * desc, int desclen,
1.1.1.2   misho     237:                   char * rhost, int rhostlen,
                    238:                   unsigned int * timestamp,
1.1       misho     239:                   u_int64_t * packets, u_int64_t * bytes)
                    240: {
                    241:        int r = -1;
                    242:        IPTC_HANDLE h;
                    243:        const struct ipt_entry * e;
                    244:        const struct ipt_entry_target * target;
                    245:        const struct ip_nat_multi_range * mr;
                    246:        const struct ipt_entry_match *match;
1.1.1.3 ! misho     247:        UNUSED(ifname);
1.1       misho     248: 
                    249:        h = iptc_init("nat");
                    250:        if(!h)
                    251:        {
                    252:                syslog(LOG_ERR, "get_redirect_rule() : "
                    253:                                "iptc_init() failed : %s",
                    254:                       iptc_strerror(errno));
                    255:                return -1;
                    256:        }
                    257:        if(!iptc_is_chain(miniupnpd_nat_chain, h))
                    258:        {
                    259:                syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
                    260:        }
                    261:        else
                    262:        {
                    263: #ifdef IPTABLES_143
                    264:                for(e = iptc_first_rule(miniupnpd_nat_chain, h);
                    265:                    e;
                    266:                        e = iptc_next_rule(e, h))
                    267: #else
                    268:                for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
                    269:                    e;
                    270:                        e = iptc_next_rule(e, &h))
                    271: #endif
                    272:                {
                    273:                        if(proto==e->ip.proto)
                    274:                        {
                    275:                                match = (const struct ipt_entry_match *)&e->elems;
                    276:                                if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    277:                                {
                    278:                                        const struct ipt_tcp * info;
                    279:                                        info = (const struct ipt_tcp *)match->data;
                    280:                                        if(eport != info->dpts[0])
                    281:                                                continue;
                    282:                                }
                    283:                                else
                    284:                                {
                    285:                                        const struct ipt_udp * info;
                    286:                                        info = (const struct ipt_udp *)match->data;
                    287:                                        if(eport != info->dpts[0])
                    288:                                                continue;
                    289:                                }
                    290:                                target = (void *)e + e->target_offset;
1.1.1.3 ! misho     291:                                /* target = ipt_get_target(e); */
1.1       misho     292:                                mr = (const struct ip_nat_multi_range *)&target->data[0];
                    293:                                snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
                    294:                                *iport = ntohs(mr->range[0].min.all);
1.1.1.2   misho     295:                                get_redirect_desc(eport, proto, desc, desclen, timestamp);
1.1       misho     296:                                if(packets)
                    297:                                        *packets = e->counters.pcnt;
                    298:                                if(bytes)
                    299:                                        *bytes = e->counters.bcnt;
1.1.1.2   misho     300:                                /* rhost */
                    301:                                if(e->ip.src.s_addr && rhost) {
                    302:                                        snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
                    303:                                }
1.1       misho     304:                                r = 0;
                    305:                                break;
                    306:                        }
                    307:                }
                    308:        }
                    309:        if(h)
                    310: #ifdef IPTABLES_143
                    311:                iptc_free(h);
                    312: #else
                    313:                iptc_free(&h);
                    314: #endif
                    315:        return r;
                    316: }
                    317: 
1.1.1.3 ! misho     318: /* get_redirect_rule_by_index()
1.1       misho     319:  * return -1 when the rule was not found */
                    320: int
                    321: get_redirect_rule_by_index(int index,
                    322:                            char * ifname, unsigned short * eport,
                    323:                            char * iaddr, int iaddrlen, unsigned short * iport,
                    324:                            int * proto, char * desc, int desclen,
1.1.1.2   misho     325:                            char * rhost, int rhostlen,
                    326:                            unsigned int * timestamp,
1.1       misho     327:                            u_int64_t * packets, u_int64_t * bytes)
                    328: {
                    329:        int r = -1;
1.1.1.2   misho     330: #if USE_INDEX_FROM_DESC_LIST
                    331:        r = get_redirect_desc_by_index(index, eport, proto,
                    332:                                       desc, desclen, timestamp);
1.1       misho     333:        if (r==0)
1.1.1.2   misho     334:        {
1.1       misho     335:                r = get_redirect_rule(ifname, *eport, *proto, iaddr, iaddrlen, iport,
                    336:                                      0, 0, packets, bytes);
1.1.1.2   misho     337:        }
                    338: #else
1.1       misho     339:        int i = 0;
                    340:        IPTC_HANDLE h;
                    341:        const struct ipt_entry * e;
                    342:        const struct ipt_entry_target * target;
                    343:        const struct ip_nat_multi_range * mr;
                    344:        const struct ipt_entry_match *match;
1.1.1.3 ! misho     345:        UNUSED(ifname);
1.1       misho     346: 
                    347:        h = iptc_init("nat");
                    348:        if(!h)
                    349:        {
                    350:                syslog(LOG_ERR, "get_redirect_rule_by_index() : "
                    351:                                "iptc_init() failed : %s",
                    352:                       iptc_strerror(errno));
                    353:                return -1;
                    354:        }
                    355:        if(!iptc_is_chain(miniupnpd_nat_chain, h))
                    356:        {
                    357:                syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
                    358:        }
                    359:        else
                    360:        {
                    361: #ifdef IPTABLES_143
                    362:                for(e = iptc_first_rule(miniupnpd_nat_chain, h);
                    363:                    e;
                    364:                        e = iptc_next_rule(e, h))
                    365: #else
                    366:                for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
                    367:                    e;
                    368:                        e = iptc_next_rule(e, &h))
                    369: #endif
                    370:                {
                    371:                        if(i==index)
                    372:                        {
                    373:                                *proto = e->ip.proto;
                    374:                                match = (const struct ipt_entry_match *)&e->elems;
                    375:                                if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    376:                                {
                    377:                                        const struct ipt_tcp * info;
                    378:                                        info = (const struct ipt_tcp *)match->data;
                    379:                                        *eport = info->dpts[0];
                    380:                                }
                    381:                                else
                    382:                                {
                    383:                                        const struct ipt_udp * info;
                    384:                                        info = (const struct ipt_udp *)match->data;
                    385:                                        *eport = info->dpts[0];
                    386:                                }
                    387:                                target = (void *)e + e->target_offset;
                    388:                                mr = (const struct ip_nat_multi_range *)&target->data[0];
                    389:                                snprintip(iaddr, iaddrlen, ntohl(mr->range[0].min_ip));
                    390:                                *iport = ntohs(mr->range[0].min.all);
1.1.1.2   misho     391:                                get_redirect_desc(*eport, *proto, desc, desclen, timestamp);
1.1       misho     392:                                if(packets)
                    393:                                        *packets = e->counters.pcnt;
                    394:                                if(bytes)
                    395:                                        *bytes = e->counters.bcnt;
1.1.1.2   misho     396:                                /* rhost */
                    397:                                if(rhost && rhostlen > 0) {
                    398:                                        if(e->ip.src.s_addr) {
                    399:                                                snprintip(rhost, rhostlen, ntohl(e->ip.src.s_addr));
                    400:                                        } else {
                    401:                                                rhost[0] = '\0';
                    402:                                        }
                    403:                                }
1.1       misho     404:                                r = 0;
                    405:                                break;
                    406:                        }
                    407:                        i++;
                    408:                }
                    409:        }
                    410:        if(h)
                    411: #ifdef IPTABLES_143
                    412:                iptc_free(h);
                    413: #else
                    414:                iptc_free(&h);
                    415: #endif
1.1.1.2   misho     416: #endif
1.1       misho     417:        return r;
                    418: }
                    419: 
                    420: /* delete_rule_and_commit() :
                    421:  * subfunction used in delete_redirect_and_filter_rules() */
1.1.1.2   misho     422: static int
                    423: delete_rule_and_commit(unsigned int index, IPTC_HANDLE h,
                    424:                        const char * miniupnpd_chain,
                    425:                        const char * logcaller)
                    426: {
                    427:        int r = 0;
                    428: #ifdef IPTABLES_143
                    429:        if(!iptc_delete_num_entry(miniupnpd_chain, index, h))
                    430: #else
                    431:        if(!iptc_delete_num_entry(miniupnpd_chain, index, &h))
                    432: #endif
                    433:        {
                    434:                syslog(LOG_ERR, "%s() : iptc_delete_num_entry(): %s\n",
                    435:                   logcaller, iptc_strerror(errno));
                    436:                r = -1;
                    437:        }
                    438: #ifdef IPTABLES_143
                    439:        else if(!iptc_commit(h))
                    440: #else
                    441:        else if(!iptc_commit(&h))
                    442: #endif
                    443:        {
                    444:                syslog(LOG_ERR, "%s() : iptc_commit(): %s\n",
                    445:                   logcaller, iptc_strerror(errno));
                    446:                r = -1;
                    447:        }
                    448:        if(h)
                    449: #ifdef IPTABLES_143
                    450:                iptc_free(h);
                    451: #else
                    452:                iptc_free(&h);
                    453: #endif
                    454:        return r;
                    455: }
                    456: 
                    457: /* delete_redirect_and_filter_rules()
                    458:  */
1.1       misho     459: int
1.1.1.2   misho     460: delete_redirect_and_filter_rules(unsigned short eport, int proto)
1.1       misho     461: {
                    462:        int r = -1;
                    463:        unsigned index = 0;
                    464:        unsigned i = 0;
                    465:        IPTC_HANDLE h;
                    466:        const struct ipt_entry * e;
1.1.1.2   misho     467:        const struct ipt_entry_target * target;
                    468:        const struct ip_nat_multi_range * mr;
1.1       misho     469:        const struct ipt_entry_match *match;
1.1.1.2   misho     470:        unsigned short iport = 0;
                    471:        uint32_t iaddr = 0;
1.1       misho     472: 
1.1.1.2   misho     473:        h = iptc_init("nat");
1.1       misho     474:        if(!h)
                    475:        {
1.1.1.2   misho     476:                syslog(LOG_ERR, "delete_redirect_and_filter_rules() : "
                    477:                                "iptc_init() failed : %s",
1.1       misho     478:                       iptc_strerror(errno));
                    479:                return -1;
                    480:        }
1.1.1.2   misho     481:        /* First step : find the right nat rule */
                    482:        if(!iptc_is_chain(miniupnpd_nat_chain, h))
1.1       misho     483:        {
1.1.1.2   misho     484:                syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
1.1       misho     485:        }
                    486:        else
                    487:        {
                    488: #ifdef IPTABLES_143
1.1.1.2   misho     489:                for(e = iptc_first_rule(miniupnpd_nat_chain, h);
1.1       misho     490:                    e;
                    491:                        e = iptc_next_rule(e, h), i++)
                    492: #else
1.1.1.2   misho     493:                for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
1.1       misho     494:                    e;
                    495:                        e = iptc_next_rule(e, &h), i++)
                    496: #endif
                    497:                {
                    498:                        if(proto==e->ip.proto)
                    499:                        {
                    500:                                match = (const struct ipt_entry_match *)&e->elems;
                    501:                                if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    502:                                {
                    503:                                        const struct ipt_tcp * info;
                    504:                                        info = (const struct ipt_tcp *)match->data;
                    505:                                        if(eport != info->dpts[0])
                    506:                                                continue;
                    507:                                }
                    508:                                else
                    509:                                {
                    510:                                        const struct ipt_udp * info;
                    511:                                        info = (const struct ipt_udp *)match->data;
                    512:                                        if(eport != info->dpts[0])
                    513:                                                continue;
                    514:                                }
1.1.1.2   misho     515:                                /* get the index, the internal address and the internal port
                    516:                                 * of the rule */
1.1       misho     517:                                index = i;
1.1.1.2   misho     518:                                target = (void *)e + e->target_offset;
                    519:                                mr = (const struct ip_nat_multi_range *)&target->data[0];
                    520:                                iaddr = mr->range[0].min_ip;
                    521:                                iport = ntohs(mr->range[0].min.all);
                    522:                                r = 0;
1.1       misho     523:                                break;
                    524:                        }
                    525:                }
                    526:        }
                    527:        if(h)
                    528: #ifdef IPTABLES_143
                    529:                iptc_free(h);
                    530: #else
                    531:                iptc_free(&h);
                    532: #endif
1.1.1.2   misho     533:        if(r == 0)
1.1       misho     534:        {
1.1.1.2   misho     535:                syslog(LOG_INFO, "Trying to delete nat rule at index %u", index);
                    536:                /* Now delete both rules */
                    537:                /* first delete the nat rule */
                    538:                h = iptc_init("nat");
                    539:                if(h)
                    540:                {
                    541:                        r = delete_rule_and_commit(index, h, miniupnpd_nat_chain, "delete_redirect_rule");
                    542:                }
                    543:                if((r == 0) && (h = iptc_init("filter")))
                    544:                {
                    545:                        i = 0;
                    546:                        /* we must find the right index for the filter rule */
1.1       misho     547: #ifdef IPTABLES_143
1.1.1.2   misho     548:                        for(e = iptc_first_rule(miniupnpd_forward_chain, h);
                    549:                            e;
                    550:                                e = iptc_next_rule(e, h), i++)
1.1       misho     551: #else
1.1.1.2   misho     552:                        for(e = iptc_first_rule(miniupnpd_forward_chain, &h);
                    553:                            e;
                    554:                                e = iptc_next_rule(e, &h), i++)
1.1       misho     555: #endif
1.1.1.2   misho     556:                        {
                    557:                                if(proto==e->ip.proto)
                    558:                                {
                    559:                                        match = (const struct ipt_entry_match *)&e->elems;
                    560:                                        /*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
                    561:                                               i, match->u.user.name, inet_ntoa(e->ip.dst));*/
                    562:                                        if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    563:                                        {
                    564:                                                const struct ipt_tcp * info;
                    565:                                                info = (const struct ipt_tcp *)match->data;
                    566:                                                if(iport != info->dpts[0])
                    567:                                                        continue;
                    568:                                        }
                    569:                                        else
                    570:                                        {
                    571:                                                const struct ipt_udp * info;
                    572:                                                info = (const struct ipt_udp *)match->data;
                    573:                                                if(iport != info->dpts[0])
                    574:                                                        continue;
                    575:                                        }
                    576:                                        if(iaddr != e->ip.dst.s_addr)
                    577:                                                continue;
                    578:                                        index = i;
                    579:                                        break;
                    580:                                }
                    581:                        }
                    582:                        syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);
                    583:                        r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
                    584:                }
1.1       misho     585:        }
1.1.1.2   misho     586:        del_redirect_desc(eport, proto);
1.1       misho     587:        return r;
                    588: }
                    589: 
                    590: /* ==================================== */
1.1.1.3 ! misho     591: /* TODO : add the -m state --state NEW,ESTABLISHED,RELATED
1.1       misho     592:  * only for the filter rule */
                    593: static struct ipt_entry_match *
                    594: get_tcp_match(unsigned short dport)
                    595: {
                    596:        struct ipt_entry_match *match;
                    597:        struct ipt_tcp * tcpinfo;
                    598:        size_t size;
                    599:        size =   IPT_ALIGN(sizeof(struct ipt_entry_match))
                    600:               + IPT_ALIGN(sizeof(struct ipt_tcp));
                    601:        match = calloc(1, size);
                    602:        match->u.match_size = size;
1.1.1.2   misho     603:        strncpy(match->u.user.name, "tcp", sizeof(match->u.user.name));
1.1       misho     604:        tcpinfo = (struct ipt_tcp *)match->data;
                    605:        tcpinfo->spts[0] = 0;           /* all source ports */
                    606:        tcpinfo->spts[1] = 0xFFFF;
                    607:        tcpinfo->dpts[0] = dport;       /* specified destination port */
                    608:        tcpinfo->dpts[1] = dport;
                    609:        return match;
                    610: }
                    611: 
                    612: static struct ipt_entry_match *
                    613: get_udp_match(unsigned short dport)
                    614: {
                    615:        struct ipt_entry_match *match;
                    616:        struct ipt_udp * udpinfo;
                    617:        size_t size;
                    618:        size =   IPT_ALIGN(sizeof(struct ipt_entry_match))
                    619:               + IPT_ALIGN(sizeof(struct ipt_udp));
                    620:        match = calloc(1, size);
                    621:        match->u.match_size = size;
1.1.1.2   misho     622:        strncpy(match->u.user.name, "udp", sizeof(match->u.user.name));
1.1       misho     623:        udpinfo = (struct ipt_udp *)match->data;
                    624:        udpinfo->spts[0] = 0;           /* all source ports */
                    625:        udpinfo->spts[1] = 0xFFFF;
                    626:        udpinfo->dpts[0] = dport;       /* specified destination port */
                    627:        udpinfo->dpts[1] = dport;
                    628:        return match;
                    629: }
                    630: 
                    631: static struct ipt_entry_target *
                    632: get_dnat_target(const char * daddr, unsigned short dport)
                    633: {
                    634:        struct ipt_entry_target * target;
                    635:        struct ip_nat_multi_range * mr;
                    636:        struct ip_nat_range * range;
                    637:        size_t size;
                    638: 
                    639:        size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
                    640:               + IPT_ALIGN(sizeof(struct ip_nat_multi_range));
                    641:        target = calloc(1, size);
                    642:        target->u.target_size = size;
1.1.1.2   misho     643:        strncpy(target->u.user.name, "DNAT", sizeof(target->u.user.name));
1.1       misho     644:        /* one ip_nat_range already included in ip_nat_multi_range */
                    645:        mr = (struct ip_nat_multi_range *)&target->data[0];
                    646:        mr->rangesize = 1;
                    647:        range = &mr->range[0];
                    648:        range->min_ip = range->max_ip = inet_addr(daddr);
                    649:        range->flags |= IP_NAT_RANGE_MAP_IPS;
                    650:        range->min.all = range->max.all = htons(dport);
                    651:        range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
                    652:        return target;
                    653: }
                    654: 
                    655: /* iptc_init_verify_and_append()
                    656:  * return 0 on success, -1 on failure */
                    657: static int
1.1.1.2   misho     658: iptc_init_verify_and_append(const char * table,
                    659:                             const char * miniupnpd_chain,
                    660:                             struct ipt_entry * e,
1.1       misho     661:                             const char * logcaller)
                    662: {
                    663:        IPTC_HANDLE h;
                    664:        h = iptc_init(table);
                    665:        if(!h)
                    666:        {
                    667:                syslog(LOG_ERR, "%s : iptc_init() error : %s\n",
                    668:                       logcaller, iptc_strerror(errno));
                    669:                return -1;
                    670:        }
                    671:        if(!iptc_is_chain(miniupnpd_chain, h))
                    672:        {
1.1.1.2   misho     673:                syslog(LOG_ERR, "%s : chain %s not found",
                    674:                       logcaller, miniupnpd_chain);
1.1       misho     675:                if(h)
                    676: #ifdef IPTABLES_143
                    677:                        iptc_free(h);
                    678: #else
                    679:                        iptc_free(&h);
                    680: #endif
                    681:                return -1;
                    682:        }
                    683:        /* iptc_insert_entry(miniupnpd_chain, e, n, h/&h) could also be used */
                    684: #ifdef IPTABLES_143
                    685:        if(!iptc_append_entry(miniupnpd_chain, e, h))
                    686: #else
                    687:        if(!iptc_append_entry(miniupnpd_chain, e, &h))
                    688: #endif
                    689:        {
                    690:                syslog(LOG_ERR, "%s : iptc_append_entry() error : %s\n",
                    691:                       logcaller, iptc_strerror(errno));
                    692:                if(h)
                    693: #ifdef IPTABLES_143
                    694:                        iptc_free(h);
                    695: #else
                    696:                        iptc_free(&h);
                    697: #endif
                    698:                return -1;
                    699:        }
                    700: #ifdef IPTABLES_143
                    701:        if(!iptc_commit(h))
                    702: #else
                    703:        if(!iptc_commit(&h))
                    704: #endif
                    705:        {
                    706:                syslog(LOG_ERR, "%s : iptc_commit() error : %s\n",
                    707:                       logcaller, iptc_strerror(errno));
                    708:                if(h)
                    709: #ifdef IPTABLES_143
                    710:                        iptc_free(h);
                    711: #else
                    712:                        iptc_free(&h);
                    713: #endif
                    714:                return -1;
                    715:        }
                    716:        if(h)
                    717: #ifdef IPTABLES_143
                    718:                iptc_free(h);
                    719: #else
                    720:                iptc_free(&h);
                    721: #endif
                    722:        return 0;
                    723: }
                    724: 
1.1.1.3 ! misho     725: /* add nat rule
1.1       misho     726:  * iptables -t nat -A MINIUPNPD -p proto --dport eport -j DNAT --to iaddr:iport
                    727:  * */
1.1.1.2   misho     728: static int
1.1       misho     729: addnatrule(int proto, unsigned short eport,
1.1.1.2   misho     730:            const char * iaddr, unsigned short iport,
                    731:            const char * rhost)
1.1       misho     732: {
                    733:        int r = 0;
                    734:        struct ipt_entry * e;
                    735:        struct ipt_entry_match *match = NULL;
                    736:        struct ipt_entry_target *target = NULL;
                    737: 
                    738:        e = calloc(1, sizeof(struct ipt_entry));
                    739:        e->ip.proto = proto;
                    740:        if(proto == IPPROTO_TCP)
                    741:        {
                    742:                match = get_tcp_match(eport);
                    743:        }
                    744:        else
                    745:        {
                    746:                match = get_udp_match(eport);
                    747:        }
                    748:        e->nfcache = NFC_IP_DST_PT;
                    749:        target = get_dnat_target(iaddr, iport);
                    750:        e->nfcache |= NFC_UNKNOWN;
                    751:        e = realloc(e, sizeof(struct ipt_entry)
                    752:                       + match->u.match_size
                    753:                                   + target->u.target_size);
                    754:        memcpy(e->elems, match, match->u.match_size);
                    755:        memcpy(e->elems + match->u.match_size, target, target->u.target_size);
                    756:        e->target_offset = sizeof(struct ipt_entry)
                    757:                           + match->u.match_size;
                    758:        e->next_offset = sizeof(struct ipt_entry)
                    759:                         + match->u.match_size
                    760:                                         + target->u.target_size;
1.1.1.2   misho     761:        /* remote host */
                    762:        if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
                    763:        {
                    764:                e->ip.src.s_addr = inet_addr(rhost);
                    765:                e->ip.smsk.s_addr = INADDR_NONE;
                    766:        }
1.1.1.3 ! misho     767: 
1.1       misho     768:        r = iptc_init_verify_and_append("nat", miniupnpd_nat_chain, e, "addnatrule()");
                    769:        free(target);
                    770:        free(match);
                    771:        free(e);
                    772:        return r;
                    773: }
                    774: /* ================================= */
                    775: static struct ipt_entry_target *
                    776: get_accept_target(void)
                    777: {
                    778:        struct ipt_entry_target * target = NULL;
                    779:        size_t size;
                    780:        size =   IPT_ALIGN(sizeof(struct ipt_entry_target))
                    781:               + IPT_ALIGN(sizeof(int));
                    782:        target = calloc(1, size);
                    783:        target->u.user.target_size = size;
1.1.1.2   misho     784:        strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
1.1       misho     785:        return target;
                    786: }
                    787: 
                    788: /* add_filter_rule()
                    789:  * */
1.1.1.2   misho     790: static int
                    791: add_filter_rule(int proto, const char * rhost,
                    792:                 const char * iaddr, unsigned short iport)
1.1       misho     793: {
                    794:        int r = 0;
                    795:        struct ipt_entry * e;
                    796:        struct ipt_entry_match *match = NULL;
                    797:        struct ipt_entry_target *target = NULL;
                    798: 
                    799:        e = calloc(1, sizeof(struct ipt_entry));
                    800:        e->ip.proto = proto;
                    801:        if(proto == IPPROTO_TCP)
                    802:        {
                    803:                match = get_tcp_match(iport);
                    804:        }
                    805:        else
                    806:        {
                    807:                match = get_udp_match(iport);
                    808:        }
                    809:        e->nfcache = NFC_IP_DST_PT;
                    810:        e->ip.dst.s_addr = inet_addr(iaddr);
                    811:        e->ip.dmsk.s_addr = INADDR_NONE;
                    812:        target = get_accept_target();
                    813:        e->nfcache |= NFC_UNKNOWN;
                    814:        e = realloc(e, sizeof(struct ipt_entry)
                    815:                       + match->u.match_size
                    816:                                   + target->u.target_size);
                    817:        memcpy(e->elems, match, match->u.match_size);
                    818:        memcpy(e->elems + match->u.match_size, target, target->u.target_size);
                    819:        e->target_offset = sizeof(struct ipt_entry)
                    820:                           + match->u.match_size;
                    821:        e->next_offset = sizeof(struct ipt_entry)
                    822:                         + match->u.match_size
                    823:                                         + target->u.target_size;
1.1.1.2   misho     824:        /* remote host */
                    825:        if(rhost && (rhost[0] != '\0') && (0 != strcmp(rhost, "*")))
                    826:        {
                    827:                e->ip.src.s_addr = inet_addr(rhost);
                    828:                e->ip.smsk.s_addr = INADDR_NONE;
                    829:        }
1.1.1.3 ! misho     830: 
1.1       misho     831:        r = iptc_init_verify_and_append("filter", miniupnpd_forward_chain, e, "add_filter_rule()");
                    832:        free(target);
                    833:        free(match);
                    834:        free(e);
                    835:        return r;
                    836: }
                    837: 
1.1.1.2   misho     838: /* return an (malloc'ed) array of "external" port for which there is
                    839:  * a port mapping. number is the size of the array */
                    840: unsigned short *
                    841: get_portmappings_in_range(unsigned short startport, unsigned short endport,
                    842:                           int proto, unsigned int * number)
                    843: {
                    844:        unsigned short * array;
                    845:        unsigned int capacity;
                    846:        unsigned short eport;
                    847:        IPTC_HANDLE h;
                    848:        const struct ipt_entry * e;
                    849:        const struct ipt_entry_match *match;
                    850: 
                    851:        *number = 0;
                    852:        capacity = 128;
                    853:        array = calloc(capacity, sizeof(unsigned short));
                    854:        if(!array)
                    855:        {
                    856:                syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
                    857:                return NULL;
                    858:        }
                    859: 
                    860:        h = iptc_init("nat");
                    861:        if(!h)
                    862:        {
                    863:                syslog(LOG_ERR, "get_redirect_rule_by_index() : "
                    864:                                "iptc_init() failed : %s",
                    865:                       iptc_strerror(errno));
                    866:                free(array);
                    867:                return NULL;
                    868:        }
                    869:        if(!iptc_is_chain(miniupnpd_nat_chain, h))
                    870:        {
                    871:                syslog(LOG_ERR, "chain %s not found", miniupnpd_nat_chain);
                    872:                free(array);
                    873:                array = NULL;
                    874:        }
                    875:        else
                    876:        {
                    877: #ifdef IPTABLES_143
                    878:                for(e = iptc_first_rule(miniupnpd_nat_chain, h);
                    879:                    e;
                    880:                        e = iptc_next_rule(e, h))
                    881: #else
                    882:                for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
                    883:                    e;
                    884:                        e = iptc_next_rule(e, &h))
                    885: #endif
                    886:                {
                    887:                        if(proto == e->ip.proto)
                    888:                        {
                    889:                                match = (const struct ipt_entry_match *)&e->elems;
                    890:                                if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    891:                                {
                    892:                                        const struct ipt_tcp * info;
                    893:                                        info = (const struct ipt_tcp *)match->data;
                    894:                                        eport = info->dpts[0];
                    895:                                }
                    896:                                else
                    897:                                {
                    898:                                        const struct ipt_udp * info;
                    899:                                        info = (const struct ipt_udp *)match->data;
                    900:                                        eport = info->dpts[0];
                    901:                                }
                    902:                                if(startport <= eport && eport <= endport)
                    903:                                {
                    904:                                        if(*number >= capacity)
                    905:                                        {
                    906:                                                /* need to increase the capacity of the array */
                    907:                                                array = realloc(array, sizeof(unsigned short)*capacity);
                    908:                                                if(!array)
                    909:                                                {
                    910:                                                        syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%u) error",
                    911:                                                               (unsigned)sizeof(unsigned short)*capacity);
                    912:                                                        *number = 0;
                    913:                                                        break;
                    914:                                                }
                    915:                                                array[*number] = eport;
                    916:                                                (*number)++;
                    917:                                        }
                    918:                                }
                    919:                        }
                    920:                }
                    921:        }
                    922:        if(h)
                    923: #ifdef IPTABLES_143
                    924:                iptc_free(h);
                    925: #else
                    926:                iptc_free(&h);
                    927: #endif
                    928:        return array;
                    929: }
                    930: 
1.1       misho     931: /* ================================ */
1.1.1.2   misho     932: #ifdef DEBUG
1.1       misho     933: static int
                    934: print_match(const struct ipt_entry_match *match)
                    935: {
                    936:        printf("match %s\n", match->u.user.name);
                    937:        if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
                    938:        {
                    939:                struct ipt_tcp * tcpinfo;
                    940:                tcpinfo = (struct ipt_tcp *)match->data;
                    941:                printf("srcport = %hu:%hu dstport = %hu:%hu\n",
                    942:                       tcpinfo->spts[0], tcpinfo->spts[1],
                    943:                           tcpinfo->dpts[0], tcpinfo->dpts[1]);
                    944:        }
                    945:        else if(0 == strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN))
                    946:        {
                    947:                struct ipt_udp * udpinfo;
                    948:                udpinfo = (struct ipt_udp *)match->data;
                    949:                printf("srcport = %hu:%hu dstport = %hu:%hu\n",
                    950:                       udpinfo->spts[0], udpinfo->spts[1],
                    951:                           udpinfo->dpts[0], udpinfo->dpts[1]);
                    952:        }
                    953:        return 0;
                    954: }
                    955: 
                    956: static void
                    957: print_iface(const char * iface, const unsigned char * mask, int invert)
                    958: {
                    959:        unsigned i;
                    960:        if(mask[0] == 0)
                    961:                return;
                    962:        if(invert)
                    963:                printf("! ");
                    964:        for(i=0; i<IFNAMSIZ; i++)
                    965:        {
                    966:                if(mask[i])
                    967:                {
                    968:                        if(iface[i])
                    969:                                putchar(iface[i]);
                    970:                }
                    971:                else
                    972:                {
                    973:                        if(iface[i-1])
                    974:                                putchar('+');
                    975:                        break;
                    976:                }
                    977:        }
                    978: }
                    979: 
                    980: static void
                    981: printip(uint32_t ip)
                    982: {
                    983:        printf("%u.%u.%u.%u", ip >> 24, (ip >> 16) & 0xff,
                    984:               (ip >> 8) & 0xff, ip & 0xff);
                    985: }
                    986: 
                    987: /* for debug */
                    988: /* read the "filter" and "nat" tables */
                    989: int
                    990: list_redirect_rule(const char * ifname)
                    991: {
                    992:        IPTC_HANDLE h;
                    993:        const struct ipt_entry * e;
                    994:        const struct ipt_entry_target * target;
                    995:        const struct ip_nat_multi_range * mr;
                    996:        const char * target_str;
1.1.1.2   misho     997:        char addr[16], mask[16];
1.1.1.3 ! misho     998:        (void)ifname;
1.1       misho     999: 
                   1000:        h = iptc_init("nat");
                   1001:        if(!h)
                   1002:        {
                   1003:                printf("iptc_init() error : %s\n", iptc_strerror(errno));
                   1004:                return -1;
                   1005:        }
                   1006:        if(!iptc_is_chain(miniupnpd_nat_chain, h))
                   1007:        {
                   1008:                printf("chain %s not found\n", miniupnpd_nat_chain);
                   1009: #ifdef IPTABLES_143
                   1010:                iptc_free(h);
                   1011: #else
                   1012:                iptc_free(&h);
                   1013: #endif
                   1014:                return -1;
                   1015:        }
                   1016: #ifdef IPTABLES_143
                   1017:        for(e = iptc_first_rule(miniupnpd_nat_chain, h);
                   1018:                e;
                   1019:                e = iptc_next_rule(e, h))
                   1020:        {
                   1021:                target_str = iptc_get_target(e, h);
                   1022: #else
                   1023:        for(e = iptc_first_rule(miniupnpd_nat_chain, &h);
                   1024:                e;
                   1025:                e = iptc_next_rule(e, &h))
                   1026:        {
                   1027:                target_str = iptc_get_target(e, &h);
                   1028: #endif
                   1029:                printf("===\n");
1.1.1.2   misho    1030:                inet_ntop(AF_INET, &e->ip.src, addr, sizeof(addr));
                   1031:                inet_ntop(AF_INET, &e->ip.smsk, mask, sizeof(mask));
1.1       misho    1032:                printf("src = %s%s/%s\n", (e->ip.invflags & IPT_INV_SRCIP)?"! ":"",
1.1.1.2   misho    1033:                       /*inet_ntoa(e->ip.src), inet_ntoa(e->ip.smsk)*/
                   1034:                       addr, mask);
                   1035:                inet_ntop(AF_INET, &e->ip.dst, addr, sizeof(addr));
                   1036:                inet_ntop(AF_INET, &e->ip.dmsk, mask, sizeof(mask));
1.1       misho    1037:                printf("dst = %s%s/%s\n", (e->ip.invflags & IPT_INV_DSTIP)?"! ":"",
1.1.1.2   misho    1038:                       /*inet_ntoa(e->ip.dst), inet_ntoa(e->ip.dmsk)*/
                   1039:                       addr, mask);
1.1       misho    1040:                /*printf("in_if = %s  out_if = %s\n", e->ip.iniface, e->ip.outiface);*/
                   1041:                printf("in_if = ");
                   1042:                print_iface(e->ip.iniface, e->ip.iniface_mask,
                   1043:                            e->ip.invflags & IPT_INV_VIA_IN);
                   1044:                printf(" out_if = ");
                   1045:                print_iface(e->ip.outiface, e->ip.outiface_mask,
                   1046:                            e->ip.invflags & IPT_INV_VIA_OUT);
                   1047:                printf("\n");
                   1048:                printf("ip.proto = %s%d\n", (e->ip.invflags & IPT_INV_PROTO)?"! ":"",
                   1049:                       e->ip.proto);
                   1050:                /* display matches stuff */
                   1051:                if(e->target_offset)
                   1052:                {
                   1053:                        IPT_MATCH_ITERATE(e, print_match);
                   1054:                        /*printf("\n");*/
                   1055:                }
                   1056:                printf("target = %s\n", target_str);
                   1057:                target = (void *)e + e->target_offset;
                   1058:                mr = (const struct ip_nat_multi_range *)&target->data[0];
                   1059:                printf("ips ");
                   1060:                printip(ntohl(mr->range[0].min_ip));
                   1061:                printf(" ");
                   1062:                printip(ntohl(mr->range[0].max_ip));
                   1063:                printf("\nports %hu %hu\n", ntohs(mr->range[0].min.all),
                   1064:                          ntohs(mr->range[0].max.all));
                   1065:                printf("flags = %x\n", mr->range[0].flags);
                   1066:        }
                   1067:        if(h)
                   1068: #ifdef IPTABLES_143
                   1069:                iptc_free(h);
                   1070: #else
                   1071:                iptc_free(&h);
                   1072: #endif
                   1073:        return 0;
                   1074: }
1.1.1.2   misho    1075: #endif

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