Miniupnpd v. 1.5 (20110618)
[tomato.git] / release / src / router / miniupnpd / ipfw / ipfwrdr.c
blobbef22ed62f470355ac5bc25db81520a6d329bfb9
1 /* $Id: ipfwrdr.c,v 1.9 2011/06/04 15:47:18 nanard Exp $ */
2 /*
3 * MiniUPnP project
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
9 */
11 #include "../config.h"
13 #include <sys/param.h>
14 #include <sys/types.h>
15 #include <sys/file.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>
22 #ifndef _KERNEL
23 # define ADD_KERNEL
24 # define _KERNEL
25 # define KERNEL
26 #endif
27 #ifdef __OpenBSD__
28 struct file;
29 #endif
30 #include <sys/uio.h>
31 #ifdef ADD_KERNEL
32 # undef _KERNEL
33 # undef KERNEL
34 #endif
36 #include <sys/time.h>
37 #include <sys/socket.h>
38 #include <sys/syslog.h>
39 #include <sys/ioctl.h>
40 #include <net/if.h>
41 #if __FreeBSD_version >= 300000
42 # include <net/if_var.h>
43 #endif
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>
55 #include <errno.h>
56 #include <limits.h>
57 #include <netdb.h>
58 #include <stdlib.h>
59 #include <fcntl.h>
60 #include <syslog.h>
61 #include <stddef.h>
62 #include <stdio.h>
63 #include <strings.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <netinet/ip_fw.h>
67 #include "ipfwaux.h"
68 #include "ipfwrdr.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;
87 unsigned short eport;
88 short proto;
89 char desc[];
92 static struct mapping_desc_time * mappings_list = NULL;
94 /* add an element to the port mappings descriptions & timestamp list */
95 static void
96 add_desc_time(unsigned short eport, int proto,
97 const char * desc, unsigned int timestamp)
99 struct mapping_desc_time * tmp;
100 size_t l;
101 if(!desc)
102 desc = "miniupnpd";
103 l = strlen(desc) + 1;
104 tmp = malloc(sizeof(struct mapping_desc_time) + l);
105 if(tmp) {
106 /* fill the element and insert it as head of the list */
107 tmp->next = mappings_list;
108 tmp->timestamp = timestamp;
109 tmp->eport = eport;
110 tmp->proto = (short)proto;
111 memcpy(tmp->desc, desc, l);
112 mappings_list = tmp;
116 /* remove an element to the port mappings descriptions & timestamp list */
117 static void
118 del_desc_time(unsigned short eport, int proto)
120 struct mapping_desc_time * e;
121 struct mapping_desc_time * * p;
122 p = &mappings_list;
123 e = *p;
124 while(e) {
125 if(e->eport == eport && e->proto == (short)proto) {
126 *p = e->next;
127 free(e);
128 return;
129 } else {
130 p = &e->next;
131 e = *p;
136 /* go through the list and find the description and timestamp */
137 static void
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) {
146 if(desc)
147 strlcpy(desc, e->desc, desclen);
148 if(timestamp)
149 *timestamp = e->timestamp;
150 return;
155 /* --- */
156 int add_redirect_rule2(
157 const char * ifname,
158 const char * rhost,
159 unsigned short eport,
160 const char * iaddr,
161 unsigned short iport,
162 int proto,
163 const char * desc,
164 unsigned int timestamp)
166 struct ip_fw rule;
167 int r;
169 if (ipfw_validate_protocol(proto) < 0)
170 return -1;
171 if (ipfw_validate_ifname(ifname) < 0)
172 return -1;
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;
188 #endif
189 if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
190 syslog(LOG_ERR, "inet_aton(): %m");
191 return -1;
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));
202 if(r >= 0)
203 add_desc_time(eport, proto, desc, timestamp);
204 return r;
207 /* get_redirect_rule()
208 * return value : 0 success (found)
209 * -1 = error or rule not found */
210 int get_redirect_rule(
211 const char * ifname,
212 unsigned short eport,
213 int proto,
214 char * iaddr,
215 int iaddrlen,
216 unsigned short * iport,
217 char * desc,
218 int desclen,
219 unsigned int * timestamp,
220 u_int64_t * packets,
221 u_int64_t * bytes)
223 int i, count_rules, total_rules = 0;
224 struct ip_fw * rules = NULL;
226 if (ipfw_validate_protocol(proto) < 0)
227 return -1;
228 if (ipfw_validate_ifname(ifname) < 0)
229 return -1;
230 if (timestamp)
231 *timestamp = 0;
233 do {
234 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
235 if (count_rules < 0)
236 goto error;
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]) {
242 if (packets != NULL)
243 *packets = ptr->fw_pcnt;
244 if (bytes != NULL)
245 *bytes = ptr->fw_bcnt;
246 if (iport != NULL)
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");
253 goto error;
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);
259 return 0;
263 error:
264 if (rules != NULL)
265 ipfw_free_ruleset(&rules);
266 return -1;
269 int delete_redirect_rule(
270 const char * ifname,
271 unsigned short eport,
272 int proto)
274 int i, count_rules, total_rules = 0;
275 struct ip_fw * rules = NULL;
277 if (ipfw_validate_protocol(proto) < 0)
278 return -1;
279 if (ipfw_validate_ifname(ifname) < 0)
280 return -1;
282 do {
283 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
284 if (count_rules < 0)
285 goto error;
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)
292 goto error;
293 // And what if we found more than 1 matching rule?
294 ipfw_free_ruleset(&rules);
295 del_desc_time(eport, proto);
296 return 0;
300 error:
301 if (rules != NULL)
302 ipfw_free_ruleset(&rules);
303 return -1;
306 int add_filter_rule2(
307 const char * ifname,
308 const char * rhost,
309 const char * iaddr,
310 unsigned short eport,
311 unsigned short iport,
312 int proto,
313 const char * desc)
315 //return -1;
316 return 0; /* nothing to do, always success */
319 int delete_filter_rule(
320 const char * ifname,
321 unsigned short eport,
322 int proto)
324 //return -1;
325 return 0; /* nothing to do, always success */
328 int get_redirect_rule_by_index(
329 int index,
330 char * ifname,
331 unsigned short * eport,
332 char * iaddr,
333 int iaddrlen,
334 unsigned short * iport,
335 int * proto,
336 char * desc,
337 int desclen,
338 char * rhost,
339 int rhostlen,
340 unsigned int * timestamp,
341 u_int64_t * packets,
342 u_int64_t * bytes)
344 int total_rules = 0;
345 struct ip_fw * rules = NULL;
347 if (index < 0) // TODO shouldn't we also validate the maximum?
348 return -1;
350 if(timestamp)
351 *timestamp = 0;
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
358 goto error;
359 if (proto != NULL)
360 *proto = ptr->fw_prot;
361 if (eport != NULL)
362 *eport = ptr->fw_uar.fw_pts[0];
363 if (iport != NULL)
364 *iport = ptr->fw_fwd_ip.sin_port;
365 if (ifname != NULL)
366 strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
367 if (packets != NULL)
368 *packets = ptr->fw_pcnt;
369 if (bytes != NULL)
370 *bytes = ptr->fw_bcnt;
371 if (iport != NULL)
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");
378 goto error;
381 /* TODO : get rhost */
382 ipfw_free_ruleset(&rules);
383 get_desc_time(*eport, *proto, desc, desclen, timestamp);
384 return 0;
387 error:
388 if (rules != NULL)
389 ipfw_free_ruleset(&rules);
390 return -1;
393 /* upnp_get_portmappings_in_range()
394 * return a list of all "external" ports for which a port
395 * mapping exists */
396 unsigned short *
397 get_portmappings_in_range(unsigned short startport,
398 unsigned short endport,
399 int proto,
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)
408 return NULL;
410 do {
411 count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
412 if (count_rules < 0)
413 goto error;
414 } while (count_rules == 10);
416 array = calloc(capacity, sizeof(unsigned short));
417 if(!array) {
418 syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
419 goto error;
421 *number = 0;
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) {
430 capacity += 128;
431 array = realloc(array, sizeof(unsigned short)*capacity);
432 if(!array) {
433 syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
434 *number = 0;
435 goto error;
438 array[*number] = eport;
439 (*number)++;
442 error:
443 if (rules != NULL)
444 ipfw_free_ruleset(&rules);
445 return array;