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