Changeset 89c57b6 in mainline for uspace/srv/net/tl/icmp/icmp.c
- Timestamp:
- 2011-04-13T14:45:41Z (14 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 88634420
- Parents:
- cefb126 (diff), 17279ead (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/srv/net/tl/icmp/icmp.c
rcefb126 r89c57b6 28 28 29 29 /** @addtogroup icmp 30 * 30 * @{ 31 31 */ 32 32 33 33 /** @file 34 * ICMP module implementation. 35 * @see icmp.h 34 * ICMP module implementation. 36 35 */ 37 36 … … 42 41 #include <stdint.h> 43 42 #include <str.h> 44 #include <ipc/ipc.h>45 43 #include <ipc/services.h> 44 #include <ipc/net.h> 45 #include <ipc/tl.h> 46 #include <ipc/icmp.h> 46 47 #include <sys/time.h> 47 48 #include <sys/types.h> 48 49 #include <net_err.h> 50 #include <net_messages.h> 51 #include <net_modules.h> 52 #include <packet/packet_client.h> 49 #include <byteorder.h> 50 #include <errno.h> 51 #include <adt/hash_table.h> 52 53 #include <net/socket_codes.h> 54 #include <net/ip_protocols.h> 55 #include <net/inet.h> 56 #include <net/modules.h> 57 #include <net/icmp_api.h> 58 #include <net/icmp_codes.h> 59 #include <net/icmp_common.h> 60 61 #include <packet_client.h> 53 62 #include <packet_remote.h> 54 #include <net_byteorder.h>55 63 #include <net_checksum.h> 56 #include <icmp_api.h>57 64 #include <icmp_client.h> 58 #include <icmp_codes.h> 59 #include <icmp_common.h> 60 #include <icmp_interface.h> 61 #include <il_interface.h> 62 #include <inet.h> 65 #include <icmp_remote.h> 66 #include <il_remote.h> 63 67 #include <ip_client.h> 64 68 #include <ip_interface.h> 65 #include <ip_protocols.h>66 69 #include <net_interface.h> 67 #include <socket_codes.h> 68 #include <socket_errno.h> 69 #include <tl_messages.h> 70 #include <tl_interface.h> 71 #include <tl_local.h> 72 #include <icmp_messages.h> 70 #include <tl_remote.h> 71 #include <tl_skel.h> 73 72 #include <icmp_header.h> 74 73 75 #include "icmp.h" 76 #include "icmp_module.h" 77 78 /** ICMP module name. 79 */ 80 #define NAME "ICMP protocol" 81 82 /** Default ICMP error reporting. 83 */ 84 #define NET_DEFAULT_ICMP_ERROR_REPORTING true 85 86 /** Default ICMP echo replying. 87 */ 88 #define NET_DEFAULT_ICMP_ECHO_REPLYING true 89 90 /** Original datagram length in bytes transfered to the error notification message. 91 */ 92 #define ICMP_KEEP_LENGTH 8 93 94 /** Free identifier numbers pool start. 95 */ 96 #define ICMP_FREE_IDS_START 1 97 98 /** Free identifier numbers pool end. 99 */ 100 #define ICMP_FREE_IDS_END UINT16_MAX 101 102 /** Computes the ICMP datagram checksum. 103 * @param[in,out] header The ICMP datagram header. 104 * @param[in] length The total datagram length. 105 * @returns The computed checksum. 106 */ 107 #define ICMP_CHECKSUM(header, length) htons(ip_checksum((uint8_t *) (header), (length))) 108 109 /** An echo request datagrams pattern. 110 */ 111 #define ICMP_ECHO_TEXT "Hello from HelenOS." 112 113 /** Computes an ICMP reply data key. 114 * @param[in] id The message identifier. 115 * @param[in] sequence The message sequence number. 116 * @returns The computed ICMP reply data key. 117 */ 118 #define ICMP_GET_REPLY_KEY(id, sequence) (((id) << 16) | (sequence &0xFFFF)) 119 120 /** Processes the received ICMP packet. 121 * Is used as an entry point from the underlying IP module. 122 * Releases the packet on error. 123 * @param device_id The device identifier. Ignored parameter. 124 * @param[in,out] packet The received packet. 125 * @param receiver The target service. Ignored parameter. 126 * @param[in] error The packet error reporting service. Prefixes the received packet. 127 * @returns EOK on success. 128 * @returns Other error codes as defined for the icmp_process_packet() function. 129 */ 130 int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error); 131 132 /** Processes the received ICMP packet. 133 * Notifies the destination socket application. 134 * @param[in,out] packet The received packet. 135 * @param[in] error The packet error reporting service. Prefixes the received packet. 136 * @returns EOK on success. 137 * @returns EINVAL if the packet is not valid. 138 * @returns EINVAL if the stored packet address is not the an_addr_t. 139 * @returns EINVAL if the packet does not contain any data. 140 * @returns NO_DATA if the packet content is shorter than the user datagram header. 141 * @returns ENOMEM if there is not enough memory left. 142 * @returns EADDRNOTAVAIL if the destination socket does not exist. 143 * @returns Other error codes as defined for the ip_client_process_packet() function. 144 */ 145 int icmp_process_packet(packet_t packet, services_t error); 146 147 /** Processes the client messages. 148 * Remembers the assigned identifier and sequence numbers. 149 * Runs until the client module disconnects. 150 * @param[in] callid The message identifier. 151 * @param[in] call The message parameters. 152 * @returns EOK. 153 * @see icmp_interface.h 154 * @see icmp_api.h 155 */ 156 int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call); 157 158 /** Processes the generic client messages. 159 * @param[in] call The message parameters. 160 * @returns EOK on success. 161 * @returns ENOTSUP if the message is not known. 162 * @returns Other error codes as defined for the packet_translate() function. 163 * @returns Other error codes as defined for the icmp_destination_unreachable_msg() function. 164 * @returns Other error codes as defined for the icmp_source_quench_msg() function. 165 * @returns Other error codes as defined for the icmp_time_exceeded_msg() function. 166 * @returns Other error codes as defined for the icmp_parameter_problem_msg() function. 167 * @see icmp_interface.h 168 */ 169 int icmp_process_message(ipc_call_t * call); 170 171 /** Releases the packet and returns the result. 172 * @param[in] packet The packet queue to be released. 173 * @param[in] result The result to be returned. 174 * @returns The result parameter. 175 */ 176 int icmp_release_and_return(packet_t packet, int result); 177 178 /** Requests an echo message. 179 * Sends a packet with specified parameters to the target host and waits for the reply upto the given timeout. 180 * Blocks the caller until the reply or the timeout occurs. 181 * @param[in] id The message identifier. 182 * @param[in] sequence The message sequence parameter. 183 * @param[in] size The message data length in bytes. 184 * @param[in] timeout The timeout in miliseconds. 185 * @param[in] ttl The time to live. 186 * @param[in] tos The type of service. 187 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery. 188 * @param[in] addr The target host address. 189 * @param[in] addrlen The torget host address length. 190 * @returns ICMP_ECHO on success. 191 * @returns ETIMEOUT if the reply has not arrived before the timeout. 192 * @returns ICMP type of the received error notification. 193 * @returns EINVAL if the addrlen parameter is less or equal to zero (<=0). 194 * @returns ENOMEM if there is not enough memory left. 195 * @returns EPARTY if there was an internal error. 196 */ 197 int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen); 198 199 /** Prepares the ICMP error packet. 200 * Truncates the original packet if longer than ICMP_KEEP_LENGTH bytes. 201 * Prefixes and returns the ICMP header. 202 * @param[in,out] packet The original packet. 203 * @returns The prefixed ICMP header. 204 * @returns NULL on errors. 205 */ 206 icmp_header_ref icmp_prepare_packet(packet_t packet); 207 208 /** Sends the ICMP message. 209 * Sets the message type and code and computes the checksum. 210 * Error messages are sent only if allowed in the configuration. 211 * Releases the packet on errors. 212 * @param[in] type The message type. 213 * @param[in] code The message code. 214 * @param[in] packet The message packet to be sent. 215 * @param[in] header The ICMP header. 216 * @param[in] error The error service to be announced. Should be SERVICE_ICMP or zero (0). 217 * @param[in] ttl The time to live. 218 * @param[in] tos The type of service. 219 * @param[in] dont_fragment The value indicating whether the datagram must not be fragmented. Is used as a MTU discovery. 220 * @returns EOK on success. 221 * @returns EPERM if the error message is not allowed. 222 */ 223 int icmp_send_packet(icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment); 224 225 /** Tries to set the pending reply result as the received message type. 226 * If the reply data is not present, the reply timed out and the other fibril 227 * is already awake. 228 * Releases the packet. 229 * @param[in] packet The received reply message. 230 * @param[in] header The ICMP message header. 231 * @param[in] type The received reply message type. 232 * @param[in] code The received reply message code. 233 * @returns EOK. 234 */ 235 int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code); 236 237 /** Assigns a new identifier for the connection. 238 * Fills the echo data parameter with the assigned values. 239 * @param[in,out] echo_data The echo data to be bound. 240 * @returns Index of the inserted echo data. 241 * @returns EBADMEM if the echo_data parameter is NULL. 242 * @returns ENOTCONN if no free identifier have been found. 243 */ 244 int icmp_bind_free_id(icmp_echo_ref echo_data); 245 246 /** ICMP global data. 247 */ 248 icmp_globals_t icmp_globals; 249 250 INT_MAP_IMPLEMENT(icmp_replies, icmp_reply_t); 251 252 INT_MAP_IMPLEMENT(icmp_echo_data, icmp_echo_t); 253 254 int icmp_echo_msg(int icmp_phone, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen){ 255 icmp_echo_ref echo_data; 256 int res; 257 258 fibril_rwlock_write_lock(&icmp_globals.lock); 259 // use the phone as the echo data index 260 echo_data = icmp_echo_data_find(&icmp_globals.echo_data, icmp_phone); 261 if(! echo_data){ 262 res = ENOENT; 263 }else{ 264 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, size, timeout, ttl, tos, dont_fragment, addr, addrlen); 265 if(echo_data->sequence_number < UINT16_MAX){ 266 ++ echo_data->sequence_number; 267 }else{ 268 echo_data->sequence_number = 0; 269 } 270 } 271 fibril_rwlock_write_unlock(&icmp_globals.lock); 272 return res; 273 } 274 275 int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size, mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment, const struct sockaddr * addr, socklen_t addrlen){ 276 ERROR_DECLARE; 277 278 icmp_header_ref header; 279 packet_t packet; 280 size_t length; 281 uint8_t * data; 282 icmp_reply_ref reply; 283 int reply_key; 74 /** ICMP module name */ 75 #define NAME "icmp" 76 77 /** Number of replies hash table keys */ 78 #define REPLY_KEYS 2 79 80 /** Number of replies hash table buckets */ 81 #define REPLY_BUCKETS 1024 82 83 /** 84 * Original datagram length in bytes transfered to the error 85 * notification message. 86 */ 87 #define ICMP_KEEP_LENGTH 8 88 89 /** Compute the ICMP datagram checksum. 90 * 91 * @param[in,out] header ICMP datagram header. 92 * @param[in] length Total datagram length. 93 * 94 * @return Computed checksum. 95 * 96 */ 97 #define ICMP_CHECKSUM(header, length) \ 98 htons(ip_checksum((uint8_t *) (header), (length))) 99 100 /** An echo request datagrams pattern. */ 101 #define ICMP_ECHO_TEXT "ICMP hello from HelenOS." 102 103 /** ICMP reply data. */ 104 typedef struct { 105 /** Hash table link */ 106 link_t link; 107 108 /** Reply identification and sequence */ 109 icmp_param_t id; 110 icmp_param_t sequence; 111 112 /** Reply signaling */ 113 fibril_condvar_t condvar; 114 115 /** Reply result */ 284 116 int result; 285 int index; 286 287 if(addrlen <= 0){ 117 } icmp_reply_t; 118 119 /** Global data */ 120 static int phone_net = -1; 121 static int phone_ip = -1; 122 static bool error_reporting = true; 123 static bool echo_replying = true; 124 static packet_dimension_t icmp_dimension; 125 126 /** ICMP client identification counter */ 127 static atomic_t icmp_client; 128 129 /** ICMP identifier and sequence number (client-specific) */ 130 static fibril_local icmp_param_t icmp_id; 131 static fibril_local icmp_param_t icmp_seq; 132 133 /** Reply hash table */ 134 static fibril_mutex_t reply_lock; 135 static hash_table_t replies; 136 137 static hash_index_t replies_hash(unsigned long key[]) 138 { 139 /* 140 * ICMP identifier and sequence numbers 141 * are 16-bit values. 142 */ 143 hash_index_t index = ((key[0] & 0xffff) << 16) | (key[1] & 0xffff); 144 return (index % REPLY_BUCKETS); 145 } 146 147 static int replies_compare(unsigned long key[], hash_count_t keys, link_t *item) 148 { 149 icmp_reply_t *reply = 150 hash_table_get_instance(item, icmp_reply_t, link); 151 152 if (keys == 1) 153 return (reply->id == key[0]); 154 else 155 return ((reply->id == key[0]) && (reply->sequence == key[1])); 156 } 157 158 static void replies_remove_callback(link_t *item) 159 { 160 } 161 162 static hash_table_operations_t reply_ops = { 163 .hash = replies_hash, 164 .compare = replies_compare, 165 .remove_callback = replies_remove_callback 166 }; 167 168 /** Release the packet and return the result. 169 * 170 * @param[in] packet Packet queue to be released. 171 * 172 */ 173 static void icmp_release(packet_t *packet) 174 { 175 pq_release_remote(phone_net, packet_get_id(packet)); 176 } 177 178 /** Send the ICMP message. 179 * 180 * Set the message type and code and compute the checksum. 181 * Error messages are sent only if allowed in the configuration. 182 * Release the packet on errors. 183 * 184 * @param[in] type Message type. 185 * @param[in] code Message code. 186 * @param[in] packet Message packet to be sent. 187 * @param[in] header ICMP header. 188 * @param[in] error Error service to be announced. Should be 189 * SERVICE_ICMP or zero. 190 * @param[in] ttl Time to live. 191 * @param[in] tos Type of service. 192 * @param[in] dont_fragment Disable fragmentation. 193 * 194 * @return EOK on success. 195 * @return EPERM if the error message is not allowed. 196 * 197 */ 198 static int icmp_send_packet(icmp_type_t type, icmp_code_t code, 199 packet_t *packet, icmp_header_t *header, services_t error, ip_ttl_t ttl, 200 ip_tos_t tos, bool dont_fragment) 201 { 202 /* Do not send an error if disabled */ 203 if ((error) && (!error_reporting)) { 204 icmp_release(packet); 205 return EPERM; 206 } 207 208 header->type = type; 209 header->code = code; 210 211 /* 212 * The checksum needs to be calculated 213 * with a virtual checksum field set to 214 * zero. 215 */ 216 header->checksum = 0; 217 header->checksum = ICMP_CHECKSUM(header, 218 packet_get_data_length(packet)); 219 220 int rc = ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos, 221 dont_fragment, 0); 222 if (rc != EOK) { 223 icmp_release(packet); 224 return rc; 225 } 226 227 return ip_send_msg(phone_ip, -1, packet, SERVICE_ICMP, error); 228 } 229 230 /** Prepare the ICMP error packet. 231 * 232 * Truncate the original packet if longer than ICMP_KEEP_LENGTH bytes. 233 * Prefix and return the ICMP header. 234 * 235 * @param[in,out] packet Original packet. 236 * 237 * @return The prefixed ICMP header. 238 * @return NULL on errors. 239 * 240 */ 241 static icmp_header_t *icmp_prepare_packet(packet_t *packet) 242 { 243 size_t total_length = packet_get_data_length(packet); 244 if (total_length <= 0) 245 return NULL; 246 247 size_t header_length = ip_client_header_length(packet); 248 if (header_length <= 0) 249 return NULL; 250 251 /* Truncate if longer than 64 bits (without the IP header) */ 252 if ((total_length > header_length + ICMP_KEEP_LENGTH) && 253 (packet_trim(packet, 0, 254 total_length - header_length - ICMP_KEEP_LENGTH) != EOK)) 255 return NULL; 256 257 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t); 258 if (!header) 259 return NULL; 260 261 bzero(header, sizeof(*header)); 262 return header; 263 } 264 265 /** Request an echo message. 266 * 267 * Send a packet with specified parameters to the target host 268 * and wait for the reply upto the given timeout. 269 * Block the caller until the reply or the timeout occurs. 270 * 271 * @param[in] id Message identifier. 272 * @param[in] sequence Message sequence parameter. 273 * @param[in] size Message data length in bytes. 274 * @param[in] timeout Timeout in miliseconds. 275 * @param[in] ttl Time to live. 276 * @param[in] tos Type of service. 277 * @param[in] dont_fragment Disable fragmentation. 278 * @param[in] addr Target host address. 279 * @param[in] addrlen Torget host address length. 280 * 281 * @return ICMP_ECHO on success. 282 * @return ETIMEOUT if the reply has not arrived before the 283 * timeout. 284 * @return ICMP type of the received error notification. 285 * @return EINVAL if the addrlen parameter is less or equal to 286 * zero. 287 * @return ENOMEM if there is not enough memory left. 288 * 289 */ 290 static int icmp_echo(icmp_param_t id, icmp_param_t sequence, size_t size, 291 mseconds_t timeout, ip_ttl_t ttl, ip_tos_t tos, bool dont_fragment, 292 const struct sockaddr *addr, socklen_t addrlen) 293 { 294 if (addrlen <= 0) 288 295 return EINVAL; 289 } 290 length = (size_t) addrlen; 291 // TODO do not ask all the time 292 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension)); 293 packet = packet_get_4_remote(icmp_globals.net_phone, size, icmp_globals.packet_dimension.addr_len, ICMP_HEADER_SIZE + icmp_globals.packet_dimension.prefix, icmp_globals.packet_dimension.suffix); 294 if(! packet){ 296 297 size_t length = (size_t) addrlen; 298 299 packet_t *packet = packet_get_4_remote(phone_net, size, 300 icmp_dimension.addr_len, ICMP_HEADER_SIZE + icmp_dimension.prefix, 301 icmp_dimension.suffix); 302 if (!packet) 295 303 return ENOMEM; 296 } 297 298 // prepare the requesting packet 299 // set the destination address 300 if(ERROR_OCCURRED(packet_set_addr(packet, NULL, (const uint8_t *) addr, length))){ 301 return icmp_release_and_return(packet, ERROR_CODE); 302 } 303 // allocate space in the packet 304 data = (uint8_t *) packet_suffix(packet, size); 305 if(! data){ 306 return icmp_release_and_return(packet, ENOMEM); 307 } 308 // fill the data 304 305 /* Prepare the requesting packet, set the destination address. */ 306 int rc = packet_set_addr(packet, NULL, (const uint8_t *) addr, length); 307 if (rc != EOK) { 308 icmp_release(packet); 309 return rc; 310 } 311 312 /* Allocate space in the packet */ 313 uint8_t *data = (uint8_t *) packet_suffix(packet, size); 314 if (!data) { 315 icmp_release(packet); 316 return ENOMEM; 317 } 318 319 /* Fill the data */ 309 320 length = 0; 310 while (size > length + sizeof(ICMP_ECHO_TEXT)){321 while (size > length + sizeof(ICMP_ECHO_TEXT)) { 311 322 memcpy(data + length, ICMP_ECHO_TEXT, sizeof(ICMP_ECHO_TEXT)); 312 323 length += sizeof(ICMP_ECHO_TEXT); 313 324 } 314 325 memcpy(data + length, ICMP_ECHO_TEXT, size - length); 315 // prefix the header 316 header = PACKET_PREFIX(packet, icmp_header_t); 317 if(! header){ 318 return icmp_release_and_return(packet, ENOMEM); 319 } 320 bzero(header, sizeof(*header)); 326 327 /* Prefix the header */ 328 icmp_header_t *header = PACKET_PREFIX(packet, icmp_header_t); 329 if (!header) { 330 icmp_release(packet); 331 return ENOMEM; 332 } 333 334 bzero(header, sizeof(icmp_header_t)); 321 335 header->un.echo.identifier = id; 322 336 header->un.echo.sequence_number = sequence; 323 324 // prepare the reply structure 325 reply = malloc(sizeof(*reply)); 326 if(! reply){ 327 return icmp_release_and_return(packet, ENOMEM); 328 } 329 fibril_mutex_initialize(&reply->mutex); 330 fibril_mutex_lock(&reply->mutex); 337 338 /* Prepare the reply structure */ 339 icmp_reply_t *reply = malloc(sizeof(icmp_reply_t)); 340 if (!reply) { 341 icmp_release(packet); 342 return ENOMEM; 343 } 344 345 reply->id = id; 346 reply->sequence = sequence; 331 347 fibril_condvar_initialize(&reply->condvar); 332 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number); 333 index = icmp_replies_add(&icmp_globals.replies, reply_key, reply); 334 if(index < 0){ 335 free(reply); 336 return icmp_release_and_return(packet, index); 337 } 338 339 // unlock the globals so that we can wait for the reply 340 fibril_rwlock_write_unlock(&icmp_globals.lock); 341 342 // send the request 343 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, dont_fragment); 344 345 // wait for the reply 346 // timeout in microseconds 347 if(ERROR_OCCURRED(fibril_condvar_wait_timeout(&reply->condvar, &reply->mutex, timeout * 1000))){ 348 result = ERROR_CODE; 349 }else{ 350 // read the result 351 result = reply->result; 352 } 353 354 // drop the reply mutex before locking the globals again 355 fibril_mutex_unlock(&reply->mutex); 356 fibril_rwlock_write_lock(&icmp_globals.lock); 357 358 // destroy the reply structure 359 icmp_replies_exclude_index(&icmp_globals.replies, index); 360 return result; 361 } 362 363 int icmp_destination_unreachable_msg(int icmp_phone, icmp_code_t code, icmp_param_t mtu, packet_t packet){ 364 icmp_header_ref header; 365 366 header = icmp_prepare_packet(packet); 367 if(! header){ 368 return icmp_release_and_return(packet, ENOMEM); 369 } 370 if(mtu){ 348 349 /* Add the reply to the replies hash table */ 350 fibril_mutex_lock(&reply_lock); 351 352 unsigned long key[REPLY_KEYS] = {id, sequence}; 353 hash_table_insert(&replies, key, &reply->link); 354 355 /* Send the request */ 356 icmp_send_packet(ICMP_ECHO, 0, packet, header, 0, ttl, tos, 357 dont_fragment); 358 359 /* Wait for the reply. Timeout in microseconds. */ 360 rc = fibril_condvar_wait_timeout(&reply->condvar, &reply_lock, 361 timeout * 1000); 362 if (rc == EOK) 363 rc = reply->result; 364 365 /* Remove the reply from the replies hash table */ 366 hash_table_remove(&replies, key, REPLY_KEYS); 367 368 fibril_mutex_unlock(&reply_lock); 369 370 free(reply); 371 372 return rc; 373 } 374 375 static int icmp_destination_unreachable(icmp_code_t code, icmp_param_t mtu, 376 packet_t *packet) 377 { 378 icmp_header_t *header = icmp_prepare_packet(packet); 379 if (!header) { 380 icmp_release(packet); 381 return ENOMEM; 382 } 383 384 if (mtu) 371 385 header->un.frag.mtu = mtu; 372 } 373 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header, SERVICE_ICMP, 0, 0, 0); 374 } 375 376 int icmp_source_quench_msg(int icmp_phone, packet_t packet){ 377 icmp_header_ref header; 378 379 header = icmp_prepare_packet(packet); 380 if(! header){ 381 return icmp_release_and_return(packet, ENOMEM); 382 } 383 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header, SERVICE_ICMP, 0, 0, 0); 384 } 385 386 int icmp_time_exceeded_msg(int icmp_phone, icmp_code_t code, packet_t packet){ 387 icmp_header_ref header; 388 389 header = icmp_prepare_packet(packet); 390 if(! header){ 391 return icmp_release_and_return(packet, ENOMEM); 392 } 393 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header, SERVICE_ICMP, 0, 0, 0); 394 } 395 396 int icmp_parameter_problem_msg(int icmp_phone, icmp_code_t code, icmp_param_t pointer, packet_t packet){ 397 icmp_header_ref header; 398 399 header = icmp_prepare_packet(packet); 400 if(! header){ 401 return icmp_release_and_return(packet, ENOMEM); 402 } 386 387 return icmp_send_packet(ICMP_DEST_UNREACH, code, packet, header, 388 SERVICE_ICMP, 0, 0, false); 389 } 390 391 static int icmp_source_quench(packet_t *packet) 392 { 393 icmp_header_t *header = icmp_prepare_packet(packet); 394 if (!header) { 395 icmp_release(packet); 396 return ENOMEM; 397 } 398 399 return icmp_send_packet(ICMP_SOURCE_QUENCH, 0, packet, header, 400 SERVICE_ICMP, 0, 0, false); 401 } 402 403 static int icmp_time_exceeded(icmp_code_t code, packet_t *packet) 404 { 405 icmp_header_t *header = icmp_prepare_packet(packet); 406 if (!header) { 407 icmp_release(packet); 408 return ENOMEM; 409 } 410 411 return icmp_send_packet(ICMP_TIME_EXCEEDED, code, packet, header, 412 SERVICE_ICMP, 0, 0, false); 413 } 414 415 static int icmp_parameter_problem(icmp_code_t code, icmp_param_t pointer, 416 packet_t *packet) 417 { 418 icmp_header_t *header = icmp_prepare_packet(packet); 419 if (!header) { 420 icmp_release(packet); 421 return ENOMEM; 422 } 423 403 424 header->un.param.pointer = pointer; 404 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header, SERVICE_ICMP, 0, 0, 0); 405 } 406 407 icmp_header_ref icmp_prepare_packet(packet_t packet){ 408 icmp_header_ref header; 409 size_t header_length; 410 size_t total_length; 411 412 total_length = packet_get_data_length(packet); 413 if(total_length <= 0){ 414 return NULL; 415 } 416 header_length = ip_client_header_length(packet); 417 if(header_length <= 0){ 418 return NULL; 419 } 420 // truncate if longer than 64 bits (without the IP header) 421 if((total_length > header_length + ICMP_KEEP_LENGTH) 422 && (packet_trim(packet, 0, total_length - header_length - ICMP_KEEP_LENGTH) != EOK)){ 423 return NULL; 424 } 425 header = PACKET_PREFIX(packet, icmp_header_t); 426 if(! header){ 427 return NULL; 428 } 429 bzero(header, sizeof(*header)); 430 return header; 431 } 432 433 int icmp_send_packet(icmp_type_t type, icmp_code_t code, packet_t packet, icmp_header_ref header, services_t error, ip_ttl_t ttl, ip_tos_t tos, int dont_fragment){ 434 ERROR_DECLARE; 435 436 // do not send an error if disabled 437 if(error && (! icmp_globals.error_reporting)){ 438 return icmp_release_and_return(packet, EPERM); 439 } 440 header->type = type; 441 header->code = code; 442 header->checksum = 0; 443 header->checksum = ICMP_CHECKSUM(header, packet_get_data_length(packet)); 444 if(ERROR_OCCURRED(ip_client_prepare_packet(packet, IPPROTO_ICMP, ttl, tos, dont_fragment, 0))){ 445 return icmp_release_and_return(packet, ERROR_CODE); 446 } 447 return ip_send_msg(icmp_globals.ip_phone, -1, packet, SERVICE_ICMP, error); 448 } 449 450 int icmp_connect_module(services_t service, suseconds_t timeout){ 451 icmp_echo_ref echo_data; 452 icmp_param_t id; 453 int index; 454 455 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data)); 456 if(! echo_data){ 457 return ENOMEM; 458 } 459 // assign a new identifier 460 fibril_rwlock_write_lock(&icmp_globals.lock); 461 index = icmp_bind_free_id(echo_data); 462 if(index < 0){ 463 free(echo_data); 464 fibril_rwlock_write_unlock(&icmp_globals.lock); 465 return index; 466 }else{ 467 id = echo_data->identifier; 468 fibril_rwlock_write_unlock(&icmp_globals.lock); 469 // return the echo data identifier as the ICMP phone 470 return id; 471 } 472 } 473 474 int icmp_initialize(async_client_conn_t client_connection){ 475 ERROR_DECLARE; 476 477 measured_string_t names[] = {{str_dup("ICMP_ERROR_REPORTING"), 20}, {str_dup("ICMP_ECHO_REPLYING"), 18}}; 478 measured_string_ref configuration; 479 size_t count = sizeof(names) / sizeof(measured_string_t); 480 char * data; 481 482 fibril_rwlock_initialize(&icmp_globals.lock); 483 fibril_rwlock_write_lock(&icmp_globals.lock); 484 icmp_replies_initialize(&icmp_globals.replies); 485 icmp_echo_data_initialize(&icmp_globals.echo_data); 486 icmp_globals.ip_phone = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, client_connection, icmp_received_msg); 487 if(icmp_globals.ip_phone < 0){ 488 return icmp_globals.ip_phone; 489 } 490 ERROR_PROPAGATE(ip_packet_size_req(icmp_globals.ip_phone, -1, &icmp_globals.packet_dimension)); 491 icmp_globals.packet_dimension.prefix += ICMP_HEADER_SIZE; 492 icmp_globals.packet_dimension.content -= ICMP_HEADER_SIZE; 493 // get configuration 494 icmp_globals.error_reporting = NET_DEFAULT_ICMP_ERROR_REPORTING; 495 icmp_globals.echo_replying = NET_DEFAULT_ICMP_ECHO_REPLYING; 496 configuration = &names[0]; 497 ERROR_PROPAGATE(net_get_conf_req(icmp_globals.net_phone, &configuration, count, &data)); 498 if(configuration){ 499 if(configuration[0].value){ 500 icmp_globals.error_reporting = (configuration[0].value[0] == 'y'); 501 } 502 if(configuration[1].value){ 503 icmp_globals.echo_replying = (configuration[1].value[0] == 'y'); 504 } 505 net_free_settings(configuration, data); 506 } 507 fibril_rwlock_write_unlock(&icmp_globals.lock); 508 return EOK; 509 } 510 511 int icmp_received_msg(device_id_t device_id, packet_t packet, services_t receiver, services_t error){ 512 ERROR_DECLARE; 513 514 if(ERROR_OCCURRED(icmp_process_packet(packet, error))){ 515 return icmp_release_and_return(packet, ERROR_CODE); 516 } 517 518 return EOK; 519 } 520 521 int icmp_process_packet(packet_t packet, services_t error){ 522 ERROR_DECLARE; 523 524 size_t length; 525 uint8_t * src; 526 int addrlen; 527 int result; 528 void * data; 529 icmp_header_ref header; 425 return icmp_send_packet(ICMP_PARAMETERPROB, code, packet, header, 426 SERVICE_ICMP, 0, 0, false); 427 } 428 429 /** Try to set the pending reply result as the received message type. 430 * 431 * If the reply data is not present, the reply timed out and the other fibril 432 * is already awake. The packet is released. 433 * 434 * @param[in] packet The received reply message. 435 * @param[in] header The ICMP message header. 436 * @param[in] type The received reply message type. 437 * @param[in] code The received reply message code. 438 * 439 */ 440 static void icmp_process_echo_reply(packet_t *packet, icmp_header_t *header, 441 icmp_type_t type, icmp_code_t code) 442 { 443 unsigned long key[REPLY_KEYS] = 444 {header->un.echo.identifier, header->un.echo.sequence_number}; 445 446 /* The packet is no longer needed */ 447 icmp_release(packet); 448 449 /* Find the pending reply */ 450 fibril_mutex_lock(&reply_lock); 451 452 link_t *link = hash_table_find(&replies, key); 453 if (link != NULL) { 454 icmp_reply_t *reply = 455 hash_table_get_instance(link, icmp_reply_t, link); 456 457 reply->result = type; 458 fibril_condvar_signal(&reply->condvar); 459 } 460 461 fibril_mutex_unlock(&reply_lock); 462 } 463 464 /** Process the received ICMP packet. 465 * 466 * Notify the destination socket application. 467 * 468 * @param[in,out] packet Received packet. 469 * @param[in] error Packet error reporting service to prefix 470 * the received packet. 471 * 472 * @return EOK on success. 473 * @return EINVAL if the packet is not valid. 474 * @return EINVAL if the stored packet address is not the an_addr_t. 475 * @return EINVAL if the packet does not contain any data. 476 * @return NO_DATA if the packet content is shorter than the user 477 * datagram header. 478 * @return ENOMEM if there is not enough memory left. 479 * @return EADDRNOTAVAIL if the destination socket does not exist. 480 * @return Other error codes as defined for the 481 * ip_client_process_packet() function. 482 * 483 */ 484 static int icmp_process_packet(packet_t *packet, services_t error) 485 { 530 486 icmp_type_t type; 531 487 icmp_code_t code; 532 533 if(error){ 534 switch(error){ 535 case SERVICE_ICMP: 536 // process error 537 result = icmp_client_process_packet(packet, &type, &code, NULL, NULL); 538 if(result < 0){ 539 return result; 540 } 541 length = (size_t) result; 542 // remove the error header 543 ERROR_PROPAGATE(packet_trim(packet, length, 0)); 544 break; 545 default: 546 return ENOTSUP; 547 } 548 } 549 // get rid of the ip header 550 length = ip_client_header_length(packet); 551 ERROR_PROPAGATE(packet_trim(packet, length, 0)); 552 488 int rc; 489 490 switch (error) { 491 case SERVICE_NONE: 492 break; 493 case SERVICE_ICMP: 494 /* Process error */ 495 rc = icmp_client_process_packet(packet, &type, &code, NULL, NULL); 496 if (rc < 0) 497 return rc; 498 499 /* Remove the error header */ 500 rc = packet_trim(packet, (size_t) rc, 0); 501 if (rc != EOK) 502 return rc; 503 504 break; 505 default: 506 return ENOTSUP; 507 } 508 509 /* Get rid of the IP header */ 510 size_t length = ip_client_header_length(packet); 511 rc = packet_trim(packet, length, 0); 512 if (rc != EOK) 513 return rc; 514 553 515 length = packet_get_data_length(packet); 554 if (length <= 0){516 if (length <= 0) 555 517 return EINVAL; 556 }557 if (length < ICMP_HEADER_SIZE){518 519 if (length < ICMP_HEADER_SIZE) 558 520 return EINVAL; 559 }560 data = packet_get_data(packet);561 if (! data){521 522 void *data = packet_get_data(packet); 523 if (!data) 562 524 return EINVAL; 563 } 564 // get icmp header 565 header = (icmp_header_ref) data; 566 // checksum 567 if(header->checksum){ 568 while(ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO){ 569 // set the original message type on error notification 570 // type swap observed in Qemu 571 if(error){ 572 switch(header->type){ 573 case ICMP_ECHOREPLY: 574 header->type = ICMP_ECHO; 575 continue; 525 526 /* Get ICMP header */ 527 icmp_header_t *header = (icmp_header_t *) data; 528 529 if (header->checksum) { 530 while (ICMP_CHECKSUM(header, length) != IP_CHECKSUM_ZERO) { 531 /* 532 * Set the original message type on error notification. 533 * Type swap observed in Qemu. 534 */ 535 if (error) { 536 switch (header->type) { 537 case ICMP_ECHOREPLY: 538 header->type = ICMP_ECHO; 539 continue; 576 540 } 577 541 } 542 578 543 return EINVAL; 579 544 } 580 545 } 581 switch(header->type){ 582 case ICMP_ECHOREPLY: 583 if(error){ 584 return icmp_process_echo_reply(packet, header, type, code); 585 }else{ 586 return icmp_process_echo_reply(packet, header, ICMP_ECHO, 0); 546 547 switch (header->type) { 548 case ICMP_ECHOREPLY: 549 if (error) 550 icmp_process_echo_reply(packet, header, type, code); 551 else 552 icmp_process_echo_reply(packet, header, ICMP_ECHO, 0); 553 554 return EOK; 555 556 case ICMP_ECHO: 557 if (error) { 558 icmp_process_echo_reply(packet, header, type, code); 559 return EOK; 560 } 561 562 /* Do not send a reply if disabled */ 563 if (echo_replying) { 564 uint8_t *src; 565 int addrlen = packet_get_addr(packet, &src, NULL); 566 567 /* 568 * Set both addresses to the source one (avoid the 569 * source address deletion before setting the 570 * destination one). 571 */ 572 if ((addrlen > 0) && (packet_set_addr(packet, src, src, 573 (size_t) addrlen) == EOK)) { 574 /* Send the reply */ 575 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, 576 header, 0, 0, 0, 0); 577 return EOK; 587 578 } 588 case ICMP_ECHO: 589 if(error){ 590 return icmp_process_echo_reply(packet, header, type, code); 591 // do not send a reply if disabled 592 }else if(icmp_globals.echo_replying){ 593 addrlen = packet_get_addr(packet, &src, NULL); 594 if((addrlen > 0) 595 // set both addresses to the source one (avoids the source address deletion before setting the destination one) 596 && (packet_set_addr(packet, src, src, (size_t) addrlen) == EOK)){ 597 // send the reply 598 icmp_send_packet(ICMP_ECHOREPLY, 0, packet, header, 0, 0, 0, 0); 599 return EOK; 600 }else{ 601 return EINVAL; 602 } 603 }else{ 604 return EPERM; 579 580 return EINVAL; 581 } 582 583 return EPERM; 584 585 case ICMP_DEST_UNREACH: 586 case ICMP_SOURCE_QUENCH: 587 case ICMP_REDIRECT: 588 case ICMP_ALTERNATE_ADDR: 589 case ICMP_ROUTER_ADV: 590 case ICMP_ROUTER_SOL: 591 case ICMP_TIME_EXCEEDED: 592 case ICMP_PARAMETERPROB: 593 case ICMP_CONVERSION_ERROR: 594 case ICMP_REDIRECT_MOBILE: 595 case ICMP_SKIP: 596 case ICMP_PHOTURIS: 597 ip_received_error_msg(phone_ip, -1, packet, 598 SERVICE_IP, SERVICE_ICMP); 599 return EOK; 600 601 default: 602 return ENOTSUP; 603 } 604 } 605 606 /** Process IPC messages from the IP module 607 * 608 * @param[in] iid Message identifier. 609 * @param[in,out] icall Message parameters. 610 * 611 */ 612 static void icmp_receiver(ipc_callid_t iid, ipc_call_t *icall) 613 { 614 bool loop = true; 615 packet_t *packet; 616 int rc; 617 618 while (loop) { 619 switch (IPC_GET_IMETHOD(*icall)) { 620 case NET_TL_RECEIVED: 621 rc = packet_translate_remote(phone_net, &packet, 622 IPC_GET_PACKET(*icall)); 623 if (rc == EOK) { 624 rc = icmp_process_packet(packet, IPC_GET_ERROR(*icall)); 625 if (rc != EOK) 626 icmp_release(packet); 605 627 } 606 case ICMP_DEST_UNREACH: 607 case ICMP_SOURCE_QUENCH: 608 case ICMP_REDIRECT: 609 case ICMP_ALTERNATE_ADDR: 610 case ICMP_ROUTER_ADV: 611 case ICMP_ROUTER_SOL: 612 case ICMP_TIME_EXCEEDED: 613 case ICMP_PARAMETERPROB: 614 case ICMP_CONVERSION_ERROR: 615 case ICMP_REDIRECT_MOBILE: 616 case ICMP_SKIP: 617 case ICMP_PHOTURIS: 618 ip_received_error_msg(icmp_globals.ip_phone, -1, packet, SERVICE_IP, SERVICE_ICMP); 619 return EOK; 628 629 async_answer_0(iid, (sysarg_t) rc); 630 break; 631 case IPC_M_PHONE_HUNGUP: 632 loop = false; 633 continue; 620 634 default: 621 return ENOTSUP; 622 } 623 } 624 625 int icmp_process_echo_reply(packet_t packet, icmp_header_ref header, icmp_type_t type, icmp_code_t code){ 626 int reply_key; 627 icmp_reply_ref reply; 628 629 // compute the reply key 630 reply_key = ICMP_GET_REPLY_KEY(header->un.echo.identifier, header->un.echo.sequence_number); 631 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet)); 632 // lock the globals 633 fibril_rwlock_write_lock(&icmp_globals.lock); 634 // find the pending reply 635 reply = icmp_replies_find(&icmp_globals.replies, reply_key); 636 if(reply){ 637 // set the result 638 reply->result = type; 639 // notify the waiting fibril 640 fibril_condvar_signal(&reply->condvar); 641 } 642 fibril_rwlock_write_unlock(&icmp_globals.lock); 635 async_answer_0(iid, (sysarg_t) ENOTSUP); 636 } 637 638 iid = async_get_call(icall); 639 } 640 } 641 642 /** Initialize the ICMP module. 643 * 644 * @param[in] net_phone Network module phone. 645 * 646 * @return EOK on success. 647 * @return ENOMEM if there is not enough memory left. 648 * 649 */ 650 int tl_initialize(int net_phone) 651 { 652 measured_string_t names[] = { 653 { 654 (uint8_t *) "ICMP_ERROR_REPORTING", 655 20 656 }, 657 { 658 (uint8_t *) "ICMP_ECHO_REPLYING", 659 18 660 } 661 }; 662 measured_string_t *configuration; 663 size_t count = sizeof(names) / sizeof(measured_string_t); 664 uint8_t *data; 665 666 if (!hash_table_create(&replies, REPLY_BUCKETS, REPLY_KEYS, &reply_ops)) 667 return ENOMEM; 668 669 fibril_mutex_initialize(&reply_lock); 670 atomic_set(&icmp_client, 0); 671 672 phone_net = net_phone; 673 phone_ip = ip_bind_service(SERVICE_IP, IPPROTO_ICMP, SERVICE_ICMP, 674 icmp_receiver); 675 if (phone_ip < 0) 676 return phone_ip; 677 678 int rc = ip_packet_size_req(phone_ip, -1, &icmp_dimension); 679 if (rc != EOK) 680 return rc; 681 682 icmp_dimension.prefix += ICMP_HEADER_SIZE; 683 icmp_dimension.content -= ICMP_HEADER_SIZE; 684 685 /* Get configuration */ 686 configuration = &names[0]; 687 rc = net_get_conf_req(phone_net, &configuration, count, &data); 688 if (rc != EOK) 689 return rc; 690 691 if (configuration) { 692 if (configuration[0].value) 693 error_reporting = (configuration[0].value[0] == 'y'); 694 695 if (configuration[1].value) 696 echo_replying = (configuration[1].value[0] == 'y'); 697 698 net_free_settings(configuration, data); 699 } 700 643 701 return EOK; 644 702 } 645 703 646 int icmp_message_standalone(ipc_callid_t callid, ipc_call_t * call, ipc_call_t * answer, int * answer_count){ 647 ERROR_DECLARE; 648 649 packet_t packet; 650 651 *answer_count = 0; 652 switch(IPC_GET_METHOD(*call)){ 653 case NET_TL_RECEIVED: 654 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){ 655 ERROR_CODE = icmp_received_msg(IPC_GET_DEVICE(call), packet, SERVICE_ICMP, IPC_GET_ERROR(call)); 656 } 657 return ERROR_CODE; 658 case NET_ICMP_INIT: 659 return icmp_process_client_messages(callid, * call); 660 default: 661 return icmp_process_message(call); 662 } 704 /** Per-connection initialization 705 * 706 * Initialize client-specific global variables. 707 * 708 */ 709 void tl_connection(void) 710 { 711 icmp_id = (icmp_param_t) atomic_postinc(&icmp_client); 712 icmp_seq = 1; 713 } 714 715 /** Process the ICMP message. 716 * 717 * @param[in] callid Message identifier. 718 * @param[in] call Message parameters. 719 * @param[out] answer Answer. 720 * @param[out] count Number of arguments of the answer. 721 * 722 * @return EOK on success. 723 * @return ENOTSUP if the message is not known. 724 * @return Other error codes as defined for the packet_translate() 725 * function. 726 * @return Other error codes as defined for the 727 * icmp_destination_unreachable() function. 728 * @return Other error codes as defined for the 729 * icmp_source_quench() function. 730 * @return Other error codes as defined for the 731 * icmp_time_exceeded() function. 732 * @return Other error codes as defined for the 733 * icmp_parameter_problem() function. 734 * 735 * @see icmp_remote.h 736 * @see IS_NET_ICMP_MESSAGE() 737 * 738 */ 739 int tl_message(ipc_callid_t callid, ipc_call_t *call, 740 ipc_call_t *answer, size_t *count) 741 { 742 struct sockaddr *addr; 743 size_t size; 744 packet_t *packet; 745 int rc; 746 747 *count = 0; 748 749 switch (IPC_GET_IMETHOD(*call)) { 750 case NET_ICMP_ECHO: 751 rc = async_data_write_accept((void **) &addr, false, 0, 0, 0, &size); 752 if (rc != EOK) 753 return rc; 754 755 rc = icmp_echo(icmp_id, icmp_seq, ICMP_GET_SIZE(*call), 756 ICMP_GET_TIMEOUT(*call), ICMP_GET_TTL(*call), 757 ICMP_GET_TOS(*call), ICMP_GET_DONT_FRAGMENT(*call), 758 addr, (socklen_t) size); 759 760 free(addr); 761 icmp_seq++; 762 return rc; 763 764 case NET_ICMP_DEST_UNREACH: 765 rc = packet_translate_remote(phone_net, &packet, 766 IPC_GET_PACKET(*call)); 767 if (rc != EOK) 768 return rc; 769 770 return icmp_destination_unreachable(ICMP_GET_CODE(*call), 771 ICMP_GET_MTU(*call), packet); 772 773 case NET_ICMP_SOURCE_QUENCH: 774 rc = packet_translate_remote(phone_net, &packet, 775 IPC_GET_PACKET(*call)); 776 if (rc != EOK) 777 return rc; 778 779 return icmp_source_quench(packet); 780 781 case NET_ICMP_TIME_EXCEEDED: 782 rc = packet_translate_remote(phone_net, &packet, 783 IPC_GET_PACKET(*call)); 784 if (rc != EOK) 785 return rc; 786 787 return icmp_time_exceeded(ICMP_GET_CODE(*call), packet); 788 789 case NET_ICMP_PARAMETERPROB: 790 rc = packet_translate_remote(phone_net, &packet, 791 IPC_GET_PACKET(*call)); 792 if (rc != EOK) 793 return rc; 794 795 return icmp_parameter_problem(ICMP_GET_CODE(*call), 796 ICMP_GET_POINTER(*call), packet); 797 } 798 663 799 return ENOTSUP; 664 800 } 665 801 666 int icmp_process_client_messages(ipc_callid_t callid, ipc_call_t call){667 ERROR_DECLARE;668 669 bool keep_on_going = true;670 // fibril_rwlock_t lock;671 ipc_call_t answer;672 int answer_count;673 size_t length;674 struct sockaddr * addr;675 ipc_callid_t data_callid;676 icmp_echo_ref echo_data;677 int res;678 679 /*680 * Accept the connection681 * - Answer the first NET_ICMP_INIT call.682 */683 res = EOK;684 answer_count = 0;685 686 // fibril_rwlock_initialize(&lock);687 688 echo_data = (icmp_echo_ref) malloc(sizeof(*echo_data));689 if(! echo_data){690 return ENOMEM;691 }692 693 // assign a new identifier694 fibril_rwlock_write_lock(&icmp_globals.lock);695 res = icmp_bind_free_id(echo_data);696 fibril_rwlock_write_unlock(&icmp_globals.lock);697 if(res < 0){698 free(echo_data);699 return res;700 }701 702 while(keep_on_going){703 704 // answer the call705 answer_call(callid, res, &answer, answer_count);706 707 // refresh data708 refresh_answer(&answer, &answer_count);709 710 // get the next call711 callid = async_get_call(&call);712 713 // process the call714 switch(IPC_GET_METHOD(call)){715 case IPC_M_PHONE_HUNGUP:716 keep_on_going = false;717 res = EHANGUP;718 break;719 case NET_ICMP_ECHO:720 // fibril_rwlock_write_lock(&lock);721 if(! async_data_write_receive(&data_callid, &length)){722 res = EINVAL;723 }else{724 addr = malloc(length);725 if(! addr){726 res = ENOMEM;727 }else{728 if(! ERROR_OCCURRED(async_data_write_finalize(data_callid, addr, length))){729 fibril_rwlock_write_lock(&icmp_globals.lock);730 res = icmp_echo(echo_data->identifier, echo_data->sequence_number, ICMP_GET_SIZE(call), ICMP_GET_TIMEOUT(call), ICMP_GET_TTL(call), ICMP_GET_TOS(call), ICMP_GET_DONT_FRAGMENT(call), addr, (socklen_t) length);731 fibril_rwlock_write_unlock(&icmp_globals.lock);732 free(addr);733 if(echo_data->sequence_number < UINT16_MAX){734 ++ echo_data->sequence_number;735 }else{736 echo_data->sequence_number = 0;737 }738 }else{739 res = ERROR_CODE;740 }741 }742 }743 // fibril_rwlock_write_unlock(&lock);744 break;745 default:746 res = icmp_process_message(&call);747 }748 }749 750 // release the identifier751 fibril_rwlock_write_lock(&icmp_globals.lock);752 icmp_echo_data_exclude(&icmp_globals.echo_data, echo_data->identifier);753 fibril_rwlock_write_unlock(&icmp_globals.lock);754 return res;755 }756 757 int icmp_process_message(ipc_call_t * call){758 ERROR_DECLARE;759 760 packet_t packet;761 762 switch(IPC_GET_METHOD(*call)){763 case NET_ICMP_DEST_UNREACH:764 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){765 ERROR_CODE = icmp_destination_unreachable_msg(0, ICMP_GET_CODE(call), ICMP_GET_MTU(call), packet);766 }767 return ERROR_CODE;768 case NET_ICMP_SOURCE_QUENCH:769 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){770 ERROR_CODE = icmp_source_quench_msg(0, packet);771 }772 return ERROR_CODE;773 case NET_ICMP_TIME_EXCEEDED:774 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){775 ERROR_CODE = icmp_time_exceeded_msg(0, ICMP_GET_CODE(call), packet);776 }777 return ERROR_CODE;778 case NET_ICMP_PARAMETERPROB:779 if(! ERROR_OCCURRED(packet_translate_remote(icmp_globals.net_phone, &packet, IPC_GET_PACKET(call)))){780 ERROR_CODE = icmp_parameter_problem_msg(0, ICMP_GET_CODE(call), ICMP_GET_POINTER(call), packet);781 }782 return ERROR_CODE;783 default:784 return ENOTSUP;785 }786 }787 788 int icmp_release_and_return(packet_t packet, int result){789 pq_release_remote(icmp_globals.net_phone, packet_get_id(packet));790 return result;791 }792 793 int icmp_bind_free_id(icmp_echo_ref echo_data){794 icmp_param_t index;795 796 if(! echo_data){797 return EBADMEM;798 }799 // from the last used one800 index = icmp_globals.last_used_id;801 do{802 ++ index;803 // til the range end804 if(index >= ICMP_FREE_IDS_END){805 // start from the range beginning806 index = ICMP_FREE_IDS_START - 1;807 do{808 ++ index;809 // til the last used one810 if(index >= icmp_globals.last_used_id){811 // none found812 return ENOTCONN;813 }814 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);815 // found, break immediately816 break;817 }818 }while(icmp_echo_data_find(&icmp_globals.echo_data, index) != NULL);819 echo_data->identifier = index;820 echo_data->sequence_number = 0;821 return icmp_echo_data_add(&icmp_globals.echo_data, index, echo_data);822 }823 824 /** Default thread for new connections.825 *826 * @param[in] iid The initial message identifier.827 * @param[in] icall The initial message call structure.828 *829 */830 static void tl_client_connection(ipc_callid_t iid, ipc_call_t * icall)831 {832 /*833 * Accept the connection834 * - Answer the first IPC_M_CONNECT_ME_TO call.835 */836 ipc_answer_0(iid, EOK);837 838 while(true) {839 ipc_call_t answer;840 int answer_count;841 842 /* Clear the answer structure */843 refresh_answer(&answer, &answer_count);844 845 /* Fetch the next message */846 ipc_call_t call;847 ipc_callid_t callid = async_get_call(&call);848 849 /* Process the message */850 int res = tl_module_message_standalone(callid, &call, &answer,851 &answer_count);852 853 /* End if said to either by the message or the processing result */854 if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) || (res == EHANGUP))855 return;856 857 /* Answer the message */858 answer_call(callid, res, &answer, answer_count);859 }860 }861 862 /** Starts the module.863 *864 * @param argc The count of the command line arguments. Ignored parameter.865 * @param argv The command line parameters. Ignored parameter.866 *867 * @returns EOK on success.868 * @returns Other error codes as defined for each specific module start function.869 *870 */871 802 int main(int argc, char *argv[]) 872 803 { 873 ERROR_DECLARE;874 875 804 /* Start the module */ 876 if (ERROR_OCCURRED(tl_module_start_standalone(tl_client_connection))) 877 return ERROR_CODE; 878 879 return EOK; 805 return tl_module_start(SERVICE_ICMP); 880 806 } 881 807
Note:
See TracChangeset
for help on using the changeset viewer.