/** * Handle LOCATOR parameter in first update packet. * * @param packet_type The packet type of the control message (RFC 5201, 5.3.) * @param ha_state The host association state (RFC 5201, 4.4.1.) * @param ctx Pointer to the packet context, containing all information for * the packet handling (received message, source and destination * address, the ports and the corresponding entry from the host * association database). * * @return zero on success, or negative error value on error. */ int hip_handle_locator_parameter(UNUSED const uint8_t packet_type, UNUSED const uint32_t ha_state, struct hip_packet_context *ctx) { int locator_addr_count = 0; union hip_locator_info_addr *locator_info_addr = NULL; struct hip_locator_info_addr_item *locator_address_item = NULL; struct update_state *localstate = NULL; struct hip_locator *locator = NULL; if (hip_classify_update_type(ctx->input_msg) == FIRST_UPDATE_PACKET) { if (!(locator = hip_get_param_readwrite(ctx->input_msg, HIP_PARAM_LOCATOR))) { HIP_ERROR("no LOCATOR parameter found\n"); return -1; } locator_addr_count = hip_get_locator_addr_item_count(locator); HIP_DEBUG("LOCATOR has %d address(es), loc param len=%d\n", locator_addr_count, hip_get_param_total_len(locator)); // Empty the addresses_to_send_echo_request list before adding the // new addresses localstate = lmod_get_state_item(ctx->hadb_entry->hip_modular_state, "update"); HIP_DEBUG("hip_get_state_item returned localstate: %p\n", localstate); hip_remove_addresses_to_send_echo_request(localstate); locator_address_item = (struct hip_locator_info_addr_item *) (locator + 1); HIP_DEBUG_IN6ADDR("Adding IP source address to locator set", &ctx->src_addr); if (!hip_add_address_to_send_echo_request(localstate, ctx->src_addr)) { HIP_ERROR("Adding source address to the container for update locators failed!\n"); return -1; } for (int i = 0; i < locator_addr_count; i++) { locator_info_addr = hip_get_locator_item(locator_address_item, i); const struct in6_addr *const peer_addr = hip_get_locator_item_address(locator_info_addr); if (ipv6_addr_cmp(&ctx->src_addr, peer_addr) != 0) { HIP_DEBUG_IN6ADDR("adding locator", peer_addr); if (!hip_add_address_to_send_echo_request(localstate, *peer_addr)) { HIP_ERROR("Adding an address to the container for update locators failed!\n"); return -1; } } } hip_print_addresses_to_send_update_request(ctx->hadb_entry); } return 0; }
void hip_delete_sa(u32 spi, struct in6_addr *peer_addr, struct in6_addr *dst_addr, int direction, hip_ha_t *entry) { int so, len, err = 0; struct sockaddr_storage ss_addr, dd_addr; struct sockaddr *saddr; struct sockaddr *daddr; in_port_t sport, dport; /* @todo: sport and dport should be used! */ if (direction == HIP_SPI_DIRECTION_OUT) { sport = entry->local_udp_port; dport = entry->peer_udp_port; entry->outbound_sa_count--; if (entry->outbound_sa_count < 0) { HIP_ERROR("Warning: out sa count negative\n"); entry->outbound_sa_count = 0; } } else { sport = entry->peer_udp_port; dport = entry->local_udp_port; entry->inbound_sa_count--; if (entry->inbound_sa_count < 0) { HIP_ERROR("Warning: in sa count negative\n"); entry->inbound_sa_count = 0; } } saddr = (struct sockaddr*) &ss_addr; daddr = (struct sockaddr*) &dd_addr; HIP_DEBUG("\n"); HIP_DEBUG("spi=0x%x\n", spi); HIP_DEBUG_IN6ADDR("peer_addr", peer_addr); HIP_DEBUG_IN6ADDR("dst_addr", dst_addr); // Sanity check HIP_IFEL((!peer_addr || !dst_addr), -1, "Addresses not valid when deleting SA's\n"); HIP_IFEL(((so = pfkey_open()) < 0), -1, "ERROR in opening pfkey socket: %s\n", ipsec_strerror()); get_sock_addr_from_in6(saddr, peer_addr); get_sock_addr_from_in6(daddr, dst_addr); HIP_IFEBL(((len = pfkey_send_delete(so, SADB_SATYPE_ESP, HIP_IPSEC_DEFAULT_MODE, saddr, daddr, spi))<0), -1, pfkey_close(so), "ERROR in deleting sa %s", ipsec_strerror()); out_err: return; }
/** * Print all IP addresses where an update packet should be sent to. * * @param ha pointer to a host association */ static void hip_print_addresses_to_send_update_request(const struct hip_hadb_state *const ha) { const struct update_state *const localstate = lmod_get_state_item(ha->hip_modular_state, "update"); HIP_DEBUG("Addresses to send update:\n"); for (unsigned i = 0; i < localstate->valid_locators; i++) { HIP_DEBUG_IN6ADDR("", &localstate->addresses_to_send_echo_request[i]); } }
static int select_source_address(struct in6_addr *src, const struct in6_addr *dst) { int err = 0; int family = AF_INET; struct idxmap *idxmap[16] = { 0 }; struct in6_addr lpback = IN6ADDR_LOOPBACK_INIT; HIP_DEBUG_IN6ADDR("dst", dst); /* Required for loopback connections */ if (!ipv6_addr_cmp(dst, &lpback)) { ipv6_addr_copy(src, dst); goto out_err; } HIP_IFEL(hip_iproute_get(&hipfw_nl_route, src, dst, NULL, NULL, family, idxmap), -1, "Finding ip route failed\n"); HIP_DEBUG_IN6ADDR("src", src); out_err: return err; }
/** * resolve_dht_gateway_info - Resolves the gateway address * @param gateway_name FQDN of the gateway * @param gateway Addrinfo struct where the result will be stored * @param af address family * * @return Returns 0 on success otherwise -1 */ int resolve_dht_gateway_info(char *gateway_name, struct addrinfo ** gateway, in_port_t gateway_port, int af) { struct addrinfo hints; struct sockaddr_in *sa_v4 = NULL; struct sockaddr_in6 *sa_v6 = NULL; int error; char opendht_serving_gateway_port_str[7]; if ((af != AF_INET) && (af != AF_INET6)) { error = -1; HIP_DEBUG("Wrong address family!\n"); return error; } memset(&hints, 0, sizeof(hints)); hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; /* For some reason this does not work anymore -samu */ //hints.ai_flags = AI_NODHT; error = 0; sprintf(opendht_serving_gateway_port_str, "%d", gateway_port); error = getaddrinfo(gateway_name, opendht_serving_gateway_port_str, &hints, gateway); if (error != 0) { HIP_DEBUG("OpenDHT gateway resolving failed %s\n", gateway_name); HIP_DEBUG("%s\n",gai_strerror(error)); } else { if (af == AF_INET) { sa_v4 = (struct sockaddr_in *) (*gateway)->ai_addr; HIP_DEBUG_INADDR("OpenDHT gateway IPv4", &(sa_v4->sin_addr)); } else if (af == AF_INET6) { sa_v6 = (struct sockaddr_in6 *) (*gateway)->ai_addr; HIP_DEBUG_IN6ADDR("OpenDHT gateway IPv6", &(sa_v6->sin6_addr)); } } return error; }
/** * Convert a string representation of an IPv6 or IPv4 address to a struct * in6_addr. * If the string contains an IPv4 address, it is converted to its * IPv6-compatible mapping. * * @param str points to the string to convert. * @param ip6 points to a buffer where the function stores the binary address * if it could be converted. * @return The return value is 0 if the conversion succeeds. It is a * negative value if str or ip6 are NULL or if str contains neither a * parseable IPv6 or IPv4 address. */ int hip_convert_string_to_address(const char *const str, struct in6_addr *const ip6) { if (str && ip6) { if (inet_pton(AF_INET6, str, ip6) == 1) { /* IPv6 address conversion was ok */ return 0; } else { struct in_addr ip4; /* Might be an ipv4 address (ret == 0). Lets catch it here. */ if (inet_pton(AF_INET, str, &ip4) == 1) { IPV4_TO_IPV6_MAP(&ip4, ip6); HIP_DEBUG("Mapped v4 to v6.\n"); HIP_DEBUG_IN6ADDR("mapped v6", ip6); return 0; } } } return -1; }
int hip_fw_handle_incoming_hit(ipq_packet_msg_t *m, struct in6_addr *ip_src, struct in6_addr *ip_dst, int lsi_support, int sys_opp_support) { int bind6 = 0, proto4_LSI = 0, proto4_IP = 0, err = 0, verdict = 1; int ip_hdr_size = 0, portDest = 0, process_as_lsi; char *proto = NULL; hip_lsi_t lsi_our = {0}, lsi_peer = {0}; struct in6_addr src_addr, dst_addr; struct in_addr src_v4, dst_v4; struct ip6_hdr* ip6_hdr = (struct ip6_hdr*) m->payload; firewall_port_cache_hl_t *port_cache_entry = NULL; ip_hdr_size = sizeof(struct ip6_hdr); switch (ip6_hdr->ip6_nxt) { case IPPROTO_UDP: portDest = ((struct udphdr*)((m->payload) + ip_hdr_size))->dest; proto = "udp6"; break; case IPPROTO_TCP: portDest = ((struct tcphdr*)((m->payload) + ip_hdr_size))->dest; proto = "tcp6"; break; case IPPROTO_ICMPV6: HIP_DEBUG("ICMPv6 packet\n"); //goto out_err; break; default: HIP_DEBUG("Unhandled packet %d\n", ip6_hdr->ip6_nxt); //goto out_err; break; } /* port caching */ port_cache_entry = firewall_port_cache_db_match(portDest, ip6_hdr->ip6_nxt); if( port_cache_entry && (port_cache_entry->traffic_type == FIREWALL_PORT_CACHE_IPV6_TRAFFIC) ){ verdict = 1; HIP_DEBUG("Cached port, accepting\n"); goto out_err; } if (sys_opp_support && lsi_support) { /* Currently preferring LSIs over opp. connections */ process_as_lsi = 1; } else if (lsi_support) { process_as_lsi = 1; } else if (sys_opp_support) { process_as_lsi = 0; } else { HIP_ASSERT(1); } //HIP_IFEL(firewall_cache_db_match(ip_src, ip_dst, HIP_IFEL(firewall_cache_db_match(ip_dst, ip_src, &lsi_our, &lsi_peer, &dst_addr, &src_addr, NULL), -1, "Failed to obtain from cache\n"); if (process_as_lsi) { HIP_DEBUG("Trying lsi transformation\n"); HIP_DEBUG_LSI("lsi_our: ", &lsi_our); HIP_DEBUG_LSI("lsi_peer: ", &lsi_peer); IPV4_TO_IPV6_MAP(&lsi_our, &src_addr); IPV4_TO_IPV6_MAP(&lsi_peer, &dst_addr); HIP_IFEL(reinject_packet(&dst_addr, &src_addr, m, 6, 1), -1, "Failed to reinject with LSIs\n"); HIP_DEBUG("Successful LSI transformation.\n"); if (ip6_hdr->ip6_nxt == IPPROTO_ICMPV6) verdict = 1; /* broadcast: dst may be ipv4 or ipv6 */ else verdict = 0; /* drop original */ } else { HIP_DEBUG("Trying sys opp transformation\n"); IPV6_TO_IPV4_MAP(&src_addr, &src_v4); IPV6_TO_IPV4_MAP(&dst_addr, &dst_v4); HIP_DEBUG_IN6ADDR("ip_src: ", &src_addr); HIP_DEBUG_IN6ADDR("ip_dst: ", &dst_addr); HIP_IFEL(reinject_packet(&src_addr, &dst_addr, m, 6, 1), -1, "Failed to reinject with IP addrs\n"); HIP_DEBUG("Successfull sysopp transformation. Drop orig\n"); verdict = 0; } out_err: if (err) return 1; /* Accept original */ else return verdict; }
/** * Executes the packet reinjection * * @param src_hit ipv6 source address * @param dst_hit ipv6 destination address * @param m pointer to the packet * @param ipOrigTraffic type of Traffic (IPv4 or IPv6) * @param incoming packet direction * @return err during the reinjection */ int reinject_packet(struct in6_addr *src_hit, struct in6_addr *dst_hit, ipq_packet_msg_t *m, int ipOrigTraffic, int incoming) { int err = 0, ip_hdr_size, packet_length = 0, protocol, ttl; u8 *msg; struct icmphdr *icmp = NULL; if (ipOrigTraffic == 4) { struct ip *iphdr = (struct ip*) m->payload; ip_hdr_size = (iphdr->ip_hl * 4); protocol = iphdr->ip_p; ttl = iphdr->ip_ttl; HIP_DEBUG_LSI("Ipv4 address src ", &(iphdr->ip_src)); HIP_DEBUG_LSI("Ipv4 address dst ", &(iphdr->ip_dst)); } else { struct ip6_hdr* ip6_hdr = (struct ip6_hdr*) m->payload; ip_hdr_size = sizeof(struct ip6_hdr); //Fixed size protocol = ip6_hdr->ip6_nxt; ttl = ip6_hdr->ip6_hlim; HIP_DEBUG_IN6ADDR("Orig packet src address: ", &(ip6_hdr->ip6_src)); HIP_DEBUG_IN6ADDR("Orig packet dst address: ", &(ip6_hdr->ip6_dst)); HIP_DEBUG_IN6ADDR("New packet src address:", src_hit); HIP_DEBUG_IN6ADDR("New packet dst address: ", dst_hit); } if (m->data_len <= (BUFSIZE - ip_hdr_size)) { packet_length = m->data_len - ip_hdr_size; HIP_DEBUG("packet size smaller than buffer size\n"); } else { packet_length = BUFSIZE - ip_hdr_size; HIP_DEBUG("HIP packet size greater than buffer size\n"); } _HIP_DEBUG("Reinject packet packet length (%d)\n", packet_length); _HIP_DEBUG(" Protocol %d\n", protocol); _HIP_DEBUG(" ipOrigTraffic %d \n", ipOrigTraffic); /* Note: using calloc to zero memory region here because I think firewall_send_incoming_pkt() calculates checksum from too long region sometimes. See bug id 874 */ msg = (u8 *)calloc((packet_length + sizeof(struct ip)), 1); memcpy(msg, (m->payload)+ip_hdr_size, packet_length); if (protocol == IPPROTO_ICMP && incoming) { icmp = (struct icmphdr *)msg; HIP_DEBUG("incoming ICMP type=%d code=%d\n", icmp->type,icmp->code); /* Manually built due to kernel messed up with the ECHO_REPLY message. Kernel was building an answer message with equals @src and @dst*/ if (icmp->type == ICMP_ECHO) { icmp->type = ICMP_ECHOREPLY; err = firewall_send_outgoing_pkt(dst_hit, src_hit, msg, packet_length, protocol); } else { err = firewall_send_incoming_pkt(src_hit, dst_hit, msg, packet_length, protocol, ttl); } } else { if (incoming) { HIP_DEBUG("Firewall send to the kernel an incoming packet\n"); err = firewall_send_incoming_pkt(src_hit, dst_hit, msg, packet_length, protocol, ttl); } else { HIP_DEBUG("Firewall send to the kernel an outgoing packet\n"); err = firewall_send_outgoing_pkt(src_hit, dst_hit, msg, packet_length, protocol); } } if(msg) HIP_FREE(msg); return err; }
/** * connect_dht_gateway - Connects to given v6 gateway * @param sockfd * @param addrinfo Address to connect to * @param blocking 1 for blocking connect 0 for nonblocking * * @return Returns 0 on success -1 otherwise, if nonblocking can return EINPRGORESS */ int connect_dht_gateway(int sockfd, struct addrinfo * gateway, int blocking){ int flags = 0, error = 0; struct sockaddr_in *sa_v4; struct sockaddr_in6 *sa_v6; struct sigaction act, oact; act.sa_handler = connect_alarm; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(gateway == NULL){ HIP_ERROR("No OpenDHT Serving Gateway Address.\n"); return(-1); } if(blocking == 0) goto unblock; // blocking connect if(sigaction(SIGALRM, &act, &oact) < 0){ HIP_DEBUG("Signal error before OpenDHT connect, " "connecting without alarm\n"); error = connect(sockfd, gateway->ai_addr, gateway->ai_addrlen); }else { HIP_DEBUG("Connecting to OpenDHT with alarm\n"); if (alarm(DHT_CONNECT_TIMEOUT) != 0) HIP_DEBUG("Alarm was already set, connecting without\n"); error = connect(sockfd, gateway->ai_addr, gateway->ai_addrlen); alarm(0); if (sigaction(SIGALRM, &oact, &act) <0 ) HIP_DEBUG("Signal error after OpenDHT connect\n"); } if(error < 0){ HIP_PERROR("OpenDHT connect:"); if (errno == EINTR) HIP_DEBUG("Connect to OpenDHT timedout\n"); return(-1); }else{ if(gateway->ai_family == AF_INET){ sa_v4 = (struct sockaddr_in *)gateway->ai_addr; HIP_DEBUG_INADDR("Connected to OpenDHT v4 gateway", &(sa_v4->sin_addr)); } else if(gateway->ai_family == AF_INET6){ sa_v6 = (struct sockaddr_in6 *)gateway->ai_addr; HIP_DEBUG_IN6ADDR("Connected to OpenDHT v6 gateway", &(sa_v6->sin6_addr)); } else{ HIP_DEBUG("Wrong address family for OPENDHT gateway %d\n", gateway->ai_family); } return(0); } unblock: // unblocking connect flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); if(gateway->ai_family == AF_INET){ sa_v4 = (struct sockaddr_in *)gateway->ai_addr; HIP_DEBUG_INADDR("Connecting to OpenDHT v4 gateway", &(sa_v4->sin_addr)); } else if(gateway->ai_family == AF_INET6){ sa_v6 = (struct sockaddr_in6 *)gateway->ai_addr; HIP_DEBUG_IN6ADDR("Connecting to OpenDHT v6 gateway", &(sa_v6->sin6_addr)); } else{ HIP_DEBUG("Wrong address family for OPENDHT gateway %d\n", gateway->ai_family); } if(connect(sockfd, gateway->ai_addr, gateway->ai_addrlen) < 0){ if (errno == EINPROGRESS) return(EINPROGRESS); else{ HIP_PERROR("OpenDHT connect:"); return(-1); } }else{ // connect ok return(0); } }
/* Moved function doxy descriptor to the header file. Lauri 11.03.2008 */ int hip_read_control_msg_all(int socket, struct hip_common *hip_msg, struct in6_addr *saddr, struct in6_addr *daddr, hip_portpair_t *msg_info, int encap_hdr_size, int is_ipv4) { struct sockaddr_storage addr_from, addr_to; struct sockaddr_in *addr_from4 = ((struct sockaddr_in *) &addr_from); struct sockaddr_in6 *addr_from6 = ((struct sockaddr_in6 *) &addr_from); struct cmsghdr *cmsg; struct msghdr msg; union { struct in_pktinfo *pktinfo_in4; struct inet6_pktinfo *pktinfo_in6; } pktinfo; struct iovec iov; char cbuff[CMSG_SPACE(256)]; int err = 0, len; int cmsg_level, cmsg_type; HIP_ASSERT(saddr); HIP_ASSERT(daddr); HIP_DEBUG("hip_read_control_msg_all() invoked.\n"); HIP_IFEL(((len = hip_peek_recv_total_len(socket, encap_hdr_size, HIP_DEFAULT_MSG_TIMEOUT))<= 0), -1, "Bad packet length (%d)\n", len); memset(msg_info, 0, sizeof(hip_portpair_t)); memset(&msg, 0, sizeof(msg)); memset(cbuff, 0, sizeof(cbuff)); memset(&addr_to, 0, sizeof(addr_to)); /* setup message header with control and receive buffers */ msg.msg_name = &addr_from; msg.msg_namelen = sizeof(struct sockaddr_storage); msg.msg_iov = &iov; msg.msg_iovlen = 1; memset(cbuff, 0, sizeof(cbuff)); msg.msg_control = cbuff; msg.msg_controllen = sizeof(cbuff); msg.msg_flags = 0; iov.iov_len = len; iov.iov_base = hip_msg; pktinfo.pktinfo_in4 = NULL; len = recvmsg(socket, &msg, 0); HIP_IFEL((len < 0), -1, "ICMP%s error: errno=%d, %s\n", (is_ipv4 ? "v4" : "v6"), errno, strerror(errno)); cmsg_level = (is_ipv4) ? IPPROTO_IP : IPPROTO_IPV6; cmsg_type = (is_ipv4) ? IP_PKTINFO : IPV6_2292PKTINFO; /* destination address comes from ancillary data passed * with msg due to IPV6_PKTINFO socket option */ for (cmsg=CMSG_FIRSTHDR(&msg); cmsg; cmsg=CMSG_NXTHDR(&msg,cmsg)){ if ((cmsg->cmsg_level == cmsg_level) && (cmsg->cmsg_type == cmsg_type)) { /* The structure is a union, so this fills also the pktinfo_in6 pointer */ pktinfo.pktinfo_in4 = (struct in_pktinfo*)CMSG_DATA(cmsg); break; } } /* If this fails, change IPV6_2292PKTINFO to IPV6_PKTINFO in hip_init_raw_sock_v6 */ HIP_IFEL(!pktinfo.pktinfo_in4, -1, "Could not determine dst addr, dropping\n"); /* UDP port numbers */ if (is_ipv4 && encap_hdr_size == HIP_UDP_ZERO_BYTES_LEN) { HIP_DEBUG("hip_read_control_msg_all() source port = %d\n", ntohs(addr_from4->sin_port)); msg_info->src_port = ntohs(addr_from4->sin_port); /* Destination port is known from the bound socket. */ msg_info->dst_port = hip_get_local_nat_udp_port(); } /* IPv4 addresses */ if (is_ipv4) { struct sockaddr_in *addr_to4 = (struct sockaddr_in *) &addr_to; IPV4_TO_IPV6_MAP(&addr_from4->sin_addr, saddr); IPV4_TO_IPV6_MAP(&pktinfo.pktinfo_in4->ipi_addr, daddr); addr_to4->sin_family = AF_INET; addr_to4->sin_addr = pktinfo.pktinfo_in4->ipi_addr; addr_to4->sin_port = msg_info->dst_port; } else /* IPv6 addresses */ { struct sockaddr_in6 *addr_to6 = (struct sockaddr_in6 *) &addr_to; memcpy(saddr, &addr_from6->sin6_addr, sizeof(struct in6_addr)); memcpy(daddr, &pktinfo.pktinfo_in6->ipi6_addr, sizeof(struct in6_addr)); addr_to6->sin6_family = AF_INET6; ipv6_addr_copy(&addr_to6->sin6_addr, daddr); } //added by santtu if (hip_read_control_msg_plugin_handler(hip_msg,len, saddr,msg_info->src_port)) goto out_err; //endadd if (is_ipv4 && (encap_hdr_size == IPV4_HDR_SIZE)) {/* raw IPv4, !UDP */ /* For some reason, the IPv4 header is always included. Let's remove it here. */ memmove(hip_msg, ((char *)hip_msg) + IPV4_HDR_SIZE, HIP_MAX_PACKET - IPV4_HDR_SIZE); } else if (is_ipv4 && encap_hdr_size == HIP_UDP_ZERO_BYTES_LEN) { /* remove 32-bits of zeroes between UDP and HIP headers */ memmove(hip_msg, ((char *)hip_msg) + HIP_UDP_ZERO_BYTES_LEN, HIP_MAX_PACKET - HIP_UDP_ZERO_BYTES_LEN); } HIP_IFEL(hip_verify_network_header(hip_msg, (struct sockaddr *) &addr_from, (struct sockaddr *) &addr_to, len - encap_hdr_size), -1, "verifying network header failed\n"); if (saddr) HIP_DEBUG_IN6ADDR("src", saddr); if (daddr) HIP_DEBUG_IN6ADDR("dst", daddr); out_err: return err; }
static int send_raw_from_one_src(const struct in6_addr *local_addr, const struct in6_addr *peer_addr, const in_port_t src_port, const in_port_t dst_port, struct hip_common *msg) { int err = 0, sa_size, sent, len = 0, dupl, try_again, udp = 0; struct sockaddr_storage src, dst; int src_is_ipv4 = 0, dst_is_ipv4 = 0, memmoved = 0; struct sockaddr_in6 *src6 = NULL, *dst6 = NULL; struct sockaddr_in *src4 = NULL, *dst4 = NULL; struct in6_addr my_addr; /* Points either to v4 or v6 raw sock */ int hipfw_raw_sock_output = 0; /* Verify the existence of obligatory parameters. */ HIP_ASSERT(peer_addr != NULL && msg != NULL); HIP_DEBUG("Sending %s packet\n", hip_message_type_name(hip_get_msg_type(msg))); HIP_DEBUG_IN6ADDR("hip_send_raw(): local_addr", local_addr); HIP_DEBUG_IN6ADDR("hip_send_raw(): peer_addr", peer_addr); HIP_DEBUG("Source port=%d, destination port=%d\n", src_port, dst_port); HIP_DUMP_MSG(msg); //check msg length if (!hip_check_network_msg_len(msg)) { err = -EMSGSIZE; HIP_ERROR("bad msg len %d\n", hip_get_msg_total_len(msg)); goto out_err; } dst_is_ipv4 = IN6_IS_ADDR_V4MAPPED(peer_addr); len = hip_get_msg_total_len(msg); /* Some convinient short-hands to avoid too much casting (could be * an union as well) */ src6 = (struct sockaddr_in6 *) &src; dst6 = (struct sockaddr_in6 *) &dst; src4 = (struct sockaddr_in *) &src; dst4 = (struct sockaddr_in *) &dst; memset(&src, 0, sizeof(src)); memset(&dst, 0, sizeof(dst)); if (dst_port && dst_is_ipv4) { HIP_DEBUG("Using IPv4 UDP socket\n"); hipfw_raw_sock_output = hipfw_nat_sock_output_udp; sa_size = sizeof(struct sockaddr_in); udp = 1; } else if (dst_is_ipv4) { HIP_DEBUG("Using IPv4 raw socket\n"); //hipfw_raw_sock_output = hipfw_raw_sock_output_v4; //sa_size = sizeof(struct sockaddr_in); } else { HIP_DEBUG("Using IPv6 raw socket\n"); //hipfw_raw_sock_output = hipfw_raw_sock_output_v6; //sa_size = sizeof(struct sockaddr_in6); } if (local_addr) { HIP_DEBUG("local address given\n"); memcpy(&my_addr, local_addr, sizeof(struct in6_addr)); } else { HIP_DEBUG("no local address, selecting one\n"); HIP_IFEL(select_source_address(&my_addr, peer_addr), -1, "Cannot find source address\n"); } src_is_ipv4 = IN6_IS_ADDR_V4MAPPED(&my_addr); if (src_is_ipv4) { IPV6_TO_IPV4_MAP(&my_addr, &src4->sin_addr); src4->sin_family = AF_INET; HIP_DEBUG_INADDR("src4", &src4->sin_addr); } else { memcpy(&src6->sin6_addr, &my_addr, sizeof(struct in6_addr)); src6->sin6_family = AF_INET6; HIP_DEBUG_IN6ADDR("src6", &src6->sin6_addr); } if (dst_is_ipv4) { IPV6_TO_IPV4_MAP(peer_addr, &dst4->sin_addr); dst4->sin_family = AF_INET; HIP_DEBUG_INADDR("dst4", &dst4->sin_addr); } else { memcpy(&dst6->sin6_addr, peer_addr, sizeof(struct in6_addr)); dst6->sin6_family = AF_INET6; HIP_DEBUG_IN6ADDR("dst6", &dst6->sin6_addr); } if (src6->sin6_family != dst6->sin6_family) { /* @todo: Check if this may cause any trouble. * It happens every time we send update packet that contains few locators in msg, one is * the IPv4 address of the source, another is IPv6 address of the source. But even if one of * them is ok to send raw IPvX to IPvX raw packet, another one cause the trouble, and all * updates are dropped. by Andrey "laser". * */ err = -1; HIP_ERROR("Source and destination address families differ\n"); goto out_err; } hip_zero_msg_checksum(msg); if (!udp) { msg->checksum = hip_checksum_packet((char *) msg, (struct sockaddr *) &src, (struct sockaddr *) &dst); } /* Handover may cause e.g. on-link duplicate address detection * which may cause bind to fail. */ HIP_IFEL(bind(hipfw_raw_sock_output, (struct sockaddr *) &src, sa_size), -1, "Binding to raw sock failed\n"); /* For some reason, neither sendmsg or send (with bind+connect) * do not seem to work properly. Thus, we use just sendto() */ len = hip_get_msg_total_len(msg); if (udp) { struct udphdr *uh = (struct udphdr *) msg; /* Insert 32 bits of zero bytes between UDP and HIP */ memmove(((char *) msg) + HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr), msg, len); memset(((char *) msg), 0, HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr)); len += HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr); uh->source = htons(src_port); uh->dest = htons(dst_port); uh->len = htons(len); uh->check = 0; memmoved = 1; } for (dupl = 0; dupl < 1; dupl++) { for (try_again = 0; try_again < 2; try_again++) { sent = sendto(hipfw_raw_sock_output, msg, len, 0, (struct sockaddr *) &dst, sa_size); if (sent != len) { HIP_ERROR("Could not send the all requested" \ " data (%d/%d)\n", sent, len); HIP_DEBUG("strerror %s\n", strerror(errno)); sleep(2); } else { HIP_DEBUG("sent=%d/%d ipv4=%d\n", sent, len, dst_is_ipv4); HIP_DEBUG("Packet sent ok\n"); break; } } } out_err: /* Reset the interface to wildcard or otherwise receiving * broadcast messages fails from the raw sockets. A better * solution would be to have separate sockets for sending * and receiving because we cannot receive a broadcast while * sending */ if (dst_is_ipv4) { src4->sin_addr.s_addr = INADDR_ANY; src4->sin_family = AF_INET; sa_size = sizeof(struct sockaddr_in); } else { struct in6_addr any = IN6ADDR_ANY_INIT; src6->sin6_family = AF_INET6; ipv6_addr_copy(&src6->sin6_addr, &any); sa_size = sizeof(struct sockaddr_in6); } bind(hipfw_raw_sock_output, (struct sockaddr *) &src, sa_size); if (udp && memmoved) { /* Remove 32 bits of zero bytes between UDP and HIP */ len -= HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr); memmove((char *) msg, ((char *) msg) + HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr), len); memset(((char *) msg) + len, 0, HIP_UDP_ZERO_BYTES_LEN + sizeof(struct udphdr)); } if (err) { HIP_ERROR("strerror: %s\n", strerror(errno)); } return err; }
int main_server_udp(int ipv4_sock, int ipv6_sock, in_port_t local_port) { /* Use recvmsg/sendmsg instead of recvfrom/sendto because the latter combination may choose a different source HIT for the server */ int err = 0, on = 1, recvnum, sendnum, is_ipv4 = 0; int cmsg_level, cmsg_type, highest_descriptor = -1; fd_set read_fdset; union { struct sockaddr_in in4; struct sockaddr_in6 in6; } peer_addr, local_addr; uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct inet6_pktinfo))]; uint8_t mylovemostdata[IP_MAXPACKET]; struct iovec iov; struct cmsghdr *cmsg = (struct cmsghdr *) cmsgbuf; union { struct in_pktinfo *in4; struct inet6_pktinfo *in6; } pktinfo; struct msghdr msg; FD_ZERO(&read_fdset); FD_SET(ipv4_sock, &read_fdset); FD_SET(ipv6_sock, &read_fdset); highest_descriptor = maxof(2, ipv4_sock, ipv6_sock); printf("=== Server listening INADDR_ANY/IN6ADDR_ANY ===\n"); while(select((highest_descriptor + 1), &read_fdset, NULL, NULL, NULL)) { /* XX FIXME: receiving two packets at the same time */ if (FD_ISSET(ipv4_sock, &read_fdset)) { is_ipv4 = 1; //FD_CLR(ipv4_sock, &read_fdset); } else if (FD_ISSET(ipv6_sock, &read_fdset)) { is_ipv4 = 0; //FD_CLR(ipv6_sock, &read_fdset); } else { printf("Unhandled select event\n"); goto reset; } msg.msg_name = &peer_addr.in6; msg.msg_namelen = sizeof(struct sockaddr_in6); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); msg.msg_flags = 0; iov.iov_base = mylovemostdata; iov.iov_len = sizeof(mylovemostdata); memset(mylovemostdata, 0, sizeof(mylovemostdata)); memset(&peer_addr, 0, sizeof(peer_addr)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); recvnum = recvmsg((is_ipv4 ? ipv4_sock : ipv6_sock), &msg, 0); if (recvnum < 0) { perror("recvmsg\n"); goto reset; } printf("Received %d bytes\n", recvnum); //is_ipv4 = IN6_IS_ADDR_V4MAPPED(&peer_addr.in6.sin6_addr); cmsg_level = (is_ipv4) ? IPPROTO_IP : IPPROTO_IPV6; cmsg_type = (is_ipv4) ? IP_PKTINFO : IPV6_2292PKTINFO; /* Local address comes from ancillary data passed * with msg due to IPV6_PKTINFO socket option */ for (cmsg=CMSG_FIRSTHDR(&msg); cmsg; cmsg=CMSG_NXTHDR(&msg,cmsg)){ if ((cmsg->cmsg_level == cmsg_level) && (cmsg->cmsg_type == cmsg_type)) { /* The structure is a union, so this fills also the pktinfo_in6 pointer */ pktinfo.in4 = (struct in_pktinfo *)CMSG_DATA(cmsg); break; } } if (is_ipv4) { local_addr.in4.sin_family = AF_INET; local_addr.in4.sin_port = htons(local_port); //local_addr.in4.sin_port = peer_addr.in6.sin6_port; local_addr.in4.sin_addr.s_addr = pktinfo.in4->ipi_addr.s_addr; HIP_DEBUG_INADDR("local addr", &local_addr.in4.sin_addr); HIP_DEBUG("local port %d\n", ntohs(local_addr.in4.sin_port)); HIP_DEBUG_INADDR("peer addr", &peer_addr.in4.sin_addr); HIP_DEBUG("peer port %d\n", ntohs(peer_addr.in4.sin_port)); } else { local_addr.in6.sin6_family = AF_INET6; memcpy(&local_addr.in6.sin6_addr, &pktinfo.in6->ipi6_addr, sizeof(struct in6_addr)); local_addr.in6.sin6_port = htons(local_port); HIP_DEBUG_IN6ADDR("local addr", &local_addr.in6.sin6_addr); HIP_DEBUG("local port %d\n", ntohs(local_addr.in6.sin6_port)); HIP_DEBUG_IN6ADDR("peer addr", &peer_addr.in6.sin6_addr); HIP_DEBUG("peer port %d\n", ntohs(peer_addr.in6.sin6_port)); } err = udp_send_msg((is_ipv4 ? ipv4_sock : ipv6_sock), mylovemostdata, recvnum, (struct sockaddr *) &local_addr, (struct sockaddr *) &peer_addr); if (err) { printf("Failed to echo data back\n"); } reset: FD_ZERO(&read_fdset); FD_SET(ipv4_sock, &read_fdset); FD_SET(ipv6_sock, &read_fdset); } out_err: return err; }
/* Security associations in the kernel with BEET are bounded to the outer * address, meaning IP addresses. As a result the parameters to be given * should be such an addresses and not the HITs. */ uint32_t hip_add_sa(struct in6_addr *saddr, struct in6_addr *daddr, struct in6_addr *src_hit, struct in6_addr *dst_hit, uint32_t spi, int ealg, struct hip_crypto_key *enckey, struct hip_crypto_key *authkey, int already_acquired, int direction, int update, hip_ha_t *entry) { int so, len, err = 0, e_keylen, a_keylen; int aalg = ealg; u_int wsize = 4; /* XXX static size of window */ struct sockaddr_storage ss_addr, dd_addr; struct sockaddr *s_saddr; struct sockaddr *d_saddr; uint32_t reqid = 0; u_int32_t seq = 0; u_int flags = 0; // always zero u_int64_t lifebyte = 0, lifetime = 0; //u_int8_t l_natt_type = HIP_UDP_ENCAP_ESPINUDP_NON_IKE; u_int8_t l_natt_type = HIP_UDP_ENCAP_ESPINUDP; // FIXME: this parameter maybe should be related to some esp parameters (according to racoon source code) u_int16_t l_natt_frag = 0; /* Mappings from HIP to PFKEY algo names */ u_int e_types[] = {SADB_EALG_NULL, SADB_X_EALG_AESCBC, SADB_EALG_3DESCBC, SADB_EALG_3DESCBC, SADB_X_EALG_BLOWFISHCBC, SADB_EALG_NULL, SADB_EALG_NULL}; u_int a_algos[] = {SADB_AALG_NONE, SADB_AALG_SHA1HMAC, SADB_AALG_SHA1HMAC, SADB_AALG_MD5HMAC, SADB_AALG_SHA1HMAC, SADB_AALG_SHA1HMAC, SADB_AALG_MD5HMAC}; u_int e_type = e_types[ealg]; u_int a_type = a_algos[aalg]; in_port_t sport = entry->local_udp_port; in_port_t dport = entry->peer_udp_port; a_keylen = hip_auth_key_length_esp(ealg); e_keylen = hip_enc_key_length(ealg); get_random_bytes(&reqid, sizeof(uint32_t)); get_random_bytes(&seq, sizeof(uint32_t)); HIP_DEBUG("\n"); HIP_DEBUG_HIT("src_hit", src_hit); HIP_DEBUG_HIT("dst_hit", dst_hit); HIP_DEBUG_IN6ADDR("saddr", saddr); HIP_DEBUG_IN6ADDR("daddr", daddr); HIP_IFEL((!saddr || !daddr), 1, "Addresses not valid when adding SA's\n"); HIP_IFEL(((so = pfkey_open()) < 0), 1, "ERROR in opening pfkey socket: %s\n", ipsec_strerror()); s_saddr = (struct sockaddr*) &ss_addr; d_saddr = (struct sockaddr*) &dd_addr; get_sock_addr_from_in6(s_saddr, saddr); get_sock_addr_from_in6(d_saddr, daddr); if (direction == HIP_SPI_DIRECTION_OUT) { entry->outbound_sa_count++; } else { entry->inbound_sa_count++; } // NOTE: port numbers remains in host representation if (update) { if (sport) { // pfkey_send_update_nat when update = 1 and sport != 0 HIP_IFEBL(((len = pfkey_send_update_nat(so, SADB_SATYPE_ESP, HIP_IPSEC_DEFAULT_MODE, s_saddr, d_saddr, spi, reqid, wsize, (void*) enckey, e_type, e_keylen, a_type, a_keylen, flags, 0, lifebyte, lifetime, 0, seq, l_natt_type, sport, dport, NULL, l_natt_frag)) < 0), 1, pfkey_close(so), "ERROR in updating sa for nat: %s\n", ipsec_strerror()); } else { // pfkey_send_update when update = 1 and sport == 0 HIP_IFEBL(((len = pfkey_send_update(so, SADB_SATYPE_ESP, HIP_IPSEC_DEFAULT_MODE, s_saddr, d_saddr, spi, reqid, wsize, (void*) enckey, e_type, e_keylen, a_type, a_keylen, flags, 0, lifebyte, lifetime, 0, seq)) < 0), 1, pfkey_close(so), "ERROR in updating sa: %s\n", ipsec_strerror()); } } else { if (sport) { // pfkey_send_add_nat when update = 0 and sport != 0 HIP_IFEBL(((len = pfkey_send_add_nat(so, SADB_SATYPE_ESP, HIP_IPSEC_DEFAULT_MODE, s_saddr, d_saddr, spi, reqid, wsize, (void*) enckey, e_type, e_keylen, a_type, a_keylen, flags, 0, lifebyte, lifetime, 0, seq, l_natt_type, sport, dport, NULL, l_natt_frag)) < 0), 1, pfkey_close(so), "ERROR in adding sa for nat: %s\n", ipsec_strerror()); } else { // pfkey_send_add when update = 0 and sport == 0 HIP_IFEBL(((len = pfkey_send_add(so, SADB_SATYPE_ESP, HIP_IPSEC_DEFAULT_MODE, s_saddr, d_saddr, spi, reqid, wsize, (void*) enckey, e_type, e_keylen, a_type, a_keylen, flags, 0, lifebyte, lifetime, 0, seq)) < 0), 1, pfkey_close(so), "ERROR in adding sa: %s\n", ipsec_strerror()); } } return 0; out_err: return err; }