Annotation of embedaddon/miniupnpd/natpmp.c, revision 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", ×tamp) == 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", ×tamp) == 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>