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

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

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