📄 fa.c
字号:
* Schedule next wake up time. The next wake up time can be caused * by any of the following events: * - a regular Agent advertisement is due * - a binding has expired * - a delayed Solicitation reply is due * - FA registration is due * - delayed tunnel deletion is due */static intset_expr_timer(struct timeval *tv, struct timeval next_agentadv) { int expire_time, diff; struct timeval solrep; struct timeval now; gettimeofday(&now, NULL); if (timerisset(&next_agentadv)) { tv->tv_sec = next_agentadv.tv_sec - now.tv_sec ; tv->tv_usec = next_agentadv.tv_usec - now.tv_usec; if (tv->tv_usec <0) { tv->tv_sec--; tv->tv_usec += 1000000; } if (tv->tv_sec < 0) { tv->tv_sec = 0; tv->tv_usec = 0; } } else { tv->tv_sec = -1; tv->tv_usec = 0; } if ((expire_time = binding_nextexpiretime(bindings_hash)) >= 0) { DEBUG(DEBUG_FLAG2, "set_expr_timer: binding next expire %d\n", expire_time); if (tv->tv_sec < 0 || expire_time < tv->tv_sec) { tv->tv_sec = expire_time; tv->tv_usec = 0; } } solrep.tv_sec = 0; solrep.tv_usec = 0; check_delayed_solrep(&now, &solrep); if (solrep.tv_sec >= 0) { DEBUG(DEBUG_FLAG, "set_expr_timer: remaining delayed " "solicitation reply\n"); if (tv->tv_sec < 0 || solrep.tv_sec < tv->tv_sec || (solrep.tv_sec == tv->tv_sec && solrep.tv_usec < tv->tv_usec)) { tv->tv_sec = solrep.tv_sec; tv->tv_usec = solrep.tv_usec; } } diff = fa_reg_next_try - now.tv_sec; if (fa_reg_next_try > 0 && (tv->tv_sec == -1 || diff < tv->tv_sec)) tv->tv_sec = diff < 0 ? 0 : diff; if (tv->tv_sec == -1 && tunnel_delayed_exists(tunnels_hash)) tv->tv_sec = TUNNEL_DELAYED_CHECK_INTERVAL; return 0;}struct search_data { struct bindingentry *binding; struct unconfirmed_request *unc; struct in_addr addr;};static inthandle_dest_iter(struct node *node, void *data){ struct bindingentry *binding; struct tunnel_data *t_data; struct search_data *search; struct unconfirmed_request *unc; binding = (struct bindingentry *) node; if (binding == NULL || data == NULL) return 0; t_data = (struct tunnel_data *) binding->data; if (t_data == NULL) return 0; search = (struct search_data *) data; unc = t_data->unc_req; while (unc != NULL) { if (unc->ha_addr.s_addr == search->addr.s_addr) { search->binding = binding; search->unc = unc; return 0; } unc = unc->next; } return 1;}static voidhandle_dest_unreach(struct dest_unreachable_data *data){ struct search_data search; struct msg_extensions ext; struct reg_req req; int reply_code; struct tunnel_data *t_data; if (!config->highest_FA) return; if (data->port != -1 && data->port != htons(config->ha_udp_port)) return; search.binding = NULL; search.unc = NULL; search.addr = data->addr; binding_iterator(bindings_hash, handle_dest_iter, &search); if (search.unc == NULL) return; switch (data->code) { case ICMP_NET_UNREACH: reply_code = REGREP_HN_UNCREACHABLE_FA; break; case ICMP_HOST_UNREACH: reply_code = REGREP_HA_HOST_UNCREACHABLE_FA; break; case ICMP_PORT_UNREACH: reply_code = REGREP_HA_PORT_UNCREACHABLE_FA; break; default: reply_code = REGREP_HA_UNREACHABLE_FA; break; } DEBUG(DEBUG_FLAG, "handle_dest_unreach: found unconfirmed " "request that resulted in HA unreachable\n"); memset(&ext, 0, sizeof(ext)); ext.req = &req; memset(&req, 0, sizeof(req)); req.home_addr.s_addr = search.binding->mn_addr.s_addr; req.ha_addr.s_addr = search.unc->ha_addr.s_addr; req.id[0] = htonl(search.unc->id[0]); req.id[1] = htonl(search.unc->id[1]); send_failure_reply(reply_code, &ext, &search.unc->info, NULL, 0); /* no need to inform MN about pending request timing out after this * error reply */ t_data = (struct tunnel_data *) search.binding->data; if (t_data != NULL) t_data->pending_request = FALSE;}static intfa_send_agent_adv(struct interface_entry *iface, struct sockaddr_ll *to, struct in_addr *dest){ __u16 adv_opts; __u8 own_adv_opts; int ret; struct challenge_ext *challenge = NULL; /* Set Agent Advertisement data */ adv_opts = AGENT_ADV_FOREIGN_AGENT; if (config->reg_required) adv_opts |= AGENT_ADV_REGISTRATION_REQUIRED; if (config->enable_reverse_tunneling) adv_opts |= AGENT_ADV_BIDIR_TUNNELING; if (bcounters.bindingcount >= config->max_bindings || (config->max_pending > 0 && bcounters.pendingcount >= config->max_pending)) adv_opts |= AGENT_ADV_BUSY; own_adv_opts = 0; if (config->enable_triangle_tunneling) own_adv_opts |= AGENT_ADV_OWN_TRIANGLE_TUNNELING; set_agent_adv_data( config->fa_default_tunnel_lifetime, config->highest_fa_addr, iface->addr, adv_opts, own_adv_opts, iface->interval > 21845 ? 65535 : iface->interval * 3, config->hfa_pubkey_hash_len > 0 ? config->hfa_pubkey_hash : NULL, config->hfa_pubkey_hash_len); set_agent_adv_nai(fa_nai);#ifdef INCLUDE_IPAY set_agent_adv_ipay(config->timePrice, config->bytePrice);#endif if (config->enable_challenge_response) { challenge = create_challenge_ext(config, AGENT_ADV_CHALLENGE_EXT); set_agent_adv_challenge(challenge); } ret = send_agent_advertisement(iface->icmp_sock, (struct sockaddr *) to, dest, iface->if_index); if (ret < 0) { DEBUG(DEBUG_FLAG, "Sending agent advertisement to interface " "'%s' failed\n", iface->dev); } if (config->enable_challenge_response) { set_agent_adv_challenge(NULL); /* record the last challenge_window advertised challenges */ if (last_challenges[last_challenges_pos] != NULL) free(last_challenges[last_challenges_pos]); last_challenges[last_challenges_pos] = challenge; last_challenges_pos++; if (last_challenges_pos >= config->challenge_window) last_challenges_pos = 0; } return ret;}/* send_agent_advs: * * Send agent advertisements on interfaces. * * Fill in next_agentadv with the time when the next advertisement * is due. * * Updates iface.last_adv in the config.interfaces list. */static voidsend_agent_advs(struct timeval *next_agentadv){ struct interface_entry *iface; struct node *node; struct timeval now, next; unsigned int diff; int first = 1; /* Send ICMP Agent Advertisement message and save the timestamp */ gettimeofday(&now, NULL); next.tv_sec = 0; next.tv_usec = 0; for (node = list_get_first(&config->interfaces); node != NULL; node = list_get_next(node)) { iface = (struct interface_entry *) node; if (iface->agentadv != INTERFACE_AGENTADV_ALL) continue; if (now.tv_sec > iface->last_adv.tv_sec + iface->interval - 1) { if (!fa_send_agent_adv(iface, NULL, NULL)) stats.adv_sent++; iface->last_adv.tv_sec = now.tv_sec; iface->last_adv.tv_usec = now.tv_usec; } next.tv_sec = iface->last_adv.tv_sec + iface->interval; /* Add random delay */ next.tv_usec = iface->last_adv.tv_usec + (int) (MAX_ADV_DELAY * rand() / (RAND_MAX + 1.0)); if (next.tv_usec > 999999) { next.tv_sec++; next.tv_usec -= 1000000; } if (first || next.tv_sec < next_agentadv->tv_sec) { DEBUG(DEBUG_FLAG, "** "); *next_agentadv = next; } first = 0; /* for debuging */ diff = ((next.tv_sec - now.tv_sec) * 1000000 + next.tv_usec - now.tv_usec) / 1000; DEBUG(DEBUG_FLAG, "send_agent_advs: next agentadv: " "%ld.%ld diff = %d msec\n", next.tv_sec, next.tv_usec, diff); }}static intcheck_delayed_solrep(struct timeval *now, struct timeval *next){ struct sol_reply_list *list = solrep_list; unsigned int passed = 0; assert(next != NULL); while (list != NULL && cmp_timeval(now, &list->send) == 1) { DEBUG(DEBUG_FLAG, "Found delayed solicitation reply\n"); fa_send_agent_adv(list->iface, &list->to, &list->addr); solrep_list = list->next; free(list); list = solrep_list; } /* if any pending replys */ if (list != NULL) { passed = 1000000 * (list->send.tv_sec - now->tv_sec) + list->send.tv_usec - now->tv_usec; DEBUG(DEBUG_FLAG, "Pending agent sol reply. %u usecs\n", passed); set_usecs(next, passed); } else next->tv_sec = -1; return 0;}static intadd_agent_sol_reply(struct interface_entry *iface, struct sockaddr_ll *to, struct in_addr *addr){ struct sol_reply_list *new, *prev, *list = solrep_list; unsigned int usec_random = (int) (MAX_ADV_DELAY * rand() / (RAND_MAX + 1.0)); assert(iface != NULL); new = (struct sol_reply_list *) calloc(1, sizeof(struct sol_reply_list)); if (new == NULL) { DEBUG(DEBUG_FLAG, "add_agent_sol_reply: Out of memory\n"); return -1; } gettimeofday(&new->send, NULL); add_usecs(&new->send, usec_random); new->iface = iface; memcpy(&new->to, to, sizeof(*to)); new->addr.s_addr = addr->s_addr; if (list == NULL) { /* add first entry */ solrep_list = new; new->next = NULL; return 0; } /* Find proper place */ prev = NULL; while (list != NULL) { if(cmp_timeval(&list->send, &new->send) == 1) break; prev = list; list = list->next; } /* Add to the list */ if (prev == NULL) { /* add to the beginning */ new->next = solrep_list; solrep_list = new; } else { prev->next = new; new->next = list; } return 0;}static voidhandle_icmp(struct interface_entry *iface){ int r; struct sockaddr_ll from; struct in_addr to, fromaddr; r = check_icmp_sol(iface->icmp_sock, &from, &to, &fromaddr); if (r == 0 && iface->agentadv != INTERFACE_AGENTADV_NONE) { /* Agent solicitation */ add_agent_sol_reply(iface, &from, &fromaddr); }}static voidmain_loop(void){ fd_set set; struct timeval tv; int oldmask; struct interface_entry *iface; struct node *node; struct timeval next_agentadv; next_agentadv.tv_sec = 0; next_agentadv.tv_usec = 0; send_agent_advs(&next_agentadv); /* Main loop * Wait for incoming ICMP and UDP packets, and API calls. */ for (;;) { fa_register(FA_REGISTER); FD_ZERO(&set); for (node = list_get_first(&config->interfaces); node != NULL; node = list_get_next(node)) { iface = (struct interface_entry *) node; FD_SET(iface->udp_sock, &set); FD_SET(iface->udp_packet, &set); FD_SET(iface->icmp_sock, &set); }#ifdef INCLUDE_IPAY FD_SET(ipay_sock, &set);#endif if (api_rw_sock > -1) FD_SET(api_rw_sock, &set); if (api_ro_sock > -1) FD_SET(api_ro_sock, &set); /* Wake up when the next scheduled task timer expires or there * is something in the sockets. */ set_expr_timer(&tv, next_agentadv); DEBUG(DEBUG_FLAG2, "FA main: timer for select %ld.%ld\n", tv.tv_sec, tv.tv_usec); if (select(FD_SETSIZE, &set, NULL, NULL, tv.tv_sec == -1 ? NULL : &tv) < 0) { /* don't exit on e.g. SIGHUP */ if (errno != EINTR) { LOG2(LOG_ERR, "select error: %s\n", strerror(errno)); clean_up(1); } continue; } /* we don't want to reread the configuration file * while we are handling requests; hold the signal * until we are ready */ oldmask = sigblock(sigmask(SIGHUP)); /* Check if any of the bindings have expired. * Due to the binding module implementation this must be done * just before using binding_add in order to get the binding * module to current time. */ check_bindings(bindings_hash, tunnels_hash, &bcounters);#ifdef INCLUDE_IPAY if (FD_ISSET(ipay_sock, &set)) handle_ipay(ipay_sock);#endif if (api_rw_sock > -1 && FD_ISSET(api_rw_sock, &set)) { /* privileged API call */ handle_api(api_rw_sock, 1); } if (api_ro_sock > -1 && FD_ISSET(api_ro_sock, &set)) { /* unprivileged API call */ handle_api(api_ro_sock, 0); } for (node = list_get_first(&config->interfaces); node != NULL; node = list_get_next(node)) { iface = (struct interface_entry *) node; if (FD_ISSET(iface->udp_packet, &set)) { DEBUG(DEBUG_FLAG, "Got UDP message(%s, packet " "socket)\n", iface->dev); handle_reg_msg_packet(iface); } if (FD_ISSET(iface->udp_sock, &set)) { DEBUG(DEBUG_FLAG, "Got UDP message(%s)\n", iface->dev); handle_reg_msg(iface); } if (FD_ISSET(iface->icmp_sock, &set)) { handle_icmp(iface); } } if (timerisset(&next_agentadv)) send_agent_advs(&next_agentadv); tunnel_check_delayed(tunnels_hash, 0); fa_hash_expire(); expire_denial_records(0);#ifdef INCLUDE_IPAY ipay_send_statistics(ipay_sock);#endif sigsetmask(oldmask); }}static intfa_parse_command_line(int argc, char *argv[]){ int c, oindex = 0; static struct option long_options[] = { /* common arguments */ {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, {"debug", no_argument, NULL, 0}, {"fg", no_argument, NULL, 0}, {"config", required_argument, NULL, 'c'}, /* FA specific arguments */ /* end of arguments */ {0, 0, 0, 0} }; DEBUG(DEBUG_FLAG, "FA command line parsing\n"); while ((c = getopt_long(argc, argv, "+hv", long_options, &oindex)) != EOF) { switch (c) { case 'h': /* show FA specific parameters */ /* N/A */ exit(1); break; case '?': printf("Command line parsing failed - aborting\n"); exit(1); case 'v': case 'c': case 0: break; default: printf("?? getopt returned character code 0%o ??\n", c); exit(1); } } return 0;}intmain(int argc, char *argv[]){ program_name = parse_long_options(argc, argv, "foreign agent", PACKAGE, VERSION, dynamics_usage); fa_parse_command_line(argc, argv); config = malloc(sizeof(struct fa_config)); if (config == NULL) { fprintf(stderr, "Not enough memory for struct fa_config\n"); exit(1); } if (load_fa(config, program_name, opt_config == NULL ? FA_GLOBAL_CONF_FILE : opt_config) == FALSE) { fprintf(stderr, "Configuration file reading failed.\n"); exit(1); } check_kernel_support(CHECK_KERNEL_ADV_ROUTING | CHECK_KERNEL_IPIP | CHECK_KERNEL_NETLINK);#ifndef NO_SUID_CHECK_FOR_FA if (getuid()) { fprintf(stderr, "This program must be run by root.\n"); exit(1); }#endif /* These routines don't return any values. If something is wrong clean_up() is used */ init_sockets(); init_data(); init_config_data(); if (!opt_foreground && dynamics_fork_daemon() == -1) clean_up(1); dynamics_write_pid_file(FA_PID_FILE); syslog(LOG_INFO, "%s foreign agent daemon version %s started\n", PACKAGE, VERSION); main_loop(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -