/** * Translate and reinject an incoming packet back to the networking stack. * Supports TCP, UDP and ICMP. LSI code uses this to translate * the HITs from an incoming packet to the corresponding LSIs. Also, * the system-based opportunistic mode uses this to translate the HITs of * an incoming packet to an IPv4 or IPv6 address. * * @param src_hit source HIT of the packet * @param dst_hit destination HIT of the packet * @param msg a pointer to the transport layer header of the packet * @param len the length of the packet in bytes * @param proto the transport layer protocol of the packet * @param ttl new ttl value for the transformed packet * * @return zero on success and non-zero on error */ int hip_firewall_send_incoming_pkt(const struct in6_addr *src_hit, const struct in6_addr *dst_hit, uint8_t *msg, uint16_t len, int proto, int ttl) { int err = 0, sent, sa_size; int firewall_raw_sock = 0, is_ipv6 = 0, on = 1; struct ip *iphdr = NULL; struct udphdr *udp = NULL; struct tcphdr *tcp = NULL; struct icmphdr *icmp = NULL; struct sockaddr_storage src = { 0 }, dst = { 0 }; struct sockaddr_in6 *sock_src6 = NULL, *sock_dst6 = NULL; struct sockaddr_in *sock_src4 = NULL, *sock_dst4 = NULL; struct in6_addr any = IN6ADDR_ANY_INIT; HIP_ASSERT(src_hit != NULL && dst_hit != NULL); sock_src4 = (struct sockaddr_in *) &src; sock_dst4 = (struct sockaddr_in *) &dst; sock_src6 = (struct sockaddr_in6 *) &src; sock_dst6 = (struct sockaddr_in6 *) &dst; if (IN6_IS_ADDR_V4MAPPED(src_hit)) { sock_src4->sin_family = AF_INET; sock_dst4->sin_family = AF_INET; IPV6_TO_IPV4_MAP(src_hit, &sock_src4->sin_addr); IPV6_TO_IPV4_MAP(dst_hit, &sock_dst4->sin_addr); sa_size = sizeof(struct sockaddr_in); HIP_DEBUG_LSI("src4 addr ", &sock_src4->sin_addr); HIP_DEBUG_LSI("dst4 addr ", &sock_dst4->sin_addr); } else { sock_src6->sin6_family = AF_INET6; ipv6_addr_copy(&sock_src6->sin6_addr, src_hit); sock_dst6->sin6_family = AF_INET6; ipv6_addr_copy(&sock_dst6->sin6_addr, dst_hit); sa_size = sizeof(struct sockaddr_in6); is_ipv6 = 1; } switch (proto) { case IPPROTO_UDP: if (is_ipv6) { HIP_DEBUG(" IPPROTO_UDP v6\n"); firewall_raw_sock = firewall_raw_sock_udp_v6; ((struct udphdr *) msg)->check = ipv6_checksum(IPPROTO_UDP, &sock_src6->sin6_addr, &sock_dst6->sin6_addr, msg, len); } else { HIP_DEBUG(" IPPROTO_UDP v4\n"); firewall_raw_sock = firewall_raw_sock_udp_v4; udp = (struct udphdr *) msg; sa_size = sizeof(struct sockaddr_in); udp->check = htons(0); udp->check = ipv4_checksum(IPPROTO_UDP, (uint8_t *) &sock_src4->sin_addr, (uint8_t *) &sock_dst4->sin_addr, (uint8_t *) udp, len); memmove(msg + sizeof(struct ip), udp, len); } break; case IPPROTO_TCP: tcp = (struct tcphdr *) msg; tcp->check = htons(0); if (is_ipv6) { HIP_DEBUG(" IPPROTO_TCP v6\n"); firewall_raw_sock = firewall_raw_sock_tcp_v6; tcp->check = ipv6_checksum(IPPROTO_TCP, &sock_src6->sin6_addr, &sock_dst6->sin6_addr, msg, len); } else { HIP_DEBUG(" IPPROTO_TCP v4\n"); firewall_raw_sock = firewall_raw_sock_tcp_v4; tcp->check = ipv4_checksum(IPPROTO_TCP, (uint8_t *) &sock_src4->sin_addr, (uint8_t *) &sock_dst4->sin_addr, (uint8_t *) tcp, len); memmove(msg + sizeof(struct ip), tcp, len); } break; case IPPROTO_ICMP: firewall_raw_sock = firewall_raw_sock_icmp_v4; icmp = (struct icmphdr *) msg; icmp->checksum = htons(0); icmp->checksum = inchksum(icmp, len); memmove(msg + sizeof(struct ip), icmp, len); break; case IPPROTO_ICMPV6: goto not_sending; break; default: HIP_ERROR("No protocol family found\n"); break; } if (!is_ipv6) { iphdr = (struct ip *) msg; iphdr->ip_v = 4; iphdr->ip_hl = sizeof(struct ip) >> 2; iphdr->ip_tos = 0; iphdr->ip_len = len + iphdr->ip_hl * 4; iphdr->ip_id = htons(0); iphdr->ip_off = 0; iphdr->ip_ttl = ttl; iphdr->ip_p = proto; iphdr->ip_src = sock_src4->sin_addr; iphdr->ip_dst = sock_dst4->sin_addr; iphdr->ip_sum = htons(0); /* @todo: move the socket option to fw initialization */ if (setsockopt(firewall_raw_sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) { HIP_IFEL(err, -1, "setsockopt IP_HDRINCL ERROR\n"); } sent = sendto(firewall_raw_sock, iphdr, iphdr->ip_len, 0, (struct sockaddr *) &dst, sa_size); if (sent != (int) (len + sizeof(struct ip))) { HIP_ERROR("Could not send the all requested" \ " data (%d/%d)\n", sent, iphdr->ip_len); } else { HIP_DEBUG("sent=%d/%d \n", sent, (len + sizeof(struct ip))); HIP_DEBUG("Packet sent ok\n"); } }
status_t ipv6_send_routed_data(net_protocol* _protocol, struct net_route* route, net_buffer* buffer) { if (route == NULL) return B_BAD_VALUE; ipv6_protocol* protocol = (ipv6_protocol*)_protocol; net_interface* interface = route->interface_address->interface; uint8 protocolNumber; if (protocol != NULL && protocol->socket != NULL) protocolNumber = protocol->socket->protocol; else protocolNumber = buffer->protocol; TRACE_SK(protocol, "SendRoutedData(%p, %p [%ld bytes])", route, buffer, buffer->size); sockaddr_in6& source = *(sockaddr_in6*)buffer->source; sockaddr_in6& destination = *(sockaddr_in6*)buffer->destination; buffer->flags &= ~(MSG_BCAST | MSG_MCAST); if (IN6_IS_ADDR_UNSPECIFIED(&destination.sin6_addr)) return EDESTADDRREQ; if (IN6_IS_ADDR_MULTICAST(&destination.sin6_addr)) buffer->flags |= MSG_MCAST; uint16 dataLength = buffer->size; // Add IPv6 header NetBufferPrepend<ip6_hdr> header(buffer); if (header.Status() != B_OK) return header.Status(); if (buffer->size > 0xffff) return EMSGSIZE; uint32 flowinfo = 0; // TODO: fill in the flow id from somewhere if (protocol) { // fill in traffic class flowinfo |= htonl(protocol->service_type << 20); } // set lower 28 bits header->ip6_flow = htonl(flowinfo) & IPV6_FLOWINFO_MASK; // set upper 4 bits header->ip6_vfc |= IPV6_VERSION; header->ip6_plen = htons(dataLength); header->ip6_nxt = protocolNumber; header->ip6_hlim = ip6_select_hoplimit(protocol, buffer); memcpy(&header->ip6_src, &source.sin6_addr, sizeof(in6_addr)); memcpy(&header->ip6_dst, &destination.sin6_addr, sizeof(in6_addr)); header.Sync(); // write the checksum for ICMPv6 sockets if (protocolNumber == IPPROTO_ICMPV6 && dataLength >= sizeof(struct icmp6_hdr)) { NetBufferField<uint16, sizeof(ip6_hdr) + offsetof(icmp6_hdr, icmp6_cksum)> icmpChecksum(buffer); // first make sure the existing checksum is zero *icmpChecksum = 0; icmpChecksum.Sync(); uint16 checksum = gBufferModule->checksum(buffer, sizeof(ip6_hdr), buffer->size - sizeof(ip6_hdr), false); checksum = ipv6_checksum(&header->ip6_src, &header->ip6_dst, dataLength, protocolNumber, checksum); *icmpChecksum = checksum; } char addrbuf[INET6_ADDRSTRLEN]; ip6_sprintf(&destination.sin6_addr, addrbuf); TRACE_SK(protocol, " SendRoutedData(): destination: %s", addrbuf); uint32 mtu = route->mtu ? route->mtu : interface->mtu; if (buffer->size > mtu) { // we need to fragment the packet return send_fragments(protocol, route, buffer, mtu); } return sDatalinkModule->send_routed_data(route, buffer); }