/* * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define TRACE_ICMP6 #ifdef TRACE_ICMP6 # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif typedef NetBufferField ICMP6ChecksumField; net_buffer_module_info *gBufferModule; static net_stack_module_info *sStackModule; static net_ndp_module_info *sIPv6NDPModule; net_protocol * icmp6_init_protocol(net_socket *socket) { net_protocol *protocol = new (std::nothrow) net_protocol; if (protocol == NULL) return NULL; return protocol; } status_t icmp6_uninit_protocol(net_protocol *protocol) { delete protocol; return B_OK; } status_t icmp6_open(net_protocol *protocol) { return B_OK; } status_t icmp6_close(net_protocol *protocol) { return B_OK; } status_t icmp6_free(net_protocol *protocol) { return B_OK; } status_t icmp6_connect(net_protocol *protocol, const struct sockaddr *address) { return B_ERROR; } status_t icmp6_accept(net_protocol *protocol, struct net_socket **_acceptedSocket) { return EOPNOTSUPP; } status_t icmp6_control(net_protocol *protocol, int level, int option, void *value, size_t *_length) { return protocol->next->module->control(protocol->next, level, option, value, _length); } status_t icmp6_getsockopt(net_protocol *protocol, int level, int option, void *value, int *length) { return protocol->next->module->getsockopt(protocol->next, level, option, value, length); } status_t icmp6_setsockopt(net_protocol *protocol, int level, int option, const void *value, int length) { return protocol->next->module->setsockopt(protocol->next, level, option, value, length); } status_t icmp6_bind(net_protocol *protocol, const struct sockaddr *address) { return B_ERROR; } status_t icmp6_unbind(net_protocol *protocol, struct sockaddr *address) { return B_ERROR; } status_t icmp6_listen(net_protocol *protocol, int count) { return EOPNOTSUPP; } status_t icmp6_shutdown(net_protocol *protocol, int direction) { return EOPNOTSUPP; } status_t icmp6_send_data(net_protocol *protocol, net_buffer *buffer) { return protocol->next->module->send_data(protocol->next, buffer); } status_t icmp6_send_routed_data(net_protocol *protocol, struct net_route *route, net_buffer *buffer) { return protocol->next->module->send_routed_data(protocol->next, route, buffer); } ssize_t icmp6_send_avail(net_protocol *protocol) { return B_ERROR; } status_t icmp6_read_data(net_protocol *protocol, size_t numBytes, uint32 flags, net_buffer **_buffer) { return B_ERROR; } ssize_t icmp6_read_avail(net_protocol *protocol) { return B_ERROR; } struct net_domain * icmp6_get_domain(net_protocol *protocol) { return protocol->next->module->get_domain(protocol->next); } size_t icmp6_get_mtu(net_protocol *protocol, const struct sockaddr *address) { return protocol->next->module->get_mtu(protocol->next, address); } static net_domain* get_domain(struct net_buffer* buffer) { net_domain* domain; if (buffer->interface_address != NULL) domain = buffer->interface_address->domain; else domain = sStackModule->get_domain(buffer->source->sa_family); if (domain == NULL || domain->module == NULL) return NULL; return domain; } status_t icmp6_receive_data(net_buffer *buffer) { TRACE(("ICMPv6 received some data, buffer length %" B_PRIu32 "\n", buffer->size)); net_domain* domain = get_domain(buffer); if (domain == NULL) return B_ERROR; NetBufferHeaderReader bufferHeader(buffer); if (bufferHeader.Status() < B_OK) return bufferHeader.Status(); icmp6_hdr &header = bufferHeader.Data(); TRACE((" got type %u, code %u, checksum 0x%x\n", header.icmp6_type, header.icmp6_code, header.icmp6_cksum)); net_address_module_info* addressModule = domain->address_module; // compute and check the checksum if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer, IPPROTO_ICMPV6) != 0) return B_BAD_DATA; switch (header.icmp6_type) { case ICMP6_ECHO_REPLY: break; case ICMP6_ECHO_REQUEST: { if (buffer->interface_address != NULL) { // We only reply to echo requests of our local interface; we // don't reply to broadcast requests if (!domain->address_module->equal_addresses( buffer->interface_address->local, buffer->destination)) break; } net_buffer *reply = gBufferModule->duplicate(buffer); if (reply == NULL) return B_NO_MEMORY; gBufferModule->swap_addresses(reply); // There already is an ICMP header, and we'll reuse it NetBufferHeaderReader header(reply); header->icmp6_type = ICMP6_ECHO_REPLY; header->icmp6_code = 0; header->icmp6_cksum = 0; header.Sync(); *ICMP6ChecksumField(reply) = Checksum::PseudoHeader(addressModule, gBufferModule, buffer, IPPROTO_ICMPV6); status_t status = domain->module->send_data(NULL, reply); if (status < B_OK) { gBufferModule->free(reply); return status; } } default: // unrecognized messages go to neighbor discovery protocol handler return sIPv6NDPModule->receive_data(buffer); } gBufferModule->free(buffer); return B_OK; } status_t icmp6_deliver_data(net_protocol *protocol, net_buffer *buffer) { // TODO: does this look OK? return icmp6_receive_data(buffer); } status_t icmp6_error_received(net_error code, net_buffer* data) { return B_ERROR; } status_t icmp6_error_reply(net_protocol* protocol, net_buffer* buffer, net_error error, net_error_data* errorData) { return B_ERROR; } // #pragma mark - static status_t icmp6_init() { sStackModule->register_domain_protocols(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6, "network/protocols/icmp6/v1", "network/protocols/ipv6/v1", NULL); sStackModule->register_domain_receiving_protocol(AF_INET6, IPPROTO_ICMPV6, "network/protocols/icmp6/v1"); return B_OK; } static status_t icmp6_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: return icmp6_init(); case B_MODULE_UNINIT: return B_OK; default: return B_ERROR; } } net_protocol_module_info sICMP6Module = { { "network/protocols/icmp6/v1", 0, icmp6_std_ops }, NET_PROTOCOL_ATOMIC_MESSAGES, icmp6_init_protocol, icmp6_uninit_protocol, icmp6_open, icmp6_close, icmp6_free, icmp6_connect, icmp6_accept, icmp6_control, icmp6_getsockopt, icmp6_setsockopt, icmp6_bind, icmp6_unbind, icmp6_listen, icmp6_shutdown, icmp6_send_data, icmp6_send_routed_data, icmp6_send_avail, icmp6_read_data, icmp6_read_avail, icmp6_get_domain, icmp6_get_mtu, icmp6_receive_data, icmp6_deliver_data, icmp6_error_received, icmp6_error_reply, NULL, // add_ancillary_data() NULL, // process_ancillary_data() NULL, // process_ancillary_data_no_container() NULL, // send_data_no_buffer() NULL // read_data_no_buffer() }; module_dependency module_dependencies[] = { {NET_STACK_MODULE_NAME, (module_info **)&sStackModule}, {NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule}, {"network/datalink_protocols/ipv6_datagram/ndp/v1", (module_info **)&sIPv6NDPModule}, {} }; module_info *modules[] = { (module_info *)&sICMP6Module, NULL };