1 /* $Id: ipfwrdr.c,v 1.9 2011/06/04 15:47:18 nanard Exp $ */
4 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * (c) 2009 Jardel Weyrich
6 * (c) 2011 Thomas Bernard
7 * This software is subject to the conditions detailed
8 * in the LICENCE file provided within the distribution
11 #include "../config.h"
13 #include <sys/param.h>
14 #include <sys/types.h>
18 // This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
19 // Needed here because on some systems <sys/uio.h> gets included by things
20 // like <sys/socket.h>
37 #include <sys/socket.h>
38 #include <sys/syslog.h>
39 #include <sys/ioctl.h>
41 #if __FreeBSD_version >= 300000
42 # include <net/if_var.h>
44 #include <netinet/in.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/ip_icmp.h>
48 #include <netinet/tcp.h>
49 #include <netinet/udp.h>
50 #include <arpa/inet.h>
52 #include <sys/types.h>
53 #include <sys/queue.h>
54 #include <sys/socket.h>
66 #include <netinet/ip_fw.h>
70 #include "../upnpglobalvars.h"
72 /* init and shutdown functions */
74 int init_redirect(void) {
75 return ipfw_exec(IP_FW_INIT
, NULL
, 0);
78 void shutdown_redirect(void) {
79 ipfw_exec(IP_FW_TERM
, NULL
, 0);
82 /* ipfw cannot store descriptions and timestamp for port mappings so we keep
83 * our own list in memory */
84 struct mapping_desc_time
{
85 struct mapping_desc_time
* next
;
86 unsigned int timestamp
;
92 static struct mapping_desc_time
* mappings_list
= NULL
;
94 /* add an element to the port mappings descriptions & timestamp list */
96 add_desc_time(unsigned short eport
, int proto
,
97 const char * desc
, unsigned int timestamp
)
99 struct mapping_desc_time
* tmp
;
103 l
= strlen(desc
) + 1;
104 tmp
= malloc(sizeof(struct mapping_desc_time
) + l
);
106 /* fill the element and insert it as head of the list */
107 tmp
->next
= mappings_list
;
108 tmp
->timestamp
= timestamp
;
110 tmp
->proto
= (short)proto
;
111 memcpy(tmp
->desc
, desc
, l
);
116 /* remove an element to the port mappings descriptions & timestamp list */
118 del_desc_time(unsigned short eport
, int proto
)
120 struct mapping_desc_time
* e
;
121 struct mapping_desc_time
* * p
;
125 if(e
->eport
== eport
&& e
->proto
== (short)proto
) {
136 /* go through the list and find the description and timestamp */
138 get_desc_time(unsigned short eport
, int proto
,
139 char * desc
, int desclen
,
140 unsigned int * timestamp
)
142 struct mapping_desc_time
* e
;
144 for(e
= mappings_list
; e
; e
= e
->next
) {
145 if(e
->eport
== eport
&& e
->proto
== (short)proto
) {
147 strlcpy(desc
, e
->desc
, desclen
);
149 *timestamp
= e
->timestamp
;
156 int add_redirect_rule2(
159 unsigned short eport
,
161 unsigned short iport
,
164 unsigned int timestamp
)
169 if (ipfw_validate_protocol(proto
) < 0)
171 if (ipfw_validate_ifname(ifname
) < 0)
174 memset(&rule
, 0, sizeof(struct ip_fw
));
175 rule
.version
= IP_FW_CURRENT_API_VERSION
;
176 //rule.fw_number = 1000; // rule number
177 //rule.context = (void *)desc; // The description is kept in a separate list
178 rule
.fw_prot
= proto
; // protocol
179 rule
.fw_flg
|= IP_FW_F_IIFACE
; // interfaces to check
180 rule
.fw_flg
|= IP_FW_F_IIFNAME
; // interfaces to check by name
181 rule
.fw_flg
|= (IP_FW_F_IN
| IP_FW_F_OUT
); // packet direction
182 rule
.fw_flg
|= IP_FW_F_FWD
; // forward action
183 #ifdef USE_IFNAME_IN_RULES
184 if (ifname
!= NULL
) {
185 strcpy(rule
.fw_in_if
.fu_via_if
.name
, ifname
); // src interface
186 rule
.fw_in_if
.fu_via_if
.unit
= -1;
189 if (inet_aton(iaddr
, &rule
.fw_out_if
.fu_via_ip
) == 0) {
190 syslog(LOG_ERR
, "inet_aton(): %m");
193 memcpy(&rule
.fw_dst
, &rule
.fw_out_if
.fu_via_ip
, sizeof(struct in_addr
));
194 memcpy(&rule
.fw_fwd_ip
.sin_addr
, &rule
.fw_out_if
.fu_via_ip
, sizeof(struct in_addr
));
195 rule
.fw_dmsk
.s_addr
= INADDR_BROADCAST
; //TODO check this
196 IP_FW_SETNDSTP(&rule
, 1); // number of external ports
197 rule
.fw_uar
.fw_pts
[0] = eport
; // external port
198 rule
.fw_fwd_ip
.sin_port
= iport
; // internal port
199 /* TODO : use rhost ! */
201 r
= ipfw_exec(IP_FW_ADD
, &rule
, sizeof(rule
));
203 add_desc_time(eport
, proto
, desc
, timestamp
);
207 /* get_redirect_rule()
208 * return value : 0 success (found)
209 * -1 = error or rule not found */
210 int get_redirect_rule(
212 unsigned short eport
,
216 unsigned short * iport
,
219 unsigned int * timestamp
,
223 int i
, count_rules
, total_rules
= 0;
224 struct ip_fw
* rules
= NULL
;
226 if (ipfw_validate_protocol(proto
) < 0)
228 if (ipfw_validate_ifname(ifname
) < 0)
234 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
237 } while (count_rules
== 10);
239 for (i
=0; i
<total_rules
-1; i
++) {
240 const struct ip_fw
const * ptr
= &rules
[i
];
241 if (proto
== ptr
->fw_prot
&& eport
== ptr
->fw_uar
.fw_pts
[0]) {
243 *packets
= ptr
->fw_pcnt
;
245 *bytes
= ptr
->fw_bcnt
;
247 *iport
= ptr
->fw_fwd_ip
.sin_port
;
248 if (iaddr
!= NULL
&& iaddrlen
> 0) {
249 /* looks like fw_out_if.fu_via_ip is zero */
250 //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
251 if (inet_ntop(AF_INET
, &ptr
->fw_fwd_ip
.sin_addr
, iaddr
, iaddrlen
) == NULL
) {
252 syslog(LOG_ERR
, "inet_ntop(): %m");
256 // And what if we found more than 1 matching rule?
257 ipfw_free_ruleset(&rules
);
258 get_desc_time(eport
, proto
, desc
, desclen
, timestamp
);
265 ipfw_free_ruleset(&rules
);
269 int delete_redirect_rule(
271 unsigned short eport
,
274 int i
, count_rules
, total_rules
= 0;
275 struct ip_fw
* rules
= NULL
;
277 if (ipfw_validate_protocol(proto
) < 0)
279 if (ipfw_validate_ifname(ifname
) < 0)
283 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
286 } while (count_rules
== 10);
288 for (i
=0; i
<total_rules
-1; i
++) {
289 const struct ip_fw
const * ptr
= &rules
[i
];
290 if (proto
== ptr
->fw_prot
&& eport
== ptr
->fw_uar
.fw_pts
[0]) {
291 if (ipfw_exec(IP_FW_DEL
, (struct ip_fw
*)ptr
, sizeof(*ptr
)) < 0)
293 // And what if we found more than 1 matching rule?
294 ipfw_free_ruleset(&rules
);
295 del_desc_time(eport
, proto
);
302 ipfw_free_ruleset(&rules
);
306 int add_filter_rule2(
310 unsigned short eport
,
311 unsigned short iport
,
316 return 0; /* nothing to do, always success */
319 int delete_filter_rule(
321 unsigned short eport
,
325 return 0; /* nothing to do, always success */
328 int get_redirect_rule_by_index(
331 unsigned short * eport
,
334 unsigned short * iport
,
340 unsigned int * timestamp
,
345 struct ip_fw
* rules
= NULL
;
347 if (index
< 0) // TODO shouldn't we also validate the maximum?
353 ipfw_fetch_ruleset(&rules
, &total_rules
, index
+ 1);
355 if (total_rules
> index
) {
356 const struct ip_fw
const * ptr
= &rules
[index
];
357 if (ptr
->fw_prot
== 0) // invalid rule
360 *proto
= ptr
->fw_prot
;
362 *eport
= ptr
->fw_uar
.fw_pts
[0];
364 *iport
= ptr
->fw_fwd_ip
.sin_port
;
366 strlcpy(ifname
, ptr
->fw_in_if
.fu_via_if
.name
, IFNAMSIZ
);
368 *packets
= ptr
->fw_pcnt
;
370 *bytes
= ptr
->fw_bcnt
;
372 *iport
= ptr
->fw_fwd_ip
.sin_port
;
373 if (iaddr
!= NULL
&& iaddrlen
> 0) {
374 /* looks like fw_out_if.fu_via_ip is zero */
375 //if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {
376 if (inet_ntop(AF_INET
, &ptr
->fw_fwd_ip
.sin_addr
, iaddr
, iaddrlen
) == NULL
) {
377 syslog(LOG_ERR
, "inet_ntop(): %m");
381 /* TODO : get rhost */
382 ipfw_free_ruleset(&rules
);
383 get_desc_time(*eport
, *proto
, desc
, desclen
, timestamp
);
389 ipfw_free_ruleset(&rules
);
393 /* upnp_get_portmappings_in_range()
394 * return a list of all "external" ports for which a port
397 get_portmappings_in_range(unsigned short startport
,
398 unsigned short endport
,
400 unsigned int * number
)
402 unsigned short * array
= NULL
;
403 unsigned int capacity
= 128;
404 int i
, count_rules
, total_rules
= 0;
405 struct ip_fw
* rules
= NULL
;
407 if (ipfw_validate_protocol(proto
) < 0)
411 count_rules
= ipfw_fetch_ruleset(&rules
, &total_rules
, 10);
414 } while (count_rules
== 10);
416 array
= calloc(capacity
, sizeof(unsigned short));
418 syslog(LOG_ERR
, "get_portmappings_in_range() : calloc error");
423 for (i
=0; i
<total_rules
-1; i
++) {
424 const struct ip_fw
const * ptr
= &rules
[i
];
425 unsigned short eport
= ptr
->fw_uar
.fw_pts
[0];
426 if (proto
== ptr
->fw_prot
427 && startport
<= eport
428 && eport
<= endport
) {
429 if(*number
>= capacity
) {
431 array
= realloc(array
, sizeof(unsigned short)*capacity
);
433 syslog(LOG_ERR
, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity
);
438 array
[*number
] = eport
;
444 ipfw_free_ruleset(&rules
);