/* * checks for ip address for hit */ int hip_hit_to_ip(hip_hit_t *hit, struct in6_addr *retval) { struct addrinfo *rp = NULL; // no C99 :( char hit_to_ip_hostname[64+HIT_TO_IP_ZONE_MAX_LEN+1]; int found_addr = 0; struct addrinfo hints; struct addrinfo *result = NULL; int res; if ((hit == NULL)||(retval == NULL)) return ERR; if (hip_get_hit_to_ip_hostname(hit, hit_to_ip_hostname, sizeof(hit_to_ip_hostname))!=OK) return ERR; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ hints.ai_socktype = SOCK_DGRAM; /* Datagram socket. Right? */ hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; /* getaddrinfo is too complex for DNS lookup, but let us use it now */ res = getaddrinfo( hit_to_ip_hostname, NULL, &hints, &result ); HIP_DEBUG("getaddrinfo(%s) returned %d\n", hit_to_ip_hostname, res); if (res!=0) { HIP_DEBUG("getaddrinfo error %s\n", gai_strerror(res)); return ERR; } /* Look at the list and return only one address, let us prefer AF_INET */ for (rp = result; rp != NULL; rp = rp->ai_next) { HIP_DEBUG_SOCKADDR("getaddrinfo result", rp->ai_addr); if (rp->ai_family == AF_INET) { struct sockaddr_in *tmp_sockaddr_in_ptr = (struct sockaddr_in *) (rp->ai_addr); IPV4_TO_IPV6_MAP(&(tmp_sockaddr_in_ptr->sin_addr), retval) found_addr = 1; break; } else if (rp->ai_family == AF_INET6) { struct sockaddr_in6 *tmp_sockaddr_in6_ptr = (struct sockaddr_in6 *) (rp->ai_addr); ipv6_addr_copy(retval, &(tmp_sockaddr_in6_ptr->sin6_addr)); found_addr = 1; } } if (result) freeaddrinfo(result); if (found_addr) return OK; else return ERR; }
/** * 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; }
/** * Checks if the outgoing packet with lsis has already ESTABLISHED the Base Exchange * with the peer host. In case the BEX is not done, it triggers it. Otherwise, it looks up * in the local database the necessary information for doing the packet reinjection with HITs. * * @param m pointer to the packet * @param lsi_src source LSI * @param lsi_dst destination LSI * @return err during the BEX */ int hip_fw_handle_outgoing_lsi(ipq_packet_msg_t *m, struct in_addr *lsi_src, struct in_addr *lsi_dst) { int err = 0, msg_type, state_ha, new_fw_entry_state; struct in6_addr src_lsi, dst_lsi; struct in6_addr src_hit, dst_hit; struct in6_addr src_ip, dst_ip; firewall_hl_t *entry_peer = NULL; if (lsi_dst) { HIP_DEBUG_LSI("lsi dst", lsi_dst); } memset(&src_lsi, 0, sizeof(struct in6_addr)); memset(&dst_lsi, 0, sizeof(struct in6_addr)); memset(&src_hit, 0, sizeof(struct in6_addr)); memset(&dst_hit, 0, sizeof(struct in6_addr)); memset(&src_ip, 0, sizeof(struct in6_addr)); memset(&dst_ip, 0, sizeof(struct in6_addr)); /* get the corresponding ip address for this lsi, as well as the current ha state */ if(firewall_cache_db_match(NULL, NULL, lsi_src, lsi_dst, &src_ip, &dst_ip, &state_ha)){ HIP_DEBUG("No HA found yet\n"); } entry_peer = (firewall_hl_t *) firewall_ip_db_match(&dst_ip); if (entry_peer) { /* if the firewall entry is still undefined check whether the base exchange has been established */ if(entry_peer->bex_state == FIREWALL_STATE_BEX_DEFAULT){ /* find the correct state for the fw entry state */ if(state_ha == HIP_STATE_ESTABLISHED) new_fw_entry_state = FIREWALL_STATE_BEX_ESTABLISHED; else if( (state_ha == HIP_STATE_FAILED) || (state_ha == HIP_STATE_CLOSING) || (state_ha == HIP_STATE_CLOSED) ) new_fw_entry_state = FIREWALL_STATE_BEX_NOT_SUPPORTED; else new_fw_entry_state = FIREWALL_STATE_BEX_DEFAULT; /* update fw entry state accordingly */ firewall_update_entry(NULL, NULL, NULL, &dst_ip, FIREWALL_STATE_BEX_ESTABLISHED); /* reobtain the entry in case it has been updated */ entry_peer = firewall_ip_db_match(&dst_ip); } /* decide whether to reinject the packet */ if (entry_peer->bex_state == FIREWALL_STATE_BEX_ESTABLISHED) HIP_IFEL(reinject_packet(&entry_peer->hit_our, &entry_peer->hit_peer, m, 4, 0), -1, "Failed to reinject\n"); } else { HIP_DEBUG("no ip db match\n"); /* add default entry in the firewall db */ HIP_IFEL(firewall_add_default_entry(&dst_ip), -1, "Adding of fw entry failed\n"); /* Check if bex is already established: server case. Get current connection state from hipd */ state_ha = hip_get_bex_state_from_LSIs(lsi_src, lsi_dst, &src_ip, &dst_ip, &src_hit, &dst_hit); if( (state_ha == -1) || (state_ha == HIP_STATE_NONE) || (state_ha == HIP_STATE_UNASSOCIATED) ){ /* initialize bex */ IPV4_TO_IPV6_MAP(lsi_src, &src_lsi); IPV4_TO_IPV6_MAP(lsi_dst, &dst_lsi); HIP_IFEL(hip_trigger_bex(&src_hit, &dst_hit, &src_lsi, &dst_lsi, NULL, NULL), -1, "Base Exchange Trigger failed\n"); /* update fw db entry */ HIP_IFEL(firewall_update_entry(&src_hit, &dst_hit, lsi_dst, &dst_ip, FIREWALL_STATE_BEX_DEFAULT), -1, "Failed to update fw entry\n"); } if(state_ha == HIP_STATE_ESTABLISHED){ /* update fw db entry */ HIP_IFEL(firewall_update_entry(&src_hit, &dst_hit, lsi_dst, &dst_ip, FIREWALL_STATE_BEX_ESTABLISHED), -1, "Failed to update fw entry\n"); HIP_IFEL(reinject_packet(&src_hit, &dst_hit, m, 4, 0), -1, "Reinject failed\n"); } } out_err: return err; }
/* 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; }
/** * Analyzes incoming TCP packets * * @param *handle the handle that has grabbed the packet, needed when allowing or dropping the packet. * @param hdr pointer to the ip packet being examined. * @param ip_version ipv4 or ipv6 type of traffic. * @return nothing */ int hip_fw_examine_incoming_tcp_packet(void *hdr, int ip_version, int header_size) { int i, optLen, optionsLen, err = 0, state_ha; char *hdrBytes = NULL; struct tcphdr *tcphdr; struct ip *iphdr; struct ip6_hdr *ip6_hdr; //fields for temporary values u_int16_t portTemp; struct in_addr addrTemp; struct in6_addr addr6Temp; /* the following vars are needed for * sending the i1 - initiating the exchange * in case we see that the peer supports hip*/ struct in6_addr peer_ip, own_ip; struct in6_addr peer_hit; in_port_t src_tcp_port; in_port_t dst_tcp_port; /*this is needed for puting in default values for the hits and lsi in the firewall entry*/ struct in6_addr all_zero_default; firewall_hl_t *entry_peer = NULL; struct in6_addr src_lsi, dst_lsi; struct in6_addr src_hit, dst_hit; HIP_DEBUG("\n"); if(ip_version == 4){ iphdr = (struct ip *)hdr; //get the tcp header tcphdr = ((struct tcphdr *) (((char *) iphdr) + header_size)); hdrBytes = ((char *) iphdr) + header_size; HIP_DEBUG_INADDR("the destination", &iphdr->ip_src); //peer and local ip needed for sending the i1 through hipd IPV4_TO_IPV6_MAP(&iphdr->ip_src, &peer_ip); IPV4_TO_IPV6_MAP(&iphdr->ip_dst, &own_ip); } else if(ip_version == 6){ ip6_hdr = (struct ip6_hdr *)hdr; //get the tcp header tcphdr = ((struct tcphdr *) (((char *) ip6_hdr) + header_size)); hdrBytes = ((char *) ip6_hdr) + header_size; //peer and local ip needed for sending the i1 through hipd ipv6_addr_copy(&peer_ip, &ip6_hdr->ip6_src); ipv6_addr_copy(&own_ip, &ip6_hdr->ip6_dst); } /* this condition was originally only for SYN 0 * but below we added a condition for RST 1 and ACK 1 * So, in order for the RST ACK condition to be reachable, * we added the condition for RST 0 here. * The purpose is to process the packets as soon as possible. * Many packets have SYN 0 and RST 0, so they get accepted quickly. */ if((tcphdr->syn == 0) && (tcphdr->rst == 0) && (tcphdr->fin == 0)){ return 1; } /* this shortcut check was removed * because we need to analyze incoming * TCP SYN_ACK, RST_ACK and FIN_ACK packets * even when there are no options * for updating the firewall entry */ /*//check that there are no options if(tcphdr->doff == 5){ return 1; }*/ if((tcphdr->syn == 1) && (tcphdr->ack == 0)){ //incoming, syn=1 and ack=0 /* We need to create state in the firewall db * if there is no entry for the peer yet. */ entry_peer = (firewall_hl_t *)firewall_ip_db_match(&peer_ip); //if there is no entry in fw, add a default one if(!entry_peer){ firewall_add_default_entry(&peer_ip); entry_peer = (firewall_hl_t *)firewall_ip_db_match(&peer_ip); } if(tcp_packet_has_i1_option(hdrBytes, 4*tcphdr->doff)){ /*//swap the ports portTemp = tcphdr->source; tcphdr->source = tcphdr->dest; tcphdr->dest = portTemp; //swap the ip addresses if(ip_version == 4){ addrTemp = iphdr->ip_src; iphdr->ip_src = iphdr->ip_dst; iphdr->ip_dst = addrTemp; } else if(ip_version == 6){ addr6Temp = ip6_hdr->ip6_src; ip6_hdr->ip6_src = ip6_hdr->ip6_dst; ip6_hdr->ip6_dst = addr6Temp; } //set ack field tcphdr->ack_seq = tcphdr->seq + 1; //set seq field tcphdr->seq = htonl(0); //set flags tcphdr->syn = 1; tcphdr->ack = 1; // send packet out after adding HIT // the option is already there but // it has to be added again since // if only the HIT is added, it will // overwrite the i1 option that is // in the options of TCP hip_request_send_tcp_packet(hdr, hdr_size + 4*tcphdr->doff, ip_version, 1, 1); */ //drop original packet return 0; } else{ /* A SYN packet without option indicates lack of peer * HIP support. Updating the fw db to NOT_SUPPORTED. */ state_ha = hip_get_bex_state_from_IPs(&own_ip, &peer_ip, &src_hit, &dst_hit, &src_lsi, &dst_lsi); if(state_ha != HIP_STATE_ESTABLISHED) firewall_update_entry(NULL, NULL, NULL, &peer_ip, FIREWALL_STATE_BEX_NOT_SUPPORTED); //allow packet return 1; } } else if( ((tcphdr->syn == 1) && (tcphdr->ack == 1)) || //SYN_ACK ((tcphdr->rst == 1) && (tcphdr->ack == 1)) || //RST_ACK ((tcphdr->fin == 1) && (tcphdr->ack == 1)) ){ //FIN_ACK //with the new implementation, the i1 is sent out directly /*if(tcp_packet_has_i1_option(hdrBytes, 4*tcphdr->doff)){ // tcp header pointer + 20(minimum header length) // + 4(i1 option length in the TCP options) memcpy(peer_hit, &hdrBytes[20 + 4], 16); hip_request_send_i1_to_hip_peer_from_hipd( peer_hit, peer_ip); //the packet is no more needed drop_packet(handle, packetId); return; } else{*/ /* Signal for normal TCP not * to be blocked with this peer. * Blacklist peer in the hipd db*/ hip_fw_unblock_and_blacklist(&peer_ip); /* updating the fw db if necessary*/ entry_peer = (firewall_hl_t *)firewall_ip_db_match(&peer_ip); //if there is no entry in fw, add a default one if(!entry_peer){ firewall_add_default_entry(&peer_ip); entry_peer = (firewall_hl_t *)firewall_ip_db_match(&peer_ip); } if(entry_peer->bex_state != FIREWALL_STATE_BEX_ESTABLISHED){ //update the firewall db entry HIP_DEBUG("updating fw entry state to NOT_SUPPORTED\n"); firewall_update_entry(NULL, NULL, NULL, &peer_ip, FIREWALL_STATE_BEX_NOT_SUPPORTED); } //normal traffic connections should be allowed to be created return 1; /*}*/ } out_err: /* Allow rest */ return 1; }