source: src/router/netconf/netconf_linux.c @ 1

Last change on this file since 1 was 1, checked in by brainslayer, 7 years ago

initial checkin

File size: 40.7 KB
Line 
1/*
2 * Network configuration layer (Linux)
3 *
4 * Copyright 2004, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id: netconf_linux.c,v 1.6 2004/04/12 05:42:39 honor Exp $
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <ctype.h>
18#include <errno.h>
19#include <error.h>
20#include <string.h>
21#include <time.h>
22#include <unistd.h>
23#include <syslog.h>
24#include <sys/ioctl.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <net/if.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include <net/if_arp.h>
31
32#include <typedefs.h>
33#include <proto/ethernet.h>
34#include <netconf.h>
35#include <netconf_linux.h>
36
37/* Loops over each match in the ipt_entry */
38#define for_each_ipt_match(match, entry) \
39        for ((match) = (struct ipt_entry_match *) &(entry)->elems[0]; \
40             (int) (match) < (int) (entry) + (entry)->target_offset; \
41             (match) = (struct ipt_entry_match *) ((int) (match) + (match)->u.match_size))
42
43/* Supported ipt table names */
44static const char *ipt_table_names[] = { "filter", "nat", NULL };
45
46/* ipt table name appropriate for target (indexed by netconf_fw_t.target) */
47static const char * ipt_table_name[] = {
48        "filter", "filter", "filter", "filter",
49        "nat", "nat", "nat", "nat"
50};
51
52/* ipt target name (indexed by netconf_fw_t.target) */
53static const char * ipt_target_name[] = {
54        "DROP", "ACCEPT", "logdrop", "logaccept",
55        "SNAT", "DNAT", "MASQUERADE", "autofw"
56};
57
58/* ipt target data size (indexed by netconf_fw_t.target) */
59static const size_t ipt_target_size[] = {
60        sizeof(int), sizeof(int), sizeof(int), sizeof(int),
61        sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_autofw_info)
62};
63
64/* ipt filter chain name appropriate for direction (indexed by netconf_filter_t.dir) */
65static const char * ipt_filter_chain_name[] = {
66        "INPUT", "FORWARD", "OUTPUT"
67};
68
69/* ipt nat chain name appropriate for target (indexed by netconf_nat_t.target) */
70static const char * ipt_nat_chain_name[] = {
71        NULL, NULL, NULL, NULL,
72        "POSTROUTING", "PREROUTING", "POSTROUTING"
73};
74
75/* Returns a netconf_dir index */
76static int
77filter_dir(const char *name)
78{
79        if (strncmp(name, "INPUT", IPT_FUNCTION_MAXNAMELEN) == 0)
80                return NETCONF_IN;
81        else if (strncmp(name, "FORWARD", IPT_FUNCTION_MAXNAMELEN) == 0)
82                return NETCONF_FORWARD;
83        else if (strncmp(name, "OUTPUT", IPT_FUNCTION_MAXNAMELEN) == 0)
84                return NETCONF_OUT;
85        else
86                return -1;
87}
88
89/* Returns a netconf_target index */
90static int
91target_num(const struct ipt_entry *entry, iptc_handle_t *handle)
92{
93        const char *name = iptc_get_target(entry, handle);
94
95        if (!name)
96                return -1;
97
98        if (strncmp(name, "DROP", IPT_FUNCTION_MAXNAMELEN) == 0)
99                return NETCONF_DROP;
100        else if (strncmp(name, "ACCEPT", IPT_FUNCTION_MAXNAMELEN) == 0)
101                return NETCONF_ACCEPT;
102        else if (strncmp(name, "logdrop", IPT_FUNCTION_MAXNAMELEN) == 0)
103                return NETCONF_LOG_DROP;
104        else if (strncmp(name, "logaccept", IPT_FUNCTION_MAXNAMELEN) == 0)
105                return NETCONF_LOG_ACCEPT;
106        else if (strncmp(name, "SNAT", IPT_FUNCTION_MAXNAMELEN) == 0)
107                return NETCONF_SNAT;
108        else if (strncmp(name, "DNAT", IPT_FUNCTION_MAXNAMELEN) == 0)
109                return NETCONF_DNAT;
110        else if (strncmp(name, "MASQUERADE", IPT_FUNCTION_MAXNAMELEN) == 0)
111                return NETCONF_MASQ;
112        else if (strncmp(name, "autofw", IPT_FUNCTION_MAXNAMELEN) == 0)
113                return NETCONF_APP;
114        else
115                return -1;
116}
117
118/*
119 * Get a list of the current firewall entries
120 * @param       fw_list list of firewall entries
121 * @return      0 on success and errno on failure
122 */
123int
124netconf_get_fw(netconf_fw_t *fw_list)
125{
126        const char **table;
127        const char *chain;
128        const struct ipt_entry *entry;
129        iptc_handle_t handle = NULL;
130
131        /* Initialize list */
132        netconf_list_init(fw_list);
133
134        /* Search all default tables */
135        for (table = &ipt_table_names[0]; *table; table++) {
136
137                if (strcmp(*table, "filter") && strcmp(*table, "nat"))
138                        continue;               
139
140                if (!(handle = iptc_init(*table))) {
141                        fprintf(stderr, "%s\n", iptc_strerror(errno));
142                        goto err;
143                }
144
145                /* Search all default chains */
146                for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) {
147
148                        if (strcmp(chain, "INPUT") && strcmp(chain, "FORWARD") && strcmp(chain, "OUTPUT") &&
149                            strcmp(chain, "PREROUTING") && strcmp(chain, "POSTROUTING"))
150                                continue;               
151
152                        /* Search all entries */
153                        for (entry = iptc_first_rule(chain, &handle); entry; entry = iptc_next_rule(entry, &handle)) {
154                                int num = target_num(entry, &handle);
155                                netconf_fw_t *fw = NULL;
156                                netconf_filter_t *filter = NULL;
157                                netconf_nat_t *nat = NULL;
158                                netconf_app_t *app = NULL;
159
160                                const struct ipt_entry_match *match;
161                                const struct ipt_entry_target *target;
162                                struct ipt_mac_info *mac = NULL;
163                                struct ipt_state_info *state = NULL;
164                                struct ipt_time_info *time = NULL;
165
166                                /* Only know about TCP/UDP */
167                                if (!netconf_valid_ipproto(entry->ip.proto))
168                                        continue;
169
170                                /* Only know about target types in the specified tables */
171                                if (!netconf_valid_target(num) ||
172                                    strncmp(ipt_table_name[num], *table, IPT_FUNCTION_MAXNAMELEN) != 0)
173                                        continue;
174
175                                /* Only know about specified target types */
176                                if (netconf_valid_filter(num))
177                                        fw = filter = calloc(1, sizeof(netconf_filter_t));
178                                else if (netconf_valid_nat(num))
179                                        fw = nat = calloc(1, sizeof(netconf_nat_t));
180                                else if (num == NETCONF_APP)
181                                        fw = app = calloc(1, sizeof(netconf_app_t));
182                                else
183                                        continue;
184
185                                if (!fw) {
186                                        perror("calloc");
187                                        goto err;
188                                }
189                                netconf_list_add(fw, fw_list);
190
191                                /* Get IP addresses */
192                                fw->match.src.ipaddr.s_addr = entry->ip.src.s_addr;
193                                fw->match.src.netmask.s_addr = entry->ip.smsk.s_addr;
194                                fw->match.dst.ipaddr.s_addr = entry->ip.dst.s_addr;
195                                fw->match.dst.netmask.s_addr = entry->ip.dmsk.s_addr;
196                                fw->match.flags |= (entry->ip.invflags & IPT_INV_SRCIP) ? NETCONF_INV_SRCIP : 0;
197                                fw->match.flags |= (entry->ip.invflags & IPT_INV_DSTIP) ? NETCONF_INV_DSTIP : 0;
198
199                                /* Get interface names */
200                                strncpy(fw->match.in.name, entry->ip.iniface, IFNAMSIZ);
201                                strncpy(fw->match.out.name, entry->ip.outiface, IFNAMSIZ);
202                                fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_IN) ? NETCONF_INV_IN : 0;
203                                fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_OUT) ? NETCONF_INV_OUT : 0;
204
205                                /* Get TCP port(s) */
206                                if (entry->ip.proto == IPPROTO_TCP) {
207                                        struct ipt_tcp *tcp = NULL;
208
209                                        for_each_ipt_match(match, entry) {
210                                                if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0)
211                                                        continue;
212
213                                                tcp = (struct ipt_tcp *) &match->data[0];
214                                                break;
215                                        }
216
217                                        if (tcp) {
218                                                /* Match ports stored in host order for some stupid reason */
219                                                fw->match.ipproto = IPPROTO_TCP;
220                                                fw->match.src.ports[0] = htons(tcp->spts[0]);
221                                                fw->match.src.ports[1] = htons(tcp->spts[1]);
222                                                fw->match.dst.ports[0] = htons(tcp->dpts[0]);
223                                                fw->match.dst.ports[1] = htons(tcp->dpts[1]);
224                                                fw->match.flags |= (tcp->invflags & IPT_TCP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0;
225                                                fw->match.flags |= (tcp->invflags & IPT_TCP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0;
226                                        }
227                                }
228
229                                /* Get UDP port(s) */
230                                else if (entry->ip.proto == IPPROTO_UDP) {
231                                        struct ipt_udp *udp = NULL;
232
233                                        for_each_ipt_match(match, entry) {
234                                                if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0)
235                                                        continue;
236
237                                                udp = (struct ipt_udp *) &match->data[0];
238                                                break;
239                                        }
240
241                                        if (udp) {
242                                                /* Match ports stored in host order for some stupid reason */
243                                                fw->match.ipproto = IPPROTO_UDP;
244                                                fw->match.src.ports[0] = htons(udp->spts[0]);
245                                                fw->match.src.ports[1] = htons(udp->spts[1]);
246                                                fw->match.dst.ports[0] = htons(udp->dpts[0]);
247                                                fw->match.dst.ports[1] = htons(udp->dpts[1]);
248                                                fw->match.flags |= (udp->invflags & IPT_UDP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0;
249                                                fw->match.flags |= (udp->invflags & IPT_UDP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0;
250                                        }
251                                }
252                               
253                                /* Get source MAC address */
254                                for_each_ipt_match(match, entry) {
255                                        if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0)
256                                                continue;
257                       
258                                        mac = (struct ipt_mac_info *) &match->data[0];
259                                        break;
260                                }
261                                if (mac) {
262                                        memcpy(fw->match.mac.octet, mac->srcaddr, ETHER_ADDR_LEN);
263                                        fw->match.flags |= mac->invert ? NETCONF_INV_MAC : 0;
264                                }
265
266                                /* Get packet state */
267                                for_each_ipt_match(match, entry) {
268                                        if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0)
269                                                continue;
270                       
271                                        state = (struct ipt_state_info *) &match->data[0];
272                                        break;
273                                }
274                                if (state) {
275                                        fw->match.state |= (state->statemask & IPT_STATE_INVALID) ? NETCONF_INVALID : 0;
276                                        fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) ? NETCONF_ESTABLISHED : 0;
277                                        fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_RELATED)) ? NETCONF_RELATED : 0;
278                                        fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_NEW)) ? NETCONF_NEW : 0;
279                                }
280
281                                /* Get local time */
282                                for_each_ipt_match(match, entry) {
283                                        if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0)
284                                                continue;
285
286                                        /* We added 8 bytes of day range at the end */
287                                        if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) +
288                                                                   IPT_ALIGN(sizeof(struct ipt_time_info) + 8)))
289                                                continue;
290
291                                        time = (struct ipt_time_info *) &match->data[0];
292                                        break;
293                                }
294                                if (time) {
295                                        unsigned int *days = (unsigned int *) &time[1];
296
297                                        fw->match.days[0] = days[0];
298                                        fw->match.days[1] = days[1];
299                                        fw->match.secs[0] = time->time_start;
300                                        fw->match.secs[1] = time->time_stop;
301                                }
302
303                                /* Set target type */
304                                fw->target = num;
305                                target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
306
307                                /* Get filter target information */
308                                if (filter) {
309                                        if (!netconf_valid_dir(filter->dir = filter_dir(chain)))
310                                                goto err;
311                                }
312
313                                /* Get NAT target information */
314                                else if (nat) {
315                                        struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
316                                        struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
317                               
318                                        /* Get mapped IP address */
319                                        nat->ipaddr.s_addr = range->min_ip;
320                               
321                                        /* Get mapped TCP port(s) */
322                                        if (entry->ip.proto == IPPROTO_TCP) {
323                                                nat->ports[0] = range->min.tcp.port;
324                                                nat->ports[1] = range->max.tcp.port;
325                                        }
326
327                                        /* Get mapped UDP port(s) */
328                                        else if (entry->ip.proto == IPPROTO_UDP) {
329                                                nat->ports[0] = range->min.udp.port;
330                                                nat->ports[1] = range->max.udp.port;
331                                        }
332                                }
333
334                                /* Get application specific port forward information */
335                                else if (app) {
336                                        struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
337
338                                        app->proto = info->proto;
339                                        app->dport[0] = info->dport[0];
340                                        app->dport[1] = info->dport[1];
341                                        app->to[0] = info->to[0];
342                                        app->to[1] = info->to[1];
343                                }
344                        }
345                }
346
347                if (!iptc_commit(&handle)) {
348                        fprintf(stderr, "%s\n", iptc_strerror(errno));
349                        handle = NULL;
350                        goto err;
351                }
352        }
353
354        return 0;
355
356 err:
357        if (handle)
358                iptc_commit(&handle);
359        netconf_list_free(fw_list);
360        return errno;   
361}
362
363/* Logical XOR */
364#define lxor(a, b) (((a) && !(b)) || (!(a) && (b)))
365
366/*
367 * Get the index of a firewall entry
368 * @param       fw      firewall entry to look for
369 * @return      index of firewall entry or <0 if not found or an error occurred
370 */
371static int
372netconf_fw_index(const netconf_fw_t *fw)
373{
374        const netconf_filter_t *filter = NULL;
375        const netconf_nat_t *nat = NULL;
376        const netconf_app_t *app = NULL;
377        const char **table;
378        const char *chain;
379        const struct ipt_entry *entry = NULL;
380        iptc_handle_t handle = NULL;
381        int ret = 0;
382
383        if (!netconf_valid_ipproto(fw->match.ipproto)) {
384                fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto);
385                return -EINVAL;
386        }
387
388        /* Only know about specified target types */
389        if (netconf_valid_filter(fw->target)) {
390                filter = (netconf_filter_t *) fw;
391                if (!netconf_valid_dir(filter->dir)) {
392                        fprintf(stderr, "invalid filter direction %d\n", filter->dir);
393                        return -EINVAL;
394                }
395        }
396        else if (netconf_valid_nat(fw->target))
397                nat = (netconf_nat_t *) fw;
398        else if (fw->target == NETCONF_APP)
399                app = (netconf_app_t *) fw;
400        else {
401                fprintf(stderr, "invalid target type %d\n", fw->target);
402                return -EINVAL;
403        }
404
405        /* Search all default tables */
406        for (table = &ipt_table_names[0]; *table; table++) {
407
408                /* Only consider specified tables */
409                if (strncmp(ipt_table_name[fw->target], *table, IPT_FUNCTION_MAXNAMELEN) != 0)
410                        continue;
411
412                if (!(handle = iptc_init(*table))) {
413                        fprintf(stderr, "%s\n", iptc_strerror(errno));
414                        return -errno;
415                }
416
417                /* Search all default chains */
418                for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) {
419
420                        /* Only consider specified chains */
421                        if (filter && strncmp(chain, ipt_filter_chain_name[filter->dir], sizeof(ipt_chainlabel)) != 0)
422                                continue;
423                        else if (nat && strncmp(chain, ipt_nat_chain_name[nat->target], sizeof(ipt_chainlabel)) != 0)
424                                continue;
425                        else if (app && strncmp(chain, "PREROUTING", sizeof(ipt_chainlabel)) != 0)
426                                continue;
427
428                        /* Search all entries */
429                        for (ret = 0, entry = iptc_first_rule(chain, &handle); entry; ret++, entry = iptc_next_rule(entry, &handle)) {
430                                const struct ipt_entry_match *match;
431                                const struct ipt_entry_target *target;
432                                struct ipt_mac_info *mac = NULL;
433                                struct ipt_state_info *state = NULL;
434                                struct ipt_time_info *time = NULL;
435
436                                /* Only know about TCP/UDP */
437                                if (entry->ip.proto != fw->match.ipproto)
438                                        continue;
439
440                                /* Compare IP address(es) */
441                                if (entry->ip.src.s_addr != fw->match.src.ipaddr.s_addr ||
442                                    entry->ip.smsk.s_addr != fw->match.src.netmask.s_addr ||
443                                    entry->ip.dst.s_addr != fw->match.dst.ipaddr.s_addr ||
444                                    entry->ip.dmsk.s_addr != fw->match.dst.netmask.s_addr)
445                                        continue;
446
447                                if (lxor(entry->ip.invflags & IPT_INV_SRCIP, fw->match.flags & NETCONF_INV_SRCIP) ||
448                                    lxor(entry->ip.invflags & IPT_INV_DSTIP, fw->match.flags & NETCONF_INV_DSTIP))
449                                        continue;
450
451                                /* Compare interface names */
452                                if (strncmp(fw->match.in.name, entry->ip.iniface, IFNAMSIZ) != 0 ||
453                                    strncmp(fw->match.out.name, entry->ip.outiface, IFNAMSIZ) != 0)
454                                        continue;
455
456                                /* Compare TCP port(s) */
457                                if (fw->match.ipproto == IPPROTO_TCP) {
458                                        struct ipt_tcp *tcp = NULL;
459
460                                        for_each_ipt_match(match, entry) {
461                                                if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0)
462                                                        continue;
463
464                                                tcp = (struct ipt_tcp *) &match->data[0];
465                                                break;
466                                        }
467                       
468                                        /* Match ports stored in host order for some stupid reason */
469                                        if (!tcp ||
470                                            tcp->spts[0] != ntohs(fw->match.src.ports[0]) ||
471                                            tcp->spts[1] != ntohs(fw->match.src.ports[1]) ||
472                                            tcp->dpts[0] != ntohs(fw->match.dst.ports[0]) ||
473                                            tcp->dpts[1] != ntohs(fw->match.dst.ports[1]))
474                                                continue;
475
476                                        if (lxor(tcp->invflags & IPT_TCP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) ||
477                                            lxor(tcp->invflags & IPT_TCP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT))
478                                                continue;
479                                }
480
481                                /* Compare UDP port(s) */
482                                else if (fw->match.ipproto == IPPROTO_UDP) {
483                                        struct ipt_udp *udp = NULL;
484
485                                        for_each_ipt_match(match, entry) {
486                                                if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0)
487                                                        continue;
488
489                                                udp = (struct ipt_udp *) &match->data[0];
490                                                break;
491                                        }
492
493                                        /* Match ports stored in host order for some stupid reason */
494                                        if (!udp ||
495                                            udp->spts[0] != ntohs(fw->match.src.ports[0]) ||
496                                            udp->spts[1] != ntohs(fw->match.src.ports[1]) ||
497                                            udp->dpts[0] != ntohs(fw->match.dst.ports[0]) ||
498                                            udp->dpts[1] != ntohs(fw->match.dst.ports[1]))
499                                                continue;
500
501                                        if (lxor(udp->invflags & IPT_UDP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) ||
502                                            lxor(udp->invflags & IPT_UDP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT))
503                                                continue;
504                                }
505
506                                /* Compare source MAC addresses */
507                                if (!ETHER_ISNULLADDR(fw->match.mac.octet)) {
508                                        for_each_ipt_match(match, entry) {
509                                                if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0)
510                                                        continue;
511                       
512                                                mac = (struct ipt_mac_info *) &match->data[0];
513                                                break;
514                                        }
515                       
516                                        if (!mac ||
517                                            memcmp(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN) != 0 ||
518                                            ( mac->invert && !(fw->match.flags & NETCONF_INV_MAC)) ||
519                                            (!mac->invert &&  (fw->match.flags & NETCONF_INV_MAC)))
520                                                continue;
521                                }
522
523                                /* Compare packet states */
524                                if (fw->match.state) {
525                                        for_each_ipt_match(match, entry) {
526                                                if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0)
527                                                        continue;
528                       
529                                                state = (struct ipt_state_info *) &match->data[0];
530                                                break;
531                                        }
532                       
533                                        if (!state ||
534                                            lxor(state->statemask & IPT_STATE_INVALID, fw->match.state & NETCONF_INVALID) ||
535                                            lxor(state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED), fw->match.state & NETCONF_ESTABLISHED) ||
536                                            lxor(state->statemask & IPT_STATE_BIT(IP_CT_RELATED), fw->match.state & NETCONF_RELATED) ||
537                                            lxor(state->statemask & IPT_STATE_BIT(IP_CT_NEW), fw->match.state & NETCONF_NEW))
538                                                continue;
539                                }
540
541                                /* Compare local time */
542                                if (fw->match.secs[0] || fw->match.secs[1]) {
543                                        for_each_ipt_match(match, entry) {
544                                                if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0)
545                                                        continue;
546
547                                                /* We added 8 bytes of day range at the end */
548                                                if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) +
549                                                                           IPT_ALIGN(sizeof(struct ipt_time_info) + 8)))
550                                                        continue;
551
552                                                time = (struct ipt_time_info *) &match->data[0];
553                                                break;
554                                        }
555
556                                        if (!time)
557                                                continue;
558                                        else {
559                                                unsigned int *days = (unsigned int *) &time[1];
560
561                                                if (fw->match.days[0] != days[0] ||
562                                                    fw->match.days[1] != days[1] ||
563                                                    fw->match.secs[0] != time->time_start ||
564                                                    fw->match.secs[1] != time->time_stop)
565                                                        continue;
566                                        }
567                                }
568
569                                /* Compare target type */
570                                if (fw->target != target_num(entry, &handle))
571                                        continue;
572                                target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
573
574                                /* Compare NAT target information */
575                                if (nat) {
576                                        struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
577                                        struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
578                       
579                                        /* Compare mapped IP address */
580                                        if (range->min_ip != nat->ipaddr.s_addr)
581                                                continue;
582                               
583                                        /* Compare mapped TCP port(s) */
584                                        if (fw->match.ipproto == IPPROTO_TCP) {
585                                                if (range->min.tcp.port != nat->ports[0] ||
586                                                    range->max.tcp.port != nat->ports[1])
587                                                        continue;
588                                        }
589
590                                        /* Compare mapped UDP port(s) */
591                                        else if (fw->match.ipproto == IPPROTO_UDP) {
592                                                if (range->min.udp.port != nat->ports[0] ||
593                                                    range->max.udp.port != nat->ports[1])
594                                                        continue;
595                                        }
596                                }
597
598                                /* Compare application specific port forward information */
599                                else if (app) {
600                                        struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
601
602                                        if (app->proto != info->proto ||
603                                            app->dport[0] != info->dport[0] ||
604                                            app->dport[1] != info->dport[1] ||
605                                            app->to[0] != info->to[0] ||
606                                            app->to[1] != info->to[1])
607                                                continue;
608                                }
609
610                                break;
611                        }
612
613                        if (entry)
614                                break;
615                }
616
617                if (!iptc_commit(&handle)) {
618                        fprintf(stderr, "%s\n", iptc_strerror(errno));
619                        return -errno;
620                }
621
622                if (entry)
623                        break;
624        }
625
626        return (entry ? ret : -ENOENT);
627}
628
629/*
630 * See if a given firewall entry already exists
631 * @param       nat     NAT entry to look for
632 * @return      whether NAT entry exists
633 */
634bool
635netconf_fw_exists(netconf_fw_t *fw)
636{
637        return (netconf_fw_index(fw) >= 0);
638}
639
640/*
641 * Allocate and append a match structure to an existing ipt_entry
642 * @param       pentry                  pointer to pointer to initialized ipt_entry
643 * @param       name                    name of match
644 * @param       match_data_size         size of data portion of match structure
645 * @return      pointer to newly created match header inside ipt_entry
646 */
647static struct ipt_entry_match *
648netconf_append_match(struct ipt_entry **pentry, const char *name, size_t match_data_size)
649{
650        struct ipt_entry *entry;
651        struct ipt_entry_match *match;
652        size_t match_size = 0;
653
654        match_size += IPT_ALIGN(sizeof(struct ipt_entry_match));
655        match_size += IPT_ALIGN(match_data_size);
656
657        if (!(entry = realloc(*pentry, (*pentry)->next_offset + match_size))) {
658                perror("realloc");
659                return NULL;
660        }
661
662        match = (struct ipt_entry_match *) ((int) entry + entry->next_offset);
663        entry->next_offset += match_size;
664        entry->target_offset += match_size;
665        memset(match, 0, match_size);
666
667        strncpy(match->u.user.name, name, IPT_FUNCTION_MAXNAMELEN);
668        match->u.match_size = match_size;
669
670        *pentry = entry;
671        return match;
672}
673
674/*
675 * Allocate and append a target structure to an existing ipt_entry
676 * @param       pentry                  pointer to pointer to initialized ipt_entry with matches
677 * @param       name                    name of target
678 * @param       target_data_size        size of data portion of target structure
679 * @return      pointer to newly created target header inside ipt_entry
680 */
681static struct ipt_entry_target *
682netconf_append_target(struct ipt_entry **pentry, const char *name, size_t target_data_size)
683{
684        struct ipt_entry *entry;
685        struct ipt_entry_target *target;
686        size_t target_size = 0;
687
688        target_size += IPT_ALIGN(sizeof(struct ipt_entry_target));
689        target_size += IPT_ALIGN(target_data_size);
690
691        if (!(entry = realloc(*pentry, (*pentry)->next_offset + target_size))) {
692                perror("realloc");
693                return NULL;
694        }
695
696        target = (struct ipt_entry_target *) ((int) entry + entry->next_offset);
697        entry->next_offset += target_size;
698        memset(target, 0, target_size);
699
700        strncpy(target->u.user.name, name, IPT_FUNCTION_MAXNAMELEN);
701        target->u.target_size = target_size;
702
703        *pentry = entry;
704        return target;
705}
706
707/*
708 * Insert an entry into a reasonable location in the chain
709 * @param       chain   chain name
710 * @param       entry   iptables entry
711 * @param       handle  table handle
712 * @return      TRUE on success and 0 on failure
713 */
714static int
715insert_entry(const char *chain, struct ipt_entry *entry, iptc_handle_t *handle)
716{
717        int i;
718        struct ipt_ip blank;
719        const struct ipt_entry *rule;
720        struct ipt_entry_target *target;
721
722        target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
723        memset(&blank, 0, sizeof(struct ipt_ip));
724
725        /* If this is a default policy (no match) insert at the end of the chain */
726        if (entry->target_offset == sizeof(struct ipt_entry) &&
727            !memcmp(&entry->ip, &blank, sizeof(struct ipt_ip)))
728                return iptc_append_entry(chain, entry, handle);
729
730        /* If dropping insert at the beginning of the chain */
731        if (!strcmp(iptc_get_target(entry, handle), "DROP") ||
732            !strcmp(iptc_get_target(entry, handle), "logdrop"))
733                return iptc_insert_entry(chain, entry, 0, handle);
734
735        /* If accepting insert after the last drop but before the first default policy */
736        else if (!strcmp(iptc_get_target(entry, handle), "ACCEPT") ||
737                 !strcmp(iptc_get_target(entry, handle), "DNAT") ||     // by honor, let UPnP Forwarding is prior to DMZ
738                 !strcmp(iptc_get_target(entry, handle), "logaccept")) {
739                for (i = 0, rule = iptc_first_rule(chain, handle); rule; i++, rule = iptc_next_rule(rule, handle)) {
740                        if ((strcmp(iptc_get_target(rule, handle), "DROP") &&
741                             strcmp(iptc_get_target(rule, handle), "logdrop")) ||
742                            (rule->target_offset == sizeof(struct ipt_entry) &&
743                             !memcmp(&rule->ip, &blank, sizeof(struct ipt_ip))))
744                                break;
745                }
746                return iptc_insert_entry(chain, entry, i, handle);
747        }
748
749        /* Otherwise insert at the end of the chain */
750        else
751                return iptc_append_entry(chain, entry, handle);
752}
753
754/*
755 * Add a firewall entry
756 * @param       fw      firewall entry
757 * @return      0 on success and errno on failure
758 */
759int
760netconf_add_fw(netconf_fw_t *fw)
761{
762        netconf_filter_t *filter = NULL;
763        netconf_nat_t *nat = NULL;
764        netconf_app_t *app = NULL;
765
766        struct ipt_entry *entry;
767        struct ipt_entry_match *match;
768        struct ipt_entry_target *target;
769        iptc_handle_t handle = NULL;
770
771        if (!netconf_valid_ipproto(fw->match.ipproto)) {
772                fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto);
773                return -EINVAL;
774        }
775
776        if (!netconf_valid_target(fw->target)) {
777                fprintf(stderr, "invalid target type %d\n", fw->target);
778                return EINVAL;
779        }
780
781        /* Only know about specified target types */
782        if (netconf_valid_filter(fw->target))
783                filter = (netconf_filter_t *) fw;
784        else if (netconf_valid_nat(fw->target))
785                nat = (netconf_nat_t *) fw;
786        else if (fw->target == NETCONF_APP)
787                app = (netconf_app_t *) fw;
788        else
789                return EINVAL;
790
791        /* Allocate entry */
792        if (!(entry = calloc(1, sizeof(struct ipt_entry)))) {
793                perror("calloc");
794                return errno;
795        }
796
797        /* Initialize entry parameters */
798        entry->nfcache |= NFC_UNKNOWN;
799        entry->next_offset = entry->target_offset = sizeof(struct ipt_entry);
800
801        /* Match by IP address(es) */
802        if (fw->match.src.ipaddr.s_addr & fw->match.src.netmask.s_addr) {
803                entry->ip.src.s_addr = fw->match.src.ipaddr.s_addr;
804                entry->ip.smsk.s_addr = fw->match.src.netmask.s_addr;
805                entry->nfcache |= NFC_IP_SRC;
806                entry->ip.invflags |= (fw->match.flags & NETCONF_INV_SRCIP) ? IPT_INV_SRCIP : 0;
807        }
808        if (fw->match.dst.ipaddr.s_addr & fw->match.dst.netmask.s_addr) {
809                entry->ip.dst.s_addr = fw->match.dst.ipaddr.s_addr;
810                entry->ip.dmsk.s_addr = fw->match.dst.netmask.s_addr;
811                entry->nfcache |= NFC_IP_DST;
812                entry->ip.invflags |= (fw->match.flags & NETCONF_INV_DSTIP) ? IPT_INV_DSTIP : 0;
813        }
814
815        /* Match by inbound or outbound interface name */
816        if (strlen(fw->match.in.name) > 0) {
817                strncpy(entry->ip.iniface, fw->match.in.name, IFNAMSIZ);
818                memset(&entry->ip.iniface_mask, 0, IFNAMSIZ);
819                memset(&entry->ip.iniface_mask, 0xff, strlen(fw->match.in.name) + 1);
820                entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_IN : 0;
821                entry->nfcache |= NFC_IP_IF_IN;
822        }
823        if (strlen(fw->match.out.name) > 0) {
824                strncpy(entry->ip.outiface, fw->match.out.name, IFNAMSIZ);
825                memset(&entry->ip.outiface_mask, 0, IFNAMSIZ);
826                memset(&entry->ip.outiface_mask, 0xff, strlen(fw->match.in.name) + 1);
827                entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_OUT : 0;
828                entry->nfcache |= NFC_IP_IF_OUT;
829        }
830
831        /* Match by TCP port(s) */
832        if (fw->match.ipproto == IPPROTO_TCP) {
833                struct ipt_tcp *tcp;
834
835                if (!(match = netconf_append_match(&entry, "tcp", sizeof(struct ipt_tcp))))
836                        goto err;
837                tcp = (struct ipt_tcp *) &match->data[0];
838
839                entry->ip.proto = IPPROTO_TCP;
840                entry->nfcache |= NFC_IP_PROTO;
841
842                /* Match ports stored in host order for some stupid reason */
843                tcp->spts[0] = ntohs(fw->match.src.ports[0]);
844                tcp->spts[1] = ntohs(fw->match.src.ports[1]);
845                tcp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_TCP_INV_SRCPT : 0;
846                entry->nfcache |= (tcp->spts[0] != 0 || tcp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0;
847               
848                /* Match ports stored in host order for some stupid reason */
849                tcp->dpts[0] = ntohs(fw->match.dst.ports[0]);
850                tcp->dpts[1] = ntohs(fw->match.dst.ports[1]);
851                tcp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_TCP_INV_DSTPT : 0;
852                entry->nfcache |= (tcp->dpts[0] != 0 || tcp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0;
853        }
854
855        /* Match by UDP port(s) */
856        else if (fw->match.ipproto == IPPROTO_UDP) {
857                struct ipt_udp *udp;
858
859                if (!(match = netconf_append_match(&entry, "udp", sizeof(struct ipt_udp))))
860                        goto err;
861                udp = (struct ipt_udp *) &match->data[0];
862
863                entry->ip.proto = IPPROTO_UDP;
864                entry->nfcache |= NFC_IP_PROTO;
865
866                /* Match ports stored in host order for some stupid reason */
867                udp->spts[0] = ntohs(fw->match.src.ports[0]);
868                udp->spts[1] = ntohs(fw->match.src.ports[1]);
869                udp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_UDP_INV_SRCPT : 0;
870                entry->nfcache |= (udp->spts[0] != 0 || udp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0;
871               
872                /* Match ports stored in host order for some stupid reason */
873                udp->dpts[0] = ntohs(fw->match.dst.ports[0]);
874                udp->dpts[1] = ntohs(fw->match.dst.ports[1]);
875                udp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_UDP_INV_DSTPT : 0;
876                entry->nfcache |= (udp->dpts[0] != 0 || udp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0;
877        }
878       
879        /* Match by source MAC address */
880        if (!ETHER_ISNULLADDR(fw->match.mac.octet)) {
881                struct ipt_mac_info *mac;
882
883                if (!(match = netconf_append_match(&entry, "mac", sizeof(struct ipt_mac_info))))
884                        goto err;
885                mac = (struct ipt_mac_info *) &match->data[0];
886
887                memcpy(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN);
888                mac->invert = (fw->match.flags & NETCONF_INV_MAC) ? 1 : 0;
889        }
890
891        /* Match by packet state */
892        if (fw->match.state) {
893                struct ipt_state_info *state;
894
895                if (!(match = netconf_append_match(&entry, "state", sizeof(struct ipt_state_info))))
896                        goto err;
897                state = (struct ipt_state_info *) &match->data[0];
898
899                state->statemask |= (fw->match.state & NETCONF_INVALID) ? IPT_STATE_INVALID : 0;
900                state->statemask |= (fw->match.state & NETCONF_ESTABLISHED) ? IPT_STATE_BIT(IP_CT_ESTABLISHED) : 0;
901                state->statemask |= (fw->match.state & NETCONF_RELATED) ? IPT_STATE_BIT(IP_CT_RELATED) : 0;
902                state->statemask |= (fw->match.state & NETCONF_NEW) ? IPT_STATE_BIT(IP_CT_NEW) : 0;
903        }               
904
905        /* Match by local time */
906        if (fw->match.secs[0] || fw->match.secs[1]) {
907                struct ipt_time_info *time;
908                unsigned int *days;
909                int i;
910
911                if (fw->match.secs[0] >= (24 * 60 * 60) || fw->match.secs[1] >= (24 * 60 * 60) ||
912                    fw->match.days[0] >= 7 || fw->match.days[1] >= 7) {
913                        fprintf(stderr, "invalid time %d-%d:%d-%d\n",
914                                fw->match.days[0], fw->match.days[1],
915                                fw->match.secs[0], fw->match.secs[1]);
916                        goto err;
917                }
918
919                if (!(match = netconf_append_match(&entry, "time", sizeof(struct ipt_time_info) + 8)))
920                        goto err;
921                time = (struct ipt_time_info *) &match->data[0];
922                days = (unsigned int *) &time[1];
923                days[0] = fw->match.days[0];
924                days[1] = fw->match.days[1];
925
926                for (i = fw->match.days[0]; i != fw->match.days[1]; i = (i + 1) % 7)
927                        time->days_match |= (1 << i);
928                time->days_match |= (1 << fw->match.days[1]);
929                time->time_start = fw->match.secs[0];
930                time->time_stop = fw->match.secs[1];
931        }
932
933        /* Allocate target */
934        if (!(target = netconf_append_target(&entry, ipt_target_name[fw->target], ipt_target_size[fw->target])))
935                goto err;
936
937        if (!(handle = iptc_init(ipt_table_name[fw->target]))) {
938                fprintf(stderr, "%s\n", iptc_strerror(errno));
939                goto err;
940        }
941
942        /* Set filter target information */
943        if (filter) {
944                if (!netconf_valid_dir(filter->dir)) {
945                        fprintf(stderr, "invalid filter direction %d\n", filter->dir);
946                        goto err;
947                }
948
949                if (!insert_entry(ipt_filter_chain_name[filter->dir], entry, &handle)) {
950                        fprintf(stderr, "%s\n", iptc_strerror(errno));
951                        goto err;
952                }
953        }
954
955        /* Set NAT target information */
956        else if (nat) {
957                struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
958                struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
959               
960                mr->rangesize = 1;
961
962                /* Map to IP address */
963                if (nat->ipaddr.s_addr) {
964                        range->min_ip = range->max_ip = nat->ipaddr.s_addr;
965                        range->flags |= IP_NAT_RANGE_MAP_IPS;
966                }
967
968                /* Map to TCP port(s) */
969                if (nat->match.ipproto == IPPROTO_TCP) {
970                        range->min.tcp.port = nat->ports[0];
971                        range->max.tcp.port = nat->ports[1];
972                        range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
973                }
974
975                /* Map to UDP port(s) */
976                else if (nat->match.ipproto == IPPROTO_UDP) {
977                        range->min.udp.port = nat->ports[0];
978                        range->max.udp.port = nat->ports[1];
979                        range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
980                }
981
982                if (!insert_entry(ipt_nat_chain_name[fw->target], entry, &handle)) {
983                        fprintf(stderr, "%s\n", iptc_strerror(errno));
984                        goto err;
985                }
986        }
987
988        else if (app) {
989                struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
990
991                info->proto = app->proto;
992                info->dport[0] = app->dport[0];
993                info->dport[1] = app->dport[1];
994                info->to[0] = app->to[0];
995                info->to[1] = app->to[1];
996
997                if (!insert_entry("PREROUTING", entry, &handle)) {
998                        fprintf(stderr, "%s\n", iptc_strerror(errno));
999                        goto err;
1000                }
1001        }
1002
1003        if (!iptc_commit(&handle)) {
1004                fprintf(stderr, "%s\n", iptc_strerror(errno));
1005                goto err;
1006        }
1007
1008        free(entry);
1009        return 0;
1010
1011 err:
1012        if (handle)
1013                iptc_commit(&handle);
1014        free(entry);
1015        return errno;
1016}
1017
1018/*
1019 * Delete a firewall entry
1020 * @param       fw      firewall entry
1021 * @return      0 on success and errno on failure
1022 */
1023int
1024netconf_del_fw(netconf_fw_t *fw)
1025{
1026        int num;
1027        const char *chain;
1028        iptc_handle_t handle;
1029
1030        /* netconf_fw_index() sanity checks fw */
1031        if ((num = netconf_fw_index(fw)) < 0)
1032                return -num;
1033
1034        /* Pick the right chain name */
1035        if (netconf_valid_filter(fw->target))
1036                chain = ipt_filter_chain_name[((netconf_filter_t *) fw)->dir];
1037        else if (netconf_valid_nat(fw->target))
1038                chain = ipt_nat_chain_name[fw->target];
1039        else if (fw->target == NETCONF_APP)
1040                chain = "PREROUTING";
1041        else
1042                return EINVAL;
1043               
1044        /* Commit changes */
1045        if (!(handle = iptc_init(ipt_table_name[fw->target])) ||
1046            !iptc_delete_num_entry(chain, num, &handle) ||
1047            !iptc_commit(&handle)) {
1048                fprintf(stderr, "%s\n", iptc_strerror(errno));
1049                return errno;
1050        }
1051
1052        return 0;
1053}
1054
1055/*
1056 * Add or delete a firewall entry or list of firewall entries
1057 * @param       fw_list firewall entry or list of firewall entries
1058 * @bool        del     whether to delete or add
1059 * @return      0 on success and errno on failure
1060 */
1061static int
1062netconf_manip_fw(netconf_fw_t *fw_list, bool del)
1063{
1064        netconf_fw_t *fw;
1065        int ret;
1066
1067        /* Single firewall entry */
1068        if (netconf_list_empty(fw_list) || !fw_list->next)
1069                return (del ? netconf_del_fw(fw_list) : netconf_add_fw(fw_list));
1070
1071        /* List of firewall entries */
1072        netconf_list_for_each(fw, fw_list) {
1073                if ((ret = del ? netconf_del_fw(fw) : netconf_add_fw(fw)))
1074                        return ret;
1075        }
1076       
1077        return 0;
1078}
1079
1080/*
1081 * Add a NAT entry or list of NAT entries
1082 * @param       nat_list        NAT entry or list of NAT entries
1083 * @return      0 on success and errno on failure
1084 */
1085int
1086netconf_add_nat(netconf_nat_t *nat_list)
1087{
1088        return netconf_manip_fw((netconf_fw_t *) nat_list, 0);
1089}
1090
1091/*
1092 * Delete a NAT entry or list of NAT entries
1093 * @param       nat_list        NAT entry or list of NAT entries
1094 * @return      0 on success and errno on failure
1095 */
1096int
1097netconf_del_nat(netconf_nat_t *nat_list)
1098{
1099        return netconf_manip_fw((netconf_fw_t *) nat_list, 1);
1100}
1101
1102/*
1103 * Get an array of the current NAT entries
1104 * @param       nat_array       array of NAT entries
1105 * @param       space           Pointer to size of nat_array in bytes
1106 * @return 0 on success and errno on failure
1107 */
1108int
1109netconf_get_nat(netconf_nat_t *nat_array, int *space)
1110{
1111        netconf_fw_t *fw, fw_list;
1112        int ret;
1113        int found = 0;
1114       
1115        if ((ret = netconf_get_fw(&fw_list)))
1116                return ret;
1117               
1118        netconf_list_for_each(fw, &fw_list) {
1119                if (netconf_valid_nat(fw->target)) {
1120                        found++;
1121                        if (*space && *space >= (found * sizeof(netconf_nat_t)))
1122                                memcpy(&nat_array[found - 1], (netconf_nat_t *) fw, sizeof(netconf_nat_t));
1123                }
1124        }
1125
1126        if (!*space)
1127                *space = found * sizeof(netconf_nat_t);
1128
1129        netconf_list_free(&fw_list);
1130        return 0;
1131}                       
1132
1133/*
1134 * Add a filter entry or list of filter entries
1135 * @param       filter_list     filter entry or list of filter entries
1136 * @return      0 on success and errno on failure
1137 */
1138int
1139netconf_add_filter(netconf_filter_t *filter_list)
1140{
1141        return netconf_manip_fw((netconf_fw_t *) filter_list, 0);
1142}
1143
1144/*
1145 * Delete a filter entry or list of filter entries
1146 * @param       filter_list     filter entry or list of filter entries
1147 * @return      0 on success and errno on failure
1148 */
1149int
1150netconf_del_filter(netconf_filter_t *filter_list)
1151{
1152        return netconf_manip_fw((netconf_fw_t *) filter_list, 1);
1153}
1154
1155/*
1156 * Get an array of the current filter entries
1157 * @param       filter_array    array of filter entries
1158 * @param       space           Pointer to size of filter_array in bytes
1159 * @return 0 on success and errno on failure
1160 */
1161int
1162netconf_get_filter(netconf_filter_t *filter_array, int *space)
1163{
1164        netconf_fw_t *fw, fw_list;
1165        int ret;
1166        int found = 0;
1167       
1168        if ((ret = netconf_get_fw(&fw_list)))
1169                return ret;
1170               
1171        netconf_list_for_each(fw, &fw_list) {
1172                if (netconf_valid_filter(fw->target)) {
1173                        found++;
1174                        if (*space && *space >= (found * sizeof(netconf_filter_t)))
1175                                memcpy(&filter_array[found - 1], (netconf_filter_t *) fw, sizeof(netconf_filter_t));
1176                }
1177        }
1178
1179        if (!*space)
1180                *space = found * sizeof(netconf_filter_t);
1181
1182        netconf_list_free(&fw_list);
1183        return 0;
1184}                       
1185
1186/*
1187 * Generates an ipt_entry with an optional match and one target
1188 * @param match_name            match name
1189 * @param match_data            match data
1190 * @param match_data_size       match data size
1191 * @param target_name           target name
1192 * @param target_data           target data
1193 * @param target_data_size      target data size
1194 * @return newly allocated and initialized ipt_entry
1195 */
1196static struct ipt_entry *
1197netconf_generate_entry(const char *match_name, const void *match_data, size_t match_data_size,
1198                       const char *target_name, const void *target_data, size_t target_data_size)
1199{
1200        struct ipt_entry *entry;
1201        struct ipt_entry_match *match;
1202        struct ipt_entry_target *target;
1203
1204        /* Allocate entry */
1205        if (!(entry = calloc(1, sizeof(struct ipt_entry)))) {
1206                perror("calloc");
1207                return NULL;
1208        }
1209
1210        /* Initialize entry parameters */
1211        entry->next_offset = entry->target_offset = sizeof(struct ipt_entry);
1212
1213        /* Allocate space for and copy match data */
1214        if (match_data) {
1215                if (!(match = netconf_append_match(&entry, match_name, match_data_size)))
1216                        goto err;
1217                memcpy(&match->data[0], match_data, match_data_size);
1218        }
1219
1220        /* Allocate space for and copy target data */
1221        if (!(target = netconf_append_target(&entry, target_name, target_data_size)))
1222                goto err;
1223        memcpy(&target->data[0], target_data, target_data_size);
1224
1225        return entry;
1226
1227 err:
1228        free(entry);
1229        return NULL;
1230}
1231
1232static int
1233netconf_reset_chain(char *table, char *chain)
1234{
1235        iptc_handle_t handle = NULL;   
1236
1237        /* Get handle to table */
1238        if (!(handle = iptc_init(table)))
1239                goto err;
1240
1241        /* Create chain if necessary */
1242        if (!iptc_is_chain(chain, handle))
1243                if (!iptc_create_chain(chain, &handle))
1244                        goto err;
1245
1246        /* Flush entries and commit */
1247        if (!iptc_flush_entries(chain, &handle) ||
1248            !iptc_commit(&handle))
1249                goto err;
1250
1251        return 0;
1252
1253 err:
1254        if (handle)
1255                iptc_commit(&handle);
1256        fprintf(stderr, "%s\n", iptc_strerror(errno));
1257        return errno;
1258}
1259
1260/*
1261 * Reset the firewall to a sane state
1262 * @return      0 on success and errno on failure
1263 */
1264int
1265netconf_reset_fw(void)
1266{
1267        iptc_handle_t handle = NULL;   
1268        struct ipt_entry *entry = NULL;
1269        struct ipt_state_info state;
1270        struct ipt_log_info log;
1271        int ret, unused;
1272
1273        /* Reset default chains */
1274        if ((ret = netconf_reset_chain("filter", "INPUT")) ||
1275            (ret = netconf_reset_chain("filter", "FORWARD")) ||
1276            (ret = netconf_reset_chain("filter", "OUTPUT")) ||
1277            (ret = netconf_reset_chain("nat", "PREROUTING")) ||
1278            (ret = netconf_reset_chain("nat", "POSTROUTING")) ||
1279            (ret = netconf_reset_chain("nat", "OUTPUT")))
1280                return ret;
1281
1282        /* Reset custom chains */
1283        if ((ret = netconf_reset_chain("filter", "logdrop")) ||
1284            (ret = netconf_reset_chain("filter", "logaccept")))
1285                goto err;
1286
1287        /* Log only when a connection is attempted */
1288        memset(&state, 0, sizeof(state));
1289        state.statemask = IPT_STATE_BIT(IP_CT_NEW);
1290
1291        /* Set miscellaneous log parameters */
1292        memset(&log, 0, sizeof(log));
1293        log.level = LOG_WARNING;
1294        log.logflags = 0xf;
1295
1296        /* Log packet */
1297        strncpy(log.prefix, "DROP ", sizeof(log.prefix));
1298        if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log))))
1299                return ENOMEM;
1300        entry->nfcache |= NFC_UNKNOWN;
1301        if (!(handle = iptc_init("filter")) ||
1302            !iptc_insert_entry("logdrop", entry, 0, &handle) ||
1303            !iptc_commit(&handle))
1304                goto err;
1305        free(entry);
1306
1307        /* Drop packet */
1308        if (!(entry = netconf_generate_entry(NULL, NULL, 0, "DROP", &unused, sizeof(unused))))
1309                return ENOMEM;
1310        entry->nfcache |= NFC_UNKNOWN;
1311        if (!(handle = iptc_init("filter")) ||
1312            !iptc_insert_entry("logdrop", entry, 1, &handle) ||
1313            !iptc_commit(&handle))
1314                goto err;
1315        free(entry);
1316
1317        /* Log packet */
1318        strncpy(log.prefix, "ACCEPT ", sizeof(log.prefix));
1319        if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log))))
1320                return ENOMEM;
1321        entry->nfcache |= NFC_UNKNOWN;
1322        if (!(handle = iptc_init("filter")) ||
1323            !iptc_insert_entry("logaccept", entry, 0, &handle) ||
1324            !iptc_commit(&handle))
1325                goto err;
1326        free(entry);
1327
1328        /* Accept packet */
1329        if (!(entry = netconf_generate_entry(NULL, NULL, 0, "ACCEPT", &unused, sizeof(unused))))
1330                return ENOMEM;
1331        entry->nfcache |= NFC_UNKNOWN;
1332        if (!(handle = iptc_init("filter")) ||
1333            !iptc_insert_entry("logaccept", entry, 1, &handle) ||
1334            !iptc_commit(&handle))
1335                goto err;
1336        free(entry);
1337
1338        return 0;
1339
1340 err:
1341        if (entry)
1342                free(entry);
1343        fprintf(stderr, "%s\n", iptc_strerror(errno));
1344        return errno;
1345}
1346
1347/*
1348 * Below are miscellaneous functions that do not fit into the grand
1349 * scheme of netconf
1350 */
1351
1352/*
1353 * Clamp TCP MSS value to PMTU of interface (for masquerading through PPPoE)
1354 * @return      0 on success and errno on failure
1355 */
1356int
1357netconf_clamp_mss_to_pmtu(void)
1358{
1359        struct ipt_entry *entry;
1360        iptc_handle_t handle;
1361        struct ipt_tcp tcp;
1362        struct ipt_tcpmss_info tcpmss;
1363
1364        /* Match on SYN=1 RST=0 */
1365        memset(&tcp, 0, sizeof(tcp));
1366        tcp.spts[1] = tcp.dpts[1] = 0xffff;
1367        tcp.flg_mask = TH_SYN | TH_RST;
1368        tcp.flg_cmp = TH_SYN;
1369
1370        /* Clamp TCP MSS to PMTU */
1371        memset(&tcpmss, 0, sizeof(tcpmss));
1372        tcpmss.mss = IPT_TCPMSS_CLAMP_PMTU;
1373
1374        /* Generate and complete the entry */
1375        if (!(entry = netconf_generate_entry("tcp", &tcp, sizeof(tcp), "TCPMSS", &tcpmss, sizeof(tcpmss))))
1376                return ENOMEM;
1377        entry->ip.proto = IPPROTO_TCP;
1378        entry->nfcache |= NFC_IP_PROTO | NFC_IP_TCPFLAGS;
1379
1380        /* Do it */
1381        if (!(handle = iptc_init("filter")) ||
1382            !iptc_insert_entry("FORWARD", entry, 0, &handle) ||
1383            !iptc_commit(&handle)) {
1384                fprintf(stderr, "%s\n", iptc_strerror(errno));
1385                free(entry);
1386                return errno;
1387        }
1388
1389        free(entry);
1390        return 0;
1391}       
Note: See TracBrowser for help on using the repository browser.