📄 fa_request.c
字号:
static intmake_preliminary_data_path(struct bindingentry *binding, struct unconfirmed_request *unc, struct interface_entry *iface){ struct tunnel_data *t_data; struct tunnel *down, *up; /* Highest FA MUST NOT do this optimization or there may be some * security problems (data transfered before getting a reply from the * HA) */ if (config->highest_FA) return 0; t_data = binding->data; /* tunnel downwards (if FA decaps is used, this will be just a dummy * entry for the non-capsulated connection) */ down = tunnel_add(tunnels_hash, unc->info.src.sin_addr, binding->tun_dev, iface->addr, 1, unc->tunnel_type, unc->tunnel_key); if (down == NULL) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "tunnel_add failed (down)\n"); return 1; } up = tunnel_add(tunnels_hash, upper_fa_addr.sin_addr, t_data->ha_tun_dev, up_interface->addr, 1, t_data->up_type, t_data->up_key); if (up == NULL) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "tunnel_add failed (up)\n"); tunnel_delete_ptr(down, 0); return 1; } /* FIX-JM: what about force_route_dev? should it be != NULL when * FA decaps is used with encaps. del.? */ /* FIX-JM: what about force_reverse_dev? should it be != NULL when * MN decaps is used without encaps. del.? */ /* Route for HA->MN direction - this enables routing of downstream * packets to the MN. Note that this does not make the reverse * connection. */ if (tunnel_connect(up, down, binding->mn_addr, 0, NULL, NULL) < 0) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "tunnel_connect failed\n"); } /* With lazy tunnel deletion the tunnel will be up long enough. * If no reply is received, the tunnel is removed automatically. * Additionally the reference count will be ok. When reply is * received the tunnels are remade which means that the reference * counts are increased. */ if (tunnel_unconnect(up, down, binding->mn_addr, 0, 0, NULL, NULL) < 0) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "tunnel_unconnect failed\n"); } if (tunnel_delete_ptr(down, 0) == -1) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "lazy tunnel delete failed (down).\n"); } if (tunnel_delete_ptr(up, 0) == -1) { LOG2(LOG_WARNING, "make_preliminary_data_path: " "lazy tunnel delete failed (up).\n"); } return 0;}static int check_mn_challenge(struct tunnel_data *t_data, struct msg_extensions *ext){ int i, found = 0; /* check whether the MN has already used this challenge; if yes and * this is not a replay of request with same id, send denial reg. reply * with code REGREP_STALE_CHALLENGE_FA */ for (i = 0; i < 2 * config->challenge_window; i++) { if (equal_challenge(t_data->used_challenges[i], ext->challenge)) { found = 1; break; } } if (found && (i != t_data->used_challenges_pos || memcmp(ext->req->id, t_data->used_challenges_id, sizeof(ext->req->id)) != 0)) return REGREP_STALE_CHALLENGE_FA; /* check whether the used challenge is known */ found = 0; for (i = 0; i < config->challenge_window; i++) { if (equal_challenge(last_challenges[i], ext->challenge)) { found = 1; break; } } if (!found && equal_challenge(t_data->last_challenge, ext->challenge)) { /* let the MN use the same challenge only once by freeing the * stored challenge */ free(t_data->last_challenge); t_data->last_challenge = NULL; found = 1; } if (!found) return REGREP_UNKNOWN_CHALLENGE_FA; return 0;}/** * handle_request: * @ext: the request with extensions * @info: source of the request * @dst_addr: destination address of the request * @iface: interface that the request arrived on * @config: configuration file parameters * @ttl: TTL of the request * * Handle registration request. * * Returns: * 0 if successful, * 1 on error */inthandle_request(struct msg_extensions *ext, struct packet_from_info *info, struct fa_config *config){ struct bindingentry *binding; int auth_ok, ret = 0; int lower_mode_changed = 0; struct tunnel_data *t_data; struct unconfirmed_request *unc = NULL; struct bindingkey bkey; struct interface_entry *prev_iface; int iface_ok; DEBUG(DEBUG_FLAG, "Handling request from MN %s", inet_ntoa(ext->req->home_addr)); DEBUG(DEBUG_FLAG, " (%s:%d)\n", inet_ntoa(info->src.sin_addr), ntohs(info->src.sin_port)); if (validate_request(ext, info)) return 1; memcpy(&bkey.mn_addr, &(ext->req->home_addr), sizeof(bkey.mn_addr)); memcpy(&bkey.ha_addr, &(ext->req->ha_addr), sizeof(bkey.ha_addr)); if (ext->priv_ha != NULL) bkey.priv_ha = ntohl(ext->priv_ha->priv_ha); else bkey.priv_ha = 0; binding = binding_fetch(bindings_hash, &bkey); if (binding == NULL) { int type, added_addr = 0; DEBUG(DEBUG_FLAG, "No binding for MN => making new binding\n"); if (config->max_pending > 0 && bcounters.pendingcount >= config->max_pending) { LOG2(LOG_ALERT, "Too many pending registration " "requests - refusing new ones\n"); send_failure_reply(REGREP_NO_RESOURCES_FA, ext, info, NULL, 0); return 1; } if (config->enable_challenge_response && ext->challenge) { int i, found = 0; for (i = 0; i < config->challenge_window; i++) { if (equal_challenge(last_challenges[i], ext->challenge)) { found = 1; break; } } if (!found) { send_failure_reply(REGREP_UNKNOWN_CHALLENGE_FA, ext, info, NULL, 0); return 1; } } if (config->highest_FA && ext->priv_ha == NULL) type = MN_ADDR_TUNNEL_IPIP; else { type = mn_addr_add(&ext->req->home_addr); DEBUG(DEBUG_FLAG, "mn_addr_add(%s) => %i\n", inet_ntoa(ext->req->home_addr), type); added_addr = 1; } if (type != MN_ADDR_TUNNEL_ERROR) { int max_time = config->fa_default_tunnel_lifetime; if (config->delete_pending_after > 0 && config->delete_pending_after < max_time) max_time = config->delete_pending_after; binding = make_new_binding( bindings_hash, ext, max_time, &bcounters); } if (binding == NULL) { LOG2(LOG_WARNING, type > -2 ? "Could not make new binding\n" : "Out of resources while checking MN address"); send_failure_reply(REGREP_NO_RESOURCES_FA, ext, info, NULL, 0); if (added_addr) mn_addr_remove(&ext->req->home_addr, type); return 1; } t_data = (struct tunnel_data *) binding->data; if (type == MN_ADDR_TUNNEL_IPIP) { t_data->up_type = TUNNEL_IPIP; t_data->up_key = 0; } else { t_data->up_type = TUNNEL_GRE; t_data->up_key = type; } if (added_addr) t_data->mn_addr_added = 1; } else { check_unconfirmed_timeout(binding); t_data = (struct tunnel_data *) binding->data; if (config->enable_challenge_response && ext->challenge) { int ret = check_mn_challenge(t_data, ext); if (ret != 0) { send_failure_reply(ret, ext, info, NULL, 0); return 1; } } } if (ext->challenge) { int n = t_data->used_challenges_pos + 1; if (n >= 2 * config->challenge_window) n = 0; t_data->used_challenges_pos = n; if (t_data->used_challenges[n] != NULL) free(t_data->used_challenges[n]); t_data->used_challenges[n] = (struct challenge_ext *) malloc(GET_CHALLENGE_EXT_LEN(ext->challenge)); if (t_data->used_challenges[n] == NULL) { DEBUG(DEBUG_FLAG, "malloc failed for " "used_challenges\n"); } else { memcpy(t_data->used_challenges[n], ext->challenge, GET_CHALLENGE_EXT_LEN(ext->challenge)); } } auth_ok = FALSE; if (!t_data->confirmed) { DEBUG(DEBUG_FLAG, "\tunconfirmed binding\n"); if (check_resources()) { LOG2(LOG_ALERT, "FA: Not enough resources to accept " "registration\n"); send_failure_reply(REGREP_NO_RESOURCES_FA, ext, info, NULL, 0); return 1; } unc = add_unconfirmed_data(bindings_hash, &bcounters, binding, ext, info, config); } else { DEBUG(DEBUG_FLAG, "\tconfirmed binding\n"); if (ext->sk_auth != NULL) {#ifdef DEBUG_SIMULATED_TEST /* for debuging only - accept any sk_auth extension */ if (0) { }#else if (!auth_check_vendor(AUTH_ALG_MD5, binding->key, binding->keylen, (unsigned char *) ext->req, ext->sk_auth)) { DEBUG(DEBUG_FLAG, "handle_request: sk_auth not valid\n"); }#endif else if (ext->ext_dyn == NULL) { DEBUG(DEBUG_FLAG, "handle_request: missing ext_dyn\n"); } else if ((void *) ext->ext_dyn > (void *) ext->sk_auth) { DEBUG(DEBUG_FLAG, "handle_request: ext_dyn " "after sk_auth\n"); } else if (ntohl(ext->ext_dyn->seq) <= t_data->last_used_seq_num) { LOG2(LOG_WARNING, "handle_request: seq# did " "not increase\n"); } else { auth_ok = TRUE; t_data->last_used_seq_num = ntohl(ext->ext_dyn->seq); } } } /* check, whether this FA is the switching FA */ iface_ok = 0; if (ext->prev_fa_nai) { prev_iface = fa_hash_check(ext->prev_fa_nai); if (GET_NAI_LEN(ext->prev_fa_nai) == config->fa_nai_len && memcmp(MSG_NAI_DATA(ext->prev_fa_nai), config->fa_nai, config->fa_nai_len) == 0) { iface_ok = 1; DEBUG(DEBUG_FLAG, "This FA was the previous FA - okay to reply\n"); } else if (prev_iface == NULL) { DEBUG(DEBUG_FLAG, "Unknown previous FA NAI - " "forwarding request upwards\n"); } else { DEBUG(DEBUG_FLAG, "Request from a known FA below us - " "okay to reply\n"); iface_ok = 1; } } else { DEBUG(DEBUG_FLAG, "No FA NAI ext - using only session key " "based SFA detection\n"); iface_ok = 1; } if (ext->fa_nai) { prev_iface = fa_hash_check(ext->fa_nai); if (prev_iface != NULL && prev_iface != info->iface) { LOG2(LOG_ALERT, "Request coming from different " "interface - forcing forwarding of the " "request\n"); LOG2(LOG_ALERT, "old iface '%s', now iface '%s'\n", prev_iface->dev, info->iface->dev); } } /* handle authorized messages self and forward unauthorized messages * and disconnect requests to upper mobility agent */ if (auth_ok && ntohs(ext->req->lifetime) > 0 && iface_ok) { /* check whether FA decapsulation to MN has changed */ if (t_data->fa_decapsulation != (is_sender_mobile(ext) && (ext->req->opts & REGREQ_MN_DECAPS) == 0)) { DEBUG(DEBUG_FLAG, "FA decaps changed (old=%i) => " "forcing lower switch\n", t_data->fa_decapsulation); lower_mode_changed = 1; } /* check whether MN encapsulation to FA has changed */ if (t_data->encaps_delivery != (!is_sender_mobile(ext) || ext->encaps_del != NULL)) { DEBUG(DEBUG_FLAG, "Encaps. delivery changed (old=%i) " "=> forcing lower switch\n", t_data->encaps_delivery); lower_mode_changed = 1; } if (unc != NULL && (t_data->down_type != unc->tunnel_type || t_data->down_key != unc->tunnel_key)) { DEBUG(DEBUG_FLAG, "Down tunnel mode changed " "(%i,%i => %i,%i) => forcing lower switch\n", t_data->down_type, t_data->down_key, unc->tunnel_type, unc->tunnel_key); lower_mode_changed = 1; } if (unc != NULL && t_data->info.iface != unc->info.iface) { DEBUG(DEBUG_FLAG, "Down interface changed (%s => %s) " "=> forcing lower switch\n", t_data->info.iface->dev, unc->info.iface->dev); lower_mode_changed = 1; } if (binding->lower_addr.s_addr != info->src.sin_addr.s_addr || lower_mode_changed) { struct saved_binding_info old; save_binding_info(&old, binding); update_request_binding_info(binding, ext, info); handle_lower_switch(binding, &old, ext, &info->src.sin_addr, TRUE); } else reply_message(binding, ext); time(&binding->mod_time); binding->id[0] = ntohl(ext->req->id[0]); binding->id[1] = ntohl(ext->req->id[1]);#ifdef USE_TEARDOWN /* Note: t_data->upper_id is left unchanged because upper * FAs/HA do not get this id */#endif } else { /* unauthorized message or disconnect request */ int use_prelim = 0; if (t_data->confirmed) unc = add_unconfirmed_data(bindings_hash, &bcounters, binding, ext, info, config); /* use preliminary data path optimization when there is not * yet a tunnel downwards or the lower end has changed */ if (!t_data->confirmed) use_prelim = 1; else if (auth_ok && binding->lower_addr.s_addr != info->src.sin_addr.s_addr) use_prelim = 1; if (ntohs(ext->req->lifetime) > 0 && use_prelim && unc != NULL && make_preliminary_data_path(binding, unc, info->iface) != 0) { DEBUG(DEBUG_FLAG, "preliminary data path optimization " "failed\n"); ret = 1; } t_data->pending_request = TRUE; /* forward request first */ forward_request(binding, ext); } return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -