Annotation of embedaddon/miniupnpd/natpmp.c, revision 1.1.1.2

1.1.1.2 ! misho       1: /* $Id: natpmp.c,v 1.26 2011/07/15 07:48:26 nanard Exp $ */
1.1       misho       2: /* MiniUPnP project
                      3:  * (c) 2007-2010 Thomas Bernard
                      4:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
                      5:  * This software is subject to the conditions detailed
                      6:  * in the LICENCE file provided within the distribution */
                      7: #include <stdio.h>
                      8: #include <string.h>
                      9: #include <unistd.h>
                     10: #include <syslog.h>
                     11: #include <time.h>
                     12: #include <sys/types.h>
                     13: #include <sys/socket.h>
                     14: #include <netinet/in.h>
                     15: #include <arpa/inet.h>
                     16: #include "config.h"
                     17: #include "natpmp.h"
                     18: #include "upnpglobalvars.h"
                     19: #include "getifaddr.h"
                     20: #include "upnpredirect.h"
                     21: #include "commonrdr.h"
                     22: 
                     23: #ifdef ENABLE_NATPMP
                     24: 
                     25: int OpenAndConfNATPMPSocket(in_addr_t addr)
                     26: {
                     27:        int snatpmp;
                     28:        snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
                     29:        if(snatpmp<0)
                     30:        {
                     31:                syslog(LOG_ERR, "socket(natpmp): %m");
                     32:                return -1;
                     33:        }
                     34:        else
                     35:        {
                     36:                struct sockaddr_in natpmp_addr;
                     37:                memset(&natpmp_addr, 0, sizeof(natpmp_addr));
                     38:                natpmp_addr.sin_family = AF_INET;
                     39:                natpmp_addr.sin_port = htons(NATPMP_PORT);
                     40:                //natpmp_addr.sin_addr.s_addr = INADDR_ANY;
                     41:                natpmp_addr.sin_addr.s_addr = addr;
                     42:                if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0)
                     43:                {
                     44:                        syslog(LOG_ERR, "bind(natpmp): %m");
                     45:                        close(snatpmp);
                     46:                        return -1;
                     47:                }
                     48:        }
                     49:        return snatpmp;
                     50: }
                     51: 
                     52: int OpenAndConfNATPMPSockets(int * sockets)
                     53: {
                     54:        int i, j;
1.1.1.2 ! misho      55:        struct lan_addr_s * lan_addr;
        !            56:        for(i = 0, lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next, i++)
1.1       misho      57:        {
1.1.1.2 ! misho      58:                sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr);
1.1       misho      59:                if(sockets[i] < 0)
                     60:                {
                     61:                        for(j=0; j<i; j++)
                     62:                        {
                     63:                                close(sockets[j]);
                     64:                                sockets[j] = -1;
                     65:                        }
                     66:                        return -1;
                     67:                }
                     68:        }
                     69:        return 0;
                     70: }
                     71: 
                     72: static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
                     73: {
                     74: #ifndef MULTIPLE_EXTERNAL_IP
                     75:        char tmp[16];
                     76: 
                     77:        if(use_ext_ip_addr) {
                     78:         inet_pton(AF_INET, use_ext_ip_addr, resp+8);
                     79:        } else {
                     80:                if(!ext_if_name || ext_if_name[0]=='\0') {
                     81:                        resp[3] = 3;    /* Network Failure (e.g. NAT box itself
                     82:                                         * has not obtained a DHCP lease) */
                     83:                } else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN) < 0) {
                     84:                        syslog(LOG_ERR, "Failed to get IP for interface %s", ext_if_name);
                     85:                        resp[3] = 3;    /* Network Failure (e.g. NAT box itself
                     86:                                         * has not obtained a DHCP lease) */
                     87:                } else {
1.1.1.2 ! misho      88:                        inet_pton(AF_INET, tmp, resp+8); /* ok */
1.1       misho      89:                }
                     90:        }
                     91: #else
1.1.1.2 ! misho      92:        struct lan_addr_s * lan_addr;
        !            93:        for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next) {
        !            94:                if( (senderaddr & lan_addr->mask.s_addr)
        !            95:                   == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) {
        !            96:                        memcpy(resp+8, &lan_addr->ext_ip_addr,
        !            97:                               sizeof(lan_addr->ext_ip_addr));
1.1       misho      98:                        break;
                     99:                }
                    100:        }
                    101: #endif
                    102: }
                    103: 
                    104: /** read the request from the socket, process it and then send the
                    105:  * response back.
                    106:  */
                    107: void ProcessIncomingNATPMPPacket(int s)
                    108: {
                    109:        unsigned char req[32];  /* request udp packet */
                    110:        unsigned char resp[32]; /* response udp packet */
                    111:        int resplen;
                    112:        struct sockaddr_in senderaddr;
                    113:        socklen_t senderaddrlen = sizeof(senderaddr);
                    114:        int n;
                    115:        char senderaddrstr[16];
                    116:        n = recvfrom(s, req, sizeof(req), 0,
                    117:                     (struct sockaddr *)&senderaddr, &senderaddrlen);
                    118:        if(n<0) {
                    119:                syslog(LOG_ERR, "recvfrom(natpmp): %m");
                    120:                return;
                    121:        }
                    122:        if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
                    123:                      senderaddrstr, sizeof(senderaddrstr))) {
                    124:                syslog(LOG_ERR, "inet_ntop(natpmp): %m");
                    125:        }
                    126:        syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
                    127:            senderaddrstr, ntohs(senderaddr.sin_port), n);
                    128:        if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
                    129:                syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
                    130:                       n);
                    131:                return;
                    132:        }
                    133:        if(req[1] & 128) {
                    134:                /* discarding NAT-PMP responses silently */
                    135:                return;
                    136:        }
                    137:        memset(resp, 0, sizeof(resp));
                    138:        resplen = 8;
                    139:        resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */
1.1.1.2 ! misho     140:        /* setting response TIME STAMP :
        !           141:         * time elapsed since its port mapping table was initialized on
        !           142:         * startup or reset for any other reason */
1.1       misho     143:        *((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
                    144:        if(req[0] > 0) {
                    145:                /* invalid version */
                    146:                syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
                    147:                       (unsigned)req[0]);
                    148:                resp[3] = 1;    /* unsupported version */
                    149:        } else switch(req[1]) {
                    150:        case 0: /* Public address request */
                    151:                syslog(LOG_INFO, "NAT-PMP public address request");
                    152:                FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr);
                    153:                resplen = 12;
                    154:                break;
                    155:        case 1: /* UDP port mapping request */
                    156:        case 2: /* TCP port mapping request */
                    157:                {
                    158:                        unsigned short iport;   /* private port */
                    159:                        unsigned short eport;   /* public port */
                    160:                        uint32_t lifetime;              /* lifetime=0 => remove port mapping */
                    161:                        int r;
                    162:                        int proto;
                    163:                        char iaddr_old[16];
                    164:                        unsigned short iport_old;
1.1.1.2 ! misho     165:                        unsigned int timestamp;
        !           166: 
1.1       misho     167:                        iport = ntohs(*((uint16_t *)(req+4)));
                    168:                        eport = ntohs(*((uint16_t *)(req+6)));
                    169:                        lifetime = ntohl(*((uint32_t *)(req+8)));
                    170:                        proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
                    171:                        syslog(LOG_INFO, "NAT-PMP port mapping request : "
                    172:                                         "%hu->%s:%hu %s lifetime=%us",
                    173:                                         eport, senderaddrstr, iport,
                    174:                                         (req[1]==1)?"udp":"tcp", lifetime);
                    175:                        if(eport==0)
                    176:                                eport = iport;
                    177:                        /* TODO: accept port mapping if iport ok but eport not ok
                    178:                         * (and set eport correctly) */
                    179:                        if(lifetime == 0) {
                    180:                                /* remove the mapping */
                    181:                                if(iport == 0) {
                    182:                                        /* remove all the mappings for this client */
                    183:                                        int index = 0;
                    184:                                        unsigned short eport2, iport2;
                    185:                                        char iaddr2[16];
                    186:                                        int proto2;
                    187:                                        char desc[64];
                    188:                                        while(get_redirect_rule_by_index(index, 0,
                    189:                                                  &eport2, iaddr2, sizeof(iaddr2),
                    190:                                                          &iport2, &proto2,
1.1.1.2 ! misho     191:                                                          desc, sizeof(desc),
        !           192:                                                  0, 0, &timestamp, 0, 0) >= 0) {
1.1       misho     193:                                                syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
                    194:                                                       index, proto2, eport2, iaddr2, iport2, desc);
                    195:                                                if(0 == strcmp(iaddr2, senderaddrstr)
1.1.1.2 ! misho     196:                                                  && 0 == memcmp(desc, "NAT-PMP", 7)) {
1.1       misho     197:                                                        r = _upnp_delete_redir(eport2, proto2);
                    198:                                                        /* TODO : check return value */
                    199:                                                        if(r<0) {
                    200:                                                                syslog(LOG_ERR, "failed to remove port mapping");
                    201:                                                                index++;
                    202:                                                        } else {
1.1.1.2 ! misho     203:                                                                syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
        !           204:                                                                       proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
1.1       misho     205:                                                        }
                    206:                                                } else {
                    207:                                                        index++;
                    208:                                                }
                    209:                                        }
                    210:                                } else {
                    211:                                        /* To improve the interworking between nat-pmp and
                    212:                                         * UPnP, we should check that we remove only NAT-PMP 
                    213:                                         * mappings */
                    214:                                        r = _upnp_delete_redir(eport, proto);
                    215:                                        /*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
                    216:                                        if(r<0) {
1.1.1.2 ! misho     217:                                                syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
        !           218:                                                       eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
1.1       misho     219:                                                resp[3] = 2;    /* Not Authorized/Refused */
                    220:                                        }
                    221:                                }
                    222:                                eport = 0; /* to indicate correct removing of port mapping */
                    223:                        } else if(iport==0
                    224:                           || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
                    225:                                resp[3] = 2;    /* Not Authorized/Refused */
                    226:                        } else do {
                    227:                                r = get_redirect_rule(ext_if_name, eport, proto,
                    228:                                                      iaddr_old, sizeof(iaddr_old),
1.1.1.2 ! misho     229:                                                      &iport_old, 0, 0, 0, 0,
        !           230:                                                      &timestamp, 0, 0);
1.1       misho     231:                                if(r==0) {
                    232:                                        if(strcmp(senderaddrstr, iaddr_old)==0
                    233:                                       && iport==iport_old) {
                    234:                                                /* redirection allready existing */
1.1.1.2 ! misho     235:                                                syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
        !           236:                                                       eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
1.1       misho     237:                                                /* remove and then add again */
                    238:                                                if(_upnp_delete_redir(eport, proto) < 0) {
                    239:                                                        syslog(LOG_ERR, "failed to remove port mapping");
                    240:                                                        break;
                    241:                                                }
                    242:                                        } else {
                    243:                                                eport++;
                    244:                                                continue;
                    245:                                        }
                    246:                                }
                    247:                                { /* do the redirection */
                    248:                                        char desc[64];
1.1.1.2 ! misho     249: #if 0
        !           250:                                        timestamp = (unsigned)(time(NULL) - startup_time)
1.1       misho     251:                                                              + lifetime;
                    252:                                        snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
1.1.1.2 ! misho     253: #else
        !           254:                                        timestamp = time(NULL) + lifetime;
        !           255:                                        snprintf(desc, sizeof(desc), "NAT-PMP %hu %s",
        !           256:                                                 eport, (proto==IPPROTO_TCP)?"tcp":"udp");
        !           257: #endif
1.1       misho     258:                                        /* TODO : check return code */
1.1.1.2 ! misho     259:                                        if(upnp_redirect_internal(NULL, eport, senderaddrstr,
        !           260:                                                                  iport, proto, desc,
        !           261:                                                                  timestamp) < 0) {
1.1       misho     262:                                                syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
                    263:                                                       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
                    264:                                                resp[3] = 3;  /* Failure */
1.1.1.2 ! misho     265: #if 0
1.1       misho     266:                                        } else if( !nextnatpmptoclean_eport
                    267:                                                 || timestamp < nextnatpmptoclean_timestamp) {
                    268:                                                nextnatpmptoclean_timestamp = timestamp;
                    269:                                                nextnatpmptoclean_eport = eport;
                    270:                                                nextnatpmptoclean_proto = proto;
1.1.1.2 ! misho     271: #endif
1.1       misho     272:                                        }
                    273:                                        break;
                    274:                                }
                    275:                        } while(r==0);
                    276:                        *((uint16_t *)(resp+8)) = htons(iport); /* private port */
                    277:                        *((uint16_t *)(resp+10)) = htons(eport);        /* public port */
1.1.1.2 ! misho     278:                        *((uint32_t *)(resp+12)) = htonl(lifetime);     /* Port Mapping lifetime */
1.1       misho     279:                }
                    280:                resplen = 16;
                    281:                break;
                    282:        default:
                    283:                resp[3] = 5;    /* Unsupported OPCODE */
                    284:        }
                    285:        n = sendto(s, resp, resplen, 0,
                    286:                   (struct sockaddr *)&senderaddr, sizeof(senderaddr));
                    287:        if(n<0) {
                    288:                syslog(LOG_ERR, "sendto(natpmp): %m");
                    289:        } else if(n<resplen) {
                    290:                syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
                    291:                       n, resplen);
                    292:        }
                    293: }
                    294: 
1.1.1.2 ! misho     295: #if 0
1.1       misho     296: /* iterate through the redirection list to find those who came
                    297:  * from NAT-PMP and select the first to expire */
                    298: int ScanNATPMPforExpiration()
                    299: {
                    300:        char desc[64];
                    301:        unsigned short iport, eport;
                    302:        int proto;
                    303:        int r, i;
                    304:        unsigned timestamp;
                    305:        nextnatpmptoclean_eport = 0;
                    306:        nextnatpmptoclean_timestamp = 0;
                    307:        for(i = 0; ; i++) {
                    308:                r = get_redirect_rule_by_index(i, 0, &eport, 0, 0,
                    309:                                               &iport, &proto, desc, sizeof(desc),
1.1.1.2 ! misho     310:                                               &timestamp, 0, 0);
1.1       misho     311:                if(r<0)
                    312:                        break;
                    313:                if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
                    314:                        if( !nextnatpmptoclean_eport
                    315:                          || timestamp < nextnatpmptoclean_timestamp) {
                    316:                                nextnatpmptoclean_eport = eport;
                    317:                                nextnatpmptoclean_proto = proto;
                    318:                                nextnatpmptoclean_timestamp = timestamp;
                    319:                                syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp);
                    320:                        }
                    321:                }
                    322:        }
                    323:        return 0;
                    324: }
                    325: 
                    326: /* remove the next redirection that is expired
                    327:  */
                    328: int CleanExpiredNATPMP()
                    329: {
                    330:        char desc[64];
                    331:        unsigned timestamp;
                    332:        unsigned short iport;
                    333:        if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport,
                    334:                             nextnatpmptoclean_proto,
                    335:                             0, 0,
1.1.1.2 ! misho     336:                             &iport, desc, sizeof(desc), &timestamp, 0, 0) < 0)
1.1       misho     337:                return ScanNATPMPforExpiration();
                    338:        /* check desc - this is important since we keep expiration time as part
                    339:         * of the desc.
                    340:         * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp 
                    341:         * can be different. In that case, the rule must not be removed ! */
                    342:        if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
                    343:                if(timestamp > nextnatpmptoclean_timestamp)
                    344:                        return ScanNATPMPforExpiration();
                    345:        }
                    346:        /* remove redirection then search for next one:) */
                    347:        if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0)
                    348:                return -1;
                    349:        syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed",
                    350:               nextnatpmptoclean_eport,
                    351:               nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP");
                    352:        return ScanNATPMPforExpiration();
                    353: }
