📄 ha.c
字号:
found = 1; break; } } if (!found && from_iface != NULL) reply->ha_addr.s_addr = from_iface->addr.s_addr; } memcpy(reply->id, ext->req->id, REG_REQ_ID_LEN); if (mn_spi) { if (mn_spi->replay_method == REPLAY_PROTECTION_TIMESTAMP && code == REGREP_ID_MISMATCH_HA) { reply->id[0] = htonl(time(NULL) + UNIX_NTP_DIFF); } else if (mn_spi->replay_method == REPLAY_PROTECTION_NONCE) { reply->id[0] = get_rand32(); if (nonce != NULL) *nonce = reply->id[0]; } } msgpos += sizeof(struct reg_rep); left -= sizeof(struct reg_rep); n = ha_add_ext_start(msg, msgpos, left, ext, 0, mn_spi, NULL, auth_type); if (n < 0) return -1; msgpos += n; left -= n; n = ha_add_ext_end(msg, msgpos, sock_addr->sin_addr, left, ext); if (n < 0) return -1; msgpos += n; left -= n; msglen = msgpos - msg; if (forced_iface != NULL && setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, forced_iface->dev, IFNAMSIZ)) { DEBUG(DEBUG_FLAG, "setsockopt(SOL_SOCKET, SO_BINDTODEVICE): " "%s\n", strerror(errno)); } DEBUG(DEBUG_FLAG, "send_reg_failure(code=%i): " "sending %i bytes to %s:%i (forced_iface=%s)\n", code, msglen, inet_ntoa(sock_addr->sin_addr), ntohs(sock_addr->sin_port), forced_iface != NULL ? forced_iface->dev : "N/A"); result = sendto(s, msg, msglen, 0, (struct sockaddr *)sock_addr, sizeof(struct sockaddr_in)); if (forced_iface != NULL) { /* kernel seems to require optlen >= sizeof(int); in addition, * 2.2 kernels seem to report error (Invalid argument) even * when this unbinding success.. */ char dummy[sizeof(int)]; memset(dummy, 0, sizeof(dummy)); if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, dummy, sizeof(dummy)) < 0 && setsockopt(s, SOL_PACKET, SO_BINDTODEVICE, NULL, 0) < 0) DEBUG(DEBUG_FLAG, "setsockopt(SOL_SOCKET, " "SO_BINDTODEVICE): %s (this is probably " "bogus)\n", strerror(errno)); } return dynamics_check_sendto(result, msglen, "send_reg_failure");}/** * handle_reg_msg: * @s: registration message socket (UDP) * * Processes a registration request message that has arrived on socket @s. * * Returns: * 0 on success or -1 on failure */static inthandle_reg_msg(struct interface_entry *from_iface, int s){ char msg[MAXMSG]; struct sockaddr_in cli_addr; struct in_addr dst_addr; struct msghdr mh; char cdata[256]; struct iovec iov; struct cmsghdr *cmsg; struct bindingentry *binding; struct spi_entry *mn_spi; int n, res, rsock; struct msg_extensions ext; int killbinding; int code, auth_type; char mn_addrstr[17]; char fa_addrstr[17]; struct ha_tunnel_data *t_data = NULL; struct bindingkey bkey; struct node *node; struct interface_entry *force_iface = NULL; memset(&cli_addr, 0, sizeof(cli_addr)); dst_addr.s_addr = 0; memset(&mh, 0, sizeof(mh)); iov.iov_base = msg; iov.iov_len = MAXMSG; mh.msg_name = &cli_addr; mh.msg_namelen = sizeof(cli_addr); mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = &cdata; mh.msg_controllen = sizeof(cdata); n = recvmsg(s, &mh, 0); DEBUG(DEBUG_FLAG, "Received %d bytes from %s:%d\n", n, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); if (n < 0) { LOG2(LOG_ERR, "handle_reg_msg - recvfrom failed - %s\n", strerror(errno)); return -1; } for (cmsg = CMSG_FIRSTHDR(&mh); cmsg != NULL; cmsg = DYNAMICS_CMSG_NXTHDR(&mh, cmsg)) { if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) { struct _in_pktinfo *pkt; pkt = (struct _in_pktinfo *) CMSG_DATA(cmsg); dst_addr = pkt->ipi_addr; DEBUG(DEBUG_FLAG, "\tIP_PKTINFO: ipi_ifindex=%i " "ipi_spec_dst=%s ", pkt->ipi_ifindex, inet_ntoa(pkt->ipi_spec_dst)); DEBUG(DEBUG_FLAG, "ipi_addr=%s\n", inet_ntoa(pkt->ipi_addr)); } } /* use the matching UDP socket for the registration reply */ rsock = -1; for (node = list_get_first(&config.interfaces); node != NULL; node = list_get_next(node)) { struct interface_entry *iface = (struct interface_entry *) node; if (iface->addr.s_addr == dst_addr.s_addr || (iface->ha_disc && iface->bcaddr.s_addr == dst_addr.s_addr)) { rsock = iface->udp_sock; } } if (rsock < 0 && from_iface != NULL && dst_addr.s_addr == (unsigned int) -1) { DEBUG(DEBUG_FLAG, "Broadcast 255.255.255.255 destination - " "using interface[%s] UDP socket[%i]\n", from_iface->dev, from_iface->udp_sock); rsock = from_iface->udp_sock; } if (rsock < 0) { LOG2(LOG_WARNING, "Could not find UDP socket for " "registration reply - trying recv. socket\n"); rsock = s; } res = parse_msg(msg, n, &ext); if (ext.req != NULL && ntohs(ext.req->lifetime) == 0 && ext.req->home_addr.s_addr == cli_addr.sin_addr.s_addr && ext.req->home_addr.s_addr == ext.req->co_addr.s_addr) { /* RFC 2002, Ch. 3.8.3.1: * When HA is replying to deregistration (lifetime == 0 && * COA == home addr) whose srcIP is MN's home address, the * reply must be send directly to home network bypassing any * mobility bindings */ DEBUG(DEBUG_FLAG, "Dereg. from home => bypassing mobility " "bindings in routing\n"); force_iface = from_iface; } if (res == -3 || res == -4) { LOG2(LOG_WARNING, "Unknown critical vendor extension in a " "request from %s:%i\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); send_reg_failure(rsock, &cli_addr, NULL, 0, res == -3 ? REGREP_UNSUPP_VENDOR_ID_MN_HA : REGREP_UNSUPP_VENDOR_ID_FA_HA, NULL, &ext, force_iface, NULL, from_iface); stats.discarded_vendor_ext++; report_discarded_msg(msg, n, &cli_addr, "unknown vendor extension"); return -1; } else if (res != 0 || ext.req == NULL) { char *reason = "N/A"; if (res == -1) { reason = "unknown extension"; stats.discarded_unknown_ext++; } else if (res == -2) { reason = "malformed message"; stats.discarded_malformed_msg++; } else if (ext.req == NULL) { reason = "not a request"; stats.discarded_not_request++; } LOG2(LOG_WARNING, "Message parser failed or not a request " "(from %s:%i): %s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), reason); report_discarded_msg(msg, n, &cli_addr, reason); return -1; } memcpy(&bkey.mn_addr, &ext.req->home_addr, sizeof(bkey.mn_addr)); bkey.ha_addr = config.sha_addr.s_addr != 0 ? config.sha_addr : ext.req->ha_addr; bkey.priv_ha = config.priv_ha; binding = binding_fetch(bindings, &bkey); if (binding != NULL) t_data = (struct ha_tunnel_data *) binding->data; mn_spi = validate_request(binding, cli_addr.sin_addr, msg, n, &ext, &code, &auth_type); if (code > 2) { send_reg_failure(rsock, &cli_addr, mn_spi, 0, code, (t_data != NULL ? &t_data->nonce : NULL), &ext, force_iface, binding, from_iface); stats.req_rejected++; report_discarded_msg(msg, n, &cli_addr, "invalid message"); return -1; } if (mn_spi == NULL) { stats.req_rejected++; report_discarded_msg(msg, n, &cli_addr, "MN SPI not found"); return -1; } dynamics_strlcpy(mn_addrstr, inet_ntoa(ext.req->home_addr), sizeof(mn_addrstr)); dynamics_strlcpy(fa_addrstr, inet_ntoa(ext.req->co_addr), sizeof(fa_addrstr)); if (binding != NULL) { t_data->auth_type = (auth_type == AUTH_MAC_RFC2002 ? AUTH_RFC2002BIS : AUTH_RFC2002); /* If the id field is identical the message was a duplicate. * In order to avoid breaking the tunnel drop this message as * a special case without sending a failure reply if the replay * protection is used */ if (mn_spi->replay_method != REPLAY_PROTECTION_NONE && binding->id[0] == ext.req->id[0] && binding->id[1] == ext.req->id[1]) { DEBUG(DEBUG_FLAG, "Received duplicate request - dropping it\n"); stats.req_rejected++; return -1; } /* Make sure that the id field is incremented if the timestamp * protection is used */ if (mn_spi->replay_method == REPLAY_PROTECTION_TIMESTAMP && (ntohl(binding->id[0]) > ntohl(ext.req->id[0]) || (ntohl(binding->id[0]) == ntohl(ext.req->id[0]) && ntohl(binding->id[1]) >= ntohl(ext.req->id[1])))) { LOG2(LOG_NOTICE, "Timestamp did not increase " "(MN=%s, FA=%s)\n", mn_addrstr, fa_addrstr); send_reg_failure(rsock, &cli_addr, mn_spi, t_data->auth_type, REGREP_ID_MISMATCH_HA, &t_data->nonce, &ext, force_iface, binding, from_iface); stats.req_rejected++; return -1; } /* remove binding from list (does not remove tunnels etc.) */ binding_remove(binding); } else { LOG2(LOG_NOTICE, "MN %s registers, FA is %s\n", mn_addrstr, fa_addrstr); } stats.req_accepted++; killbinding = 0; if (binding && (binding->lower_addr.s_addr != (config.sha_addr.s_addr != 0 ? config.sha_addr.s_addr : ext.req->co_addr.s_addr) || ext.req->lifetime == 0)) { DEBUG(DEBUG_FLAG, "Found existing binding for MN %s\n", mn_addrstr); if (ext.req->co_addr.s_addr == ext.req->home_addr.s_addr && ext.req->lifetime == 0) { DEBUG(DEBUG_FLAG, "MN is deregistering all its " "simultaneous bindings\n"); /* Note: RFC 2002, 3.6.1.2, special case * This should be taken into account if the * simultaneous bindings are implemented. * At the moment we just deregister the only possible * binding below. */ }#ifdef USE_TEARDOWN /* if co_addr differs in request, send reply to old * co_addr to purge old tunnel segment */ if (config.sha_addr.s_addr == 0 && binding->lower_addr.s_addr != ext.req->co_addr.s_addr) { binding->timeout = 0; DEBUG(DEBUG_FLAG, "Sending purge msg to %s\n", inet_ntoa(binding->lower_addr)); send_reg_repl(s, binding, mn_spi, NULL, force_iface, REGREP_ACCEPTED); }#endif if (ext.req->lifetime > 0) { /* it was a location update */ LOG2(LOG_INFO, "MN %s updating tunnel to FA %s\n", mn_addrstr, fa_addrstr); if (switch_tunnel(binding, &ext, mn_spi) < 0) return -1; } else { /* if it is a deregistration request, * destroy the binding after the reply is sent, * since the binding is needed when sending the * reply */ LOG2(LOG_NOTICE, "MN %s deregisters\n", mn_addrstr); remove_tunnel(binding); if (cli_addr.sin_addr.s_addr == binding->mn_addr.s_addr && ext.req->co_addr.s_addr == ext.req->home_addr.s_addr) { DEBUG(DEBUG_FLAG, "MN returned home and " "deregistered all care-of addresses\n"); /* FIX: also HA should send gratuitous ARP * telling that MN takes care of its own * packets from now on; * draft-ietf-mobileip-rfc2002-bis-03.txt, 4.6; * This would require that HA would get the * link-layer address of the MN from the * deregistration message and use it in the * gratuitous ARP packet */ } killbinding = 1; } } if (binding == NULL) { if (ntohs(ext.req->lifetime) == 0) { DEBUG(DEBUG_FLAG, "Deregistration attempt, but binding" " not found - creating temporary binding\n"); binding = create_binding(&ext, mn_spi, 0); killbinding = 1; } else { binding = create_binding(&ext, mn_spi, 1); } if (binding == NULL) { LOG2(LOG_ERR, "Failed to create binding for MN: %s, " "FA: %s\n", mn_addrstr, fa_addrstr); send_reg_failure(rsock, &cli_addr, mn_spi, (auth_type == AUTH_MAC_RFC2002 ? 1 : 0), REGREP_NO_RESOURCES_HA, NULL, &ext, force_iface, binding, from_iface); return -1; } t_data = (struct ha_tunnel_data *) binding->data; t_data->auth_type = (auth_type == AUTH_MAC_RFC2002 ? AUTH_RFC2002BIS : AUTH_RFC2002); } ASSERT(binding != NULL); t_data = (struct ha_tunnel_data *) binding->data; ASSERT(t_data != NULL); binding->mod_time = time(NULL); t_data->reverse_tunnel = (ext.req->opts & REGREQ_REVERSE_TUNNEL) != 0; if (ext.req->opts & REGREQ_MINIMAL_ENCAPS) t_data->encapsulation = ENCAPS_MINIMAL; else if (ext.req->opts & REGREQ_GRE_ENCAPS) t_data->encapsulation = ENCAPS_GRE; else t_data->encapsulation = ENCAPS_IPIP; if (ext.fa_keyreq) binding->fa_spi = ntohl(ext.fa_keyreq->spi); else binding->fa_spi = 0; /* If the request has a public key add it to the binding */ if (ext.fa_pubkey) { /* binding->fa_pubkey is NULL if we have generated a * new SK. If the public key sent by the FA is the same * as in a previous binding, we can reuse the encrypted * SK and save some CPU cycles */ if (binding->fa_pubkey && binding->last_sent_fa_pubkeyrep != NULL && (memcmp(binding->fa_pubkey, ext.fa_pubkey, GET_KEY_EXT_LEN(ext.fa_pubkey)) == 0)) { DEBUG(DEBUG_FLAG, "Reusing encrypted SK for FA %s\n", fa_addrstr); } else { if (binding->fa_pubkey) free(binding->fa_pubkey); if (binding->last_sent_fa_pubkeyrep) free(binding->last_sent_fa_pubkeyrep); binding->last_sent_fa_pubkeyrep = NULL; binding->fa_pubkey = (struct msg_key *) malloc(GET_KEY_EXT_LEN(ext.fa_pubkey)); if (binding->fa_pubkey != NULL) { memcpy(binding->fa_pubkey, ext.fa_pubkey, GET_KEY_EXT_LEN(ext.fa_pubkey)); } else { LOG2(LOG_ERR, "Not enough memory " "for fa_pubkey\n"); return -1; } binding->last_sent_fa_pubkeyrep = dynamics_do_rsa_encrypt(binding->key, binding->keylen, binding->fa_pubkey); if (binding->last_sent_fa_pubkeyrep == NULL) { LOG2(LOG_ERR, "RSA encryption failed (MN=%s, " "FA=%s)\n", mn_addrstr, fa_addrstr); } } } else { /* remove the (possible) old FA pubkey from the binding to make * sure that the next FA using RSA will get the correct key */ if (binding->fa_pubkey) free(binding->fa_pubkey); binding->fa_pubkey = NULL; if (binding->last_sent_fa_pubkeyrep) free(binding->last_sent_fa_pubkeyrep); binding->last_sent_fa_pubkeyrep = NULL; } memcpy(&binding->id, ext.req->id, REG_REQ_ID_LEN)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -