1 /* $Id: natpmp.c,v 1.53 2015/09/22 10:10:54 nanard Exp $ */
3 * (c) 2007-2015 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 */
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
22 #include "upnpglobalvars.h"
23 #include "getifaddr.h"
24 #include "upnpredirect.h"
25 #include "commonrdr.h"
26 #include "upnputils.h"
27 #include "portinuse.h"
28 #include "asyncsendto.h"
32 int OpenAndConfNATPMPSocket(in_addr_t addr
)
36 snatpmp
= socket(PF_INET
, SOCK_DGRAM
, 0/*IPPROTO_UDP*/);
39 syslog(LOG_ERR
, "%s: socket(): %m",
40 "OpenAndConfNATPMPSocket");
43 if(setsockopt(snatpmp
, SOL_SOCKET
, SO_REUSEADDR
, &i
, sizeof(i
)) < 0)
45 syslog(LOG_WARNING
, "%s: setsockopt(SO_REUSEADDR): %m",
46 "OpenAndConfNATPMPSocket");
48 if(!set_non_blocking(snatpmp
))
50 syslog(LOG_WARNING
, "%s: set_non_blocking(): %m",
51 "OpenAndConfNATPMPSocket");
54 struct sockaddr_in natpmp_addr
;
55 memset(&natpmp_addr
, 0, sizeof(natpmp_addr
));
56 natpmp_addr
.sin_family
= AF_INET
;
57 natpmp_addr
.sin_port
= htons(NATPMP_PORT
);
58 /*natpmp_addr.sin_addr.s_addr = INADDR_ANY; */
59 natpmp_addr
.sin_addr
.s_addr
= addr
;
60 if(bind(snatpmp
, (struct sockaddr
*)&natpmp_addr
, sizeof(natpmp_addr
)) < 0)
62 syslog(LOG_ERR
, "%s: bind(): %m",
63 "OpenAndConfNATPMPSocket");
71 int OpenAndConfNATPMPSockets(int * sockets
)
74 struct lan_addr_s
* lan_addr
;
75 for(i
= 0, lan_addr
= lan_addrs
.lh_first
;
77 lan_addr
= lan_addr
->list
.le_next
)
79 sockets
[i
] = OpenAndConfNATPMPSocket(lan_addr
->addr
.s_addr
);
94 static void FillPublicAddressResponse(unsigned char * resp
, in_addr_t senderaddr
)
96 #ifndef MULTIPLE_EXTERNAL_IP
100 if(use_ext_ip_addr
) {
101 inet_pton(AF_INET
, use_ext_ip_addr
, resp
+8);
103 if(!ext_if_name
|| ext_if_name
[0]=='\0') {
104 resp
[3] = 3; /* Network Failure (e.g. NAT box itself
105 * has not obtained a DHCP lease) */
106 } else if(getifaddr(ext_if_name
, tmp
, INET_ADDRSTRLEN
, NULL
, NULL
) < 0) {
107 syslog(LOG_ERR
, "Failed to get IP for interface %s", ext_if_name
);
108 resp
[3] = 3; /* Network Failure (e.g. NAT box itself
109 * has not obtained a DHCP lease) */
111 inet_pton(AF_INET
, tmp
, resp
+8); /* ok */
115 struct lan_addr_s
* lan_addr
;
117 for(lan_addr
= lan_addrs
.lh_first
; lan_addr
!= NULL
; lan_addr
= lan_addr
->list
.le_next
) {
118 if( (senderaddr
& lan_addr
->mask
.s_addr
)
119 == (lan_addr
->addr
.s_addr
& lan_addr
->mask
.s_addr
)) {
120 memcpy(resp
+8, &lan_addr
->ext_ip_addr
,
121 sizeof(lan_addr
->ext_ip_addr
));
129 * Receives NATPMP and PCP packets and stores them in msg_buff.
130 * The sender information is stored in senderaddr.
131 * Returns number of bytes recevied, even if number is negative.
133 int ReceiveNATPMPOrPCPPacket(int s
, struct sockaddr
* senderaddr
,
134 socklen_t
* senderaddrlen
,
135 struct sockaddr_in6
* receiveraddr
,
136 unsigned char * msg_buff
, size_t msg_buff_size
)
145 iov
.iov_base
= msg_buff
;
146 iov
.iov_len
= msg_buff_size
;
147 memset(&msg
, 0, sizeof(msg
));
150 msg
.msg_name
= senderaddr
;
151 msg
.msg_namelen
= *senderaddrlen
;
153 msg
.msg_controllen
= sizeof(c
);
155 n
= recvmsg(s
, &msg
, 0);
157 /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time)
158 * other errors : log to LOG_ERR */
159 if(errno
!= EAGAIN
&&
160 errno
!= EWOULDBLOCK
&&
162 syslog(LOG_ERR
, "recvmsg(natpmp): %m");
168 memset(receiveraddr
, 0, sizeof(struct sockaddr_in6
));
170 if ((msg
.msg_flags
& MSG_TRUNC
) || (msg
.msg_flags
& MSG_CTRUNC
)) {
171 syslog(LOG_WARNING
, "%s: truncated message",
172 "ReceiveNATPMPOrPCPPacket");
174 for(h
= CMSG_FIRSTHDR(&msg
); h
;
175 h
= CMSG_NXTHDR(&msg
, h
)) {
176 if(h
->cmsg_level
== IPPROTO_IPV6
&& h
->cmsg_type
== IPV6_PKTINFO
) {
177 char tmp
[INET6_ADDRSTRLEN
];
178 struct in6_pktinfo
*ipi6
= (struct in6_pktinfo
*)CMSG_DATA(h
);
179 syslog(LOG_DEBUG
, "%s: packet destination: %s scope_id=%u",
180 "ReceiveNATPMPOrPCPPacket",
181 inet_ntop(AF_INET6
, &ipi6
->ipi6_addr
, tmp
, sizeof(tmp
)),
184 receiveraddr
->sin6_addr
= ipi6
->ipi6_addr
;
185 receiveraddr
->sin6_scope_id
= ipi6
->ipi6_ifindex
;
186 receiveraddr
->sin6_family
= AF_INET6
;
187 receiveraddr
->sin6_port
= htons(NATPMP_PORT
);
191 #else /* IPV6_PKTINFO */
194 n
= recvfrom(s
, msg_buff
, msg_buff_size
, 0,
195 senderaddr
, senderaddrlen
);
198 /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time)
199 * other errors : log to LOG_ERR */
200 if(errno
!= EAGAIN
&&
201 errno
!= EWOULDBLOCK
&&
203 syslog(LOG_ERR
, "recvfrom(natpmp): %m");
207 #endif /* IPV6_PKTINFO */
212 /** read the request from the socket, process it and then send the
215 void ProcessIncomingNATPMPPacket(int s
, unsigned char *msg_buff
, int len
,
216 struct sockaddr_in
*senderaddr
)
218 unsigned char *req
=msg_buff
; /* request udp packet */
219 unsigned char resp
[32]; /* response udp packet */
222 char senderaddrstr
[16];
224 if(!inet_ntop(AF_INET
, &senderaddr
->sin_addr
,
225 senderaddrstr
, sizeof(senderaddrstr
))) {
226 syslog(LOG_ERR
, "inet_ntop(natpmp): %m");
229 syslog(LOG_INFO
, "NAT-PMP request received from %s:%hu %dbytes",
230 senderaddrstr
, ntohs(senderaddr
->sin_port
), n
);
232 if(n
<2 || ((((req
[1]-1)&~1)==0) && n
<12)) {
233 syslog(LOG_WARNING
, "discarding NAT-PMP request (too short) %dBytes",
238 /* discarding NAT-PMP responses silently */
241 memset(resp
, 0, sizeof(resp
));
243 resp
[1] = 128 + req
[1]; /* response OPCODE is request OPCODE + 128 */
244 /* setting response TIME STAMP :
245 * time elapsed since its port mapping table was initialized on
246 * startup or reset for any other reason */
247 WRITENU32(resp
+4, time(NULL
) - startup_time
);
249 /* invalid version */
250 syslog(LOG_WARNING
, "unsupported NAT-PMP version : %u",
252 resp
[3] = 1; /* unsupported version */
253 } else switch(req
[1]) {
254 case 0: /* Public address request */
255 syslog(LOG_INFO
, "NAT-PMP public address request");
256 FillPublicAddressResponse(resp
, senderaddr
->sin_addr
.s_addr
);
259 case 1: /* UDP port mapping request */
260 case 2: /* TCP port mapping request */
262 unsigned short iport
; /* private port */
263 unsigned short eport
; /* public port */
264 uint32_t lifetime
; /* lifetime=0 => remove port mapping */
268 unsigned short iport_old
;
269 unsigned int timestamp
;
271 iport
= READNU16(req
+4);
272 eport
= READNU16(req
+6);
273 lifetime
= READNU32(req
+8);
274 proto
= (req
[1]==1)?IPPROTO_UDP
:IPPROTO_TCP
;
275 syslog(LOG_INFO
, "NAT-PMP port mapping request : "
276 "%hu->%s:%hu %s lifetime=%us",
277 eport
, senderaddrstr
, iport
,
278 (req
[1]==1)?"udp":"tcp", lifetime
);
279 /* TODO: accept port mapping if iport ok but eport not ok
280 * (and set eport correctly) */
282 /* remove the mapping */
284 * A client MAY also send an explicit packet to request deletion of a
285 * mapping that is no longer needed. A client requests explicit
286 * deletion of a mapping by sending a message to the NAT gateway
287 * requesting the mapping, with the Requested Lifetime in Seconds set to
288 * zero. The Suggested External Port MUST be set to zero by the client
289 * on sending, and MUST be ignored by the gateway on reception. */
291 unsigned short eport2
, iport2
;
295 eport
= 0; /* to indicate correct removing of port mapping */
296 while(get_redirect_rule_by_index(index
, 0,
297 &eport2
, iaddr2
, sizeof(iaddr2
),
300 0, 0, ×tamp
, 0, 0) >= 0) {
301 syslog(LOG_DEBUG
, "%d %d %hu->'%s':%hu '%s'",
302 index
, proto2
, eport2
, iaddr2
, iport2
, desc
);
303 if(0 == strcmp(iaddr2
, senderaddrstr
)
304 && 0 == memcmp(desc
, "NAT-PMP", 7)) {
305 /* (iport == 0) => remove all the mappings for this client */
306 if((iport
== 0) || ((iport
== iport2
) && (proto
== proto2
))) {
307 r
= _upnp_delete_redir(eport2
, proto2
);
309 syslog(LOG_ERR
, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
310 eport2
, (proto2
==IPPROTO_TCP
)?"TCP":"UDP");
311 resp
[3] = 2; /* Not Authorized/Refused */
314 syslog(LOG_INFO
, "NAT-PMP %s port %hu mapping removed",
315 proto2
==IPPROTO_TCP
?"TCP":"UDP", eport2
);
322 } else if(iport
==0) {
323 resp
[3] = 2; /* Not Authorized/Refused */
324 } else { /* iport > 0 && lifetime > 0 */
325 unsigned short eport_first
= 0;
326 int any_eport_allowed
= 0;
328 if(eport
==0) /* if no suggested external port, use same a internal port */
330 while(resp
[3] == 0) {
331 if(eport_first
== 0) { /* first time in loop */
333 } else if(eport
== eport_first
) { /* no eport available */
334 if(any_eport_allowed
== 0) { /* all eports rejected by permissions */
335 syslog(LOG_ERR
, "No allowed eport for NAT-PMP %hu %s->%s:%hu",
336 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp", senderaddrstr
, iport
);
337 resp
[3] = 2; /* Not Authorized/Refused */
338 } else { /* at least one eport allowed (but none available) */
339 syslog(LOG_ERR
, "Failed to find available eport for NAT-PMP %hu %s->%s:%hu",
340 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp", senderaddrstr
, iport
);
341 resp
[3] = 4; /* Out of resources */
345 if(!check_upnp_rule_against_permissions(upnppermlist
, num_upnpperm
, eport
, senderaddr
->sin_addr
, iport
)) {
347 if(eport
== 0) eport
++; /* skip port zero */
350 any_eport_allowed
= 1; /* at lease one eport is allowed */
351 #ifdef CHECK_PORTINUSE
352 if (port_in_use(ext_if_name
, eport
, proto
, senderaddrstr
, iport
) > 0) {
353 syslog(LOG_INFO
, "port %hu protocol %s already in use",
354 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp");
356 if(eport
== 0) eport
++; /* skip port zero */
360 r
= get_redirect_rule(ext_if_name
, eport
, proto
,
361 iaddr_old
, sizeof(iaddr_old
),
362 &iport_old
, 0, 0, 0, 0,
365 if(strcmp(senderaddrstr
, iaddr_old
)==0
366 && iport
==iport_old
) {
367 /* redirection allready existing */
368 syslog(LOG_INFO
, "port %hu %s already redirected to %s:%hu, replacing",
369 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp", iaddr_old
, iport_old
);
370 /* remove and then add again */
371 if(_upnp_delete_redir(eport
, proto
) < 0) {
372 syslog(LOG_ERR
, "failed to remove port mapping");
377 if(eport
== 0) eport
++; /* skip port zero */
381 /* do the redirection */
383 timestamp
= (unsigned)(time(NULL
) - startup_time
)
385 snprintf(desc
, sizeof(desc
), "NAT-PMP %u", timestamp
);
387 timestamp
= time(NULL
) + lifetime
;
388 snprintf(desc
, sizeof(desc
), "NAT-PMP %hu %s",
389 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp");
391 /* TODO : check return code */
392 if(upnp_redirect_internal(NULL
, eport
, senderaddrstr
,
395 syslog(LOG_ERR
, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
396 eport
, (proto
==IPPROTO_TCP
)?"tcp":"udp", senderaddrstr
, iport
, desc
);
397 resp
[3] = 3; /* Failure */
402 WRITENU16(resp
+8, iport
); /* private port */
403 WRITENU16(resp
+10, eport
); /* public port */
404 WRITENU32(resp
+12, lifetime
); /* Port Mapping lifetime */
409 resp
[3] = 5; /* Unsupported OPCODE */
411 n
= sendto_or_schedule(s
, resp
, resplen
, 0,
412 (struct sockaddr
*)senderaddr
, sizeof(*senderaddr
));
414 syslog(LOG_ERR
, "sendto(natpmp): %m");
415 } else if(n
<resplen
) {
416 syslog(LOG_ERR
, "sendto(natpmp): sent only %d bytes out of %d",
421 /* SendNATPMPPublicAddressChangeNotification()
422 * should be called when the public IP address changed */
423 void SendNATPMPPublicAddressChangeNotification(int * sockets
, int n_sockets
)
425 struct sockaddr_in sockname
;
426 unsigned char notif
[12];
429 notif
[0] = 0; /* vers */
430 notif
[1] = 128; /* OP code */
431 notif
[2] = 0; /* result code */
432 notif
[3] = 0; /* result code */
433 /* seconds since "start of epoch" :
434 * time elapsed since the port mapping table was initialized on
435 * startup or reset for any other reason */
436 WRITENU32(notif
+4, time(NULL
) - startup_time
);
437 #ifndef MULTIPLE_EXTERNAL_IP
438 FillPublicAddressResponse(notif
, 0);
441 syslog(LOG_WARNING
, "%s: cannot get public IP address, stopping",
442 "SendNATPMPPublicAddressChangeNotification");
446 memset(&sockname
, 0, sizeof(struct sockaddr_in
));
447 sockname
.sin_family
= AF_INET
;
448 sockname
.sin_addr
.s_addr
= inet_addr(NATPMP_NOTIF_ADDR
);
450 for(j
=0; j
<n_sockets
; j
++)
454 #ifdef MULTIPLE_EXTERNAL_IP
456 struct lan_addr_s
* lan_addr
= lan_addrs
.lh_first
;
459 lan_addr
= lan_addr
->list
.le_next
;
460 FillPublicAddressResponse(notif
, lan_addr
->addr
.s_addr
);
463 /* Port to use in 2006 version of the NAT-PMP specification */
464 sockname
.sin_port
= htons(NATPMP_PORT
);
465 n
= sendto_or_schedule(sockets
[j
], notif
, 12, 0,
466 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
));
469 syslog(LOG_ERR
, "%s: sendto(s_udp=%d): %m",
470 "SendNATPMPPublicAddressChangeNotification", sockets
[j
]);
473 /* Port to use in 2008 version of the NAT-PMP specification */
474 sockname
.sin_port
= htons(NATPMP_NOTIF_PORT
);
475 n
= sendto_or_schedule(sockets
[j
], notif
, 12, 0,
476 (struct sockaddr
*)&sockname
, sizeof(struct sockaddr_in
));
479 syslog(LOG_ERR
, "%s: sendto(s_udp=%d): %m",
480 "SendNATPMPPublicAddressChangeNotification", sockets
[j
]);
486 #endif /* ENABLE_NATPMP */