1 /* $Id: natpmp.c,v 1.26 2011/07/15 07:48:26 nanard Exp $ */
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 */
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
18 #include "upnpglobalvars.h"
19 #include "getifaddr.h"
20 #include "upnpredirect.h"
21 #include "commonrdr.h"
25 int OpenAndConfNATPMPSocket(in_addr_t addr
)
28 snatpmp
= socket(PF_INET
, SOCK_DGRAM
, 0/*IPPROTO_UDP*/);
31 syslog(LOG_ERR
, "socket(natpmp): %m");
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");
52 int OpenAndConfNATPMPSockets(int * sockets
)
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
);
72 static void FillPublicAddressResponse(unsigned char * resp
, in_addr_t senderaddr
)
74 #ifndef MULTIPLE_EXTERNAL_IP
78 inet_pton(AF_INET
, use_ext_ip_addr
, resp
+8);
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) */
88 inet_pton(AF_INET
, tmp
, resp
+8); /* ok */
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
));
104 /** read the request from the socket, process it and then send the
107 void ProcessIncomingNATPMPPacket(int s
)
109 unsigned char req
[32]; /* request udp packet */
110 unsigned char resp
[32]; /* response udp packet */
112 struct sockaddr_in senderaddr
;
113 socklen_t senderaddrlen
= sizeof(senderaddr
);
115 char senderaddrstr
[16];
116 n
= recvfrom(s
, req
, sizeof(req
), 0,
117 (struct sockaddr
*)&senderaddr
, &senderaddrlen
);
119 syslog(LOG_ERR
, "recvfrom(natpmp): %m");
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",
134 /* discarding NAT-PMP responses silently */
137 memset(resp
, 0, sizeof(resp
));
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
);
145 /* invalid version */
146 syslog(LOG_WARNING
, "unsupported NAT-PMP version : %u",
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
);
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 */
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
);
177 /* TODO: accept port mapping if iport ok but eport not ok
178 * (and set eport correctly) */
180 /* remove the mapping */
182 /* remove all the mappings for this client */
184 unsigned short eport2
, iport2
;
188 while(get_redirect_rule_by_index(index
, 0,
189 &eport2
, iaddr2
, sizeof(iaddr2
),
192 0, 0, ×tamp
, 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 */
200 syslog(LOG_ERR
, "failed to remove port mapping");
203 syslog(LOG_INFO
, "NAT-PMP %s port %hu mapping removed",
204 proto2
==IPPROTO_TCP
?"TCP":"UDP", eport2
);
211 /* To improve the interworking between nat-pmp and
212 * UPnP, we should check that we remove only NAT-PMP
214 r
= _upnp_delete_redir(eport
, proto
);
215 /*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
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 */
225 || !check_upnp_rule_against_permissions(upnppermlist
, num_upnpperm
, eport
, senderaddr
.sin_addr
, iport
)) {
226 resp
[3] = 2; /* Not Authorized/Refused */
228 r
= get_redirect_rule(ext_if_name
, eport
, proto
,
229 iaddr_old
, sizeof(iaddr_old
),
230 &iport_old
, 0, 0, 0, 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");
248 { /* do the redirection */
251 timestamp
= (unsigned)(time(NULL
) - startup_time
)
253 snprintf(desc
, sizeof(desc
), "NAT-PMP %u", timestamp
);
255 timestamp
= time(NULL
) + lifetime
;
256 snprintf(desc
, sizeof(desc
), "NAT-PMP %hu %s",
257 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp");
259 /* TODO : check return code */
260 if(upnp_redirect_internal(NULL
, eport
, senderaddrstr
,
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 */
267 } else if( !nextnatpmptoclean_eport
268 || timestamp
< nextnatpmptoclean_timestamp
) {
269 nextnatpmptoclean_timestamp
= timestamp
;
270 nextnatpmptoclean_eport
= eport
;
271 nextnatpmptoclean_proto
= proto
;
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 */
284 resp
[3] = 5; /* Unsupported OPCODE */
286 n
= sendto(s
, resp
, resplen
, 0,
287 (struct sockaddr
*)&senderaddr
, sizeof(senderaddr
));
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",
297 /* iterate through the redirection list to find those who came
298 * from NAT-PMP and select the first to expire */
299 int ScanNATPMPforExpiration()
302 unsigned short iport
, eport
;
306 nextnatpmptoclean_eport
= 0;
307 nextnatpmptoclean_timestamp
= 0;
309 r
= get_redirect_rule_by_index(i
, 0, &eport
, 0, 0,
310 &iport
, &proto
, desc
, sizeof(desc
),
314 if(sscanf(desc
, "NAT-PMP %u", ×tamp
) == 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
);
327 /* remove the next redirection that is expired
329 int CleanExpiredNATPMP()
333 unsigned short iport
;
334 if(get_redirect_rule(ext_if_name
, nextnatpmptoclean_eport
,
335 nextnatpmptoclean_proto
,
337 &iport
, desc
, sizeof(desc
), ×tamp
, 0, 0) < 0)
338 return ScanNATPMPforExpiration();
339 /* check desc - this is important since we keep expiration time as part
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", ×tamp
) == 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)
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();
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];
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);
377 syslog(LOG_WARNING
, "%s: cannot get public IP address, stopping",
378 "SendNATPMPPublicAddressChangeNotification");
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
++)
390 #ifdef MULTIPLE_EXTERNAL_IP
392 struct lan_addr_s
* lan_addr
= lan_addrs
.lh_first
;
395 lan_addr
= lan_addr
->list
.le_next
;
396 FillPublicAddressResponse(notif
, lan_addr
->addr
.s_addr
);
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
));
405 syslog(LOG_ERR
, "%s: sendto(s_udp=%d): %m",
406 "SendNATPMPPublicAddressChangeNotification", sockets
[j
]);
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
));
415 syslog(LOG_ERR
, "%s: sendto(s_udp=%d): %m",
416 "SendNATPMPPublicAddressChangeNotification", sockets
[j
]);