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