Beispiel #1
0
/**
 * 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);
}