File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / natpmp.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:32:35 2013 UTC (10 years, 10 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: natpmp.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */
    2: /* MiniUPnP project
    3:  * (c) 2007-2012 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 <errno.h>
   12: #include <time.h>
   13: #include <sys/types.h>
   14: #include <sys/socket.h>
   15: #include <netinet/in.h>
   16: #include <arpa/inet.h>
   17: 
   18: #include "macros.h"
   19: #include "config.h"
   20: #include "natpmp.h"
   21: #include "upnpglobalvars.h"
   22: #include "getifaddr.h"
   23: #include "upnpredirect.h"
   24: #include "commonrdr.h"
   25: #include "upnputils.h"
   26: 
   27: #ifdef ENABLE_NATPMP
   28: 
   29: int OpenAndConfNATPMPSocket(in_addr_t addr)
   30: {
   31: 	int snatpmp;
   32: 	int i = 1;
   33: 	snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
   34: 	if(snatpmp<0)
   35: 	{
   36: 		syslog(LOG_ERR, "%s: socket(natpmp): %m",
   37: 		       "OpenAndConfNATPMPSocket");
   38: 		return -1;
   39: 	}
   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: 	}
   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);
   55: 		/*natpmp_addr.sin_addr.s_addr = INADDR_ANY; */
   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;
   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++)
   72: 	{
   73: 		sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr);
   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];
   91: 	UNUSED(senderaddr);
   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 {
  104: 			inet_pton(AF_INET, tmp, resp+8); /* ok */
  105: 		}
  106: 	}
  107: #else
  108: 	struct lan_addr_s * lan_addr;
  109: 
  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));
  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) {
  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: 		}
  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 */
  163: 	/* setting response TIME STAMP :
  164: 	 * time elapsed since its port mapping table was initialized on
  165: 	 * startup or reset for any other reason */
  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;
  188: 			unsigned int timestamp;
  189: 
  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,
  214: 							  desc, sizeof(desc),
  215: 					          0, 0, &timestamp, 0, 0) >= 0) {
  216: 						syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
  217: 						       index, proto2, eport2, iaddr2, iport2, desc);
  218: 						if(0 == strcmp(iaddr2, senderaddrstr)
  219: 						  && 0 == memcmp(desc, "NAT-PMP", 7)) {
  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 {
  226: 								syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
  227: 								       proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
  228: 							}
  229: 						} else {
  230: 							index++;
  231: 						}
  232: 					}
  233: 				} else {
  234: 					/* To improve the interworking between nat-pmp and
  235: 					 * UPnP, we should check that we remove only NAT-PMP
  236: 					 * mappings */
  237: 					r = _upnp_delete_redir(eport, proto);
  238: 					/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
  239: 					if(r<0) {
  240: 						syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
  241: 						       eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
  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),
  252: 				                      &iport_old, 0, 0, 0, 0,
  253: 				                      &timestamp, 0, 0);
  254: 				if(r==0) {
  255: 					if(strcmp(senderaddrstr, iaddr_old)==0
  256: 				       && iport==iport_old) {
  257: 						/* redirection allready existing */
  258: 						syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
  259: 						       eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
  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];
  272: #if 0
  273: 					timestamp = (unsigned)(time(NULL) - startup_time)
  274: 					                      + lifetime;
  275: 					snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
  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
  281: 					/* TODO : check return code */
  282: 					if(upnp_redirect_internal(NULL, eport, senderaddrstr,
  283: 					                          iport, proto, desc,
  284: 					                          timestamp) < 0) {
  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 */
  288: #if 0
  289: 					} else if( !nextnatpmptoclean_eport
  290: 					         || timestamp < nextnatpmptoclean_timestamp) {
  291: 						nextnatpmptoclean_timestamp = timestamp;
  292: 						nextnatpmptoclean_eport = eport;
  293: 						nextnatpmptoclean_proto = proto;
  294: #endif
  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 */
  301: 			*((uint32_t *)(resp+12)) = htonl(lifetime);	/* Port Mapping lifetime */
  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: 
  318: #if 0
  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),
  333: 		                               &timestamp, 0, 0);
  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,
  359: 	                     &iport, desc, sizeof(desc), &timestamp, 0, 0) < 0)
  360: 		return ScanNATPMPforExpiration();
  361: 	/* check desc - this is important since we keep expiration time as part
  362: 	 * of the desc.
  363: 	 * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp
  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: }
  377: #endif
  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: 
  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 */
  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
  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: 		}
  420: #endif
  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)
  426: 		{
  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);
  433: 		n = sendto(sockets[j], notif, 12, 0,
  434: 		           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
  435: 		if(n < 0)
  436: 		{
  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>