File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / natpmp.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:55:57 2012 UTC (12 years, 2 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_6elwix, HEAD
miniupnpd 1.6+patches

    1: /* $Id: natpmp.c,v 1.1.1.2 2012/05/29 12:55:57 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: 		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;
   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++)
   57: 	{
   58: 		sockets[i] = OpenAndConfNATPMPSocket(lan_addr->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); /* ok */
   89: 		}
   90: 	}
   91: #else
   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));
   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 */
  140: 	/* setting response TIME STAMP :
  141: 	 * time elapsed since its port mapping table was initialized on
  142: 	 * startup or reset for any other reason */
  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;
  165: 			unsigned int timestamp;
  166: 
  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,
  191: 							  desc, sizeof(desc),
  192: 					          0, 0, &timestamp, 0, 0) >= 0) {
  193: 						syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
  194: 						       index, proto2, eport2, iaddr2, iport2, desc);
  195: 						if(0 == strcmp(iaddr2, senderaddrstr)
  196: 						  && 0 == memcmp(desc, "NAT-PMP", 7)) {
  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 {
  203: 								syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
  204: 								       proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
  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) {
  217: 						syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
  218: 						       eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
  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),
  229: 				                      &iport_old, 0, 0, 0, 0,
  230: 				                      &timestamp, 0, 0);
  231: 				if(r==0) {
  232: 					if(strcmp(senderaddrstr, iaddr_old)==0
  233: 				       && iport==iport_old) {
  234: 						/* redirection allready existing */
  235: 						syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
  236: 						       eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
  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];
  249: #if 0
  250: 					timestamp = (unsigned)(time(NULL) - startup_time)
  251: 					                      + lifetime;
  252: 					snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
  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
  258: 					/* TODO : check return code */
  259: 					if(upnp_redirect_internal(NULL, eport, senderaddrstr,
  260: 					                          iport, proto, desc,
  261: 					                          timestamp) < 0) {
  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 */
  265: #if 0
  266: 					} else if( !nextnatpmptoclean_eport
  267: 					         || timestamp < nextnatpmptoclean_timestamp) {
  268: 						nextnatpmptoclean_timestamp = timestamp;
  269: 						nextnatpmptoclean_eport = eport;
  270: 						nextnatpmptoclean_proto = proto;
  271: #endif
  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 */
  278: 			*((uint32_t *)(resp+12)) = htonl(lifetime);	/* Port Mapping lifetime */
  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: 
  295: #if 0
  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),
  310: 		                               &timestamp, 0, 0);
  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,
  336: 	                     &iport, desc, sizeof(desc), &timestamp, 0, 0) < 0)
  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: }
  354: #endif
  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: 
  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 */
  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
  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: 		}
  397: #endif
  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);
  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>