File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / natpmp.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:02 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* $Id: natpmp.c,v 1.1 2012/02/21 23:16:02 misho 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>