/* * fwd - OpenWrt firewall daemon - main part * * Copyright (C) 2009 Jo-Philipp Wich * * The fwd program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * The fwd program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with the fwd program. If not, see http://www.gnu.org/licenses/. */ #include "fwd.h" #include "fwd_addr.h" #include "fwd_rules.h" #include "fwd_config.h" #include "fwd_xtables.h" #include "fwd_ipc.h" #include "fwd_utils.h" static void fwd_foreach_network( struct fwd_handle *h, void (*cb)(struct fwd_handle *h, struct fwd_network *net) ) { struct fwd_data *data; struct fwd_network *net; for( data = h->conf; data; data = data->next ) { if( data->type != FWD_S_ZONE ) continue; for( net = data->section.zone.networks; net; net = net->next ) cb(h, net); } } static void fwd_addif_all_cb(struct fwd_handle *h, struct fwd_network *net) { fwd_ipt_addif(h, net->name); } static void fwd_delif_all_cb(struct fwd_handle *h, struct fwd_network *net) { fwd_ipt_delif(h, net->name); } #define fwd_addif_all(h) fwd_foreach_network(h, fwd_addif_all_cb) #define fwd_delif_all(h) fwd_foreach_network(h, fwd_delif_all_cb) static int fwd_server_main(int argc, const char *argv[]) { struct fwd_handle *h; struct fwd_network *net; struct fwd_addr *addrs; struct fwd_data *data; struct fwd_cidr *addr_old, *addr_new; struct sigaction sa; int unix_client; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, NULL); if( getuid() > 0 ) fwd_fatal("Need root permissions!"); if( !(h = fwd_alloc_ptr(struct fwd_handle)) ) fwd_fatal("Out of memory"); if( (h->rtnl_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1 ) fwd_fatal("Failed to create AF_NETLINK socket (%m)"); if( (h->unix_socket = fwd_ipc_listen()) == -1 ) fwd_fatal("Failed to create AF_UNIX socket (%m)"); if( !(h->conf = fwd_read_config(h)) ) fwd_fatal("Failed to read configuration"); fwd_log_init(); fwd_ipt_build_ruleset(h); fwd_addif_all(h); while(1) { if( (addrs = fwd_get_addrs(h->rtnl_socket, AF_INET)) != NULL ) { for( data = h->conf; data; data = data->next ) { if( data->type != FWD_S_ZONE ) continue; for( net = data->section.zone.networks; net; net = net->next ) { addr_new = fwd_lookup_addr(addrs, net->ifname); addr_old = net->addr; if( !fwd_empty_cidr(addr_new) && fwd_empty_cidr(addr_old) ) { fwd_log_info( "Interface %s brought up - adding rules", net->ifname ); fwd_update_cidr(addr_old, addr_new); fwd_ipt_addif(h, net->name); } else if( fwd_empty_cidr(addr_new) && !fwd_empty_cidr(addr_old) ) { fwd_log_info( "Interface %s went down - removing rules", net->ifname ); fwd_update_cidr(addr_old, NULL); fwd_ipt_delif(h, net->name); } else if( ! fwd_equal_cidr(addr_old, addr_new) ) { fwd_log_info( "Interface %s changed IP - rebuilding rules", net->ifname ); fwd_update_cidr(addr_old, addr_new); fwd_ipt_chgif(h, net->name); } } } fwd_free_addrs(addrs); } if( (unix_client = fwd_ipc_accept(h->unix_socket)) > -1 ) { struct fwd_ipc_msg msg; memset(&msg, 0, sizeof(struct fwd_ipc_msg)); while( fwd_ipc_recvmsg(unix_client, &msg, sizeof(struct fwd_ipc_msg)) > 0 ) { fwd_log_info("Got message [%i]", msg.type); switch(msg.type) { case FWD_IPC_FLUSH: fwd_log_info("Flushing rules ..."); fwd_ipt_clear_ruleset(h); fwd_ipc_sendtype(unix_client, FWD_IPC_OK); break; case FWD_IPC_BUILD: fwd_log_info("Building rules ..."); fwd_ipt_clear_ruleset(h); fwd_ipt_build_ruleset(h); fwd_addif_all(h); fwd_ipc_sendtype(unix_client, FWD_IPC_OK); break; case FWD_IPC_RELOAD: if( (data = fwd_read_config(h)) != NULL ) { fwd_log_info("Flushing rules ..."); fwd_ipt_clear_ruleset(h); fwd_free_config(h->conf); h->conf = data; fwd_log_info("Building rules ..."); fwd_ipt_build_ruleset(h); fwd_addif_all(h); fwd_ipc_sendtype(unix_client, FWD_IPC_OK); } else { fwd_log_err("Cannot reload configuration!"); fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR); } break; case FWD_IPC_ADDIF: case FWD_IPC_DELIF: if( strlen(msg.data.network) > 0 ) { fwd_ipt_delif(h, msg.data.network); if( msg.type == FWD_IPC_ADDIF ) fwd_ipt_addif(h, msg.data.network); fwd_ipc_sendtype(unix_client, FWD_IPC_OK); } else { fwd_log_err("No network name provided!"); fwd_ipc_sendtype(unix_client, FWD_IPC_ERROR); } break; case FWD_IPC_OK: case FWD_IPC_ERROR: break; } } fwd_ipc_shutdown(unix_client); } sleep(1); } fwd_delif_all(h); fwd_ipt_clear_ruleset(h); close(h->rtnl_socket); fwd_free_config(h->conf); fwd_free_ptr(h); return 0; } static void fwd_client_usage(const char *msg) { printf( "%s\n\n" "Usage:\n" " fw flush\n" " Flush all rules in the firewall and reset policy\n\n" " fw build\n" " Rebuild firewall rules\n\n" " fw reload\n" " Reload configuration and rebuild firewall rules\n\n" " fw addif {network}\n" " Add rules for given network\n\n" " fw delif {network}\n" " Remove rules for given network\n\n" "", msg ); exit(1); } static int fwd_client_main(int argc, const char *argv[]) { int unix_server; struct fwd_ipc_msg msg; enum fwd_ipc_msgtype type; if( argc < 2 ) fwd_client_usage("Command required"); if( (unix_server = fwd_ipc_connect()) < 0 ) fwd_fatal("Cannot connect to server instance (%m)"); memset(&msg, 0, sizeof(struct fwd_ipc_msg)); if( !strcmp(argv[1], "flush") ) type = FWD_IPC_FLUSH; else if( !strcmp(argv[1], "build") ) type = FWD_IPC_BUILD; else if( !strcmp(argv[1], "reload") ) type = FWD_IPC_RELOAD; else if( !strcmp(argv[1], "addif") || !strcmp(argv[1], "delif") ) { if( argc < 3 ) fwd_client_usage("The command requires a parameter."); type = strcmp(argv[1], "addif") ? FWD_IPC_DELIF : FWD_IPC_ADDIF; strncpy(msg.data.network, argv[2], sizeof(msg.data.network)); } else fwd_client_usage("Invalid command given."); msg.type = type; fwd_ipc_sendmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg)); memset(&msg, 0, sizeof(struct fwd_ipc_msg)); while( fwd_ipc_recvmsg(unix_server, &msg, sizeof(struct fwd_ipc_msg)) == 0 ) continue; switch(msg.type) { case FWD_IPC_OK: printf("Success\n"); break; case FWD_IPC_ERROR: printf("The server reported an error, check logread!\n"); break; default: fwd_fatal("Unexpected response type %i", msg.type); } fwd_ipc_shutdown(unix_server); return 0; } int main(int argc, const char *argv[]) { if( strstr(argv[0], "fwd") ) return fwd_server_main(argc, argv); else return fwd_client_main(argc, argv); }