Annotation of embedaddon/miniupnpd/natpmp.c, revision 1.1.1.1
1.1 misho 1: /* $Id: natpmp.c,v 1.20 2010/05/06 13:42:47 nanard 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", ×tamp) == 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", ×tamp) == 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>