00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00037 #include <async.h>
00038 #include <atomic.h>
00039 #include <fibril.h>
00040 #include <fibril_synch.h>
00041 #include <stdint.h>
00042 #include <str.h>
00043 #include <ipc/services.h>
00044 #include <ipc/net.h>
00045 #include <ipc/tl.h>
00046 #include <ipc/icmp.h>
00047 #include <sys/time.h>
00048 #include <sys/types.h>
00049 #include <byteorder.h>
00050 #include <errno.h>
00051 #include <adt/hash_table.h>
00052
00053 #include <net/socket_codes.h>
00054 #include <net/ip_protocols.h>
00055 #include <net/inet.h>
00056 #include <net/modules.h>
00057 #include <net/icmp_api.h>
00058 #include <net/icmp_codes.h>
00059 #include <net/icmp_common.h>
00060
00061 #include <packet_client.h>
00062 #include <packet_remote.h>
00063 #include <net_checksum.h>
00064 #include <icmp_client.h>
00065 #include <icmp_remote.h>
00066 #include <il_remote.h>
00067 #include <ip_client.h>
00068 #include <ip_interface.h>
00069 #include <net_interface.h>
00070 #include <tl_remote.h>
00071 #include <tl_skel.h>
00072 #include <icmp_header.h>
00073
00075 #define NAME "icmp"
00076
00078 #define REPLY_KEYS 2
00079
00081 #define REPLY_BUCKETS 1024
00082
00087 #define ICMP_KEEP_LENGTH 8
00088
00097 #define ICMP_CHECKSUM(header, length) \
00098 htons(ip_checksum((uint8_t *) (header), (length)))
00099
00101 #define ICMP_ECHO_TEXT "ICMP hello from HelenOS."
00102
00104 typedef struct {
00106 link_t link;
00107
00109 icmp_param_t id;
00110 icmp_param_t sequence;
00111
00113 fibril_condvar_t condvar;
00114
00116 int result;
00117 } icmp_reply_t;
00118
00120 static int phone_net = -1;
00121 static int phone_ip = -1;
00122 static bool error_reporting = true;
00123 static bool echo_replying = true;
00124 static packet_dimension_t icmp_dimension;
00125
00127 static atomic_t icmp_client;
00128
00130 static fibril_local icmp_param_t icmp_id;
00131 static fibril_local icmp_param_t icmp_seq;
00132
00134 static fibril_mutex_t reply_lock;
00135 static hash_table_t replies;
00136
00137 static hash_index_t replies_hash(unsigned long key[])
00138 {
00139
00140
00141
00142
00143 hash_index_t index = ((key[0] & 0xffff) << 16) | (key[1] & 0xffff);
00144 return (index % REPLY_BUCKETS);
00145 }
00146
00147 static int replies_compare(unsigned long key[], hash_count_t keys, link_t *item)
00148 {
00149 icmp_reply_t *reply =
00150 hash_table_get_instance(item, icmp_reply_t, link);
00151
00152 if (keys == 1)
00153 return (reply->id == key[0]);
00154 else
00155 return ((reply->id == key[0]) && (reply->sequence == key[1]));
00156 }
00157
00158 static void replies_remove_callback(link_t *item)
00159 {
00160 }
00161
00162 static hash_table_operations_t reply_ops = {
00163 .hash = replies_hash,
00164 .compare = replies_compare,
00165 .remove_callback = replies_remove_callback
00166 };
00167
00173 static void icmp_release(packet_t *packet)
00174 {
00175 pq_release_remote(phone_net, packet_get_id(packet));
00176 }
00177
00198 static int icmp_send_packet(icmp_type_t type, icmp_code_t code,
00199 packet_t *packet, icmp_header_t *header, services_t error, ip_ttl_t ttl,
00200 ip_tos_t tos, bool dont_fragment)
00201 {
00202
00203 if ((error) && (!error_reporting)) {
00204 icmp_release(packet);
00205 return EPERM;
00206 }
00207
00208 header->type = type;
00209 header->code = code;
00210
00211
00212
00213
00214
00215
00216 header->checksum = 0;
00217 header->checksum = ICMP_CHECKSUM(header,
00218 packet_get_data_length(packet));
00219
00220 int rc = ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos,
00221 dont_fragment, 0);
00222 if (rc != EOK) {
00223 icmp_release(packet);
00224 return rc;
00225 }
00226
00227 return ip_send_msg(phone_ip, -1, packet, SERVICE_ICMP, error);
00228 }
00229
00241 static icmp_header_t *icmp_prepare_packet(packet_t *packet)
00242 {
00243 size_t total_length = packet_get_data_length(packet);
00244 if (total_length <= 0)
00245 return NULL;
00246
00247 size_t header_length = ip_client_header_length(packet);
00248 if (header_length <= 0)
00249 return NULL;
00250
00251
00252 if ((total_length > header_length + ICMP_KEEP_LENGTH) &&
00253 (packet_trim(packet, 0,
00254 total_length - header_length - ICMP_KEEP_LENGTH) != EOK))
00255 return NULL;
00256
00257 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
00258 if (!header)
00259 return NULL;
00260
00261 bzero(header, sizeof(*header));
00262 return header;
00263 }
00264
00290 static int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size,
00291 mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, bool dont_fragment,
00292 const struct sockaddr *addr, socklen_t addrlen)
00293 {
00294 if (addrlen <= 0)
00295 return EINVAL;
00296
00297 size_t length = (size_t) addrlen;
00298
00299 packet_t *packet = packet_get_4_remote(phone_net, size,
00300 icmp_dimension.addr_len, ICMP_HEADER_SIZE + icmp_dimension.prefix,
00301 icmp_dimension.suffix);
00302 if (!packet)
00303 return ENOMEM;
00304
00305
00306 int rc = packet_set_addr(packet, NULL, (const uint8_t *) addr, length);
00307 if (rc != EOK) {
00308 icmp_release(packet);
00309 return rc;
00310 }
00311
00312
00313 uint8_t *data = (uint8_t *) packet_suffix(packet, size);
00314 if (!data) {
00315 icmp_release(packet);
00316 return ENOMEM;
00317 }
00318
00319
00320 length = 0;
00321 while (size > length + sizeof(ICMP_ECHO_TEXT)) {
00322 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT));
00323 length += sizeof(ICMP_ECHO_TEXT);
00324 }
00325 memcpy(data + length, ICMP_ECHO_TEXT, size - length);
00326
00327
00328 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t);
00329 if (!header) {
00330 icmp_release(packet);
00331 return ENOMEM;
00332 }
00333
00334 bzero(header, sizeof(icmp_header_t));
00335 header->un.echo.identifier = id;
00336 header->un.echo.sequence_number = sequence;
00337
00338
00339 icmp_reply_t *reply = malloc(sizeof(icmp_reply_t));
00340 if (!reply) {
00341 icmp_release(packet);
00342 return ENOMEM;
00343 }
00344
00345 reply->id = id;
00346 reply->sequence = sequence;
00347 fibril_condvar_initialize(&reply->condvar);
00348
00349
00350 fibril_mutex_lock(&reply_lock);
00351
00352 unsigned long key[REPLY_KEYS] = {id, sequence};
00353 hash_table_insert(&replies, key, &reply->link);
00354
00355
00356 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos,
00357 dont_fragment);
00358
00359
00360 rc = fibril_condvar_wait_timeout(&reply->condvar, &reply_lock,
00361 timeout * 1000);
00362 if (rc == EOK)
00363 rc = reply->result;
00364
00365
00366 hash_table_remove(&replies, key, REPLY_KEYS);
00367
00368 fibril_mutex_unlock(&reply_lock);
00369
00370 free(reply);
00371
00372 return rc;
00373 }
00374
00375 static int icmp_destination_unreachable(icmp_code_t code, icmp_param_t mtu,
00376 packet_t *packet)
00377 {
00378 icmp_header_t *header = icmp_prepare_packet(packet);
00379 if (!header) {
00380 icmp_release(packet);
00381 return ENOMEM;
00382 }
00383
00384 if (mtu)
00385 header->un.frag.mtu = mtu;
00386
00387 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header,
00388 SERVICE_ICMP, 0, 0, false);
00389 }
00390
00391 static int icmp_source_quench(packet_t *packet)
00392 {
00393 icmp_header_t *header = icmp_prepare_packet(packet);
00394 if (!header) {
00395 icmp_release(packet);
00396 return ENOMEM;
00397 }
00398
00399 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header,
00400 SERVICE_ICMP, 0, 0, false);
00401 }
00402
00403 static int icmp_time_exceeded(icmp_code_t code, packet_t *packet)
00404 {
00405 icmp_header_t *header = icmp_prepare_packet(packet);
00406 if (!header) {
00407 icmp_release(packet);
00408 return ENOMEM;
00409 }
00410
00411 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header,
00412 SERVICE_ICMP, 0, 0, false);
00413 }
00414
00415 static int icmp_parameter_problem(icmp_code_t code, icmp_param_t pointer,
00416 packet_t *packet)
00417 {
00418 icmp_header_t *header = icmp_prepare_packet(packet);
00419 if (!header) {
00420 icmp_release(packet);
00421 return ENOMEM;
00422 }
00423
00424 header->un.param.pointer = pointer;
00425 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header,
00426 SERVICE_ICMP, 0, 0, false);
00427 }
00428
00440 static void icmp_process_echo_reply(packet_t *packet, icmp_header_t *header,
00441 icmp_type_t type, icmp_code_t code)
00442 {
00443 unsigned long key[REPLY_KEYS] =
00444 {header->un.echo.identifier, header->un.echo.sequence_number};
00445
00446
00447 icmp_release(packet);
00448
00449
00450 fibril_mutex_lock(&reply_lock);
00451
00452 link_t *link = hash_table_find(&replies, key);
00453 if (link != NULL) {
00454 icmp_reply_t *reply =
00455 hash_table_get_instance(link, icmp_reply_t, link);
00456
00457 reply->result = type;
00458 fibril_condvar_signal(&reply->condvar);
00459 }
00460
00461 fibril_mutex_unlock(&reply_lock);
00462 }
00463
00484 static int icmp_process_packet(packet_t *packet, services_t error)
00485 {
00486 icmp_type_t type;
00487 icmp_code_t code;
00488 int rc;
00489
00490 switch (error) {
00491 case SERVICE_NONE:
00492 break;
00493 case SERVICE_ICMP:
00494
00495 rc = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
00496 if (rc < 0)
00497 return rc;
00498
00499
00500 rc = packet_trim(packet, (size_t) rc, 0);
00501 if (rc != EOK)
00502 return rc;
00503
00504 break;
00505 default:
00506 return ENOTSUP;
00507 }
00508
00509
00510 size_t length = ip_client_header_length(packet);
00511 rc = packet_trim(packet, length, 0);
00512 if (rc != EOK)
00513 return rc;
00514
00515 length = packet_get_data_length(packet);
00516 if (length <= 0)
00517 return EINVAL;
00518
00519 if (length < ICMP_HEADER_SIZE)
00520 return EINVAL;
00521
00522 void *data = packet_get_data(packet);
00523 if (!data)
00524 return EINVAL;
00525
00526
00527 icmp_header_t *header = (icmp_header_t *) data;
00528
00529 if (header->checksum) {
00530 while (ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO) {
00531
00532
00533
00534
00535 if (error) {
00536 switch (header->type) {
00537 case ICMP_ECHOREPLY:
00538 header->type = ICMP_ECHO;
00539 continue;
00540 }
00541 }
00542
00543 return EINVAL;
00544 }
00545 }
00546
00547 switch (header->type) {
00548 case ICMP_ECHOREPLY:
00549 if (error)
00550 icmp_process_echo_reply(packet, header, type, code);
00551 else
00552 icmp_process_echo_reply(packet, header, ICMP_ECHO, 0);
00553
00554 return EOK;
00555
00556 case ICMP_ECHO:
00557 if (error) {
00558 icmp_process_echo_reply(packet, header, type, code);
00559 return EOK;
00560 }
00561
00562
00563 if (echo_replying) {
00564 uint8_t *src;
00565 int addrlen = packet_get_addr(packet, &src, NULL);
00566
00567
00568
00569
00570
00571
00572 if ((addrlen > 0) && (packet_set_addr(packet, src, src,
00573 (size_t) addrlen) == EOK)) {
00574
00575 icmp_send_packet(ICMP_ECHOREPLY, 0, packet,
00576 header, 0, 0, 0, 0);
00577 return EOK;
00578 }
00579
00580 return EINVAL;
00581 }
00582
00583 return EPERM;
00584
00585 case ICMP_DEST_UNREACH:
00586 case ICMP_SOURCE_QUENCH:
00587 case ICMP_REDIRECT:
00588 case ICMP_ALTERNATE_ADDR:
00589 case ICMP_ROUTER_ADV:
00590 case ICMP_ROUTER_SOL:
00591 case ICMP_TIME_EXCEEDED:
00592 case ICMP_PARAMETERPROB:
00593 case ICMP_CONVERSION_ERROR:
00594 case ICMP_REDIRECT_MOBILE:
00595 case ICMP_SKIP:
00596 case ICMP_PHOTURIS:
00597 ip_received_error_msg(phone_ip, -1, packet,
00598 SERVICE_IP, SERVICE_ICMP);
00599 return EOK;
00600
00601 default:
00602 return ENOTSUP;
00603 }
00604 }
00605
00612 static void icmp_receiver(ipc_callid_t iid, ipc_call_t *icall)
00613 {
00614 bool loop = true;
00615 packet_t *packet;
00616 int rc;
00617
00618 while (loop) {
00619 switch (IPC_GET_IMETHOD(*icall)) {
00620 case NET_TL_RECEIVED:
00621 rc = packet_translate_remote(phone_net, &packet,
00622 IPC_GET_PACKET(*icall));
00623 if (rc == EOK) {
00624 rc = icmp_process_packet(packet, IPC_GET_ERROR(*icall));
00625 if (rc != EOK)
00626 icmp_release(packet);
00627 }
00628
00629 async_answer_0(iid, (sysarg_t) rc);
00630 break;
00631 case IPC_M_PHONE_HUNGUP:
00632 loop = false;
00633 continue;
00634 default:
00635 async_answer_0(iid, (sysarg_t) ENOTSUP);
00636 }
00637
00638 iid = async_get_call(icall);
00639 }
00640 }
00641
00650 int tl_initialize(int net_phone)
00651 {
00652 measured_string_t names[] = {
00653 {
00654 (uint8_t *) "ICMP_ERROR_REPORTING",
00655 20
00656 },
00657 {
00658 (uint8_t *) "ICMP_ECHO_REPLYING",
00659 18
00660 }
00661 };
00662 measured_string_t *configuration;
00663 size_t count = sizeof(names) / sizeof(measured_string_t);
00664 uint8_t *data;
00665
00666 if (!hash_table_create(&replies, REPLY_BUCKETS, REPLY_KEYS, &reply_ops))
00667 return ENOMEM;
00668
00669 fibril_mutex_initialize(&reply_lock);
00670 atomic_set(&icmp_client, 0);
00671
00672 phone_net = net_phone;
00673 phone_ip = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP,
00674 icmp_receiver);
00675 if (phone_ip < 0)
00676 return phone_ip;
00677
00678 int rc = ip_packet_size_req(phone_ip, -1, &icmp_dimension);
00679 if (rc != EOK)
00680 return rc;
00681
00682 icmp_dimension.prefix += ICMP_HEADER_SIZE;
00683 icmp_dimension.content -= ICMP_HEADER_SIZE;
00684
00685
00686 configuration = &names[0];
00687 rc = net_get_conf_req(phone_net, &configuration, count, &data);
00688 if (rc != EOK)
00689 return rc;
00690
00691 if (configuration) {
00692 if (configuration[0].value)
00693 error_reporting = (configuration[0].value[0] == 'y');
00694
00695 if (configuration[1].value)
00696 echo_replying = (configuration[1].value[0] == 'y');
00697
00698 net_free_settings(configuration, data);
00699 }
00700
00701 return EOK;
00702 }
00703
00709 void tl_connection(void)
00710 {
00711 icmp_id = (icmp_param_t) atomic_postinc(&icmp_client);
00712 icmp_seq = 1;
00713 }
00714
00739 int tl_message(ipc_callid_t callid, ipc_call_t *call,
00740 ipc_call_t *answer, size_t *count)
00741 {
00742 struct sockaddr *addr;
00743 size_t size;
00744 packet_t *packet;
00745 int rc;
00746
00747 *count = 0;
00748
00749 switch (IPC_GET_IMETHOD(*call)) {
00750 case NET_ICMP_ECHO:
00751 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size);
00752 if (rc != EOK)
00753 return rc;
00754
00755 rc = icmp_echo(icmp_id, icmp_seq, ICMP_GET_SIZE(*call),
00756 ICMP_GET_TIMEOUT(*call), ICMP_GET_TTL(*call),
00757 ICMP_GET_TOS(*call), ICMP_GET_DONT_FRAGMENT(*call),
00758 addr, (socklen_t) size);
00759
00760 free(addr);
00761 icmp_seq++;
00762 return rc;
00763
00764 case NET_ICMP_DEST_UNREACH:
00765 rc = packet_translate_remote(phone_net, &packet,
00766 IPC_GET_PACKET(*call));
00767 if (rc != EOK)
00768 return rc;
00769
00770 return icmp_destination_unreachable(ICMP_GET_CODE(*call),
00771 ICMP_GET_MTU(*call), packet);
00772
00773 case NET_ICMP_SOURCE_QUENCH:
00774 rc = packet_translate_remote(phone_net, &packet,
00775 IPC_GET_PACKET(*call));
00776 if (rc != EOK)
00777 return rc;
00778
00779 return icmp_source_quench(packet);
00780
00781 case NET_ICMP_TIME_EXCEEDED:
00782 rc = packet_translate_remote(phone_net, &packet,
00783 IPC_GET_PACKET(*call));
00784 if (rc != EOK)
00785 return rc;
00786
00787 return icmp_time_exceeded(ICMP_GET_CODE(*call), packet);
00788
00789 case NET_ICMP_PARAMETERPROB:
00790 rc = packet_translate_remote(phone_net, &packet,
00791 IPC_GET_PACKET(*call));
00792 if (rc != EOK)
00793 return rc;
00794
00795 return icmp_parameter_problem(ICMP_GET_CODE(*call),
00796 ICMP_GET_POINTER(*call), packet);
00797 }
00798
00799 return ENOTSUP;
00800 }
00801
00802 int main(int argc, char *argv[])
00803 {
00804
00805 return tl_module_start(SERVICE_ICMP);
00806 }
00807