0b25cb03816756cd6947cc93fa57f2296d832ae6
[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                                                 printf("IFUP[%s]\n", net->ifname);
110                                                 fwd_update_cidr(addr_old, addr_new);
111                                                 fwd_ipt_addif(h, net->name);
112                                         }
113                                         else if( fwd_empty_cidr(addr_new) && !fwd_empty_cidr(addr_old) )
114                                         {
115                                                 printf("IFDOWN[%s]\n", net->ifname);
116                                                 fwd_update_cidr(addr_old, NULL);
117                                                 fwd_ipt_delif(h, net->name);
118                                         }
119                                         else if( ! fwd_equal_cidr(addr_old, addr_new) )
120                                         {
121                                                 printf("IFCHANGE[%s]\n", net->ifname);
122                                                 fwd_update_cidr(addr_old, addr_new);
123                                                 fwd_ipt_chgif(h, net->name);
124                                         }
125                                 }
126                         }
127
128                         fwd_free_addrs(addrs);
129                 }
130
131
132                 if( (unix_client = fwd_ipc_accept(h->unix_socket)) > -1 )
133                 {
134                         struct fwd_ipc_msg msg;
135                         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
136
137                         while( fwd_ipc_recvmsg(unix_client, &msg, sizeof(struct fwd_ipc_msg)) > 0 )
138                         {
139                                 fwd_log_info("Got message [%i]", msg.type);
140
141                                 switch(msg.type)
142                                 {
143                                         case FWD_IPC_FLUSH:
144                                                 fwd_log_info("Flushing rules ...");
145                                                 fwd_ipt_clear_ruleset(h);
146                                                 fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
147                                                 break;
148
149                                         case FWD_IPC_BUILD:
150                                                 fwd_log_info("Building rules ...");
151                                                 fwd_ipt_clear_ruleset(h);
152                                                 fwd_ipt_build_ruleset(h);
153                                                 fwd_addif_all(h);
154                                                 fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
155                                                 break;
156
157                                         case FWD_IPC_RELOAD:
158                                                 if( (data = fwd_read_config(h)) != NULL )
159                                                 {
160                                                         fwd_log_info("Flushing rules ...");
161                                                         fwd_ipt_clear_ruleset(h);
162                                                         fwd_free_config(h->conf);
163                                                         h->conf = data;
164                                                         fwd_log_info("Building rules ...");
165                                                         fwd_ipt_build_ruleset(h);
166                                                         fwd_addif_all(h);
167                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
168                                                 }
169                                                 else
170                                                 {
171                                                         fwd_log_err("Cannot reload configuration!");
172                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR);
173                                                 }
174                                                 break;
175
176                                         case FWD_IPC_ADDIF:
177                                         case FWD_IPC_DELIF:
178                                                 if( strlen(msg.data.network) > 0 )
179                                                 {
180                                                         fwd_ipt_delif(h, msg.data.network);
181
182                                                         if( msg.type == FWD_IPC_ADDIF )
183                                                                 fwd_ipt_addif(h, msg.data.network);
184
185                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_OK);
186                                                 }
187                                                 else
188                                                 {
189                                                         fwd_log_err("No network name provided!");
190                                                         fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR);
191                                                 }
192                                                 break;
193
194                                         case FWD_IPC_OK:
195                                         case FWD_IPC_ERROR:
196                                                 break;
197                                 }
198                         }
199
200                         fwd_ipc_shutdown(unix_client);
201                 }
202
203
204                 sleep(1);
205         }
206
207         fwd_delif_all(h);
208         fwd_ipt_clear_ruleset(h);
209
210         close(h->rtnl_socket);
211         fwd_free_config(h->conf);
212         fwd_free_ptr(h);
213
214         return 0;
215 }
216
217 static void fwd_client_usage(const char *msg)
218 {
219         printf(
220                 "%s\n\n"
221                 "Usage:\n"
222                 "  fw flush\n"
223                 "    Flush all rules in the firewall and reset policy\n\n"
224                 "  fw build\n"
225                 "    Rebuild firewall rules\n\n"
226                 "  fw reload\n"
227                 "    Reload configuration and rebuild firewall rules\n\n"
228                 "  fw addif {network}\n"
229                 "    Add rules for given network\n\n"
230                 "  fw delif {network}\n"
231                 "    Remove rules for given network\n\n"
232                 "", msg
233         );
234
235         exit(1);
236 }
237
238 static int fwd_client_main(int argc, const char *argv[])
239 {
240         int unix_server;
241         struct fwd_ipc_msg msg;
242         enum fwd_ipc_msgtype type;
243
244         if( argc < 2 )
245                 fwd_client_usage("Command required");
246
247         if( (unix_server = fwd_ipc_connect()) < 0 )
248                 fwd_fatal("Cannot connect to server instance (%m)");
249
250
251         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
252
253         if( !strcmp(argv[1], "flush") )
254                 type = FWD_IPC_FLUSH;
255
256         else if( !strcmp(argv[1], "build") )
257                 type = FWD_IPC_BUILD;
258
259         else if( !strcmp(argv[1], "reload") )
260                 type = FWD_IPC_RELOAD;
261
262         else if( !strcmp(argv[1], "addif") || !strcmp(argv[1], "delif") )
263         {
264                 if( argc < 3 )
265                         fwd_client_usage("The command requires a parameter.");
266
267                 type = strcmp(argv[1], "addif") ? FWD_IPC_DELIF : FWD_IPC_ADDIF;
268                 strncpy(msg.data.network, argv[2], sizeof(msg.data.network));
269         }
270
271         else
272                 fwd_client_usage("Invalid command given.");
273
274         msg.type = type;
275         fwd_ipc_sendmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg));
276
277         memset(&msg, 0, sizeof(struct fwd_ipc_msg));
278
279         while( fwd_ipc_recvmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg)) == 0 )
280                 continue;
281
282         switch(msg.type)
283         {
284                 case FWD_IPC_OK:
285                         printf("Success\n");
286                         break;
287
288                 case FWD_IPC_ERROR:
289                         printf("The server reported an error, check logread!\n");
290                         break;
291
292                 default:
293                         fwd_fatal("Unexpected response type %i", msg.type);
294         }
295
296         fwd_ipc_shutdown(unix_server);
297
298         return 0;
299 }
300
301 int main(int argc, const char *argv[])
302 {
303         if( strstr(argv[0], "fwd") )
304                 return fwd_server_main(argc, argv);
305         else
306                 return fwd_client_main(argc, argv);
307 }
308