Add PPTP runtime and GUI
[tomato.git] / release / src / router / miniupnpd / natpmp.c
bloba90efb917400ab7004e22b3d318df2e77d315915
1 /* $Id: natpmp.c,v 1.26 2011/07/15 07:48:26 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"
23 #ifdef ENABLE_NATPMP
25 int OpenAndConfNATPMPSocket(in_addr_t addr)
27 int snatpmp;
28 snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/);
29 if(snatpmp<0)
31 syslog(LOG_ERR, "socket(natpmp): %m");
32 return -1;
34 else
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)
44 syslog(LOG_ERR, "bind(natpmp): %m");
45 close(snatpmp);
46 return -1;
49 return snatpmp;
52 int OpenAndConfNATPMPSockets(int * sockets)
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++)
58 sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr);
59 if(sockets[i] < 0)
61 for(j=0; j<i; j++)
63 close(sockets[j]);
64 sockets[j] = -1;
66 return -1;
69 return 0;
72 static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
74 #ifndef MULTIPLE_EXTERNAL_IP
75 char tmp[16];
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 */
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;
101 #endif
104 /** read the request from the socket, process it and then send the
105 * response back.
107 void ProcessIncomingNATPMPPacket(int s)
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;
122 if(!inet_ntop(AF_INET, &senderaddr.sin_addr,
123 senderaddrstr, sizeof(senderaddrstr))) {
124 syslog(LOG_ERR, "inet_ntop(natpmp): %m");
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",
131 return;
133 if(req[1] & 128) {
134 /* discarding NAT-PMP responses silently */
135 return;
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 */
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;
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);
206 } else {
207 index++;
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 // noisy; removed logging -- zzz
218 syslog(LOG_DEBUG, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
219 eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
220 resp[3] = 2; /* Not Authorized/Refused */
223 eport = 0; /* to indicate correct removing of port mapping */
224 } else if(iport==0
225 || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr.sin_addr, iport)) {
226 resp[3] = 2; /* Not Authorized/Refused */
227 } else do {
228 r = get_redirect_rule(ext_if_name, eport, proto,
229 iaddr_old, sizeof(iaddr_old),
230 &iport_old, 0, 0, 0, 0,
231 &timestamp, 0, 0);
232 if(r==0) {
233 if(strcmp(senderaddrstr, iaddr_old)==0
234 && iport==iport_old) {
235 /* redirection allready existing */
236 syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
237 eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
238 /* remove and then add again */
239 if(_upnp_delete_redir(eport, proto) < 0) {
240 syslog(LOG_ERR, "failed to remove port mapping");
241 break;
243 } else {
244 eport++;
245 continue;
248 { /* do the redirection */
249 char desc[64];
250 #if 0
251 timestamp = (unsigned)(time(NULL) - startup_time)
252 + lifetime;
253 snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
254 #else
255 timestamp = time(NULL) + lifetime;
256 snprintf(desc, sizeof(desc), "NAT-PMP %hu %s",
257 eport, (proto==IPPROTO_TCP)?"tcp":"udp");
258 #endif
259 /* TODO : check return code */
260 if(upnp_redirect_internal(NULL, eport, senderaddrstr,
261 iport, proto, desc,
262 timestamp) < 0) {
263 syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
264 eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
265 resp[3] = 3; /* Failure */
266 #if 0
267 } else if( !nextnatpmptoclean_eport
268 || timestamp < nextnatpmptoclean_timestamp) {
269 nextnatpmptoclean_timestamp = timestamp;
270 nextnatpmptoclean_eport = eport;
271 nextnatpmptoclean_proto = proto;
272 #endif
274 break;
276 } while(r==0);
277 *((uint16_t *)(resp+8)) = htons(iport); /* private port */
278 *((uint16_t *)(resp+10)) = htons(eport); /* public port */
279 *((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */
281 resplen = 16;
282 break;
283 default:
284 resp[3] = 5; /* Unsupported OPCODE */
286 n = sendto(s, resp, resplen, 0,
287 (struct sockaddr *)&senderaddr, sizeof(senderaddr));
288 if(n<0) {
289 syslog(LOG_ERR, "sendto(natpmp): %m");
290 } else if(n<resplen) {
291 syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
292 n, resplen);
296 #if 0
297 /* iterate through the redirection list to find those who came
298 * from NAT-PMP and select the first to expire */
299 int ScanNATPMPforExpiration()
301 char desc[64];
302 unsigned short iport, eport;
303 int proto;
304 int r, i;
305 unsigned timestamp;
306 nextnatpmptoclean_eport = 0;
307 nextnatpmptoclean_timestamp = 0;
308 for(i = 0; ; i++) {
309 r = get_redirect_rule_by_index(i, 0, &eport, 0, 0,
310 &iport, &proto, desc, sizeof(desc),
311 &timestamp, 0, 0);
312 if(r<0)
313 break;
314 if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
315 if( !nextnatpmptoclean_eport
316 || timestamp < nextnatpmptoclean_timestamp) {
317 nextnatpmptoclean_eport = eport;
318 nextnatpmptoclean_proto = proto;
319 nextnatpmptoclean_timestamp = timestamp;
320 syslog(LOG_DEBUG, "set nextnatpmptoclean_timestamp to %u", timestamp);
324 return 0;
327 /* remove the next redirection that is expired
329 int CleanExpiredNATPMP()
331 char desc[64];
332 unsigned timestamp;
333 unsigned short iport;
334 if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport,
335 nextnatpmptoclean_proto,
336 0, 0,
337 &iport, desc, sizeof(desc), &timestamp, 0, 0) < 0)
338 return ScanNATPMPforExpiration();
339 /* check desc - this is important since we keep expiration time as part
340 * of the desc.
341 * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp
342 * can be different. In that case, the rule must not be removed ! */
343 if(sscanf(desc, "NAT-PMP %u", &timestamp) == 1) {
344 if(timestamp > nextnatpmptoclean_timestamp)
345 return ScanNATPMPforExpiration();
347 /* remove redirection then search for next one:) */
348 if(_upnp_delete_redir(nextnatpmptoclean_eport, nextnatpmptoclean_proto)<0)
349 return -1;
350 syslog(LOG_NOTICE, "Expired NAT-PMP mapping port %hu %s removed",
351 nextnatpmptoclean_eport,
352 nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP");
353 return ScanNATPMPforExpiration();
355 #endif
357 /* SendNATPMPPublicAddressChangeNotification()
358 * should be called when the public IP address changed */
359 void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
361 struct sockaddr_in sockname;
362 unsigned char notif[12];
363 int j, n;
365 notif[0] = 0; /* vers */
366 notif[1] = 128; /* OP code */
367 notif[2] = 0; /* result code */
368 notif[3] = 0; /* result code */
369 /* seconds since "start of epoch" :
370 * time elapsed since the port mapping table was initialized on
371 * startup or reset for any other reason */
372 *((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time);
373 #ifndef MULTIPLE_EXTERNAL_IP
374 FillPublicAddressResponse(notif, 0);
375 if(notif[3])
377 syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
378 "SendNATPMPPublicAddressChangeNotification");
379 return;
381 #endif
382 memset(&sockname, 0, sizeof(struct sockaddr_in));
383 sockname.sin_family = AF_INET;
384 sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);
386 for(j=0; j<n_sockets; j++)
388 if(sockets[j] < 0)
389 continue;
390 #ifdef MULTIPLE_EXTERNAL_IP
392 struct lan_addr_s * lan_addr = lan_addrs.lh_first;
393 int i;
394 for(i=0; i<j; i++)
395 lan_addr = lan_addr->list.le_next;
396 FillPublicAddressResponse(notif, lan_addr->addr.s_addr);
398 #endif
399 /* Port to use in 2006 version of the NAT-PMP specification */
400 sockname.sin_port = htons(NATPMP_PORT);
401 n = sendto(sockets[j], notif, 12, 0,
402 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
403 if(n < 0)
405 syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
406 "SendNATPMPPublicAddressChangeNotification", sockets[j]);
407 return;
409 /* Port to use in 2008 version of the NAT-PMP specification */
410 sockname.sin_port = htons(NATPMP_NOTIF_PORT);
411 n = sendto(sockets[j], notif, 12, 0,
412 (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
413 if(n < 0)
415 syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
416 "SendNATPMPPublicAddressChangeNotification", sockets[j]);
417 return;
422 #endif