662524e3587d9d240fc3ac9402d9b4ae1c46aa30
[project/luci.git] / contrib / fwd / src / fwd.c
1 /*
2  * fwd - OpenWrt firewall daemon - main part
3  *
4  *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  * The fwd program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * The fwd program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with the fwd program. If not, see http://www.gnu.org/licenses/.
17  */
18
19
20 #include "fwd.h"
21 #include "fwd_addr.h"
22 #include "fwd_rules.h"
23 #include "fwd_config.h"
24 #include "fwd_xtables.h"
25 #include "fwd_ipc.h"
26 #include "fwd_utils.h"
27
28
29 static void fwd_foreach_network(
30         struct fwd_handle *h,
31         void (*cb)(struct fwd_handle *h, struct fwd_network *net)
32 ) {
33         struct fwd_data *data;
34         struct fwd_network *net;
35
36         for( data = h->conf; data; data = data->next )
37         {
38                 if( data->type != FWD_S_ZONE )
39                         continue;
40
41                 for( net = data->section.zone.networks; net; net = net->next )
42                         cb(h, net);
43         }
44 }
45
46 static void fwd_addif_all_cb(struct fwd_handle *h, struct fwd_network *net)
47 {
48         fwd_ipt_addif(h, net->name);
49 }
50
51 static void fwd_delif_all_cb(struct fwd_handle *h, struct fwd_network *net)
52 {
53         fwd_ipt_delif(h, net->name);
54 }
55
56 #define fwd_addif_all(h) fwd_foreach_network(h, fwd_addif_all_cb)
57 #define fwd_delif_all(h) fwd_foreach_network(h, fwd_delif_all_cb)
58
59
60 static int fwd_server_main(int argc, const char *argv[])
61 {
62         struct fwd_handle *h;
63         struct fwd_network *net;
64         struct fwd_addr *addrs;
65         struct fwd_data *data;
66         struct fwd_cidr *addr_old, *addr_new;
67         struct sigaction sa;
68         int unix_client;
69
70         sa.sa_handler = SIG_IGN;
71         sigaction(SIGPIPE, &sa, NULL);
72
73         if( getuid() > 0 )
74                 fwd_fatal("Need root permissions!");
75
76         if( !(h = fwd_alloc_ptr(struct fwd_handle)) )
77                 fwd_fatal("Out of memory");
78
79         if( (h->rtnl_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1 )
80                 fwd_fatal("Failed to create AF_NETLINK socket (%m)");
81
82         if( (h->unix_socket = fwd_ipc_listen()) == -1 )
83                 fwd_fatal("Failed to create AF_UNIX socket (%m)");
84
85         if( !(h->conf = fwd_read_config(h)) )
86                 fwd_fatal("Failed to read configuration");
87
88         fwd_log_init();
89
90         fwd_ipt_build_ruleset(h);
91         fwd_addif_all(h);
92
93         while(1)
94         {
95                 if( (addrs = fwd_get_addrs(h->rtnl_socket, AF_INET)) != NULL )
96                 {
97                         for( data = h->conf; data; data = data->next )
98                         {
99                                 if( data->type != FWD_S_ZONE )
100                                         continue;
101
102                                 for( net = data->section.zone.networks; net; net = net->next )
103                                 {
104                                         addr_new = fwd_lookup_addr(addrs, net->ifname);
105                                         addr_old = net->addr;
106
107                                         if( !fwd_empty_cidr(addr_new) && fwd_empty_cidr(addr_old) )
108                                         {
109                                                 fwd_log_info(
110                                                         "Interface %s brought up - adding rules",
111                                                         net->ifname
112                                                 );
113
114                                                 fwd_update_cidr(addr_old, addr_new);
115                                                 fwd_ipt_addif(h, net->name);
116                                         }
117                                         else if( fwd_empty_cidr(addr_new) && !fwd_empty_cidr(addr_old) )
118                                         {
119                                                 fwd_log_info(
120                                                         "Interface %s went down - removing rules",
121                                                         net->ifname
122                                                 );
123
124                                                 fwd_update_cidr(addr_old, NULL);
125                                                 fwd_ipt_delif(h, net->name);
126                                         }
127                                         else if( ! fwd_equal_cidr(addr_old, addr_new) )
128                                         {
129                                                 fwd_log_info(
130                                                         "Interface %s changed IP - rebuilding rules",
131                                                         net->ifname
132                                                 );
133
134                                                 fwd_update_cidr(addr_old, addr_new);
135                                                 fwd_ipt_chgif(h, net->name);
136                                         }
137                                 }
138                         }
139
140                         fwd_free_addrs(addrs);
141                 }
142
143
144                 if( (unix_client = fwd_ipc_accept(h->unix_socket)) > -1 )
145                 {
146                         struct fwd_ipc_msg msg;
147                         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
148
149                         while( fwd_ipc_recvmsg(unix_client, &msg, sizeof(struct fwd_ipc_msg)) > 0 )
150                         {
151                                 fwd_log_info("Got message [%i]", msg.type);
152
153                                 switch(msg.type)
154                                 {
155                                         case FWD_IPC_FLUSH:
156                                                 fwd_log_info("Flushing rules ...");
157                                                 fwd_ipt_clear_ruleset(h);
158                                                 fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
159                                                 break;
160
161                                         case FWD_IPC_BUILD:
162                                                 fwd_log_info("Building rules ...");
163                                                 fwd_ipt_clear_ruleset(h);
164                                                 fwd_ipt_build_ruleset(h);
165                                                 fwd_addif_all(h);
166                                                 fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
167                                                 break;
168
169                                         case FWD_IPC_RELOAD:
170                                                 if( (data = fwd_read_config(h)) != NULL )
171                                                 {
172                                                         fwd_log_info("Flushing rules ...");
173                                                         fwd_ipt_clear_ruleset(h);
174                                                         fwd_free_config(h->conf);
175                                                         h->conf = data;
176                                                         fwd_log_info("Building rules ...");
177                                                         fwd_ipt_build_ruleset(h);
178                                                         fwd_addif_all(h);
179                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
180                                                 }
181                                                 else
182                                                 {
183                                                         fwd_log_err("Cannot reload configuration!");
184                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR);
185                                                 }
186                                                 break;
187
188                                         case FWD_IPC_ADDIF:
189                                         case FWD_IPC_DELIF:
190                                                 if( strlen(msg.data.network) > 0 )
191                                                 {
192                                                         fwd_ipt_delif(h, msg.data.network);
193
194                                                         if( msg.type == FWD_IPC_ADDIF )
195                                                                 fwd_ipt_addif(h, msg.data.network);
196
197                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
198                                                 }
199                                                 else
200                                                 {
201                                                         fwd_log_err("No network name provided!");
202                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR);
203                                                 }
204                                                 break;
205
206                                         case FWD_IPC_OK:
207                                         case FWD_IPC_ERROR:
208                                                 break;
209                                 }
210                         }
211
212                         fwd_ipc_shutdown(unix_client);
213                 }
214
215
216                 sleep(1);
217         }
218
219         fwd_delif_all(h);
220         fwd_ipt_clear_ruleset(h);
221
222         close(h->rtnl_socket);
223         fwd_free_config(h->conf);
224         fwd_free_ptr(h);
225
226         return 0;
227 }
228
229 static void fwd_client_usage(const char *msg)
230 {
231         printf(
232                 "%s\n\n"
233                 "Usage:\n"
234                 "  fw flush\n"
235                 "    Flush all rules in the firewall and reset policy\n\n"
236                 "  fw build\n"
237                 "    Rebuild firewall rules\n\n"
238                 "  fw reload\n"
239                 "    Reload configuration and rebuild firewall rules\n\n"
240                 "  fw addif {network}\n"
241                 "    Add rules for given network\n\n"
242                 "  fw delif {network}\n"
243                 "    Remove rules for given network\n\n"
244                 "", msg
245         );
246
247         exit(1);
248 }
249
250 static int fwd_client_main(int argc, const char *argv[])
251 {
252         int unix_server;
253         struct fwd_ipc_msg msg;
254         enum fwd_ipc_msgtype type;
255
256         if( argc < 2 )
257                 fwd_client_usage("Command required");
258
259         if( (unix_server = fwd_ipc_connect()) < 0 )
260                 fwd_fatal("Cannot connect to server instance (%m)");
261
262
263         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
264
265         if( !strcmp(argv[1], "flush") )
266                 type = FWD_IPC_FLUSH;
267
268         else if( !strcmp(argv[1], "build") )
269                 type = FWD_IPC_BUILD;
270
271         else if( !strcmp(argv[1], "reload") )
272                 type = FWD_IPC_RELOAD;
273
274         else if( !strcmp(argv[1], "addif") || !strcmp(argv[1], "delif") )
275         {
276                 if( argc < 3 )
277                         fwd_client_usage("The command requires a parameter.");
278
279                 type = strcmp(argv[1], "addif") ? FWD_IPC_DELIF : FWD_IPC_ADDIF;
280                 strncpy(msg.data.network, argv[2], sizeof(msg.data.network));
281         }
282
283         else
284                 fwd_client_usage("Invalid command given.");
285
286         msg.type = type;
287         fwd_ipc_sendmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg));
288
289         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
290
291         while( fwd_ipc_recvmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg)) == 0 )
292                 continue;
293
294         switch(msg.type)
295         {
296                 case FWD_IPC_OK:
297                         printf("Success\n");
298                         break;
299
300                 case FWD_IPC_ERROR:
301                         printf("The server reported an error, check logread!\n");
302                         break;
303
304                 default:
305                         fwd_fatal("Unexpected response type %i", msg.type);
306         }
307
308         fwd_ipc_shutdown(unix_server);
309
310         return 0;
311 }
312
313 int main(int argc, const char *argv[])
314 {
315         if( strstr(argv[0], "fwd") )
316                 return fwd_server_main(argc, argv);
317         else
318                 return fwd_client_main(argc, argv);
319 }
320