📄 fa.c
字号:
/* $Id: fa.c,v 1.266 2001/09/29 07:52:24 jm Exp $ * Foreign Agent * * 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 <stdlib.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/un.h>#include <sys/time.h>#include <string.h>#include <arpa/inet.h>#include <errno.h>#include <unistd.h>#include <syslog.h>#include <signal.h>#include <time.h>#include <assert.h>#include <getopt.h>#include <sys/uio.h>#include <fcntl.h>#include <netinet/ip_icmp.h>#include "fa.h"#include "fa_hash.h"#include "tunnel.h"#include "list.h"#include "hashtable.h"#include "agentadv.h"#include "rsa_dyn.h"#include "debug.h"#include "fixed_fd_zero.h"#include "dyn_ip.h"#include "agentapi.h"#include "util.h"#include "dyn_api.h"#include "agent_utils.h"#include "fa_mn_addr.h"#ifdef INCLUDE_IPAY#include "fa_ipay.h"#endifstruct sol_reply_list { struct timeval send; struct interface_entry *iface; struct sockaddr_ll to; struct in_addr addr; struct sol_reply_list *next;};struct dest_unreachable_data { char code; struct in_addr addr; int port;};/* sockets */#ifdef INCLUDE_IPAYstatic int ipay_sock = -1;#endifstatic int api_rw_sock = -1;static int api_ro_sock = -1;int fa_info_sock = -1;struct sockaddr_un fa_info_addr;/* dynamic data */static struct dynamics_fa_status stats;struct hashtable *tunnels_hash;struct bindingtable *bindings_hash;struct sockaddr_in upper_fa_addr;struct interface_entry *up_interface = NULL;struct msg_key *fa_public_key = NULL;struct fa_nai_ext *fa_nai = NULL;struct fa_config *config;struct binding_counters bcounters;struct challenge_ext **last_challenges;int last_challenges_pos;static char *program_name; /* the name this program was run with */static struct sol_reply_list *solrep_list = NULL;extern time_t fa_reg_next_try; /* fa_hash */static void init_sockets(void);static void init_config_data(void);static int check_delayed_solrep(struct timeval *now, struct timeval *next);static intfa_send_agent_adv(struct interface_entry *iface, struct sockaddr_ll *to, struct in_addr *dest);static void handle_dest_unreach(struct dest_unreachable_data *data);/* util.c - common command line arguments */extern int opt_foreground;extern char *opt_config;static voidclose_sockets(void){#ifdef INCLUDE_IPAY close_socket(ipay_sock); ipay_sock = -1;#endif if (api_rw_sock > -1) close_socket(api_rw_sock); api_rw_sock = -1; if (api_ro_sock > -1) close_socket(api_ro_sock); api_ro_sock = -1; if (fa_info_sock > -1) { info_send(FA_INFO_CLOSE, FA_INFO_OK, NULL, 0); close_socket(fa_info_sock); fa_info_sock = -1; } /* remove unix sockets */ if (config != NULL && config->fa_api_read_socket_path != NULL) unlink(config->fa_api_read_socket_path); if (config != NULL && config->fa_api_admin_socket_path != NULL) unlink(config->fa_api_admin_socket_path);}static voidflush_solrep_list(void){ struct sol_reply_list *prev, *tmp = solrep_list; while (tmp != NULL) { DEBUG(DEBUG_FLAG, "Flushed solrep entry\n"); prev = tmp; tmp = tmp->next; free(prev); } solrep_list = NULL;}/* Free all reserved memory. Destroy hashes. * Unlink the unix domain socket filenames. */static voidclean_up(int sig){ /* destroy all hashes and finish nicely */ DEBUG(DEBUG_FLAG, "clean_up(%d)\n", sig); syslog(LOG_INFO, "Foreign agent daemon cleaning up (signal %d)", sig); fa_register(FA_DEREGISTER); close_sockets(); unlink(FA_PID_FILE); /* Free hashtables */ if (bindings_hash != NULL) { DEBUG(DEBUG_FLAG, "Removing bindings..\n"); binding_iterator(bindings_hash, eliminate_binding_entry_force, &bcounters); binding_destroy(bindings_hash); } if (tunnels_hash != NULL) { DEBUG(DEBUG_FLAG, "Removing tunnels..\n"); tunnel_destroy_hash(tunnels_hash); } fa_hash_destroy(); expire_denial_records(1); flush_solrep_list(); /* remove possible entries from the solrep list */ cleanup_fa_config(config); if (config != NULL) free(config); if (fa_nai != NULL) { free(fa_nai); fa_nai = NULL; } closelog(); exit(sig);}static struct interface_entry *get_iface(int ifindex){ struct interface_entry *iface; struct node *node; for (node = list_get_first(&config->interfaces); node != NULL; node = list_get_next(node)) { iface = (struct interface_entry *) node; if (iface->if_index == ifindex) return iface; } return NULL;}/* Update the binding iface pointers after configuration reload. * old_if_index is used to get the new pointer as the interface index does not * change */static intreload_binding_iter(struct node *node, void *data){ struct bindingentry *binding; struct tunnel_data *t_data; struct unconfirmed_request *unc; binding = (struct bindingentry *) node; t_data = (struct tunnel_data *) binding->data; assert(t_data != NULL); if (t_data->info.iface != NULL) { t_data->info.iface = get_iface(t_data->old_if_index); if (t_data->info.iface == NULL) { LOG2(LOG_ALERT, "Interface disappeared during SIGHUP " "processing\n"); /* drop the binding and continue operation */ remove_binding_tunnels(binding, tunnels_hash, &bcounters); } } unc = t_data->unc_req; while (unc != NULL) { unc->info.iface = get_iface(unc->old_if_index); if (unc->info.iface == NULL) { LOG2(LOG_ALERT, "Interface disappeared during SIGHUP " "processing\n"); /* drop the unconfirmed requests and continue * operation */ free_unconfirmed_data(t_data->unc_req); t_data->unc_req = NULL; break; } unc = unc->next; } return 1;}/* Update the interface pointers in bindings and unconfirmed requests */static voidreload_interface_pointers(void){ binding_iterator(bindings_hash, reload_binding_iter, NULL);}static voidreload_config(int sig){ int i; LOG2(LOG_INFO, "Reloading configuration\n"); for (i = 0; i < config->challenge_window; i++) { if (last_challenges[i] != NULL) free(last_challenges[i]); } free(last_challenges); last_challenges = NULL; /* remove possible entries from the solrep list; this needs to be done * because the iface pointers change during configuration reloading */ flush_solrep_list(); cleanup_fa_config(config); if (load_fa(config, program_name, opt_config == NULL ? FA_GLOBAL_CONF_FILE : opt_config) == FALSE) { LOG2(LOG_ALERT, "Reloading configuration failed\n"); clean_up(-1); } close_sockets(); closelog(); init_sockets(); init_config_data(); reload_interface_pointers();}/* search for spi/addr/agent_type match from the FA SPI list * if spi=0, accept any spi * if agent_type=0, accept any type * Returns: pointer to the SPI entry or NULL on failure */struct fa_spi_entry *get_fa_spi(int spi, struct in_addr addr, int agent_type){ struct fa_spi_entry *s; struct node *node; /* FIX: this might be optimized by using a hash table index by the * address field */ for (node = list_get_first(&config->fa_spi_list); node != NULL; node = list_get_next(node)) { s = (struct fa_spi_entry *) node; if ((spi == 0 || s->spi == spi) && s->addr.s_addr == addr.s_addr && (agent_type == 0 || agent_type == s->agent_type)) { return s; } } return NULL;}/** * binding_get_tunnels_iter: * @node: pointer to bindingentry * @data: pointer to the message to be returned to the API caller * * Called from handle_api on an API_GET_TUNNELS request. This function is * called for each existing binding in the bindings list. * * Returns: * 1 (to continue iteration loop) */static intbinding_get_tunnels_iter(struct node *node, void *data){ struct bindingentry *binding = (struct bindingentry *) node; struct api_msg *api_msg = (struct api_msg *) data; dyn_tunnel_id id; if (api_msg->length + sizeof(dyn_tunnel_id) > API_DATA_SIZE) { DEBUG(DEBUG_FLAG, "API message overflow - skipping rest of " "the bindings\n"); return 0; } id.mn_addr.s_addr = binding->mn_addr.s_addr; id.ha_addr.s_addr = binding->ha_addr.s_addr; id.priv_ha = binding->priv_ha; memcpy((char *) api_msg->params + api_msg->length, &id, sizeof(dyn_tunnel_id)); api_msg->length += sizeof(dyn_tunnel_id); return 1;}/* Handle api call from socket sock. If it is privileged, the admin * is set nonzero. */static voidhandle_api(int sock, int admin){ struct sockaddr_un addr; socklen_t addrlen; struct api_msg recv_msg; struct api_msg send_msg; struct bindingentry *binding; struct dynamics_tunnel_info *info; struct dynamics_fa_status *status; char *path; int flags; dyn_tunnel_id *tid; struct in_addr tmp_addr; struct bindingkey bkey; DEBUG(DEBUG_FLAG, "receive from api\n"); addrlen = sizeof(addr); /* set size of address buffer */ memset(&addr, 0, addrlen); /* zero the address */ if (api_receive(sock, &addr, &addrlen, &recv_msg) < 0) { LOG2(LOG_ERR, "api receive: %s\n", strerror(errno)); return; } if (recv_msg.type != API_CALL_MSG) return; send_msg.code = API_UNDEFINED; send_msg.length = 0; if (admin) stats.apicalls_admin++; else stats.apicalls_read++; switch (recv_msg.code) { case API_GET_TUNNELS: DEBUG(DEBUG_FLAG, "Received API_GET_TUNNELS\n"); if (recv_msg.length != 0) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } if (binding_iterator(bindings_hash, binding_get_tunnels_iter, &send_msg)) send_msg.code = API_SUCCESS; else send_msg.code = API_INSUFFICIENT_SPACE; break; case API_GET_TUNNEL_INFO: DEBUG(DEBUG_FLAG, "Received API_GET_TUNNEL_INFO\n"); if (recv_msg.length != sizeof(dyn_tunnel_id)) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } tid = (dyn_tunnel_id *) recv_msg.params; UNALIGNED_(&tmp_addr, &tid->ha_addr); if (tmp_addr.s_addr == 0) { UNALIGNED_(&tmp_addr, &tid->mn_addr); binding = binding_fetch2(bindings_hash, &tmp_addr); } else { UNALIGNED_(&bkey.mn_addr, &tid->mn_addr); UNALIGNED_(&bkey.ha_addr, &tid->ha_addr); UNALIGNED_(&bkey.priv_ha, &tid->priv_ha); binding = binding_fetch(bindings_hash, &bkey); } if (!binding) { DEBUG(DEBUG_FLAG, "\tbinding not found\n"); send_msg.code = API_FAILED; break; } info = (struct dynamics_tunnel_info *) send_msg.params; memset(info, 0, sizeof(struct dynamics_tunnel_info)); UNALIGNED_(&info->id.mn_addr, &binding->mn_addr); UNALIGNED_(&info->id.ha_addr, &binding->ha_addr); UNALIGNED_(&info->mn_addr, &binding->mn_addr); UNALIGNED_(&info->co_addr, &binding->lower_addr); UNALIGNED_(&info->ha_addr, &binding->ha_addr); UNALIGNED_(&info->priv_ha, &binding->priv_ha); if (binding->data != NULL) { struct tunnel_data *t_data = binding->data; UNALIGNED_(&info->confirmed, &t_data->confirmed); } UNALIGNED_(&info->expiration_time, &binding->exp_time); UNALIGNED_(&info->creation_time, &binding->create_time); UNALIGNED_(&info->refresh_time, &binding->mod_time); UNALIGNED_(&info->last_timestamp[0], &binding->id[0]); UNALIGNED_(&info->last_timestamp[1], &binding->id[1]); UNALIGNED_(&info->spi, &binding->spi); UNALIGNED_(&info->timeout, &binding->timeout); send_msg.code = API_SUCCESS; send_msg.length = sizeof(struct dynamics_tunnel_info); break; case API_DESTROY_TUNNEL: DEBUG(DEBUG_FLAG, "Received API_DESTROY_TUNNEL\n"); if (!admin) { send_msg.code = API_NOT_PERMITTED; break; } if (recv_msg.length != sizeof(dyn_tunnel_id)) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } tid = (dyn_tunnel_id *) recv_msg.params; if (tid->ha_addr.s_addr == 0) binding = binding_fetch2(bindings_hash, &tid->mn_addr); else { bkey.mn_addr = tid->mn_addr; bkey.ha_addr = tid->ha_addr; bkey.priv_ha = tid->priv_ha; binding = binding_fetch(bindings_hash, &bkey); } if (!binding) { send_msg.code = API_FAILED; break; } eliminate_binding_entry((struct node *) binding, &bcounters); send_msg.length = 0; send_msg.code = API_SUCCESS; break; case API_GET_STATUS: DEBUG(DEBUG_FLAG, "Received API_GET_STATUS\n"); if (recv_msg.length != 0) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } status = (struct dynamics_fa_status *) send_msg.params; memcpy(status, &stats, sizeof(struct dynamics_fa_status)); UNALIGNED_(&status->tunnel_count, &bcounters.bindingcount); UNALIGNED_(&status->pending_count, &bcounters.pendingcount); send_msg.length = sizeof(struct dynamics_fa_status); send_msg.code = API_SUCCESS; break; case API_ATTACH_INFO_SOCKET: if (!admin) { send_msg.code = API_NOT_PERMITTED; break; } if (recv_msg.length < 1 || recv_msg.length >= sizeof(fa_info_addr.sun_path)) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } if (fa_info_sock > -1) { info_send(FA_INFO_CLOSE, FA_INFO_OK, NULL, 0); close_socket(fa_info_sock); } path = (char *) recv_msg.params; path[recv_msg.length - 1] = '\0'; DEBUG(DEBUG_FLAG, "API attach info socket[%s]\n", path); fa_info_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (fa_info_sock < 0) { DEBUG(DEBUG_FLAG, "\tfailed: %s\n", strerror(errno)); send_msg.code = API_FAILED; break; } /* Set non-blocking */ flags = fcntl(fa_info_sock, F_GETFL, 0); if (flags == -1 || fcntl(fa_info_sock, F_SETFL, flags | O_NDELAY) == -1) { DEBUG(DEBUG_FLAG, "\tfcntl F_SETFL failed: %s\n", strerror(errno)); close(fa_info_sock); fa_info_sock = -1; send_msg.code = API_FAILED; break; } memset(&fa_info_addr, 0, sizeof(fa_info_addr)); fa_info_addr.sun_family = AF_LOCAL; dynamics_strlcpy(fa_info_addr.sun_path, path, recv_msg.length); send_msg.code = API_SUCCESS; break; case API_DETACH_INFO_SOCKET: if (!admin) { send_msg.code = API_NOT_PERMITTED; break; } DEBUG(DEBUG_FLAG, "API detach info socket - "); if (fa_info_sock < 0) { DEBUG(DEBUG_FLAG, "failed\n"); send_msg.code = API_FAILED; break; } info_send(FA_INFO_CLOSE, FA_INFO_OK, NULL, 0); close_socket(fa_info_sock); fa_info_sock = -1; send_msg.code = API_SUCCESS; break; case API_SEND_ADV: { /** * Now the only parameter we use is ifindex * but i think we should expand it to support * direct (send to mac addr) advertisements. * - roger@lifix.fi */ struct interface_entry *iface; struct dyn_param_adv *adv; if (recv_msg.length != sizeof(struct dyn_param_adv) ) { send_msg.code = API_ILLEGAL_PARAMETERS; break; } adv = (struct dyn_param_adv *) recv_msg.params; if ( (iface = get_iface(adv->ifindex) ) == NULL ) { send_msg.code = API_FAILED; break; } fa_send_agent_adv(iface, NULL, NULL); send_msg.length = 0; send_msg.code = API_SUCCESS; break; } default: /* just send a message indicating that function is not * supported. No need to confirm that message gets through. */ LOG2(LOG_ERR, "Received unknown API command: %d\n", recv_msg.code); send_msg.code = API_NOT_SUPPORTED;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -