Annotation of embedaddon/miniupnpd/netfilter/iptpinhole.c, revision 1.1

1.1     ! misho       1: /* $Id: iptpinhole.c,v 1.8 2012/09/18 08:29:17 nanard Exp $ */
        !             2: /* MiniUPnP project
        !             3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
        !             4:  * (c) 2012 Thomas Bernard
        !             5:  * This software is subject to the conditions detailed
        !             6:  * in the LICENCE file provided within the distribution */
        !             7: 
        !             8: #include <stdlib.h>
        !             9: #include <string.h>
        !            10: #include <syslog.h>
        !            11: #include <errno.h>
        !            12: #include <arpa/inet.h>
        !            13: #include <sys/queue.h>
        !            14: 
        !            15: #include "../config.h"
        !            16: #include "iptpinhole.h"
        !            17: #include "../upnpglobalvars.h"
        !            18: 
        !            19: #ifdef ENABLE_6FC_SERVICE
        !            20: 
        !            21: #include <xtables.h>
        !            22: #include <libiptc/libip6tc.h>
        !            23: #include "tiny_nf_nat.h"
        !            24: 
        !            25: #define IP6TC_HANDLE struct ip6tc_handle *
        !            26: 
        !            27: static int next_uid = 1;
        !            28: 
        !            29: static LIST_HEAD(pinhole_list_t, pinhole_t) pinhole_list;
        !            30: 
        !            31: static struct pinhole_t *
        !            32: get_pinhole(unsigned short uid);
        !            33: 
        !            34: struct pinhole_t {
        !            35:        struct in6_addr saddr;
        !            36:        struct in6_addr daddr;
        !            37:        LIST_ENTRY(pinhole_t) entries;
        !            38:        unsigned int timestamp;
        !            39:        unsigned short sport;
        !            40:        unsigned short dport;
        !            41:        unsigned short uid;
        !            42:        unsigned char proto;
        !            43: };
        !            44: 
        !            45: void init_iptpinhole(void)
        !            46: {
        !            47:        LIST_INIT(&pinhole_list);
        !            48: }
        !            49: 
        !            50: void shutdown_iptpinhole(void)
        !            51: {
        !            52:        /* TODO empty list */
        !            53: }
        !            54: 
        !            55: /* return uid */
        !            56: static int
        !            57: add_to_pinhole_list(struct in6_addr * saddr, unsigned short sport,
        !            58:                     struct in6_addr * daddr, unsigned short dport,
        !            59:                     int proto, unsigned int timestamp)
        !            60: {
        !            61:        struct pinhole_t * p;
        !            62: 
        !            63:        p = calloc(1, sizeof(struct pinhole_t));
        !            64:        if(!p) {
        !            65:                syslog(LOG_ERR, "add_to_pinhole_list calloc() error");
        !            66:                return -1;
        !            67:        }
        !            68:        memcpy(&p->saddr, saddr, sizeof(struct in6_addr));
        !            69:        p->sport = sport;
        !            70:        memcpy(&p->daddr, daddr, sizeof(struct in6_addr));
        !            71:        p->dport = dport;
        !            72:        p->timestamp = timestamp;
        !            73:        p->proto = (unsigned char)proto;
        !            74:        LIST_INSERT_HEAD(&pinhole_list, p, entries);
        !            75:        while(get_pinhole(next_uid) != NULL) {
        !            76:                next_uid++;
        !            77:                if(next_uid > 65535)
        !            78:                        next_uid = 1;
        !            79:        }
        !            80:        p->uid = next_uid;
        !            81:        next_uid++;
        !            82:        if(next_uid > 65535)
        !            83:                next_uid = 1;
        !            84:        return p->uid;
        !            85: }
        !            86: 
        !            87: static struct pinhole_t *
        !            88: get_pinhole(unsigned short uid)
        !            89: {
        !            90:        struct pinhole_t * p;
        !            91: 
        !            92:        for(p = pinhole_list.lh_first; p != NULL; p = p->entries.le_next) {
        !            93:                if(p->uid == uid)
        !            94:                        return p;
        !            95:        }
        !            96:        return NULL;    /* not found */
        !            97: }
        !            98: 
        !            99: /* new_match()
        !           100:  * Allocate and set a new ip6t_entry_match structure
        !           101:  * The caller must free() it after usage */
        !           102: static struct ip6t_entry_match *
        !           103: new_match(int proto, unsigned short sport, unsigned short dport)
        !           104: {
        !           105:        struct ip6t_entry_match *match;
        !           106:        struct ip6t_tcp *info;  /* TODO : use ip6t_udp if needed */
        !           107:        size_t size;
        !           108:        const char * name;
        !           109:        size =   XT_ALIGN(sizeof(struct ip6t_entry_match))
        !           110:               + XT_ALIGN(sizeof(struct ip6t_tcp));
        !           111:        match = calloc(1, size);
        !           112:        match->u.user.match_size = size;
        !           113:        switch(proto) {
        !           114:        case IPPROTO_TCP:
        !           115:                name = "tcp";
        !           116:                break;
        !           117:        case IPPROTO_UDP:
        !           118:                name = "udp";
        !           119:                break;
        !           120:        case IPPROTO_UDPLITE:
        !           121:                name = "udplite";
        !           122:                break;
        !           123:        default:
        !           124:                name = NULL;
        !           125:        }
        !           126:        if(name)
        !           127:                strncpy(match->u.user.name, name, sizeof(match->u.user.name));
        !           128:        else
        !           129:                syslog(LOG_WARNING, "no name for protocol %d", proto);
        !           130:        info = (struct ip6t_tcp *)match->data;
        !           131:        if(sport) {
        !           132:                info->spts[0] = sport;  /* specified source port */
        !           133:                info->spts[1] = sport;
        !           134:        } else {
        !           135:                info->spts[0] = 0;              /* all source ports */
        !           136:                info->spts[1] = 0xFFFF;
        !           137:        }
        !           138:        info->dpts[0] = dport;  /* specified destination port */
        !           139:        info->dpts[1] = dport;
        !           140:        return match;
        !           141: }
        !           142: 
        !           143: static struct ip6t_entry_target *
        !           144: get_accept_target(void)
        !           145: {
        !           146:        struct ip6t_entry_target * target = NULL;
        !           147:        size_t size;
        !           148:        size =   XT_ALIGN(sizeof(struct ip6t_entry_target))
        !           149:               + XT_ALIGN(sizeof(int));
        !           150:        target = calloc(1, size);
        !           151:        target->u.user.target_size = size;
        !           152:        strncpy(target->u.user.name, "ACCEPT", sizeof(target->u.user.name));
        !           153:        return target;
        !           154: }
        !           155: 
        !           156: static int
        !           157: ip6tc_init_verify_append(const char * table,
        !           158:                          const char * chain,
        !           159:                          struct ip6t_entry * e)
        !           160: {
        !           161:        IP6TC_HANDLE h;
        !           162: 
        !           163:        h = ip6tc_init(table);
        !           164:        if(!h) {
        !           165:                syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
        !           166:                return -1;
        !           167:        }
        !           168:        if(!ip6tc_is_chain(chain, h)) {
        !           169:                syslog(LOG_ERR, "chain %s not found", chain);
        !           170:                goto error;
        !           171:        }
        !           172:        if(!ip6tc_append_entry(chain, e, h)) {
        !           173:                syslog(LOG_ERR, "ip6tc_append_entry() error : %s", ip6tc_strerror(errno));
        !           174:                goto error;
        !           175:        }
        !           176:        if(!ip6tc_commit(h)) {
        !           177:                syslog(LOG_ERR, "ip6tc_commit() error : %s", ip6tc_strerror(errno));
        !           178:                goto error;
        !           179:        }
        !           180:        return 0;       /* ok */
        !           181: error:
        !           182:        ip6tc_free(h);
        !           183:        return -1;
        !           184: }
        !           185: 
        !           186: /*
        !           187: ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT
        !           188: ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT
        !           189: 
        !           190: miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport
        !           191: 
        !           192: ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE
        !           193: ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE
        !           194: */
        !           195: int add_pinhole(const char * ifname,
        !           196:                 const char * rem_host, unsigned short rem_port,
        !           197:                 const char * int_client, unsigned short int_port,
        !           198:                 int proto, unsigned int timestamp)
        !           199: {
        !           200:        int uid;
        !           201:        struct ip6t_entry * e;
        !           202:        struct ip6t_entry_match *match = NULL;
        !           203:        struct ip6t_entry_target *target = NULL;
        !           204: 
        !           205:        e = calloc(1, sizeof(struct ip6t_entry));
        !           206:        e->ipv6.proto = proto;
        !           207: 
        !           208:        if(ifname)
        !           209:                strncpy(e->ipv6.iniface, ifname, IFNAMSIZ);
        !           210:        if(rem_host) {
        !           211:                inet_pton(AF_INET6, rem_host, &e->ipv6.src);
        !           212:                memset(&e->ipv6.smsk, 0xff, sizeof(e->ipv6.smsk));
        !           213:        }
        !           214:        inet_pton(AF_INET6, int_client, &e->ipv6.dst);
        !           215:        memset(&e->ipv6.dmsk, 0xff, sizeof(e->ipv6.dmsk));
        !           216: 
        !           217:        /*e->nfcache = NFC_IP_DST_PT;*/
        !           218:        /*e->nfcache |= NFC_UNKNOWN;*/
        !           219: 
        !           220:        match = new_match(proto, rem_port, int_port);
        !           221:        target = get_accept_target();
        !           222:        e = realloc(e, sizeof(struct ip6t_entry)
        !           223:                       + match->u.match_size
        !           224:                       + target->u.target_size);
        !           225:        memcpy(e->elems, match, match->u.match_size);
        !           226:        memcpy(e->elems + match->u.match_size, target, target->u.target_size);
        !           227:        e->target_offset = sizeof(struct ip6t_entry)
        !           228:                           + match->u.match_size;
        !           229:        e->next_offset = sizeof(struct ip6t_entry)
        !           230:                         + match->u.match_size
        !           231:                                         + target->u.target_size;
        !           232:        free(match);
        !           233:        free(target);
        !           234: 
        !           235:        if(ip6tc_init_verify_append("filter", miniupnpd_v6_filter_chain, e) < 0) {
        !           236:                free(e);
        !           237:                return -1;
        !           238:        }
        !           239:        uid = add_to_pinhole_list(&e->ipv6.src, rem_port,
        !           240:                                  &e->ipv6.dst, int_port,
        !           241:                                  proto, timestamp);
        !           242:        free(e);
        !           243:        return uid;
        !           244: }
        !           245: 
        !           246: int
        !           247: delete_pinhole(unsigned short uid)
        !           248: {
        !           249:        struct pinhole_t * p;
        !           250:        IP6TC_HANDLE h;
        !           251:        const struct ip6t_entry * e;
        !           252:        const struct ip6t_entry_match *match = NULL;
        !           253:        /*const struct ip6t_entry_target *target = NULL;*/
        !           254:        unsigned int index;
        !           255: 
        !           256:        p = get_pinhole(uid);
        !           257:        if(!p)
        !           258:                return -2;      /* not found */
        !           259: 
        !           260:        h = ip6tc_init("filter");
        !           261:        if(!h) {
        !           262:                syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
        !           263:                return -1;
        !           264:        }
        !           265:        if(!ip6tc_is_chain(miniupnpd_v6_filter_chain, h)) {
        !           266:                syslog(LOG_ERR, "chain %s not found", miniupnpd_v6_filter_chain);
        !           267:                goto error;
        !           268:        }
        !           269:        index = 0;
        !           270:        for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
        !           271:            e;
        !           272:            e = ip6tc_next_rule(e, h)) {
        !           273:                if((e->ipv6.proto == p->proto) &&
        !           274:                   (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
        !           275:                   (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
        !           276:                        const struct ip6t_tcp * info;
        !           277:                        match = (const struct ip6t_entry_match *)&e->elems;
        !           278:                        info = (const struct ip6t_tcp *)&match->data;
        !           279:                        if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
        !           280:                                if(!ip6tc_delete_num_entry(miniupnpd_v6_filter_chain, index, h)) {
        !           281:                                        syslog(LOG_ERR, "ip6tc_delete_num_entry(%s,%d,...): %s",
        !           282:                                               miniupnpd_v6_filter_chain, index, ip6tc_strerror(errno));
        !           283:                                        goto error;
        !           284:                                }
        !           285:                                if(!ip6tc_commit(h)) {
        !           286:                                        syslog(LOG_ERR, "ip6tc_commit(): %s",
        !           287:                                               ip6tc_strerror(errno));
        !           288:                                        goto error;
        !           289:                                }
        !           290:                                ip6tc_free(h);
        !           291:                                LIST_REMOVE(p, entries);
        !           292:                                return 0;       /* ok */
        !           293:                        }
        !           294:                }
        !           295:                index++;
        !           296:        }
        !           297:        ip6tc_free(h);
        !           298:        syslog(LOG_WARNING, "delete_pinhole() rule with PID=%hu not found", uid);
        !           299:        return -2;      /* not found */
        !           300: error:
        !           301:        ip6tc_free(h);
        !           302:        return -1;
        !           303: }
        !           304: 
        !           305: int
        !           306: update_pinhole(unsigned short uid, unsigned int timestamp)
        !           307: {
        !           308:        struct pinhole_t * p;
        !           309: 
        !           310:        p = get_pinhole(uid);
        !           311:        if(p) {
        !           312:                p->timestamp = timestamp;
        !           313:                return 0;
        !           314:        } else {
        !           315:                return -2;      /* Not found */
        !           316:        }
        !           317: }
        !           318: 
        !           319: int
        !           320: get_pinhole_info(unsigned short uid,
        !           321:                  char * rem_host, int rem_hostlen, unsigned short * rem_port,
        !           322:                  char * int_client, int int_clientlen, unsigned short * int_port,
        !           323:                  int * proto, unsigned int * timestamp,
        !           324:                  u_int64_t * packets, u_int64_t * bytes)
        !           325: {
        !           326:        struct pinhole_t * p;
        !           327: 
        !           328:        p = get_pinhole(uid);
        !           329:        if(!p)
        !           330:                return -2;      /* Not found */
        !           331:        if(rem_host) {
        !           332:                if(inet_ntop(AF_INET6, &p->saddr, rem_host, rem_hostlen) == NULL)
        !           333:                        return -1;
        !           334:        }
        !           335:        if(rem_port)
        !           336:                *rem_port = p->sport;
        !           337:        if(int_client) {
        !           338:                if(inet_ntop(AF_INET6, &p->daddr, int_client, int_clientlen) == NULL)
        !           339:                        return -1;
        !           340:        }
        !           341:        if(int_port)
        !           342:                *int_port = p->dport;
        !           343:        if(proto)
        !           344:                *proto = p->proto;
        !           345:        if(timestamp)
        !           346:                *timestamp = p->timestamp;
        !           347:        if(packets || bytes) {
        !           348:                /* theses informations need to be read from netfilter */
        !           349:                IP6TC_HANDLE h;
        !           350:                const struct ip6t_entry * e;
        !           351:                const struct ip6t_entry_match * match;
        !           352:                h = ip6tc_init("filter");
        !           353:                if(!h) {
        !           354:                        syslog(LOG_ERR, "ip6tc_init error : %s", ip6tc_strerror(errno));
        !           355:                        return -1;
        !           356:                }
        !           357:                for(e = ip6tc_first_rule(miniupnpd_v6_filter_chain, h);
        !           358:                    e;
        !           359:                    e = ip6tc_next_rule(e, h)) {
        !           360:                        if((e->ipv6.proto == p->proto) &&
        !           361:                           (0 == memcmp(&e->ipv6.src, &p->saddr, sizeof(e->ipv6.src))) &&
        !           362:                           (0 == memcmp(&e->ipv6.dst, &p->daddr, sizeof(e->ipv6.dst)))) {
        !           363:                                const struct ip6t_tcp * info;
        !           364:                                match = (const struct ip6t_entry_match *)&e->elems;
        !           365:                                info = (const struct ip6t_tcp *)&match->data;
        !           366:                                if((info->spts[0] == p->sport) && (info->dpts[0] == p->dport)) {
        !           367:                                        if(packets)
        !           368:                                                *packets = e->counters.pcnt;
        !           369:                                        if(bytes)
        !           370:                                                *bytes = e->counters.bcnt;
        !           371:                                        break;
        !           372:                                }
        !           373:                        }
        !           374:                }
        !           375:                ip6tc_free(h);
        !           376:        }
        !           377:        return 0;
        !           378: }
        !           379: 
        !           380: int
        !           381: clean_pinhole_list(unsigned int * next_timestamp)
        !           382: {
        !           383:        unsigned int min_ts = UINT_MAX;
        !           384:        struct pinhole_t * p;
        !           385:        time_t current_time;
        !           386:        int n = 0;
        !           387: 
        !           388:        current_time = time(NULL);
        !           389:        p = pinhole_list.lh_first;
        !           390:        while(p != NULL) {
        !           391:                if(p->timestamp <= (unsigned int)current_time) {
        !           392:                        unsigned short uid = p->uid;
        !           393:                        syslog(LOG_INFO, "removing expired pinhole with uid=%hu", uid);
        !           394:                        p = p->entries.le_next;
        !           395:                        if(delete_pinhole(uid) == 0)
        !           396:                                n++;
        !           397:                        else
        !           398:                                break;
        !           399:                } else {
        !           400:                        if(p->timestamp < min_ts)
        !           401:                                min_ts = p->timestamp;
        !           402:                        p = p->entries.le_next;
        !           403:                }
        !           404:        }
        !           405:        if(next_timestamp && (min_ts != UINT_MAX))
        !           406:                *next_timestamp = min_ts;
        !           407:        return n;
        !           408: }
        !           409: 
        !           410: #endif
        !           411: 

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