1.1.1.2 ! misho     354: #endif
1.1       misho     355: 
                    356: /* SendNATPMPPublicAddressChangeNotification()
                    357:  * should be called when the public IP address changed */
                    358: void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
                    359: {
                    360:        struct sockaddr_in sockname;
                    361:        unsigned char notif[12];
                    362:        int j, n;
                    363: 
1.1.1.2 ! misho     364:        notif[0] = 0;   /* vers */
        !           365:        notif[1] = 128; /* OP code */
        !           366:        notif[2] = 0;   /* result code */
        !           367:        notif[3] = 0;   /* result code */
        !           368:        /* seconds since "start of epoch" :
        !           369:         * time elapsed since the port mapping table was initialized on
        !           370:         * startup or reset for any other reason */
1.1       misho     371:        *((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
                    372: #ifndef MULTIPLE_EXTERNAL_IP
                    373:        FillPublicAddressResponse(notif, 0);
                    374:        if(notif[3])
                    375:        {
                    376:                syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
                    377:                       "SendNATPMPPublicAddressChangeNotification");
                    378:                return;
                    379:        }
                    380: #endif
                    381:        memset(&sockname, 0, sizeof(struct sockaddr_in));
                    382:     sockname.sin_family = AF_INET;
                    383:     sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);
                    384: 
                    385:        for(j=0; j<n_sockets; j++)
                    386:        {
                    387:                if(sockets[j] < 0)
                    388:                        continue;
                    389: #ifdef MULTIPLE_EXTERNAL_IP
1.1.1.2 ! misho     390:                {
        !           391:                        struct lan_addr_s * lan_addr = lan_addrs.lh_first;
        !           392:                        int i;
        !           393:                        for(i=0; i<j; i++)
        !           394:                                lan_addr = lan_addr->list.le_next;
        !           395:                        FillPublicAddressResponse(notif, lan_addr->addr.s_addr);
        !           396:                }
1.1       misho     397: #endif
1.1.1.2 ! misho     398:                /* Port to use in 2006 version of the NAT-PMP specification */
        !           399:        sockname.sin_port = htons(NATPMP_PORT);
        !           400:                n = sendto(sockets[j], notif, 12, 0,
        !           401:                           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
        !           402:                if(n < 0)
        !           403:                {       
        !           404:                        syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
        !           405:                               "SendNATPMPPublicAddressChangeNotification", sockets[j]);
        !           406:                        return;
        !           407:                }
        !           408:                /* Port to use in 2008 version of the NAT-PMP specification */
        !           409:        sockname.sin_port = htons(NATPMP_NOTIF_PORT);
1.1       misho     410:                n = sendto(sockets[j], notif, 12, 0,
                    411:                           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
                    412:                if(n < 0)
                    413:                {       
                    414:                        syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
                    415:                               "SendNATPMPPublicAddressChangeNotification", sockets[j]);
                    416:                        return;
                    417:                }
                    418:        }
                    419: }
                    420: 
                    421: #endif
                    422: 

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