📄 mn.c
字号:
/* $Id: mn.c,v 1.239 2001/10/20 14:44:19 jm Exp $ * Mobile Node - state machine and main loop * * Dynamic hierarchial IP tunnel * Copyright (C) 1998-2001, Dynamics group * * This 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. See README and COPYING for * more details. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <string.h>#include <stdio.h>#include <arpa/inet.h>#include <stdlib.h>#include <time.h>#include <assert.h>#include <errno.h>#include <unistd.h>#include "agentapi.h"#include "debug.h"#include "util.h"#include "dyn_ip.h"#include "mn.h"#ifndef DYN_TARGET_WINDOWS#include "monitor.h"#endif /* DYN_TARGET_WINDOWS */#include "fixed_fd_zero.h"#ifdef INCLUDE_IPAY#include "mn_ipay.h"#endifextern int real_tunnel_up; /* from mn_tunnel */struct mn_data mn;struct mn_config config; /* configuration information *//* Timers containing ending time for the timer */struct timeval timers[TIMER_COUNT];char *program_name;/* * Determine the timeout value in seconds for a solicitation request. * Parameters: * counter - pointer to variable holding information, * how many requests have been sent. * Returns: timeout in seconds or zero when it's time to go passive mode. */static int get_solicitation_interval(int *counter){ static int intervals[] = SOLICITATION_INTERVALS; int t; ASSERT(counter != NULL); ASSERT(sizeof(intervals) > 0); if (*counter < 0) *counter = 0; if (*counter >= sizeof(intervals) / sizeof(int)) t = 0; else t = intervals[*counter]; (*counter)++; return t;}/* * Due to the unsuccessful registration to the FA, degrade its priority. */int degrade_current_fa_priority(void){ if (mn.current_adv == NULL) return 1; if (mn.current_adv->prio_degrade_percent == 0) { mn.current_adv->prio_degrade_percent = DEFAULT_PRIO_DEGRADE_INITIAL; } else { mn.current_adv->prio_degrade_percent *= DEFAULT_PRIO_DEGRADE_FACTOR; } if (mn.current_adv->prio_degrade_percent > 100) { mn.current_adv->prio_degrade_percent = 100; } DEBUG(DEBUG_INFO, "degrade_current_fa_priority: %d\n", mn.current_adv->prio_degrade_percent); return 0;}/* * Set reregistration time. Binary exponential backoff is used on * reregistrations. Maximum reregistration time is limited with * NORMAL_REREGISTRATION_TIME. At least half of the lifetime is * allowed to pass before registration is initiated. */static void set_reregistration_time(int total_lifetime){ int rt; /* remaining lifetime */ struct timeval expire_time, now; struct fa_spi_entry *fa_spi; expire_time = timers[TIMER_LIFETIME]; gettimeofday(&now, NULL); DEBUG(DEBUG_TIMERS, "set_reregistration_time(%i) - now=%li.%06li\n", total_lifetime, now.tv_sec, now.tv_usec); DEBUG(DEBUG_TIMERS, "\tTIMER_LIFETIME: %li.%06li\n", timers[TIMER_LIFETIME].tv_sec, timers[TIMER_LIFETIME].tv_usec); if (config.mn_ha_key_timestamp != 0 && config.mn_ha_key_lifetime != 0 && expire_time.tv_sec > config.mn_ha_key_timestamp + config.mn_ha_key_lifetime) { expire_time.tv_sec = config.mn_ha_key_timestamp + config.mn_ha_key_lifetime; expire_time.tv_usec = 0; mn.aaa_rekey = 1; DEBUG(DEBUG_TIMERS, "\tMN-HA key lifetime overriding at " "%li\n", expire_time.tv_sec); } fa_spi = get_fa_spi(0, mn.fa_addr); if (fa_spi != NULL && fa_spi->created > 0 && expire_time.tv_sec > fa_spi->created + fa_spi->lifetime) { expire_time.tv_sec = fa_spi->created + fa_spi->lifetime; expire_time.tv_usec = 0; mn.aaa_rekey = 1; DEBUG(DEBUG_TIMERS, "\tMN-FA key lifetime overriding at " "%li\n", expire_time.tv_sec); } rt = sec_passed(&expire_time); /* if total lifetime is set ( > 0), set initial reregistration * time, else use exponential backoff */ if (total_lifetime > 0) { /* bound to end of the lifetime */ if (rt > 2*NORMAL_REREGISTRATION_TIME) { timers[TIMER_REREG] = expire_time; timers[TIMER_REREG].tv_sec -= mn.aaa_rekey ? MN_AAA_REG_TIME : NORMAL_REREGISTRATION_TIME; } else { struct timeval rereg; now.tv_sec++; rereg = expire_time; rereg.tv_sec -= mn.aaa_rekey ? MN_AAA_REG_TIME : total_lifetime / 2; if (cmp_timeval(&rereg, &now) > 0) timers[TIMER_REREG] = rereg; else timers[TIMER_REREG] = now; } if (mn.current_adv == NULL) { LOG2(LOG_ALERT, "set_registration_time - current_adv " "== NULL\n"); mn.HA_reg_retry_time = MIN_REGISTRATION_TIME; } else { DEBUG(DEBUG_TIMERS, "FA[%s] reg_retry_time = %i\n", inet_ntoa(mn.current_adv->addr), MIN_REGISTRATION_TIME); mn.current_adv->reg_retry_time = MIN_REGISTRATION_TIME; } } else { if (mn.current_adv == NULL) { LOG2(LOG_ALERT, "set_registration_time - current_adv " "== NULL\n"); if (mn.HA_reg_retry_time == 0) mn.HA_reg_retry_time = MIN_REGISTRATION_TIME; else mn.HA_reg_retry_time *= 2; timers[TIMER_REREG].tv_sec += mn.HA_reg_retry_time; } else {#ifndef NO_PRIO_DEGRADE /* degrade FA priority */ degrade_current_fa_priority();#endif /* NO_PRIO_DEGRADE */ DEBUG(DEBUG_TIMERS, "FA[%s] reg_retry_time: %i => ", inet_ntoa(mn.current_adv->addr), mn.current_adv->reg_retry_time); if (mn.current_adv->reg_retry_time == 0) mn.current_adv->reg_retry_time = MIN_REGISTRATION_TIME; else mn.current_adv->reg_retry_time *= 2; DEBUG(DEBUG_TIMERS, "%i\n", mn.current_adv->reg_retry_time); timers[TIMER_REREG].tv_sec += mn.current_adv->reg_retry_time; } }}/* Select next timer to timeout and place timeout to *tv. * Returns non-zero if timeout is valid otherwise 0. */static int get_next_timeout(struct timeval *tv){ struct timeval *t, now; t = &timers[TIMER_GEN]; if (config.wlan_ap_poll_interval > -1 && timerisset(&timers[TIMER_WLAN_AP_POLL]) && (!timerisset(t) || cmp_timeval(&timers[TIMER_WLAN_AP_POLL], t) < 0)) t = &timers[TIMER_WLAN_AP_POLL]; if (config.solicitation_interval > -1 && timerisset(&timers[TIMER_SOLICITATION]) && (!timerisset(t) || cmp_timeval(&timers[TIMER_SOLICITATION], t) < 0)) t = &timers[TIMER_SOLICITATION]; if (!timerisset(t)) return 0; /* check if the current agentadv expires before the TIMER_GEN */ if (mn.current_adv && mn.expire_check && cmp_timeval(&mn.current_adv->expire, t) < 0) { mn.expire_check = 0; t = &mn.current_adv->expire; } gettimeofday(&now, NULL); if (now.tv_usec > t->tv_usec) { tv->tv_sec = t->tv_sec - now.tv_sec - 1; tv->tv_usec = 1000000 + t->tv_usec - now.tv_usec; } else { tv->tv_sec = t->tv_sec - now.tv_sec; tv->tv_usec = t->tv_usec - now.tv_usec; } if (tv->tv_sec < 0) timerclear(tv); return 1;}/* State change functions * Same functions are used for entering the for the state first time and * retrying after timeout. If state is already set, timeout processing is done. *//* Just listen to agent advertisements and switch to request_tunnel after one * is heard. */void passive_find(void){ mn.state = MN_PASSIVE_FIND; DEBUG(DEBUG_STATES, "State: Passive find\n");}static void send_solicitations(struct mn_data *mn){ int i; struct timeval now; gettimeofday(&now, NULL); for (i = 0; i < MAX_INTERFACES; i++) { if (mn->iface[i].s > -1) { if ((now.tv_sec < mn->iface[i].last_solicitation.tv_sec || !timerisset(&mn->iface[i].last_solicitation) || usec_passed(&mn->iface[i].last_solicitation, &now) > MIN_SOLICITATION_DELAY)) { send_agent_solicitation(mn->iface[i].s); mn->iface[i].last_solicitation = now; } else { DEBUG(DEBUG_TIMERS, "Too frequent agent " "solicitations (dev=%s) - skipping\n", mn->iface[i].device); } } }}/** * find_agent: * @entry - STATE_INIT to move to this state * STATE_TIMEOUT to indicate timeout in this state, * ie. we haven't received an agent adv to our last sent * solicitation * * Enter find agent state * */void find_agent(int entry){ int t, chg; static int retry_time = 0; /* time in seconds between retries of agent * solicitations */ DEBUG(DEBUG_INFO, "find_agent(%i)\n", entry); mn.tunnel_up = 0; if (mn.device_count == 0) { passive_find(); return; } if (mn.tunnel_mode == API_TUNNEL_FULL_HA) { DEBUG(DEBUG_INFO, "find_agent: trying to register directly to HA\n"); if (config.ha_ip_addr.s_addr == 0 && config.home_net_addr_plen > -1) { /* dynamic HA address resolution */ mn.fa_addr.s_addr = config.home_net_subnet_bc.s_addr; } else mn.fa_addr.s_addr = config.ha_ip_addr.s_addr; mn.co_addr.s_addr = mn.local_addr.s_addr; if (mn.current_adv != NULL) { mn.current_adv->in_use = 0; mn.current_adv = NULL; } request_tunnel(STATE_INIT, 0, 0); return; } /* try to find the best FA from the previously received agentadvs; * if a valid entry is found, use it; otherwise send agent * solicitation */ chg = get_fa(NULL); if (chg != FA_GET_NO) { DEBUG(DEBUG_INFO, "find_agent: found FA from stored agentadv " "data - FA=%s, HFA=", inet_ntoa(mn.fa_addr)); DEBUG(DEBUG_INFO, "%s\n", inet_ntoa(mn.co_addr)); request_tunnel(STATE_INIT, 0, 0); return; } if (entry == STATE_INIT) { mn.state = MN_FIND_AGENT; retry_time = 0; mn.req_lifetime = config.mn_default_tunnel_lifetime; } t = get_solicitation_interval(&retry_time); if (t == 0) { passive_find(); return; } /* send agent solicitation to all the active interfaces */ send_solicitations(&mn); DEBUG(DEBUG_TIMERS, "find_agent - TIMER_GEN: set now+%i sec\n", t); gettimeofday(&timers[TIMER_GEN], NULL); timers[TIMER_GEN].tv_sec += t; add_usecs(&timers[TIMER_GEN], get_rand32() % MAX_RANDOM_SOLICITATION_DELAY); DEBUG(DEBUG_STATES, "State: Find agent\n");}/** * rqeuest_tunnel: * @entry: STATE_INIT to move to this state, * STATE_TIMEOUT to indicate timeout in this state, * i.e. no reply for last request within retry time * * Enter request tunnel state */void request_tunnel(int entry, int forced, int check_timer){ int retry_time; DEBUG(DEBUG_INFO, "request_tunnel(%i, %i, %i)\n", entry, forced, check_timer); if (mn.current_adv == NULL) { retry_time = mn.HA_reg_retry_time; DEBUG(DEBUG_INFO, "No current agent advertisement " "when requesting tunnel (direct tunnel to HA?)\n"); } else retry_time = mn.current_adv->reg_retry_time; if (check_timer && retry_time > 1) { struct timeval now; gettimeofday(&now, NULL); if (timerisset(&timers[TIMER_GEN]) && cmp_timeval(&now, &timers[TIMER_GEN]) < 0) { DEBUG(DEBUG_TIMERS, "request_tunnel: TIMER_GEN not " "yet reached\n"); return; } } if (config.enable_fa_decapsulation && mn.current_adv != NULL) add_fa_host_route(mn.current_adv->ifname, mn.agentadv, mn.current_adv->ifindex, mn.fa_addr); if (entry == STATE_INIT) { mn.state = MN_REQUEST_TUNNEL; if (!forced) mn.tunnel_up = 0; /* if last registration was sent less than a second ago, wait a second before sending the request */ if (usec_passed(&mn.last_reg_send_time, NULL) < MIN_REGISTRATION_DELAY) { DEBUG(DEBUG_INFO, "Too frequent registration request " "- delaying\n"); DEBUG(DEBUG_TIMERS, "request_tunnel: TIMER_GEN: set " "now+1 sec (too freq.)\n"); gettimeofday(&timers[TIMER_GEN], NULL); timers[TIMER_GEN].tv_sec++; return; } } else if (retry_time > MAX_REGISTRATION_TIME) { /* STATE_TIMEOUT */ if (config.use_hadisc) { DEBUG(DEBUG_INFO, "HA does not reply - try to discover" " another HA\n"); mn.info_str = "restarting HA discovery"; config.ha_ip_addr.s_addr = 0; if (mn.current_adv != NULL) mn.current_adv->reg_retry_time = MIN_REGISTRATION_TIME; else mn.HA_reg_retry_time = MIN_REGISTRATION_TIME; } else { /* give up, switch to find agent */ if (forced) { DEBUG(DEBUG_INFO, "Could not confirm tunnel.\n"); reply_waiting_api(API_FAILED, NULL, 0); } else { DEBUG(DEBUG_INFO, "Could not register.\n"); } find_agent(STATE_INIT); return; } } else if (mn.current_adv != NULL) { /* STATE_TIMEOUT */#ifndef NO_PRIO_DEGRADE /* degrade the FA priority */ degrade_current_fa_priority();#endif /* NO_PRIO_DEGRADE */ DEBUG(DEBUG_TIMERS, "FA[%s] reg_retry_time: %i => ", inet_ntoa(mn.current_adv->addr), mn.current_adv->reg_retry_time); if (mn.current_adv->reg_retry_time == 0) mn.current_adv->reg_retry_time = MIN_REGISTRATION_TIME; else mn.current_adv->reg_retry_time *= 2; DEBUG(DEBUG_TIMERS, "%i\n", mn.current_adv->reg_retry_time); } else { if (mn.HA_reg_retry_time == 0) mn.HA_reg_retry_time = MIN_REGISTRATION_TIME; else mn.HA_reg_retry_time *= 2; } if (send_registration(forced ? REG_REREG : REG_CONNECT) < 0) { DEBUG(DEBUG_INFO, "Registration failed for this FA - trying to" " find a new one\n"); mn.warn_str = "Registration failed for this FA, trying to find" " a new one"; find_agent(STATE_INIT); return; } gettimeofday(&timers[TIMER_GEN], NULL); if (mn.current_adv != NULL) { if (mn.current_adv->reg_retry_time < MIN_REGISTRATION_TIME) mn.current_adv->reg_retry_time = MIN_REGISTRATION_TIME; retry_time = mn.current_adv->reg_retry_time; } else { if (mn.HA_reg_retry_time < MIN_REGISTRATION_TIME) mn.HA_reg_retry_time = MIN_REGISTRATION_TIME; retry_time = mn.HA_reg_retry_time; } DEBUG(DEBUG_TIMERS, "request_tunnel - TIMER_GEN: set now+%i sec\n", retry_time); timers[TIMER_GEN].tv_sec += retry_time; DEBUG(DEBUG_STATES, "State: Request tunnel%s\n", forced ? " (forced)" : "");}/* Enter connected state. * Paramters: * type: * CON_HA : connection was approved by HA * CON_FA : connection was approved by FA * lifetime: lifetime received in reply */void connected(int type, __u16 lifetime){ DEBUG(DEBUG_INFO, "connected(%i, %i)\n", type, lifetime); ASSERT(lifetime > 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -