#include #include #include #include #include #include "stream.h" #include "Maat_rule.h" #include "kni_redirect.h" #include "kni_sendlog.h" #include "kni_entry.h" #include "kni_comm.h" extern "C" int sendpacket_do_checksum(unsigned char* buf,int protocol,int len); long redirect_htable_search_cb(void* data,const unsigned char* key,unsigned int size,void* user_arg) { long result=0; if(data!=NULL) { memcpy(user_arg,data,sizeof(struct redirect_htable_data)); result = 1; } return result; } //only snat_replay_pending or dnat_replay_pending call this function //1:exit;0:not exit -1:error int redirect_search_htable(unsigned char addr_type,struct kni_pme_info* pmeinfo,int thread_seq,const void* a_packet,int protocol) { struct ip* ipv4_hdr = NULL; struct kni_ipv6_hdr* ipv6_hdr = NULL; struct kni_tcp_hdr* tcphdr=NULL; struct kni_udp_hdr* udphdr=NULL; long result = 0; struct stream_tuple4_v4 htable_key_v4; struct stream_tuple4_v6 htable_key_v6; if(addr_type==ADDR_TYPE_IPV4) { ipv4_hdr = (struct ip*)a_packet; htable_key_v4.saddr=(ipv4_hdr->ip_src).s_addr; htable_key_v4.daddr=(ipv4_hdr->ip_dst).s_addr; if(protocol==PROTO_TYPE_TCP) { tcphdr=(struct kni_tcp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v4.source=tcphdr->th_sport; htable_key_v4.dest=tcphdr->th_dport; } else if(protocol == PROTO_TYPE_UDP) { udphdr=(struct kni_udp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v4.source=udphdr->uh_sport; htable_key_v4.dest=udphdr->uh_dport; } else { htable_key_v4.source=0; htable_key_v4.dest=0; } MESA_htable_search_cb(g_kni_structinfo.htable_redirect,(unsigned char*)&htable_key_v4,sizeof(htable_key_v4),redirect_htable_search_cb,(void*)&(pmeinfo->redirect_info),&result); } else if(addr_type==ADDR_TYPE_IPV6) { ipv6_hdr = (struct kni_ipv6_hdr*)a_packet; memcpy(htable_key_v6.saddr,&(ipv6_hdr->ip6_src),sizeof(htable_key_v6.saddr)); memcpy(htable_key_v6.daddr,&(ipv6_hdr->ip6_dst),sizeof(htable_key_v6.daddr)); if(protocol==PROTO_TYPE_TCP) { tcphdr=(struct kni_tcp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v6.source=tcphdr->th_sport; htable_key_v6.dest=tcphdr->th_dport; } else if(protocol == PROTO_TYPE_UDP) { udphdr=(struct kni_udp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v6.source=udphdr->uh_sport; htable_key_v6.dest=udphdr->uh_dport; } else { htable_key_v6.source=0; htable_key_v6.dest=0; } MESA_htable_search_cb(g_kni_structinfo.htable_redirect,(unsigned char*)&htable_key_v6,sizeof(htable_key_v6),redirect_htable_search_cb,(void*)&(pmeinfo->redirect_info),&result); } if(result == 1) { pmeinfo->action=KNI_ACTION_REDIRECT; kni_log_debug(RLOG_LV_DEBUG,(char*)"redirect",a_packet,"redirect_search_htable()"); kni_filestate2_set(thread_seq,FS_REDIRECT_REPLY,0,1); } return result; } int redirect_add_htable(unsigned char addr_type,struct kni_pme_info* pmeinfo,int thread_seq,const void* a_packet,int protocol) { int ret =0; struct ip* ipv4_hdr = NULL; struct kni_ipv6_hdr* ipv6_hdr = NULL; struct kni_tcp_hdr* tcphdr=NULL; struct kni_udp_hdr* udphdr=NULL; struct stream_tuple4_v4 htable_key_v4; struct stream_tuple4_v6 htable_key_v6; struct redirect_htable_data* htable_data=(struct redirect_htable_data*)malloc(sizeof(struct redirect_htable_data)); memset(htable_data,0,sizeof(struct redirect_htable_data)); htable_data->addr_type=addr_type; if(addr_type==4) { ipv4_hdr = (struct ip*)a_packet; if(pmeinfo->redirect_info.nat_type == REDIRECT_SNAT_TYPE) { htable_data->nat_type=REDIRECT_SNAT_REPLAY; htable_data->ipv4=(ipv4_hdr->ip_src).s_addr; htable_key_v4.saddr=(ipv4_hdr->ip_dst).s_addr; htable_key_v4.daddr=pmeinfo->redirect_info.ipv4; } else if(pmeinfo->redirect_info.nat_type == REDIRECT_DNAT_TYPE) { htable_data->nat_type=REDIRECT_DNAT_REPLAY; htable_data->ipv4=(ipv4_hdr->ip_dst).s_addr; htable_key_v4.saddr=pmeinfo->redirect_info.ipv4; htable_key_v4.daddr=(ipv4_hdr->ip_src).s_addr; } else { MESA_handle_runtime_log(g_kni_comminfo.logger,RLOG_LV_FATAL,"redirect","redirect_add_htable err,nat_type:%d",pmeinfo->redirect_info.nat_type); return -1; } if(protocol==PROTO_TYPE_TCP) { tcphdr=(struct kni_tcp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v4.source=tcphdr->th_dport; htable_key_v4.dest=tcphdr->th_sport; } else if(protocol == PROTO_TYPE_UDP) { udphdr=(struct kni_udp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v4.source=udphdr->uh_dport; htable_key_v4.dest=udphdr->uh_sport; } else { htable_key_v4.source=0; htable_key_v4.dest=0; } pmeinfo->redirect_key_len=sizeof(htable_key_v4); pmeinfo->redirect_htable_key=(char*)malloc(pmeinfo->redirect_key_len); memcpy(pmeinfo->redirect_htable_key,&htable_key_v4,pmeinfo->redirect_key_len); ret = MESA_htable_add(g_kni_structinfo.htable_redirect,(unsigned char*)pmeinfo->redirect_htable_key,pmeinfo->redirect_key_len,(const void *)htable_data); kni_log_debug(RLOG_LV_DEBUG,(char*)"redirect_htable_add",a_packet,"key:%u,%d,%u,%d,data:%u", htable_key_v4.saddr,htable_key_v4.source,htable_key_v4.daddr,htable_key_v4.dest,htable_data->ipv4); } else if(addr_type==6) { ipv6_hdr = (struct kni_ipv6_hdr*)a_packet; if(pmeinfo->redirect_info.nat_type == REDIRECT_SNAT_TYPE) { htable_data->nat_type=REDIRECT_SNAT_REPLAY; memcpy(htable_data->ipv6,&(ipv6_hdr->ip6_src),sizeof(htable_data->ipv6)); memcpy(htable_key_v6.saddr,&(ipv6_hdr->ip6_dst),sizeof(htable_key_v6.saddr)); memcpy(htable_key_v6.daddr,pmeinfo->redirect_info.ipv6,sizeof(htable_key_v6.daddr)); } else if(pmeinfo->redirect_info.nat_type == REDIRECT_DNAT_TYPE) { htable_data->nat_type=REDIRECT_DNAT_REPLAY; memcpy(htable_data->ipv6,&(ipv6_hdr->ip6_dst),sizeof(htable_data->ipv6)); memcpy(htable_key_v6.saddr,pmeinfo->redirect_info.ipv6,sizeof(htable_key_v6.saddr)); memcpy(htable_key_v6.daddr,&(ipv6_hdr->ip6_src),sizeof(htable_key_v6.daddr)); } else { MESA_handle_runtime_log(g_kni_comminfo.logger,RLOG_LV_FATAL,"redirect","redirect_add_htable err,nat_type:%d",pmeinfo->redirect_info.nat_type); return -1; } if(protocol==PROTO_TYPE_TCP) { tcphdr=(struct kni_tcp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v6.source=tcphdr->th_dport; htable_key_v6.dest=tcphdr->th_sport; } else if(protocol == PROTO_TYPE_UDP) { udphdr=(struct kni_udp_hdr*)((char*)ipv4_hdr+4*(ipv4_hdr->ip_hl)); htable_key_v6.source=udphdr->uh_dport; htable_key_v6.dest=udphdr->uh_sport; } else { htable_key_v6.source=0; htable_key_v6.dest=0; } pmeinfo->redirect_key_len=sizeof(htable_key_v6); pmeinfo->redirect_htable_key=(char*)malloc(pmeinfo->redirect_key_len); memcpy(pmeinfo->redirect_htable_key,&htable_key_v6,pmeinfo->redirect_key_len); ret = MESA_htable_add(g_kni_structinfo.htable_redirect,(unsigned char*)pmeinfo->redirect_htable_key,pmeinfo->redirect_key_len,(const void *)htable_data); } return ret; } void redirect_free_htable_data(void* data) { if(data != NULL) { free(data); } data=NULL; return; } int redirect_del_htable(void* htable_key,int key_len) { MESA_htable_del(g_kni_structinfo.htable_redirect,(const unsigned char*)htable_key,key_len,redirect_free_htable_data); return 0; } /* int redirect_get_service_define(char* service_defined,int ser_def_len,struct redirect_serdef_info* out) { int ip_pool_len =0; int nat_type_len = 0; char* ip_pool = NULL; char* nat_type = NULL; // char* tmp = NULL; ip_pool = kni_memncasemem(service_defined, ser_def_len,(char*)"spoofing_ip_pool=", strlen("spoofing_ip_pool=")); if(ip_pool == NULL) { return -1; } ip_pool += 1; ip_pool_len = strlen(ip_pool); nat_type = kni_memncasemem(ip_pool,ip_pool_len,(char*)"nat_type=", strlen("nat_type=")); if(nat_type == NULL) { return -1; } nat_type += 1; nat_type_len = strlen(nat_type); out->ip_pool_len= nat_type - ip_pool -1; assert((int)sizeof(out->ip_pool)>=out->ip_pool_len); memcpy(out->ip_pool,ip_pool,out->ip_pool_len); out->nat_type_len= nat_type_len-1; assert((int)sizeof(out->nat_type)>=out->nat_type_len); memcpy(out->nat_type,nat_type,out->nat_type_len); return 0; } */ int redirect_get_service_define(char* service_defined,int ser_def_len,struct redirect_serdef_info* out) { int ip_pool_len =0; int nat_type_len = 0; char* ip_pool = NULL; char* nat_type = NULL; char* tmp = NULL; ip_pool = kni_memncasemem(service_defined, ser_def_len,(char*)"=", strlen("=")); if(ip_pool == NULL) { return -1; } ip_pool += 1; ip_pool_len = strlen(ip_pool); nat_type = kni_memncasemem(ip_pool,ip_pool_len,(char*)"=", strlen("=")); if(nat_type == NULL) { return -1; } nat_type += 1; nat_type_len = strlen(nat_type); tmp = kni_memncasemem(ip_pool, ip_pool_len,(char*)";", strlen(";")); if(ip_pool == NULL) { return -1; } out->ip_pool_len= tmp-ip_pool; assert((int)sizeof(out->ip_pool)>=out->ip_pool_len); memcpy(out->ip_pool,ip_pool,out->ip_pool_len); out->nat_type_len= nat_type_len-1; assert((int)sizeof(out->nat_type)>=out->nat_type_len); memcpy(out->nat_type,nat_type,out->nat_type_len); return 0; } void plugin_EX_new_cb(int table_id, const char* key, const char* table_line, MAAT_PLUGIN_EX_DATA* ad, long argl, void *argp) { struct redirect_plugin_ex_data* add_data = (struct redirect_plugin_ex_data*)calloc(sizeof(struct redirect_plugin_ex_data), 1); int policy_group=0; int id,protocol,direction,location,is_valid,service,ret; char port[REDIRECT_SERDEF_LEN]; char user_region[REDIRECT_SERDEF_LEN]; char effective_range[REDIRECT_SERDEF_LEN]; char op_time[REDIRECT_SERDEF_LEN]; ret=sscanf(table_line, "%d\t%d\t%d\t%s\t%s\t%d\t%s\t%d\t%d\t%d\t%d\t%s\t%s", &id,&(add_data->addr_type),&protocol,add_data->spoofing_ip,port,&direction,user_region,&location,&is_valid,&service,&policy_group,effective_range,op_time); if(ret < 0) { *ad=NULL; return ; } *ad=add_data; return; } void plugin_EX_free_cb(int table_id, MAAT_PLUGIN_EX_DATA* ad, long argl, void *argp) { struct redirect_plugin_ex_data* free_data=(struct redirect_plugin_ex_data*)(*ad); free(free_data); *ad=NULL; return ; } void plugin_EX_dup_cb(int table_id, MAAT_PLUGIN_EX_DATA *to, MAAT_PLUGIN_EX_DATA *from, long argl, void *argp) { struct redirect_plugin_ex_data* dup_data=(struct redirect_plugin_ex_data*)malloc(sizeof(struct redirect_plugin_ex_data)); memcpy(dup_data,*from,sizeof(struct redirect_plugin_ex_data)); *to=dup_data; return; } int redirect_sendpkt_v4(int protocol,unsigned char dir,int thread_seq,struct ip* a_packet,struct redirect_htable_data* sendpkt_args) { int ret = 0; unsigned short sendbuf_len = ntohs(a_packet->ip_len); struct ip* iphdr_sendbuf = NULL; char* sendbuf = (char*)malloc(sendbuf_len); memcpy(sendbuf,(char*)a_packet,sendbuf_len); iphdr_sendbuf=(struct ip*)sendbuf; if((sendpkt_args->nat_type == REDIRECT_SNAT_TYPE)||(sendpkt_args->nat_type == REDIRECT_DNAT_REPLAY)) { iphdr_sendbuf->ip_src.s_addr=sendpkt_args->ipv4; } else { iphdr_sendbuf->ip_dst.s_addr=sendpkt_args->ipv4; } if(protocol == PROTO_TYPE_TCP) { sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_TCP,(sendbuf_len-4*(a_packet->ip_hl))); } else if(protocol == PROTO_TYPE_UDP) { sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_UDP,(sendbuf_len-4*(a_packet->ip_hl))); } sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_IP,sizeof(struct ip)); MESA_sendpacket_iplayer(thread_seq,sendbuf,sendbuf_len,dir); free(sendbuf); sendbuf = NULL; return ret; } int redirect_sendpkt_v6(int protocol,unsigned char dir,int thread_seq,struct kni_ipv6_hdr* ipv6_hdr,struct redirect_htable_data* sendpkt_args) { struct kni_ipv6_hdr* ipv6_hdr_sendbuf=NULL; int sendbuf_len = ntohs(ipv6_hdr->ip6_payload_len) + sizeof(struct kni_ipv6_hdr); char* sendbuf = (char*)malloc(sendbuf_len); memcpy(sendbuf,ipv6_hdr,sendbuf_len); ipv6_hdr_sendbuf = (struct kni_ipv6_hdr*)sendbuf; if((sendpkt_args->nat_type == REDIRECT_SNAT_TYPE)||(sendpkt_args->nat_type == REDIRECT_DNAT_REPLAY)) { memcpy(&(ipv6_hdr_sendbuf->ip6_src),sendpkt_args->ipv6,sizeof(ipv6_hdr_sendbuf->ip6_src)); } else { memcpy(&(ipv6_hdr_sendbuf->ip6_dst),sendpkt_args->ipv6,sizeof(ipv6_hdr_sendbuf->ip6_dst)); } if(protocol == PROTO_TYPE_TCP) { sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_TCP,ntohs(ipv6_hdr->ip6_payload_len)); } else if(protocol == PROTO_TYPE_UDP) { sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_UDP,ntohs(ipv6_hdr->ip6_payload_len)); } // sendpacket_do_checksum((unsigned char*)sendbuf,IPPROTO_IP,sizeof(struct ip)); MESA_sendpacket_iplayer(thread_seq,sendbuf,sendbuf_len,dir); free(sendbuf); sendbuf = NULL; return 0; } int redirect_sendlog(const struct streaminfo* pstream,struct kni_pme_info* pmeinfo,int thread_seq,struct redirect_plugin_ex_data* dup_data,const void* a_packet) { struct kni_log log_msg; struct ip* ipv4_hdr = (struct ip*)a_packet; struct kni_ipv6_hdr* ipv6_hdr = (struct kni_ipv6_hdr*)a_packet; char content[1024]={0}; char orginal_ip[INET6_ADDRSTRLEN]={0}; if(pmeinfo->redirect_info.nat_type ==REDIRECT_SNAT_TYPE) { if(dup_data->addr_type == 4) { inet_ntop(AF_INET, (void *)&((ipv4_hdr->ip_src).s_addr), orginal_ip, INET_ADDRSTRLEN); } else { inet_ntop(AF_INET, (void *)&(ipv6_hdr->ip6_src), orginal_ip, INET6_ADDRSTRLEN); } } else if(pmeinfo->redirect_info.nat_type ==REDIRECT_DNAT_TYPE) { if(dup_data->addr_type == 4) { inet_ntop(AF_INET, (void *)&((ipv4_hdr->ip_dst).s_addr), orginal_ip, INET_ADDRSTRLEN); } else { inet_ntop(AF_INET, (void *)&(ipv6_hdr->ip6_dst), orginal_ip, INET6_ADDRSTRLEN); } } sprintf(content,"%s->%s",orginal_ip,dup_data->spoofing_ip); log_msg.stream = pstream; log_msg.result = pmeinfo->maat_result; log_msg.result_num = pmeinfo->maat_result_num; kni_send_log(&log_msg,(char*)"redirect",content); kni_log_debug(RLOG_LV_DEBUG,(char*)"redirect",a_packet,"process_redirect_pending(),%s",content); kni_filestate2_set(thread_seq,FS_REDIRECT,0,1); return 0; } char process_redirect_pending(const struct streaminfo* pstream,struct kni_pme_info* pmeinfo,int thread_seq,const void* a_packet,int protocol,unsigned char dir) { char ret=APP_STATE_FAWPKT|APP_STATE_DROPME; int result=0; struct in_addr ipv4_addr; struct in6_addr ipv6_addr; struct redirect_plugin_ex_data* dup_data=NULL; struct redirect_serdef_info redirect_args; memset(&redirect_args,0,sizeof(redirect_args)); //get service_defined // result=sscanf(pmeinfo->service_defined,"spoofing_ip_pool=%s;nat_type=%s",redirect_args.ip_pool,redirect_args.nat_type); result=redirect_get_service_define(pmeinfo->service_defined,pmeinfo->ser_def_len,&redirect_args); if(result < 0) { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","redirect_get_service_define() error,cfg_id:%d,service_def:%s",pmeinfo->cfg_id,pmeinfo->service_defined); return ret; } //get dup_data dup_data=(struct redirect_plugin_ex_data*)Maat_plugin_get_EX_data(g_kni_maatinfo.maat_feather,g_kni_maatinfo.tableid_spoofing_ip,redirect_args.ip_pool); if(dup_data == NULL) { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","Maat_plugin_get_EX_data() get NULL,key:%s!",redirect_args.ip_pool); return ret; } //set pmeinfo->redirect_info if(memcmp(redirect_args.nat_type,"snat",strlen("snat")) == 0) { pmeinfo->redirect_info.nat_type=REDIRECT_SNAT_TYPE; } else if(memcmp(redirect_args.nat_type,"dnat",strlen("dnat")) == 0) { pmeinfo->redirect_info.nat_type=REDIRECT_DNAT_TYPE; } else { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","nat_type:%s,error!",redirect_args.nat_type); return ret; } pmeinfo->redirect_info.addr_type=dup_data->addr_type; if(pmeinfo->redirect_info.addr_type == 4) { inet_pton(AF_INET,dup_data->spoofing_ip,&ipv4_addr); pmeinfo->redirect_info.ipv4 = ipv4_addr.s_addr; redirect_sendpkt_v4(protocol,dir,thread_seq,(struct ip*)a_packet,&(pmeinfo->redirect_info)); } else if(pmeinfo->redirect_info.addr_type == 6) { inet_pton(AF_INET6,dup_data->spoofing_ip,&ipv6_addr); memcpy(pmeinfo->redirect_info.ipv6 ,&(ipv6_addr),sizeof(pmeinfo->redirect_info.ipv6)); redirect_sendpkt_v6(protocol,dir,thread_seq,(struct kni_ipv6_hdr*)a_packet,&pmeinfo->redirect_info); } else { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","addr_type:%d,error!",pmeinfo->redirect_info.addr_type); return ret; } //send_log redirect_sendlog(pstream,pmeinfo,thread_seq,dup_data,a_packet); //add htable result=redirect_add_htable(pmeinfo->redirect_info.addr_type,pmeinfo,thread_seq,a_packet,protocol); if(result < 0) { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","redirect_add_htable() error,ret:%d",result); return ret; } return APP_STATE_GIVEME|APP_STATE_DROPPKT; } char process_redirect_data(const struct streaminfo* pstream,struct kni_pme_info* pmeinfo,int thread_seq,const void* a_packet,int protocol,unsigned char dir) { char ret=APP_STATE_GIVEME|APP_STATE_DROPPKT; if(pmeinfo->redirect_info.addr_type==4) { redirect_sendpkt_v4(protocol,dir,thread_seq,(struct ip*)a_packet,&pmeinfo->redirect_info); } else if(pmeinfo->redirect_info.addr_type==6) { redirect_sendpkt_v6(protocol,dir,thread_seq,(struct kni_ipv6_hdr*)a_packet,&pmeinfo->redirect_info); } else { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,(char*)"redirect","process_redirect_data() error,addr_type:%d",pmeinfo->redirect_info.addr_type); return APP_STATE_DROPPKT|APP_STATE_DROPME; } return ret; } char process_redirect_close(const struct streaminfo* pstream,struct kni_pme_info* pmeinfo,int thread_seq,const void* a_packet,int protocol,unsigned char dir) { char ret=APP_STATE_GIVEME|APP_STATE_DROPPKT; if(a_packet != NULL) { process_redirect_data(pstream,pmeinfo,thread_seq,a_packet,protocol,dir); } redirect_del_htable(pmeinfo->redirect_htable_key,pmeinfo->redirect_key_len); free(pmeinfo->redirect_htable_key); pmeinfo->redirect_htable_key=NULL; return ret; } int kni_init_redirect_htable() { MESA_htable_create_args_t hash_frags; memset(&hash_frags,0,sizeof(hash_frags)); hash_frags.thread_safe=KNI_THREAD_SAFE; hash_frags.recursive=1; hash_frags.hash_slot_size=KNI_HTABLE_SIZE; hash_frags.max_elem_num=KNI_HTABLE_MAXNUM; hash_frags.eliminate_type=HASH_ELIMINATE_ALGO_FIFO; hash_frags.expire_time=0; hash_frags.key_comp=NULL; hash_frags.key2index=NULL; hash_frags.data_free=NULL; hash_frags.data_expire_with_condition=NULL; g_kni_structinfo.htable_redirect=MESA_htable_create(&hash_frags,sizeof(MESA_htable_create_args_t)); if(g_kni_structinfo.htable_redirect==NULL) { MESA_handle_runtime_log(g_kni_comminfo.logger, RLOG_LV_FATAL,KNI_MODULE_INIT,"htable_redirect MESA_htable_create() error!"); return -1; } return 0; }