/* * Copyright (C) 2006-2009 BATMAN contributors: * * Thomas Lopatic, Marek Lindner * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../os.h" #include "../batman.h" #include "../hna.h" #define BAT_LOGO_PRINT(x,y,z) printf( "\x1B[%i;%iH%c", y + 1, x, z ) /* write char 'z' into column 'x', row 'y' */ #define BAT_LOGO_END(x,y) printf("\x1B[8;0H");fflush(NULL);bat_wait( x, y ); /* end of current picture */ #define IOCREMDEV 2 static clock_t last_clock_tick; static float system_tick; uint8_t tunnel_running = 0; static pthread_mutex_t batman_clock_mutex = PTHREAD_MUTEX_INITIALIZER; static struct tms dummy_tms_struct; /* Make times(2) behave rationally on Linux */ static clock_t times_wrapper(void) { int save_errno = errno; clock_t ret; /** * times(2) really returns an unsigned value ... * * We don't check to see if we got back the error value (-1), because * the only possibility for an error would be if the address of * dummy_tms_struct was invalid. Since it's a * compiler-generated address, we assume that errors are impossible. * And, unfortunately, it is quite possible for the correct return * from times(2) to be exactly (clock_t)-1. Sigh... * */ errno = 0; ret = times(&dummy_tms_struct); /** * This is to work around a bug in the system call interface * for times(2) found in glibc on Linux (and maybe elsewhere) * It changes the return values from -1 to -4096 all into * -1 and then dumps the -(return value) into errno. * * This totally bizarre behavior seems to be widespread in * versions of Linux and glibc. * * Many thanks to Wolfgang Dumhs * for finding and documenting this bizarre behavior. */ if (errno != 0) { ret = (clock_t) (-errno); } errno = save_errno; return ret; } static void update_internal_clock(void) { clock_t current_clock_tick = times_wrapper(); batman_clock_ticks += (current_clock_tick - last_clock_tick); last_clock_tick = current_clock_tick; } uint32_t get_time_msec(void) { uint32_t time; pthread_mutex_lock(&batman_clock_mutex); update_internal_clock(); time = (uint32_t)(((float)(batman_clock_ticks) * 1000) / system_tick); pthread_mutex_unlock(&batman_clock_mutex); return time; } uint64_t get_time_msec64(void) { uint64_t time; pthread_mutex_lock(&batman_clock_mutex); update_internal_clock(); time = (uint64_t)(((float)(batman_clock_ticks) * 1000) / system_tick); pthread_mutex_unlock(&batman_clock_mutex); return time; } /* batman animation */ static void sym_print( char x, char y, char *z ) { char i = 0, Z; do{ BAT_LOGO_PRINT( 25 + (int)x + (int)i, (int)y, z[(int)i] ); switch ( z[(int)i] ) { case 92: Z = 47; /* "\" --> "/" */ break; case 47: Z = 92; /* "/" --> "\" */ break; case 41: Z = 40; /* ")" --> "(" */ break; default: Z = z[(int)i]; break; } BAT_LOGO_PRINT( 24 - (int)x - (int)i, (int)y, Z ); i++; } while( z[(int)i - 1] ); return; } static void bat_wait( int32_t T, int32_t t ) { struct timeval time; time.tv_sec = T; time.tv_usec = ( t * 10000 ); select( 0, NULL, NULL, NULL, &time ); return; } void print_animation( void ) { system( "clear" ); BAT_LOGO_END( 0, 50 ); sym_print( 0, 3, "." ); BAT_LOGO_END( 1, 0 ); sym_print( 0, 4, "v" ); BAT_LOGO_END( 0, 20 ); sym_print( 1, 3, "^" ); BAT_LOGO_END( 0, 20 ); sym_print( 1, 4, "/" ); sym_print( 0, 5, "/" ); BAT_LOGO_END( 0, 10 ); sym_print( 2, 3, "\\" ); sym_print( 2, 5, "/" ); sym_print( 0, 6, ")/" ); BAT_LOGO_END( 0, 10 ); sym_print( 2, 3, "_\\" ); sym_print( 4, 4, ")" ); sym_print( 2, 5, " /" ); sym_print( 0, 6, " )/" ); BAT_LOGO_END( 0, 10 ); sym_print( 4, 2, "'\\" ); sym_print( 2, 3, "__/ \\" ); sym_print( 4, 4, " )" ); sym_print( 1, 5, " " ); sym_print( 2, 6, " /" ); sym_print( 3, 7, "\\" ); BAT_LOGO_END( 0, 15 ); sym_print( 6, 3, " \\" ); sym_print( 3, 4, "_ \\ \\" ); sym_print( 10, 5, "\\" ); sym_print( 1, 6, " \\" ); sym_print( 3, 7, " " ); BAT_LOGO_END( 0, 20 ); sym_print( 7, 1, "____________" ); sym_print( 7, 3, " _ \\" ); sym_print( 3, 4, "_ " ); sym_print( 10, 5, " " ); sym_print( 11, 6, " " ); BAT_LOGO_END( 0, 25 ); sym_print( 3, 1, "____________ " ); sym_print( 1, 2, "'|\\ \\" ); sym_print( 2, 3, " / " ); sym_print( 3, 4, " " ); BAT_LOGO_END( 0, 25 ); sym_print( 3, 1, " ____________" ); sym_print( 1, 2, " '\\ " ); sym_print( 2, 3, "__/ _ \\" ); sym_print( 3, 4, "_" ); BAT_LOGO_END( 0, 35 ); sym_print( 7, 1, " " ); sym_print( 7, 3, " \\ " ); sym_print( 5, 4, "\\ \\" ); sym_print( 11, 5, "\\" ); sym_print( 12, 6, "\\" ); BAT_LOGO_END( 0 ,35 ); } void addr_to_string( uint32_t addr, char *str, int32_t len ) { inet_ntop( AF_INET, &addr, str, len ); } int32_t rand_num( int32_t limit ) { return ( limit == 0 ? 0 : rand() % limit ); } int8_t is_aborted(void) { return stop != 0; } void handler( int32_t BATMANUNUSED(sig) ) { stop = 1; } void del_default_route(void) { curr_gateway = NULL; } void add_default_route(void) { struct curr_gw_data *curr_gw_data; if (tunnel_running) { debug_output(3, "Error - couldn't create tunnel: old tunnel is still active\n"); return; } curr_gw_data = debugMalloc( sizeof(struct curr_gw_data), 207 ); curr_gw_data->orig = curr_gateway->orig_node->orig; curr_gw_data->gw_node = curr_gateway; curr_gw_data->batman_if = curr_gateway->orig_node->batman_if; tunnel_running = 1; if (pthread_create(&curr_gateway_thread_id, NULL, &client_to_gw_tun, curr_gw_data) != 0) { debug_output(0, "Error - couldn't spawn thread: %s\n", strerror(errno)); debugFree(curr_gw_data, 1213); curr_gateway = NULL; tunnel_running = 0; } else { pthread_detach(curr_gateway_thread_id); } } int8_t receive_packet(unsigned char *packet_buff, int32_t packet_buff_len, int16_t *packet_len, uint32_t *neigh, uint32_t timeout, struct batman_if **if_incoming) { struct sockaddr_in addr; struct timeval tv; struct list_head *if_pos; struct batman_if *batman_if; uint32_t addr_len; int8_t res; fd_set tmp_wait_set; addr_len = sizeof(struct sockaddr_in); memcpy( &tmp_wait_set, &receive_wait_set, sizeof(fd_set) ); while (1) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; res = select(receive_max_sock + 1, &tmp_wait_set, NULL, NULL, &tv); if (res >= 0) break; if (errno != EINTR) { debug_output(0, "Error - can't select (receive_packet): %s\n", strerror(errno)); /* we might have a deactivated interface - check all active interfaces for problems */ check_active_interfaces(); /* on error the wait_sets are reset - we have to re-create them */ interface_listen_sockets(); return -1; } } if ( res == 0 ) return 0; list_for_each(if_pos, &if_list) { batman_if = list_entry(if_pos, struct batman_if, list); if (FD_ISSET(batman_if->udp_recv_sock, &tmp_wait_set)) { if ((*packet_len = recvfrom(batman_if->udp_recv_sock, packet_buff, packet_buff_len - 1, 0, (struct sockaddr *)&addr, &addr_len)) < 0) { debug_output(0, "Error - can't receive packet: %s\n", strerror(errno)); deactivate_interface(batman_if); return -1; } if (((unsigned int)*packet_len) < sizeof(struct bat_packet)) return 0; (*if_incoming) = batman_if; break; } } *neigh = addr.sin_addr.s_addr; return 1; } int8_t send_udp_packet(unsigned char *packet_buff, int32_t packet_buff_len, struct sockaddr_in *broad, int32_t send_sock, struct batman_if *batman_if) { if ((batman_if != NULL) && (!batman_if->if_active)) return 0; if ( sendto( send_sock, packet_buff, packet_buff_len, 0, (struct sockaddr *)broad, sizeof(struct sockaddr_in) ) < 0 ) { if ( errno == 1 ) { debug_output(0, "Error - can't send udp packet: %s.\nDoes your firewall allow outgoing packets on port %i ?\n", strerror(errno), ntohs(broad->sin_port)); } else { debug_output(0, "Error - can't send udp packet: %s\n", strerror(errno)); } return -1; } return 0; } void del_gw_interface(void) { struct batman_if *batman_if = (struct batman_if *)if_list.next; struct batgat_ioc_args args; /* TODO: unregister from kernel module per ioctl */ if (batman_if->udp_tunnel_sock > 0) { if (batman_if->listen_thread_id != 0) { pthread_join(batman_if->listen_thread_id, NULL); } else { if (batman_if->dev != NULL) { strncpy(args.dev_name, batman_if->dev, IFNAMSIZ - 1); args.universal = strlen(batman_if->dev); if (ioctl(batman_if->udp_tunnel_sock, IOCREMDEV, &args) < 0) debug_output(0, "Error - can't remove device %s from kernel module : %s\n", batman_if->dev,strerror(errno)); } } close(batman_if->udp_tunnel_sock); batman_if->listen_thread_id = 0; batman_if->udp_tunnel_sock = 0; } } void restore_defaults(void) { struct list_head *if_pos, *if_pos_tmp; struct batman_if *batman_if; stop = 1; if ( routing_class > 0 ) add_del_interface_rules(RULE_DEL); del_gw_interface(); list_for_each_safe( if_pos, if_pos_tmp, &if_list ) { batman_if = list_entry( if_pos, struct batman_if, list ); deactivate_interface(batman_if); list_del( (struct list_head *)&if_list, if_pos, &if_list ); debugFree( if_pos, 1214 ); } /* delete rule for hna networks */ add_del_rule(0, 0, BATMAN_RT_TABLE_NETWORKS, BATMAN_RT_PRIO_UNREACH - 1, 0, RULE_TYPE_DST, RULE_DEL); /* delete unreachable routing table entry */ add_del_route(0, 0, 0, 0, 0, "unknown", BATMAN_RT_TABLE_UNREACH, ROUTE_TYPE_UNREACHABLE, ROUTE_DEL); if ( ( routing_class != 0 ) && ( curr_gateway != NULL ) ) del_default_route(); if ( vis_if.sock ) close( vis_if.sock ); if ( unix_if.unix_sock ) close( unix_if.unix_sock ); if ( unix_if.listen_thread_id != 0 ) { pthread_join( unix_if.listen_thread_id, NULL ); unix_if.listen_thread_id = 0; } if ( debug_level == 0 ) closelog(); if (policy_routing_script != NULL) { close(policy_routing_pipe); waitpid(policy_routing_script_pid, NULL, 0); } } void restore_and_exit( uint8_t is_sigsegv ) { struct orig_node *orig_node; struct hash_it_t *hashit = NULL; if ( !unix_client ) { /* remove tun interface first */ stop = 1; del_gw_interface(); if ( ( routing_class != 0 ) && ( curr_gateway != NULL ) ) del_default_route(); /* all rules and routes were purged in segmentation_fault() */ if ( !is_sigsegv ) { while ( NULL != ( hashit = hash_iterate( orig_hash, hashit ) ) ) { orig_node = hashit->bucket->data; update_routes( orig_node, NULL, NULL, 0 ); } } restore_defaults(); } if (is_sigsegv) raise(SIGSEGV); /* call exit always even if it will not be reached to make the compiler happy */ exit(EXIT_FAILURE); } void segmentation_fault(int32_t BATMANUNUSED(sig)) { signal(SIGSEGV, SIG_DFL); debug_output( 0, "Error - SIGSEGV received, trying to clean up ... \n" ); flush_routes_rules(0); flush_routes_rules(1); restore_and_exit(1); } void cleanup(void) { int8_t i; struct list_head *debug_pos, *debug_pos_tmp; for ( i = 0; i < debug_level_max; i++ ) { if ( debug_clients.clients_num[i] > 0 ) { list_for_each_safe( debug_pos, debug_pos_tmp, (struct list_head *)debug_clients.fd_list[i] ) { list_del( (struct list_head *)debug_clients.fd_list[i], debug_pos, (struct list_head_first *)debug_clients.fd_list[i] ); debugFree( debug_pos, 1218 ); } } debugFree( debug_clients.fd_list[i], 1219 ); debugFree( debug_clients.mutex[i], 1220 ); } debugFree( debug_clients.fd_list, 1221 ); debugFree( debug_clients.mutex, 1222 ); debugFree( debug_clients.clients_num, 1223 ); } int main(int argc, char *argv[]) { int8_t res; /* check if user is root */ if ((getuid()) || (getgid())) { fprintf(stderr, "Error - you must be root to run %s !\n", argv[0]); exit(EXIT_FAILURE); } INIT_LIST_HEAD_FIRST(forw_list); INIT_LIST_HEAD_FIRST(gw_list); INIT_LIST_HEAD_FIRST(if_list); hna_init(); /* save start value */ system_tick = (float)sysconf(_SC_CLK_TCK); last_clock_tick = times_wrapper(); update_internal_clock(); apply_init_args(argc, argv); init_bh_ports(); srand(getpid()); res = batman(); /* cleaning up */ hna_free(); restore_defaults(); cleanup(); checkLeak(); return res; }