1 /* $Id: ipfwrdr.c,v 1.15 2016/01/13 15:51:06 nanard Exp $ */
4 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * (c) 2009 Jardel Weyrich
6 * (c) 2011-2012 Thomas Bernard
7 * This software is subject to the conditions detailed
8 * in the LICENCE file provided within the distribution
11 #include "../config.h"
12 #include "../macros.h"
14 #include <sys/param.h>
15 #include <sys/types.h>
19 This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
20 Needed here because on some systems <sys/uio.h> gets included by things
38 #include <sys/socket.h>
39 #include <sys/syslog.h>
40 #include <sys/ioctl.h>
42 #if __FreeBSD_version >= 300000
43 # include <net/if_var.h>
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip.h>
48 #include <netinet/ip_icmp.h>
49 #include <netinet/tcp.h>
50 #include <netinet/udp.h>
51 #include <arpa/inet.h>
53 #include <sys/types.h>
54 #include <sys/queue.h>
55 #include <sys/socket.h>
67 #include <netinet/ip_fw.h>
71 #include "../upnpglobalvars.h"
73 /* init and shutdown functions */
75 int init_redirect(void) {
76 return ipfw_exec(IP_FW_INIT
, NULL
, 0);
79 void shutdown_redirect(void) {
80 ipfw_exec(IP_FW_TERM
, NULL
, 0);
83 /* ipfw cannot store descriptions and timestamp for port mappings so we keep
84 * our own list in memory */
85 struct mapping_desc_time
{
86 struct mapping_desc_time
* next
;
87 unsigned int timestamp
;
93 static struct mapping_desc_time
* mappings_list
= NULL
;
95 /* add an element to the port mappings descriptions & timestamp list */
97 add_desc_time(unsigned short eport
, int proto
,
98 const char * desc
, unsigned int timestamp
)
100 struct mapping_desc_time
* tmp
;
104 l
= strlen(desc
) + 1;
105 tmp
= malloc(sizeof(struct mapping_desc_time
) + l
);
107 /* fill the element and insert it as head of the list */
108 tmp
->next
= mappings_list
;
109 tmp
->timestamp
= timestamp
;
111 tmp
->proto
= (short)proto
;
112 memcpy(tmp
->desc
, desc
, l
);
117 /* remove an element to the port mappings descriptions & timestamp list */
119 del_desc_time(unsigned short eport
, int proto
)
121 struct mapping_desc_time
* e
;
122 struct mapping_desc_time
* * p
;
126 if(e
->eport
== eport
&& e
->proto
== (short)proto
) {
137 /* go through the list and find the description and timestamp */
139 get_desc_time(unsigned short eport
, int proto
,
140 char * desc
, int desclen
,
141 unsigned int * timestamp
)
143 struct mapping_desc_time
* e
;
145 for(e
= mappings_list
; e
; e
= e
->next
) {
146 if(e
->eport
== eport
&& e
->proto
== (short)proto
) {
148 strlcpy(desc
, e
->desc
, desclen
);
150 *timestamp
= e
->timestamp
;
157 int add_redirect_rule2(
160 unsigned short eport
,
162 unsigned short iport
,
165 unsigned int timestamp
)
170 if (ipfw_validate_protocol(proto
) < 0)
172 if (ipfw_validate_ifname(ifname
) < 0)
175 memset(&rule
, 0, sizeof(struct ip_fw
));
176 rule
.version
= IP_FW_CURRENT_API_VERSION
;
178 rule
.fw_number
= 1000; /* rule number */
179 rule
.context
= (void *)desc
; /* The description is kept in a separate list */
181 rule
.fw_prot
= proto
; /* protocol */
182 rule
.fw_flg
|= IP_FW_F_IIFACE
; /* interfaces to check */
183 rule
.fw_flg
|= IP_FW_F_IIFNAME
; /* interfaces to check by name */
184 rule
.fw_flg
|= (IP_FW_F_IN
| IP_FW_F_OUT
); /* packet direction */
185 rule
.fw_flg
|= IP_FW_F_FWD
; /* forward action */
186 #ifdef USE_IFNAME_IN_RULES
187 if (ifname
!= NULL
) {
188 strlcpy(rule
.fw_in_if
.fu_via_if
.name
, ifname
, IFNAMSIZ
); /* src interface */
189 rule
.fw_in_if
.fu_via_if
.unit
= -1;
192 if (inet_aton(iaddr
, &rule
.fw_out_if
.fu_via_ip
) == 0) {
193 syslog(LOG_ERR
, "inet_aton(): %m");
196 memcpy(&rule
.fw_dst
, &rule
.fw_out_if
.fu_via_ip
, sizeof(struct in_addr
));
197 memcpy(&rule
.fw_fwd_ip
.sin_addr
, &rule
.fw_out_if
.fu_via_ip
, sizeof(struct in_addr
));
198 rule
.fw_dmsk
.s_addr
= INADDR_BROADCAST
; /* TODO check this */
199 IP_FW_SETNDSTP(&rule
, 1); /* number of external ports */
200 rule
.fw_uar
.fw_pts
[0] = eport
; /* external port */
201 rule
.fw_fwd_ip
.sin_port
= iport
; /* internal port */
202 if (rhost
&& rhost
[0] != '\0') {
203 inet_aton(rhost
, &rule
.fw_src
);
204 rule
.fw_smsk
.s_addr
= htonl(INADDR_NONE
);
207 r
= ipfw_exec(IP_FW_ADD
, &rule
, sizeof(rule
));
209 add_desc_time(eport
, proto
, desc
, timestamp
);
213 /* get_redirect_rule()
214 * return value : 0 success (found)
215 * -1 = error or rule not found */
216 int get_redirect_rule(
218 unsigned short eport
,
222 unsigned short * iport
,
227 unsigned int * timestamp
,
231 int i
, count_rules
, total_rules
= 0;
232 struct ip_fw
* rules
= NULL
;
234 if (ipfw_validate_protocol(proto
) < 0)
236 if (ipfw_validate_ifname(ifname
) < 0)
242 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
245 } while (count_rules
== 10);
247 for (i
=0; i
<total_rules
-1; i
++) {
248 const struct ip_fw
const * ptr
= &rules
[i
];
249 if (proto
== ptr
->fw_prot
&& eport
== ptr
->fw_uar
.fw_pts
[0]) {
251 *packets
= ptr
->fw_pcnt
;
253 *bytes
= ptr
->fw_bcnt
;
255 *iport
= ptr
->fw_fwd_ip
.sin_port
;
256 if (iaddr
!= NULL
&& iaddrlen
> 0) {
257 /* looks like fw_out_if.fu_via_ip is zero */
258 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
259 if (inet_ntop(AF_INET
, &ptr
->fw_fwd_ip
.sin_addr
, iaddr
, iaddrlen
) == NULL
) {
260 syslog(LOG_ERR
, "inet_ntop(): %m");
264 if (rhost
!= NULL
&& rhostlen
> 0) {
265 if (ptr
->fw_src
.s_addr
== 0)
267 else if (inet_ntop(AF_INET
, &ptr
->fw_src
.s_addr
, rhost
, rhostlen
) == NULL
) {
268 syslog(LOG_ERR
, "inet_ntop(): %m");
272 /* And what if we found more than 1 matching rule? */
273 ipfw_free_ruleset(&rules
);
274 get_desc_time(eport
, proto
, desc
, desclen
, timestamp
);
281 ipfw_free_ruleset(&rules
);
285 int delete_redirect_rule(
287 unsigned short eport
,
290 int i
, count_rules
, total_rules
= 0;
291 struct ip_fw
* rules
= NULL
;
293 if (ipfw_validate_protocol(proto
) < 0)
295 if (ipfw_validate_ifname(ifname
) < 0)
299 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
302 } while (count_rules
== 10);
304 for (i
=0; i
<total_rules
-1; i
++) {
305 const struct ip_fw
const * ptr
= &rules
[i
];
306 if (proto
== ptr
->fw_prot
&& eport
== ptr
->fw_uar
.fw_pts
[0]) {
307 if (ipfw_exec(IP_FW_DEL
, (struct ip_fw
*)ptr
, sizeof(*ptr
)) < 0)
309 /* And what if we found more than 1 matching rule? */
310 ipfw_free_ruleset(&rules
);
311 del_desc_time(eport
, proto
);
318 ipfw_free_ruleset(&rules
);
322 int add_filter_rule2(
326 unsigned short eport
,
327 unsigned short iport
,
338 return 0; /* nothing to do, always success */
341 int delete_filter_rule(
343 unsigned short eport
,
349 return 0; /* nothing to do, always success */
352 int get_redirect_rule_by_index(
355 unsigned short * eport
,
358 unsigned short * iport
,
364 unsigned int * timestamp
,
369 struct ip_fw
* rules
= NULL
;
371 if (index
< 0) /* TODO shouldn't we also validate the maximum? */
377 ipfw_fetch_ruleset(&rules
, &total_rules
, index
+ 1);
379 if (total_rules
> index
) {
380 const struct ip_fw
const * ptr
= &rules
[index
];
381 if (ptr
->fw_prot
== 0) /* invalid rule */
384 *proto
= ptr
->fw_prot
;
386 *eport
= ptr
->fw_uar
.fw_pts
[0];
388 *iport
= ptr
->fw_fwd_ip
.sin_port
;
390 strlcpy(ifname
, ptr
->fw_in_if
.fu_via_if
.name
, IFNAMSIZ
);
392 *packets
= ptr
->fw_pcnt
;
394 *bytes
= ptr
->fw_bcnt
;
396 *iport
= ptr
->fw_fwd_ip
.sin_port
;
397 if (iaddr
!= NULL
&& iaddrlen
> 0) {
398 /* looks like fw_out_if.fu_via_ip is zero */
399 /*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
400 if (inet_ntop(AF_INET
, &ptr
->fw_fwd_ip
.sin_addr
, iaddr
, iaddrlen
) == NULL
) {
401 syslog(LOG_ERR
, "inet_ntop(): %m");
405 if (rhost
!= NULL
&& rhostlen
> 0) {
406 if (ptr
->fw_src
.s_addr
== 0)
408 else if (inet_ntop(AF_INET
, &ptr
->fw_src
.s_addr
, rhost
, rhostlen
) == NULL
) {
409 syslog(LOG_ERR
, "inet_ntop(): %m");
413 ipfw_free_ruleset(&rules
);
414 get_desc_time(*eport
, *proto
, desc
, desclen
, timestamp
);
420 ipfw_free_ruleset(&rules
);
424 /* upnp_get_portmappings_in_range()
425 * return a list of all "external" ports for which a port
428 get_portmappings_in_range(unsigned short startport
,
429 unsigned short endport
,
431 unsigned int * number
)
433 unsigned short *array
= NULL
, *array2
= NULL
;
434 unsigned int capacity
= 128;
435 int i
, count_rules
, total_rules
= 0;
436 struct ip_fw
* rules
= NULL
;
438 if (ipfw_validate_protocol(proto
) < 0)
442 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
445 } while (count_rules
== 10);
447 array
= calloc(capacity
, sizeof(unsigned short));
449 syslog(LOG_ERR
, "get_portmappings_in_range() : calloc error");
454 for (i
=0; i
<total_rules
-1; i
++) {
455 const struct ip_fw
const * ptr
= &rules
[i
];
456 unsigned short eport
= ptr
->fw_uar
.fw_pts
[0];
457 if (proto
== ptr
->fw_prot
458 && startport
<= eport
459 && eport
<= endport
) {
460 if(*number
>= capacity
) {
462 array2
= realloc(array
, sizeof(unsigned short)*capacity
);
464 syslog(LOG_ERR
, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity
);
471 array
[*number
] = eport
;
477 ipfw_free_ruleset(&rules
);