Annotation of embedaddon/miniupnpd/netfilter/iptpinhole.c, revision 1.1.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>