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

1.1       misho       1: /* $Id: natpmp.c,v 1.20 2010/05/06 13:42:47 nanard Exp $ */
                      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:                //natpmp_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
                     43:                if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0)
                     44:                {
                     45:                        syslog(LOG_ERR, "bind(natpmp): %m");
                     46:                        close(snatpmp);
                     47:                        return -1;
                     48:                }
                     49:        }
                     50:        return snatpmp;
                     51: }
                     52: 
                     53: int OpenAndConfNATPMPSockets(int * sockets)
                     54: {
                     55:        int i, j;
                     56:        for(i=0; i<n_lan_addr; i++)
                     57:        {
                     58:                sockets[i] = OpenAndConfNATPMPSocket(lan_addr[i].addr.s_addr);
                     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 {
                     88:                        inet_pton(AF_INET, tmp, resp+8);
                     89:                }
                     90:        }
                     91: #else
                     92:        int i;
                     93: 
                     94:        for(i = 0; i<n_lan_addr; i++) {
                     95:                if( (senderaddr & lan_addr[i].mask.s_addr)
                     96:                   == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr)) {
                     97:                        memcpy(resp+8, &lan_addr[i].ext_ip_addr,
                     98:                               sizeof(lan_addr[i].ext_ip_addr));
                     99:                        break;
                    100:                }
                    101:        }
                    102: #endif
                    103: }
                    104: 
                    105: /** read the request from the socket, process it and then send the
                    106:  * response back.
                    107:  */
                    108: void ProcessIncomingNATPMPPacket(int s)
                    109: {
                    110:        unsigned char req[32];  /* request udp packet */
                    111:        unsigned char resp[32]; /* response udp packet */
                    112:        int resplen;
                    113:        struct sockaddr_in senderaddr;
                    114:        socklen_t senderaddrlen = sizeof(senderaddr);
                    115:        int n;
                    116:        char senderaddrstr[16];
                    117:        n = recvfrom(s, req, sizeof(req), 0,
                    118:                     (struct sockaddr *)&senderaddr, &senderaddrlen);
                    119:        if(n<0) {
                    120:                syslog(LOG_ERR, "recvfrom(natpmp): %m");
                    121:                return;
                    122:        }
                    123:        if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
                    124:                      senderaddrstr, sizeof(senderaddrstr))) {
                    125:                syslog(LOG_ERR, "inet_ntop(natpmp): %m");
                    126:        }
                    127:        syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
                    128:            senderaddrstr, ntohs(senderaddr.sin_port), n);
                    129:        if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
                    130:                syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
                    131:                       n);
                    132:                return;
                    133:        }
                    134:        if(req[1] & 128) {
                    135:                /* discarding NAT-PMP responses silently */
                    136:                return;
                    137:        }
                    138:        memset(resp, 0, sizeof(resp));
                    139:        resplen = 8;
                    140:        resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */
                    141:        /* setting response TIME STAMP */
                    142:        *((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
                    143:        if(req[0] > 0) {
                    144:                /* invalid version */
                    145:                syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
                    146:                       (unsigned)req[0]);
                    147:                resp[3] = 1;    /* unsupported version */
                    148:        } else switch(req[1]) {
                    149:        case 0: /* Public address request */
                    150:                syslog(LOG_INFO, "NAT-PMP public address request");
                    151:                FillPublicAddressResponse(resp, senderaddr.sin_addr.s_addr);
                    152:                resplen = 12;
                    153:                break;
                    154:        case 1: /* UDP port mapping request */
                    155:        case 2: /* TCP port mapping request */
                    156:                {
                    157:                        unsigned short iport;   /* private port */
                    158:                        unsigned short eport;   /* public port */
                    159:                        uint32_t lifetime;              /* lifetime=0 => remove port mapping */
                    160:                        int r;
                    161:                        int proto;
                    162:                        char iaddr_old[16];
                    163:                        unsigned short iport_old;
                    164:                        iport = ntohs(*((uint16_t *)(req+4)));
                    165:                        eport = ntohs(*((uint16_t *)(req+6)));
                    166:                        lifetime = ntohl(*((uint32_t *)(req+8)));
                    167:                        proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
                    168:                        syslog(LOG_INFO, "NAT-PMP port mapping request : "
                    169:                                         "%hu->%s:%hu %s lifetime=%us",
                    170:                                         eport, senderaddrstr, iport,
                    171:                                         (req[1]==1)?"udp":"tcp", lifetime);
                    172:                        if(eport==0)
                    173:                                eport = iport;
                    174:                        /* TODO: accept port mapping if iport ok but eport not ok
                    175:                         * (and set eport correctly) */
                    176:                        if(lifetime == 0) {
                    177:                                /* remove the mapping */
                    178:                                if(iport == 0) {
                    179:                                        /* remove all the mappings for this client */
                    180:                                        int index = 0;
                    181:                                        unsigned short eport2, iport2;
                    182:                                        char iaddr2[16];
                    183:                                        int proto2;
                    184:                                        char desc[64];
                    185:                                        while(get_redirect_rule_by_index(index, 0,
                    186:                                                  &eport2, iaddr2, sizeof(iaddr2),
                    187:                                                          &iport2, &proto2,
                    188:                                                          desc, sizeof(desc), 0, 0) >= 0) {
                    189:                                                syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
                    190:                                                       index, proto2, eport2, iaddr2, iport2, desc);
                    191:                                                if(0 == strcmp(iaddr2, senderaddrstr)
                    192:                                                  && 0 == memcmp(desc, "NAT-PMP ", 8)) {
                    193:                                                        r = _upnp_delete_redir(eport2, proto2);
                    194:                                                        /* TODO : check return value */
                    195:                                                        if(r<0) {
                    196:                                                                syslog(LOG_ERR, "failed to remove port mapping");
                    197:                                                                index++;
                    198:                                                        } else {
                    199:                                                                syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
                    200:                                                        }
                    201:                                                } else {
                    202:                                                        index++;
                    203:                                                }
                    204:                                        }
                    205:                                } else {
                    206:                                        /* To improve the interworking between nat-pmp and
                    207:                                         * UPnP, we should check that we remove only NAT-PMP 
                    208:                                         * mappings */
                    209:                                        r = _upnp_delete_redir(eport, proto);
                    210:                                        /*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
                    211:                                        if(r<0) {
                    212:                                                syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s", eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
                    213:                                                resp[3] = 2;    /* Not Authorized/Refused */
                    214:                                        }
                    215:                                }
                    216:                                eport = 0; /* to indicate correct removing of port mapping */
                    217:                        } else if(iport==0
                    218:                           || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
                    219:                                resp[3] = 2;    /* Not Authorized/Refused */
                    220:                        } else do {
                    221:                                r = get_redirect_rule(ext_if_name, eport, proto,
                    222:                                                      iaddr_old, sizeof(iaddr_old),
                    223:                                                      &iport_old, 0, 0, 0, 0);
                    224:                                if(r==0) {
                    225:                                        if(strcmp(senderaddrstr, iaddr_old)==0
                    226:                                       && iport==iport_old) {
                    227:                                                /* redirection allready existing */
                    228:                                                syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing", eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
                    229:                                                /* remove and then add again */
                    230:                                                if(_upnp_delete_redir(eport, proto) < 0) {
                    231:                                                        syslog(LOG_ERR, "failed to remove port mapping");
                    232:                                                        break;
                    233:                                                }
                    234:                                        } else {
                    235:                                                eport++;
                    236:                                                continue;
                    237:                                        }
                    238:                                }
                    239:                                { /* do the redirection */
                    240:                                        char desc[64];
                    241:                                        unsigned timestamp = (unsigned)(time(NULL) - startup_time)
                    242:                                                              + lifetime;
                    243:                                        snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
                    244:                                        /* TODO : check return code */
                    245:                                        if(upnp_redirect_internal(eport, senderaddrstr,
                    246:                                                                  iport, proto, desc) < 0) {
                    247:                                                syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
                    248:                                                       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
                    249:                                                resp[3] = 3;  /* Failure */
                    250:                                        } else if( !nextnatpmptoclean_eport
                    251:                                                 || timestamp < nextnatpmptoclean_timestamp) {
                    252:                                                nextnatpmptoclean_timestamp = timestamp;
                    253:                                                nextnatpmptoclean_eport = eport;
                    254:                                                nextnatpmptoclean_proto = proto;
                    255:                                        }
                    256:                                        break;
                    257:                                }
                    258:                        } while(r==0);
                    259:                        *((uint16_t *)(resp+8)) = htons(iport); /* private port */
                    260:                        *((uint16_t *)(resp+10)) = htons(eport);        /* public port */
                    261:                        *((uint32_t *)(resp+12)) = htonl(lifetime);
                    262:                }
                    263:                resplen = 16;
                    264:                break;
                    265:        default:
                    266:                resp[3] = 5;    /* Unsupported OPCODE */
                    267:        }
                    268:        n = sendto(s, resp, resplen, 0,
                    269:                   (struct sockaddr *)&senderaddr, sizeof(senderaddr));
                    270:        if(n<0) {
                    271:                syslog(LOG_ERR, "sendto(natpmp): %m");
                    272:        } else if(n<resplen) {
                    273:                syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
                    274:                       n, resplen);
                    275:        }
                    276: }
                    277: 
                    278: /* iterate through the redirection list to find those who came
                    279:  * from NAT-PMP and select the first to expire */
                    280: int ScanNATPMPforExpiration()
                    281: {
                    282:        char desc[64];
                    283:        unsigned short iport, eport;
                    284:        int proto;
                    285:        int r, i;
                    286:        unsigned timestamp;
                    287:        nextnatpmptoclean_eport = 0;
                    288:        nextnatpmptoclean_timestamp = 0;
                    289:        for(i = 0; ; i++) {
                    290:                r = get_redirect_rule_by_index(i, 0, &eport, 0, 0,
                    291:                                               &iport, &proto, desc, sizeof(desc),
                    292:                                               0, 0);
                    293:                if(r<0)
                    294:                        break;
                    295:                if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
                    296:                        if( !nextnatpmptoclean_eport
                    297:                          || timestamp < nextnatpmptoclean_timestamp) {
                    298:                                nextnatpmptoclean_eport = eport;
                    299:                                nextnatpmptoclean_proto = proto;
                    300:                                nextnatpmptoclean_timestamp = timestamp;
                    301:                                syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp);
                    302:                        }
                    303:                }
                    304:        }
                    305:        return 0;
                    306: }
                    307: 
                    308: /* remove the next redirection that is expired
                    309:  */
                    310: int CleanExpiredNATPMP()
                    311: {
                    312:        char desc[64];
                    313:        unsigned timestamp;
                    314:        unsigned short iport;
                    315:        if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport,
                    316:                             nextnatpmptoclean_proto,
                    317:                             0, 0,
                    318:                             &iport, desc, sizeof(desc), 0, 0) < 0)
                    319:                return ScanNATPMPforExpiration();
                    320:        /* check desc - this is important since we keep expiration time as part
                    321:         * of the desc.
                    322:         * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp 
                    323:         * can be different. In that case, the rule must not be removed ! */
                    324:        if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
                    325:                if(timestamp > nextnatpmptoclean_timestamp)
                    326:                        return ScanNATPMPforExpiration();
                    327:        }
                    328:        /* remove redirection then search for next one:) */
                    329:        if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0)
                    330:                return -1;
                    331:        syslog(LOG_INFO, "Expired NAT-PMP mapping port %hu %s removed",
                    332:               nextnatpmptoclean_eport,
                    333:               nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP");
                    334:        return ScanNATPMPforExpiration();
                    335: }
                    336: 
                    337: /* SendNATPMPPublicAddressChangeNotification()
                    338:  * should be called when the public IP address changed */
                    339: void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
                    340: {
                    341:        struct sockaddr_in sockname;
                    342:        unsigned char notif[12];
                    343:        int j, n;
                    344: 
                    345:        notif[0] = 0;
                    346:        notif[1] = 128;
                    347:        notif[2] = 0;
                    348:        notif[3] = 0;
                    349:        *((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
                    350: #ifndef MULTIPLE_EXTERNAL_IP
                    351:        FillPublicAddressResponse(notif, 0);
                    352:        if(notif[3])
                    353:        {
                    354:                syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
                    355:                       "SendNATPMPPublicAddressChangeNotification");
                    356:                return;
                    357:        }
                    358: #endif
                    359:        memset(&sockname, 0, sizeof(struct sockaddr_in));
                    360:     sockname.sin_family = AF_INET;
                    361:     sockname.sin_port = htons(NATPMP_PORT);
                    362:     sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);
                    363: 
                    364:        for(j=0; j<n_sockets; j++)
                    365:        {
                    366:                if(sockets[j] < 0)
                    367:                        continue;
                    368: #ifdef MULTIPLE_EXTERNAL_IP
                    369:                FillPublicAddressResponse(notif, lan_addr[j].addr.s_addr);
                    370: #endif
                    371:                n = sendto(sockets[j], notif, 12, 0,
                    372:                           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
                    373:                if(n < 0)
                    374:                {       
                    375:                        syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
                    376:                               "SendNATPMPPublicAddressChangeNotification", sockets[j]);
                    377:                        return;
                    378:                }
                    379:        }
                    380: }
                    381: 
                    382: #endif
                    383: 

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