Repository navigation
Expand file tree
/
Copy pathipfwrdr.c
More file actions
467 lines (424 loc) · 11.1 KB
/
Copy pathipfwrdr.c
File metadata and controls
467 lines (424 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
/* $Id: ipfwrdr.c,v 1.12 2012/02/11 13:10:57 nanard Exp $ */
/*
* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* (c) 2009 Jardel Weyrich
* (c) 2011-2012 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution
*/
#include "../config.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/file.h>
/*
This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
Needed here because on some systems <sys/uio.h> gets included by things
like <sys/socket.h>
*/
#ifndef _KERNEL
# define ADD_KE
10BC8
RNEL
# define _KERNEL
# define KERNEL
#endif
#ifdef __OpenBSD__
struct file;
#endif
#include <sys/uio.h>
#ifdef ADD_KERNEL
# undef _KERNEL
# undef KERNEL
#endif
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <net/if.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <errno.h>
#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <fcntl.h>
#include <syslog.h>
#include <stddef.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <netinet/ip_fw.h>
#include "ipfwaux.h"
#include "ipfwrdr.h"
#include "../upnpglobalvars.h"
/* init and shutdown functions */
int init_redirect(void) {
return ipfw_exec(IP_FW_INIT, NULL, 0);
}
void shutdown_redirect(void) {
ipfw_exec(IP_FW_TERM, NULL, 0);
}
/* ipfw cannot store descriptions and timestamp for port mappings so we keep
* our own list in memory */
struct mapping_desc_time {
struct mapping_desc_time * next;
unsigned int timestamp;
unsigned short eport;
short proto;
char desc[];
};
static struct mapping_desc_time * mappings_list = NULL;
/* add an element to the port mappings descriptions & timestamp list */
static void
add_desc_time(unsigned short eport, int proto,
const char * desc, unsigned int timestamp)
{
struct mapping_desc_time * tmp;
size_t l;
if(!desc)
desc = "miniupnpd";
l = strlen(desc) + 1;
tmp = malloc(sizeof(struct mapping_desc_time) + l);
if(tmp) {
/* fill the element and insert it as head of the list */
tmp->next = mappings_list;
tmp->timestamp = timestamp;
tmp->eport = eport;
tmp->proto = (short)proto;
memcpy(tmp->desc, desc, l);
mappings_list = tmp;
}
}
/* remove an element to the port mappings descriptions & timestamp list */
static void
del_desc_time(unsigned short eport, int proto)
{
struct mapping_desc_time * e;
struct mapping_desc_time * * p;
p = &mappings_list;
e = *p;
while(e) {
if(e->eport == eport && e->proto == (short)proto) {
*p = e->next;
free(e);
return;
} else {
p = &e->next;
e = *p;
}
}
}
/* go through the list and find the description and timestamp */
static void
get_desc_time(unsigned short eport, int proto,
char * desc, int desclen,
unsigned int * timestamp)
{
struct mapping_desc_time * e;
for(e = mappings_list; e; e = e->next) {
if(e->eport == eport && e->proto == (short)proto) {
if(desc)
strlcpy(desc, e->desc, desclen);
if(timestamp)
*timestamp = e->timestamp;
return;
}
}
}
/* --- */
int add_redirect_rule2(
const char * ifname,
const char * rhost,
unsigned short eport,
const char * iaddr,
unsigned short iport,
int proto,
const char * desc,
unsigned int timestamp)
{
struct ip_fw rule;
int r;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
memset(&rule, 0, sizeof(struct ip_fw));
rule.version = IP_FW_CURRENT_API_VERSION;
#if 0
rule.fw_number = 1000; /* rule number */
rule.context = (void *)desc; /* The description is kept in a separate list */
#endif
rule.fw_prot = proto; /* protocol */
rule.fw_flg |= IP_FW_F_IIFACE; /* interfaces to check */
rule.fw_flg |= IP_FW_F_IIFNAME; /* interfaces to check by name */
rule.fw_flg |= (IP_FW_F_IN | IP_FW_F_OUT); /* packet direction */
rule.fw_flg |= IP_FW_F_FWD; /* forward action */
#ifdef USE_IFNAME_IN_RULES
if (ifname != NULL) {
strlcpy(rule.fw_in_if.fu_via_if.name, ifname, IFNAMSIZ); /* src interface */
rule.fw_in_if.fu_via_if.unit = -1;
}
#endif
if (inet_aton(iaddr, &rule.fw_out_if.fu_via_ip) == 0) {
syslog(LOG_ERR, "inet_aton(): %m");
return -1;
}
memcpy(&rule.fw_dst, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
memcpy(&rule.fw_fwd_ip.sin_addr, &rule.fw_out_if.fu_via_ip, sizeof(struct in_addr));
rule.fw_dmsk.s_addr = INADDR_BROADCAST; /* TODO check this */
IP_FW_SETNDSTP(&rule, 1); /* number of external ports */
rule.fw_uar.fw_pts[0] = eport; /* external port */
rule.fw_fwd_ip.sin_port = iport; /* internal port */
if (rhost && rhost[0] != '\0') {
inet_aton(rhost, &rule.fw_src);
rule.fw_smsk.s_addr = htonl(INADDR_NONE);
}
r = ipfw_exec(IP_FW_ADD, &rule, sizeof(rule));
if(r >= 0)
add_desc_time(eport, proto, desc, timestamp);
return r;
}
/* get_redirect_rule()
* return value : 0 success (found)
* -1 = error or rule not found */
int get_redirect_rule(
const char * ifname,
unsigned short eport,
int proto,
char * iaddr,
int iaddrlen,
unsigned short * iport,
char * desc,
int desclen,
char * rhost,
int rhostlen,
unsigned int * timestamp,
u_int64_t * packets,
u_int64_t * bytes)
{
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
if (timestamp)
*timestamp = 0;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
if (packets != NULL)
*packets = ptr->fw_pcnt;
if (bytes != NULL)
*bytes = ptr->fw_bcnt;
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (iaddr != NULL && iaddrlen > 0) {
/* looks like fw_out_if.fu_via_ip is zero */
/*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
if (rhost != NULL && rhostlen > 0) {
if (ptr->fw_src.s_addr == 0)
rhost[0] = '\0';
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
/* And what if we found more than 1 matching rule? */
ipfw_free_ruleset(&rules);
get_desc_time(eport, proto, desc, desclen, timestamp);
return 0;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
int delete_redirect_rule(
const char * ifname,
unsigned short eport,
int proto)
{
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return -1;
if (ipfw_validate_ifname(ifname) < 0)
return -1;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
if (proto == ptr->fw_prot && eport == ptr->fw_uar.fw_pts[0]) {
if (ipfw_exec(IP_FW_DEL, (struct ip_fw *)ptr, sizeof(*ptr)) < 0)
goto error;
/* And what if we found more than 1 matching rule? */
ipfw_free_ruleset(&rules);
del_desc_time(eport, proto);
return 0;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
int add_filter_rule2(
const char * ifname,
const char * rhost,
const char * iaddr,
unsigned short eport,
unsigned short iport,
int proto,
const char * desc)
{
return 0; /* nothing to do, always success */
}
int delete_filter_rule(
const char * ifname,
unsigned short eport,
int proto)
{
return 0; /* nothing to do, always success */
}
int get_redirect_rule_by_index(
int index,
char * ifname,
unsigned short * eport,
char * iaddr,
int iaddrlen,
unsigned short * iport,
int * proto,
char * desc,
int desclen,
char * rhost,
int rhostlen,
unsigned int * timestamp,
u_int64_t * packets,
u_int64_t * bytes)
{
int total_rules = 0;
struct ip_fw * rules = NULL;
if (index < 0) /* TODO shouldn't we also validate the maximum? */
return -1;
if(timestamp)
*timestamp = 0;
ipfw_fetch_ruleset(&rules, &total_rules, index + 1);
if (total_rules > index) {
const struct ip_fw const * ptr = &rules[index];
if (ptr->fw_prot == 0) /* invalid rule */
goto error;
if (proto != NULL)
*proto = ptr->fw_prot;
if (eport != NULL)
*eport = ptr->fw_uar.fw_pts[0];
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (ifname != NULL)
strlcpy(ifname, ptr->fw_in_if.fu_via_if.name, IFNAMSIZ);
if (packets != NULL)
*packets = ptr->fw_pcnt;
if (bytes != NULL)
*bytes = ptr->fw_bcnt;
if (iport != NULL)
*iport = ptr->fw_fwd_ip.sin_port;
if (iaddr != NULL && iaddrlen > 0) {
/* looks like fw_out_if.fu_via_ip is zero */
/*if (inet_ntop(AF_INET, &ptr->fw_out_if.fu_via_ip, iaddr, iaddrlen) == NULL) {*/
if (inet_ntop(AF_INET, &ptr->fw_fwd_ip.sin_addr, iaddr, iaddrlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
if (rhost != NULL && rhostlen > 0) {
if (ptr->fw_src.s_addr == 0)
rhost[0] = '\0';
else if (inet_ntop(AF_INET, &ptr->fw_src.s_addr, rhost, rhostlen) == NULL) {
syslog(LOG_ERR, "inet_ntop(): %m");
goto error;
}
}
ipfw_free_ruleset(&rules);
get_desc_time(*eport, *proto, desc, desclen, timestamp);
return 0;
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return -1;
}
/* upnp_get_portmappings_in_range()
* return a list of all "external" ports for which a port
* mapping exists */
unsigned short *
get_portmappings_in_range(unsigned short startport,
unsigned short endport,
int proto,
unsigned int * number)
{
unsigned short * array = NULL;
unsigned int capacity = 128;
int i, count_rules, total_rules = 0;
struct ip_fw * rules = NULL;
if (ipfw_validate_protocol(proto) < 0)
return NULL;
do {
count_rules = ipfw_fetch_ruleset(&rules, &total_rules, 10);
if (count_rules < 0)
goto error;
} while (count_rules == 10);
array = calloc(capacity, sizeof(unsigned short));
if(!array) {
syslog(LOG_ERR, "get_portmappings_in_range() : calloc error");
goto error;
}
*number = 0;
for (i=0; i<total_rules-1; i++) {
const struct ip_fw const * ptr = &rules[i];
unsigned short eport = ptr->fw_uar.fw_pts[0];
if (proto == ptr->fw_prot
&& startport <= eport
&& eport <= endport) {
if(*number >= capacity) {
capacity += 128;
array = realloc(array, sizeof(unsigned short)*capacity);
if(!array) {
syslog(LOG_ERR, "get_portmappings_in_range() : realloc(%lu) error", sizeof(unsigned short)*capacity);
*number = 0;
goto error;
}
}
array[*number] = eport;
(*number)++;
}
}
error:
if (rules != NULL)
ipfw_free_ruleset(&rules);
return array;
}