From 4dab49757620d80a7b99f48149193b1787cb4d1d Mon Sep 17 00:00:00 2001 From: Sparchatus Date: Tue, 24 May 2022 16:16:39 +0000 Subject: [PATCH 1/6] [Bugfix] ump channel did not set initial state for send queue entries --- include/aos/ump_chan.h | 12 ++++++------ lib/aos/ump_chan.c | 15 +++++++++++---- usr/echoserver/main.c | 2 +- usr/hello/hello.c | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/include/aos/ump_chan.h b/include/aos/ump_chan.h index 9d7a03f..206baaa 100644 --- a/include/aos/ump_chan.h +++ b/include/aos/ump_chan.h @@ -17,7 +17,7 @@ struct ump_send_queue_entry; typedef void (*ump_recv_header_callback_fn_t)(void *arg, size_t header_size, void *header, size_t payload_size); typedef void (*ump_recv_payload_callback_fn_t)(void *arg, size_t payload_size, void *payload); -typedef void (*ump_send_callback_fn_t)(void *arg, void *payload, struct ump_send_queue_entry *entry); +typedef void (*ump_send_callback_fn_t)(void *arg, struct ump_send_queue_entry *entry); /** * @brief Specifies which role an endpoint would like to take in the UMP protocol @@ -71,10 +71,10 @@ struct ump_send_queue_entry { enum ump_send_entry_state state; size_t header_size; - void *header; + const void *header; size_t payload_size; - void *payload; + const void *payload; ump_send_callback_fn_t callback; void *callback_arg; @@ -84,7 +84,7 @@ struct ump_send_queue_entry { * it will be updated to refer to the payload */ size_t send_bytes_left; - void *send_buf_position; + const void *send_buf_position; struct ump_send_queue_entry *next; }; @@ -213,9 +213,9 @@ void ump_send ( struct ump_send_chan *chan, struct ump_send_queue_entry *entry, size_t header_size, - void *header, + const void *header, size_t payload_size, - void *payload, + const void *payload, ump_send_callback_fn_t callback, void *callback_arg ); diff --git a/lib/aos/ump_chan.c b/lib/aos/ump_chan.c index 546735d..c9fd08f 100644 --- a/lib/aos/ump_chan.c +++ b/lib/aos/ump_chan.c @@ -28,7 +28,7 @@ static void ump_ring_init(struct ump_ring_state *ring_state, void *buf, size_t b * @return true successfully wrote the entry * @return false failed to write the entry because the buffer is full */ -static bool ump_ring_try_write_next(struct ump_ring_state *ring_state, void *buf, size_t bytes) { +static bool ump_ring_try_write_next(struct ump_ring_state *ring_state, const void *buf, size_t bytes) { assert(bytes <= UMP_RING_BUF_DATA_SIZE); assert(buf != NULL); @@ -223,7 +223,7 @@ static void ump_try_send(struct ump_send_chan *send_chan) { // send the callback assert(entry->callback != NULL); - entry->callback(entry->callback_arg, entry->payload, entry); + entry->callback(entry->callback_arg, entry); } } @@ -409,13 +409,16 @@ void ump_send ( struct ump_send_chan *chan, struct ump_send_queue_entry *entry, size_t header_size, - void *header, + const void *header, size_t payload_size, - void *payload, + const void *payload, ump_send_callback_fn_t callback, void *callback_arg ) { + assert(chan != NULL); + // initialize the queue entry + entry->state = UMP_SEND_ENTRY_STATE_START_MESSAGE; entry->header_size = header_size; entry->header = header; entry->payload_size = payload_size; @@ -440,6 +443,8 @@ void ump_recv_header ( ump_recv_header_callback_fn_t callback, void *callback_arg ) { + assert(chan != NULL); + thread_mutex_lock(&chan->recv_register_lock); // TODO rueegges: caller has to ensure it only registers at most once?! Or should we just make it a noop? assert(chan->state == UMP_RECV_STATE_IDLE); @@ -459,6 +464,8 @@ void ump_recv_payload ( ump_recv_payload_callback_fn_t callback, void *callback_arg ) { + assert(chan != NULL); + thread_mutex_lock(&chan->recv_register_lock); // TODO rueegges: caller has to ensure it only registers at most once?! Or should we just make it a noop? assert(chan->state == UMP_RECV_STATE_PAYLOAD_IDLE); diff --git a/usr/echoserver/main.c b/usr/echoserver/main.c index 8a55646..59edb3a 100644 --- a/usr/echoserver/main.c +++ b/usr/echoserver/main.c @@ -22,7 +22,7 @@ static void handle_header_recv(void *arg, size_t header_size, void *header, size } __attribute__((__used__)) -static void handle_send(void *arg, void *payload, struct ump_send_queue_entry *entry) { +static void handle_send(void *arg, struct ump_send_queue_entry *entry) { printf("Echo server: Message sent!\n"); } diff --git a/usr/hello/hello.c b/usr/hello/hello.c index 04c65d6..c4d8a48 100644 --- a/usr/hello/hello.c +++ b/usr/hello/hello.c @@ -54,7 +54,7 @@ static void handle_header_recv(void *arg, size_t header_size, void *header, size } __attribute__((__used__)) -static void handle_send(void *arg, void *payload, struct ump_send_queue_entry *entry) { +static void handle_send(void *arg, struct ump_send_queue_entry *entry) { printf("Echo client: Message sent!\n"); } From 0c79d7048da59b818432c01059123abf32cbd68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Fri, 27 May 2022 19:11:22 +0200 Subject: [PATCH 2/6] enet: Implement driver --- devices/imx8x/enet.dev | 29 +- errors/errno.fugu | 8 + hake/menu.lst.armv8_imx8x | 4 +- include/aos/simpleslab.h | 43 ++ include/aos/ump_binding.h | 3 +- include/aos/ump_chan.h | 26 +- include/aos/ump_net.h | 59 ++ include/aos/ump_net_client.h | 87 +++ include/devif/backends/net/enet_devif.h | 4 +- include/netutil/etharp.h | 26 +- include/netutil/icmp.h | 9 +- include/netutil/ip.h | 15 +- include/netutil/types.h | 82 +++ include/netutil/udp.h | 9 +- lib/aos/Hakefile | 2 + lib/aos/simpleslab.c | 94 ++++ lib/aos/ump_net_client.c | 207 +++++++ lib/netutil/checksum.c | 5 +- usr/drivers/enet/Hakefile | 5 +- usr/drivers/enet/enet.h | 118 +++- usr/drivers/enet/enet_devq.c | 70 +-- usr/drivers/enet/enet_module.c | 66 ++- usr/drivers/enet/enet_proto.c | 706 ++++++++++++++++++++++++ usr/echoserver/main.c | 107 +++- usr/init/main.c | 8 + 25 files changed, 1676 insertions(+), 116 deletions(-) create mode 100644 include/aos/simpleslab.h create mode 100644 include/aos/ump_net.h create mode 100644 include/aos/ump_net_client.h create mode 100644 include/netutil/types.h create mode 100644 lib/aos/simpleslab.c create mode 100644 lib/aos/ump_net_client.c create mode 100644 usr/drivers/enet/enet_proto.c diff --git a/devices/imx8x/enet.dev b/devices/imx8x/enet.dev index 8a16f90..84ae986 100644 --- a/devices/imx8x/enet.dev +++ b/devices/imx8x/enet.dev @@ -99,7 +99,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.3/3638 ***************************************************************************/ - + register rdar rw addr(base, 0x0010) "Receive Descriptor Active Register ring0" { _ 24 rsvd; rdar 1 "Receive Descriptor Active"; @@ -109,7 +109,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.3/3638 ***************************************************************************/ - + register tdar rw addr(base, 0x0014) "Transmit Descriptor Active Register ring0" { _ 24 rsvd; tdar 1 "Transmit Descriptor Active"; @@ -119,7 +119,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.5/3640 Ethernet control register ***************************************************************************/ - + register ecr rw addr(base, 0x0024) "Control register" { reset 1 "Ethernet MAC Reset"; etheren 1 "Ethernet enable"; @@ -142,7 +142,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.6/3643 MII Management Frame Register ***************************************************************************/ - + register mmfr rw addr(base, 0x0040) "MII Management Frame Register" { data 16 "Data written to or read from PHY register"; ta 2 "Turn Around: needs to be programmed to 10 to be valid"; @@ -155,7 +155,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.6/3643 MII Speed Control Register ***************************************************************************/ - + register mscr rw addr(base, 0x0044) "MII Speed Control Register" { _ 1 rsvd; mii_speed 6 "MII Speed"; @@ -167,7 +167,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { /**************************************************************************** * 14.6.5.9/3646 Receive Control Register ***************************************************************************/ - + register rcr rw addr(base, 0x0084) "Receive Control Register" { loop 1 "Internal Loopback"; drt 1 "Disable Receive on Transmit"; @@ -224,7 +224,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { typ 16 "Always contains 0x8808"; paddr2 16 "Pause Address"; }; - + /**************************************************************************** * 14.6.5.12/3651 Opcode/Pause Duration Register ***************************************************************************/ @@ -338,6 +338,19 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { _ 22 rsvd; }; + /**************************************************************************** + * 14.6.5.40/3745 + ***************************************************************************/ + register racc rw addr(base, 0x001C4) "Receive Accelerator Function Configuration" { + padrem 1 ""; + ipdis 1 ""; + prodis 1 ""; + _ 3 rsvd; + linedis 1 "Enable Discard Of Frames With MAC Layer Errors"; + shift16 1 ""; + _ 24 rsvd; + }; + /**************************************************************************** * 14.6.5.49/3679 Tx Packet Count Statistic Register @@ -458,7 +471,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" { addr 32 "Buffer address"; esc 32 ""; prot 32 ""; - ts 32 ""; + ts 32 ""; res0 64 ""; }; */ diff --git a/errors/errno.fugu b/errors/errno.fugu index f32e761..fcf10dc 100755 --- a/errors/errno.fugu +++ b/errors/errno.fugu @@ -379,6 +379,14 @@ errors libaos LIB_ERR_ { failure UMP_GET_GLOBAL "Failure in get_global()", failure UMP_REGISTER "Failure in ump_register()", + // UMP net + failure NET_ALREADY_INIT "Already initialized", + failure NET_PORT_IN_USE "Can't listen on this port, someone else is already listening", + failure NET_ALLOC_PORT "Failed to allocate ephemeral port", + failure NET_NOT_LISTENING "You are not listening on this port", + failure NET_PACKET_TOO_BIG "Can't send packet because it is too big", + failure NET_ARP_MISS "The packet was dropped and an ARP request was sent instead. Try again later.", + // IDC binding/export and Monitor client interface failure MONITOR_CLIENT_BIND "Error in monitor_client_lmp_bind()", failure MONITOR_CLIENT_ACCEPT "Error in monitor_client_lmp_accept()", diff --git a/hake/menu.lst.armv8_imx8x b/hake/menu.lst.armv8_imx8x index 481e6fa..529feec 100644 --- a/hake/menu.lst.armv8_imx8x +++ b/hake/menu.lst.armv8_imx8x @@ -5,8 +5,8 @@ bootdriver /armv8/sbin/boot_armv8_generic cpudriver /armv8/sbin/cpu_imx8x module /armv8/sbin/init -module /armv8/sbin/enet -module /armv8/sbin/hello echoclient +module /armv8/sbin/enet 192.168.2.2 24 +module /armv8/sbin/hello hi # module /armv8/sbin/memeater module /armv8/sbin/mallocator module /armv8/sbin/stackoverflow diff --git a/include/aos/simpleslab.h b/include/aos/simpleslab.h new file mode 100644 index 0000000..ae7b31c --- /dev/null +++ b/include/aos/simpleslab.h @@ -0,0 +1,43 @@ +/** + * \file + * \brief Very simple slab allocator + */ + +/* + * Copyright (c) 2008, ETH Zurich. + * All rights reserved. + * + * This file is distributed under the terms in the attached LICENSE file. + * If you do not find this file, copies can be found by writing to: + * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. + */ + +#ifndef LIBBARRELFISH_SIMPLESLAB_H +#define LIBBARRELFISH_SIMPLESLAB_H + +#include + +__BEGIN_DECLS + +// forward declarations +struct simpleslab_allocator; +struct simpleblock_head; + +struct simpleslab_allocator { + void *start, *end; + uint32_t total, free; + struct simpleblock_head *blocks; +}; + +void simpleslab_init(struct simpleslab_allocator *slabs, size_t blocksize, void *buf, size_t buflen); +void *simpleslab_alloc(struct simpleslab_allocator *slabs); +void simpleslab_free(struct simpleslab_allocator *slabs, void *block); + +// size of block header +#define SIMPLESLAB_BLOCK_HDRSIZE (sizeof(void *)) +// should be able to fit the header into the block + + +__END_DECLS + +#endif // LIBBARRELFISH_SIMPLESLAB_H diff --git a/include/aos/ump_binding.h b/include/aos/ump_binding.h index 1f89119..6e4029a 100644 --- a/include/aos/ump_binding.h +++ b/include/aos/ump_binding.h @@ -3,10 +3,11 @@ #include -#define UMP_FRAME_SIZE BASE_PAGE_SIZE +#define UMP_FRAME_SIZE (2 * BASE_PAGE_SIZE) enum ump_server_id { UMP_SERVER_ECHO, + UMP_SERVER_NET, UMP_SERVER_COUNT // How many servers exist }; diff --git a/include/aos/ump_chan.h b/include/aos/ump_chan.h index 206baaa..543b800 100644 --- a/include/aos/ump_chan.h +++ b/include/aos/ump_chan.h @@ -21,7 +21,7 @@ typedef void (*ump_send_callback_fn_t)(void *arg, struct ump_send_queue_entry *e /** * @brief Specifies which role an endpoint would like to take in the UMP protocol - * + * * This is just used to agree on which part of the shared frame should be used * to send data and which to receive data */ @@ -51,7 +51,7 @@ struct ump_ring_state { /** * @brief State of a send queue entry - * + * * Used to keep track of which part of the message we are * currently sending */ @@ -64,12 +64,12 @@ enum ump_send_entry_state { /** * @brief An entry in the sending queue of a channel. - * + * * Contains all metadata information needed to send this message */ struct ump_send_queue_entry { enum ump_send_entry_state state; - + size_t header_size; const void *header; @@ -103,7 +103,7 @@ struct ump_send_chan { /** * @brief Used to keep track of which part of a message was received or is being received - * + * */ enum recv_state { UMP_RECV_STATE_IDLE, @@ -116,7 +116,7 @@ enum recv_state { /** * @brief Metadata for the receiving side of the channel - * + * */ struct ump_recv_chan { struct ump_ring_state ring_state; @@ -175,18 +175,18 @@ struct ump_start_message { // in sizes on the two endpoints size_t header_size; }; -STATIC_ASSERT(sizeof(struct ump_start_message) <= sizeof(struct ump_ring_buf_entry), "struct ump_start_message is too big"); +STATIC_ASSERT(sizeof(struct ump_start_message) <= UMP_RING_BUF_DATA_SIZE, "struct ump_start_message is too big"); /** * @brief Initialize the send and receive objects for a channel. - * + * * @param role Specifies if this side should be the server or client in the connection * @param send_chan send channel object to be allocated and initialized * @param recv_chan receive channel object to be allocated and initialized * @param recv_header_size size of the headers that will be received * @param shared_frame the frame capability shared between the client and server * @param run_on_waitset the waitset on which the async tasks should run. if it is NULL a thread will be started to handle the tasks - * @return errval_t + * @return errval_t */ errval_t ump_chan_init( enum ump_role role, @@ -199,7 +199,7 @@ errval_t ump_chan_init( /** * @brief Send a message on the channel - * + * * @param chan channel to send on * @param entry an allocated object to carry the send information * @param header_size size of the header to be sent @@ -214,7 +214,7 @@ void ump_send ( struct ump_send_queue_entry *entry, size_t header_size, const void *header, - size_t payload_size, + size_t payload_size, const void *payload, ump_send_callback_fn_t callback, void *callback_arg @@ -222,7 +222,7 @@ void ump_send ( /** * @brief Receive the header of the next message. Must only be called on a fresh channel or after the receive payload completed - * + * * @param chan channel to listen on * @param callback function to call with the header and the size of the payload * @param callback_arg argument to pass to the callback function @@ -235,7 +235,7 @@ void ump_recv_header ( /** * @brief Receive the payload of the next message. Must only be called after the receive header completed - * + * * @param chan channel to listen on * @param payload buffer to store the payload in. must be at least as large as the payload_size argument in the corresponding callback from receive header. if this is NULL then the payload will be dropped * @param callback function to call with payload diff --git a/include/aos/ump_net.h b/include/aos/ump_net.h new file mode 100644 index 0000000..53b1ad8 --- /dev/null +++ b/include/aos/ump_net.h @@ -0,0 +1,59 @@ +#ifndef _LIB_BARRELFISH_UMP_NET_H +#define _LIB_BARRELFISH_UMP_NET_H + +#include + +enum ump_net_op { + // calls (out: call, in: return) + UMP_NET_OP_UDP_SEND, + UMP_NET_OP_UDP_LISTEN, + UMP_NET_OP_UDP_LISTEN_STOP, + + // events (in only) + UMP_NET_EV_UDP_RECV, +}; + +struct ump_net_op_udp_send { + uint32_t dest_ip; + uint16_t src_port; + uint16_t dest_port; +}; + +struct ump_net_op_udp_listen { + uint16_t dest_port; // If 0, allocate a port +}; + +struct ump_net_ret_udp_listen { + uint16_t dest_port; +}; + +struct ump_net_op_udp_listen_stop { + uint16_t dest_port; +}; + +struct ump_net_ev_udp_recv { + uint32_t src_ip; + uint32_t dest_ip; + uint16_t src_port; + uint16_t dest_port; +}; + +struct ump_net_out_header { + enum ump_net_op op; + union ump_net_out_d { + struct ump_net_op_udp_send udp_send; + struct ump_net_op_udp_listen udp_listen; + struct ump_net_op_udp_listen_stop udp_listen_stop; + } d; +}; + +struct ump_net_in_header { + enum ump_net_op op; + errval_t ret_err; + union ump_net_in_d { + struct ump_net_ret_udp_listen udp_listen; + struct ump_net_ev_udp_recv udp_recv; + } d; +}; + +#endif // _LIB_BARRELFISH_UMP_NET_H diff --git a/include/aos/ump_net_client.h b/include/aos/ump_net_client.h new file mode 100644 index 0000000..1371627 --- /dev/null +++ b/include/aos/ump_net_client.h @@ -0,0 +1,87 @@ +#ifndef _LIB_BARRELFISH_UMP_NET_CLIENT_H +#define _LIB_BARRELFISH_UMP_NET_CLIENT_H + +#include +#include +#include + +// This is the client library for the UMP net server. + +/** + * @brief Initialize networking in this process. + * + * @param ws waitset on which networking runs + */ +errval_t ump_net_init (struct waitset *ws); + +/** + * @brief Internal data structure. You need to allocate this space when + * making a call, and can release it in the callback. + */ +struct ump_net_call { + struct ump_send_queue_entry ump_entry; + struct ump_net_out_header out_header; + struct ump_net_in_header in_header; + bool half_done; + void *cb; + void *cb_arg; + struct ump_net_call *next; +}; + +typedef void (*ump_net_udp_send_callback_t)(void *arg, errval_t err); +/** + * @brief Send an UDP packet. + * @param call You need to allocate this space until cb is called. + * @param cb This will be called when the operation is complete, with cb_arg and an error value. + */ +void ump_net_udp_send( + struct ump_net_call *call, + uint32_t dest_ip, + uint16_t src_port, + uint16_t dest_port, + size_t payload_size, const void *payload, + ump_net_udp_send_callback_t cb, void *cb_arg +); + +typedef void (*ump_net_udp_recv_handler_t)(void *arg, struct ump_net_ev_udp_recv *udp_recv, size_t payload_size); +typedef void (*ump_net_udp_listen_callback_t)(void *arg, errval_t err, uint16_t dest_port); +/** + * @brief Listen for UDP packets. + * @param call You need to allocate this space until cb is called. + * @param dest_port UDP port to listen on. If 0, an ephemeral port is allocated, and passed to the callback. + * @param recv_handler This will be called when a packet arrives. + * When the callback is called, you *must* call ump_net_recv_payload. + * @param cb This will be called when the operation is complete, with cb_arg, an error value, and the port. + */ +void ump_net_udp_listen ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg, + ump_net_udp_listen_callback_t cb, void *cb_arg +); + +typedef void (*ump_net_udp_listen_stop_callback_t)(void *arg, errval_t err); +/** + * @brief Stop listening for UDP packets. + * @param call You need to allocate this space until cb is called. + * @param cb This will be called when the operation is complete, with cb_arg and an error value. + */ +void ump_net_udp_listen_stop ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_udp_listen_stop_callback_t cb, void *cb_arg +); +/** + * @brief Receive payload. + * @param payload Buffer to write data into. + * Must have size payload_size (argument of recv_handler). + * Can be NULL to drop the payload. + * @param callback Will be called when all data has been received. + * Can be NULL when payload is NULL. + */ +void ump_net_recv_payload ( + void *payload, + ump_recv_payload_callback_fn_t callback, void *callback_arg +); + +#endif // _LIB_BARRELFISH_UMP_NET_CLIENT_H diff --git a/include/devif/backends/net/enet_devif.h b/include/devif/backends/net/enet_devif.h index b9d802b..3bac422 100644 --- a/include/devif/backends/net/enet_devif.h +++ b/include/devif/backends/net/enet_devif.h @@ -6,13 +6,15 @@ * If you do not find this file, copies can be found by writing to: * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. */ - + #ifndef ENET_DEVIF_H #define ENET_DEVIF_H struct enet_queue; struct enet_t; +struct region_entry* enet_get_region(struct enet_queue* q, regionid_t rid); + errval_t enet_rx_queue_create(struct enet_queue ** q, struct enet_t* dev); errval_t enet_tx_queue_create(struct enet_queue ** q, struct enet_t* dev); diff --git a/include/netutil/etharp.h b/include/netutil/etharp.h index 631f238..cb18a3a 100644 --- a/include/netutil/etharp.h +++ b/include/netutil/etharp.h @@ -4,6 +4,7 @@ #include #include #include +#include //#define ETHARP_DEBUG_OPTION 1 @@ -24,14 +25,10 @@ #define ETH_ADDR_LEN 6 -struct eth_addr { - uint8_t addr[6]; -} __attribute__((__packed__)); - struct eth_hdr { - struct eth_addr dst; - struct eth_addr src; - uint16_t type; + struct eth_addr_net dst; + struct eth_addr_net src; + struct uint16_net type; } __attribute__((__packed__)); #define ARP_HW_TYPE_ETH 0x1 @@ -41,17 +38,16 @@ struct eth_hdr { #define ARP_HLEN 28 struct arp_hdr { - uint16_t hwtype; - uint16_t proto; + struct uint16_net hwtype; + struct uint16_net proto; uint8_t hwlen; uint8_t protolen; - uint16_t opcode; - struct eth_addr eth_src; - uint32_t ip_src; - struct eth_addr eth_dst; - uint32_t ip_dst; + struct uint16_net opcode; + struct eth_addr_net eth_src; + struct uint32_net ip_src; + struct eth_addr_net eth_dst; + struct uint32_net ip_dst; } __attribute__((__packed__)); - #endif diff --git a/include/netutil/icmp.h b/include/netutil/icmp.h index 9e23071..b78a50c 100644 --- a/include/netutil/icmp.h +++ b/include/netutil/icmp.h @@ -4,6 +4,7 @@ #include #include #include +#include //#define ICMP_DEBUG_OPTION 1 @@ -46,14 +47,14 @@ enum icmp_te_type { * This header is also used for other ICMP types that do not * use the data part. */ -#define ICMP_HLEN 8 +#define ICMP_HLEN 8 struct icmp_echo_hdr { uint8_t type; uint8_t code; - uint16_t chksum; - uint16_t id; - uint16_t seqno; + struct uint16_net chksum; + struct uint16_net id; + struct uint16_net seqno; } __attribute__((__packed__)) ; #define ICMPH_TYPE(hdr) ((hdr)->type) diff --git a/include/netutil/ip.h b/include/netutil/ip.h index 08ce879..0bbee48 100644 --- a/include/netutil/ip.h +++ b/include/netutil/ip.h @@ -4,6 +4,7 @@ #include #include #include +#include //#define IP_DEBUG_OPTION 1 @@ -17,6 +18,7 @@ #define IP_DF 0x4000U /* dont fragment flag */ #define IP_MF 0x2000U /* more fragments flag */ #define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ +#define IP_ADDR_LEN 4 #define IP_HLEN 20 /* Default size for ip header */ #define IP_PROTO_ICMP 1 #define IP_PROTO_IGMP 2 @@ -24,7 +26,6 @@ #define IP_PROTO_UDPLITE 136 #define IP_PROTO_TCP 6 -typedef uint32_t ip_addr_t; #define MK_IP(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) #define IPH_V(hdr) ((hdr)->v_hl >> 4) @@ -37,20 +38,20 @@ struct ip_hdr { /* type of service */ uint8_t tos; /* total length */ - uint16_t len; + struct uint16_net len; /* identification */ - uint16_t id; + struct uint16_net id; /* fragment offset field */ - uint16_t offset; + struct uint16_net offset; /* time to live */ uint8_t ttl; /* protocol*/ uint8_t proto; /* checksum */ - uint16_t chksum; + struct uint16_net chksum; /* source and destination IP addresses */ - ip_addr_t src; - ip_addr_t dest; + ip_addr_net_t src; + ip_addr_net_t dest; } __attribute__((__packed__)); /** diff --git a/include/netutil/types.h b/include/netutil/types.h new file mode 100644 index 0000000..cc28584 --- /dev/null +++ b/include/netutil/types.h @@ -0,0 +1,82 @@ +#ifndef _NETUTIL_TYPES_H_ +#define _NETUTIL_TYPES_H_ + +#include +#include + +struct uint16_net { + uint8_t val[2]; +} __attribute__((__packed__)); + +static inline struct uint16_net uint16_wr(uint16_t val) +{ + return (struct uint16_net){{ + (val >> 8) & 0xff, + val & 0xff + }}; +} + +static inline uint16_t uint16_rd(struct uint16_net val_net) +{ + return ( + ((uint16_t)val_net.val[0] << 8) | + (uint16_t)val_net.val[1] + ); +} + +struct uint32_net { + uint8_t val[4]; +} __attribute__((__packed__)); + +static inline struct uint32_net uint32_wr(uint32_t val) +{ + return (struct uint32_net){{ + (val >> 24) & 0xff, + (val >> 16) & 0xff, + (val >> 8) & 0xff, + val & 0xff + }}; +} + +static inline uint32_t uint32_rd(struct uint32_net val_net) +{ + return ( + ((uint32_t)val_net.val[0] << 24) | + ((uint32_t)val_net.val[1] << 16) | + ((uint32_t)val_net.val[2] << 8) | + (uint32_t)val_net.val[3] + ); +} + +struct eth_addr_net { + uint8_t addr[6]; +} __attribute__((__packed__)); + +static inline struct eth_addr_net eth_addr_wr(uint64_t addr) +{ + return (struct eth_addr_net){{ + (addr >> 40) & 0xff, + (addr >> 32) & 0xff, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff + }}; +} + +static inline uint64_t eth_addr_rd(struct eth_addr_net addr_net) +{ + return ( + ((uint64_t)addr_net.addr[0] << 40) | + ((uint64_t)addr_net.addr[1] << 32) | + ((uint64_t)addr_net.addr[2] << 24) | + ((uint64_t)addr_net.addr[3] << 16) | + ((uint64_t)addr_net.addr[4] << 8) | + (uint64_t)addr_net.addr[5] + ); +} + +typedef uint32_t ip_addr_t; +typedef struct uint32_net ip_addr_net_t; + +#endif diff --git a/include/netutil/udp.h b/include/netutil/udp.h index 9c0ab30..deaddc1 100644 --- a/include/netutil/udp.h +++ b/include/netutil/udp.h @@ -2,6 +2,7 @@ #define _UDP_H_ #include +#include //#define UDP_DEBUG_OPTION 1 @@ -17,10 +18,10 @@ */ #define UDP_HLEN 8 struct udp_hdr { - uint16_t src; - uint16_t dest; /* src/dest UDP ports */ - uint16_t len; - uint16_t chksum; + struct uint16_net src; + struct uint16_net dest; /* src/dest UDP ports */ + struct uint16_net len; + struct uint16_net chksum; } __attribute__((__packed__)); diff --git a/lib/aos/Hakefile b/lib/aos/Hakefile index 10398d6..909e0ea 100644 --- a/lib/aos/Hakefile +++ b/lib/aos/Hakefile @@ -27,6 +27,7 @@ "aos_urpc.c", "ump_binding.c", "ump_chan.c", + "ump_net_client.c", "performance.c", "capabilities.c", "coreset.c", @@ -48,6 +49,7 @@ "paging.c", "ram_alloc.c", "slab.c", + "simpleslab.c", "sys_debug.c", "syscalls.c", "systime.c", diff --git a/lib/aos/simpleslab.c b/lib/aos/simpleslab.c new file mode 100644 index 0000000..5e873c5 --- /dev/null +++ b/lib/aos/simpleslab.c @@ -0,0 +1,94 @@ +/** + * \file + * \brief Very simple slab allocator. + * + * This file implements a simple slab allocator. It allocates blocks of a fixed + * size from one contiguous memory region. + */ + +/* + * Copyright (c) 2008, 2009, 2010, ETH Zurich. + * All rights reserved. + * + * This file is distributed under the terms in the attached LICENSE file. + * If you do not find this file, copies can be found by writing to: + * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. + */ + +#include +#include +#include + +struct simpleblock_head { + struct simpleblock_head *next;///< Pointer to next block in free list +}; + +STATIC_ASSERT_SIZEOF(struct simpleblock_head, SIMPLESLAB_BLOCK_HDRSIZE); + +/** + * \brief Initialise a new slab allocator + * + * \param slabs Pointer to slab allocator instance, to be filled-in + * \param blocksize Size of blocks to be allocated by this allocator + * \param buf Buffer from which to allocate blocks + * \param buflen Size of buf + */ +void simpleslab_init(struct simpleslab_allocator *slabs, size_t blocksize, void *buf, size_t buflen) +{ + assert(blocksize >= sizeof(struct simpleblock_head)); + + /* calculate number of blocks in buffer */ + assert(buflen / blocksize <= UINT32_MAX); + slabs->free = slabs->total = buflen / blocksize; + assert(slabs->total > 0); + assert(buf != NULL); + + slabs->start = buf; + slabs->end = buf + buflen; + + /* enqueue blocks in freelist */ + struct simpleblock_head *bh = slabs->blocks = buf; + for (uint32_t i = slabs->total; i > 1; i--) { + buf = (char *)buf + blocksize; + bh->next = buf; + bh = buf; + } + bh->next = NULL; +} + +/** + * \brief Allocate a new block from the slab allocator + * + * \param slabs Pointer to slab allocator instance + * + * \returns Pointer to block on success, NULL on error (out of blocks) + */ +void *simpleslab_alloc(struct simpleslab_allocator *slabs) +{ + struct simpleblock_head *bh = slabs->blocks; + if (bh == NULL) return NULL; + slabs->blocks = bh->next; + slabs->free--; + + return bh; +} + +/** + * \brief Free a block to the slab allocator + * + * \param slabs Pointer to slab allocator instance + * \param block Pointer to block previously returned by #slab_alloc + */ +void simpleslab_free(struct simpleslab_allocator *slabs, void *block) +{ + assert(block != NULL); + assert(slabs->start <= block && block < slabs->end); + + struct simpleblock_head *bh = (struct simpleblock_head *)block; + + /* re-enqueue in slab's free list */ + bh->next = slabs->blocks; + slabs->blocks = bh; + slabs->free++; + assert(slabs->free <= slabs->total); +} diff --git a/lib/aos/ump_net_client.c b/lib/aos/ump_net_client.c new file mode 100644 index 0000000..44e2306 --- /dev/null +++ b/lib/aos/ump_net_client.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct udp_listen_entry { + ump_net_udp_recv_handler_t recv_handler; + void *recv_handler_arg; +}; + +static bool net_initialized = false; + +static struct ump_send_chan *net_send_chan; +static struct ump_recv_chan *net_recv_chan; + +static struct ump_net_call *call_queue_head = NULL; +static struct ump_net_call *call_queue_tail = NULL; + +static collections_hash_table* udp_listen_table; + +static void net_recv_next (void); +static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size); + +errval_t ump_net_init (struct waitset *ws) { + if (net_initialized) return LIB_ERR_NET_ALREADY_INIT; + net_initialized = true; + + errval_t err; + struct aos_rpc *rpc = aos_rpc_get_init_channel(); + struct capref net_cap; + for (int attempts = 0; attempts < 60; attempts++) { + err = aos_rpc_ump_connect(rpc, UMP_SERVER_NET, &net_cap); + if (err != LIB_ERR_UMP_NOT_REGISTERED) break; + barrelfish_usleep(500000); + } + if (err_is_fail(err)) return err; + + // Create the two uni-directional channels + err = ump_chan_init(UMP_ROLE_CLIENT, &net_send_chan, &net_recv_chan, + sizeof(struct ump_net_in_header), net_cap, ws); + if (err_is_fail(err)) return err; + + net_recv_next(); + + collections_hash_create_with_buckets(&udp_listen_table, 100, free); + + return SYS_ERR_OK; +} + +static void net_recv_next (void) { + ump_recv_header(net_recv_chan, handle_net_header_recv, NULL); +} + +static void net_recv_ignore_payload (void *arg, size_t payload_size, void *payload) { + net_recv_next(); +} + +// This is called twice, once from the send callback, once from the receive +// handler. We do it this way because we don't know in which order these +// two events happen. +static void ump_net_op_callback (void *arg, struct ump_send_queue_entry *entry) { + struct ump_net_call *call = arg; + if (call->half_done == false) { + call->half_done = true; + return; + } + if (call->in_header.op == UMP_NET_OP_UDP_SEND) { + ((ump_net_udp_send_callback_t)call->cb)( + call->cb_arg, call->in_header.ret_err + ); + } else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN) { + ((ump_net_udp_listen_callback_t)call->cb)( + call->cb_arg, call->in_header.ret_err, + call->in_header.d.udp_listen.dest_port + ); + } else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN_STOP) { + ((ump_net_udp_listen_stop_callback_t)call->cb)( + call->cb_arg, call->in_header.ret_err + ); + } +} + +static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size) { + struct ump_net_in_header *header = header_raw; + + if (header->op == UMP_NET_EV_UDP_RECV) { + struct udp_listen_entry *entry = collections_hash_find(udp_listen_table, header->d.udp_recv.dest_port); + if (entry != NULL) { + entry->recv_handler(entry->recv_handler_arg, &header->d.udp_recv, payload_size); + return; + } + } else { + assert(call_queue_head != NULL); + assert(call_queue_head->out_header.op == header->op); + call_queue_head->in_header = *header; + ump_net_op_callback(call_queue_head, NULL); + call_queue_head = call_queue_head->next; + if (call_queue_head == NULL) call_queue_tail = NULL; + } + ump_recv_payload(net_recv_chan, NULL, net_recv_ignore_payload, NULL); +} + +static void ump_net_send_call ( + struct ump_net_call *call, + size_t payload_size, const void *payload, + void *cb, void *cb_arg +) { + call->half_done = false; + call->cb = cb; + call->cb_arg = cb_arg; + if (call_queue_head == NULL) { + call_queue_head = call; + } else { + call_queue_tail->next = call; + } + call_queue_tail = call; + ump_send(net_send_chan, &call->ump_entry, + sizeof(struct ump_net_out_header), &call->out_header, + payload_size, payload, + ump_net_op_callback, call); +} + +void ump_net_udp_send ( + struct ump_net_call *call, + uint32_t dest_ip, + uint16_t src_port, + uint16_t dest_port, + size_t payload_size, const void *payload, + ump_net_udp_send_callback_t cb, void *cb_arg +) { + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_UDP_SEND, + .d = { .udp_send = { + .dest_ip = dest_ip, + .src_port = src_port, + .dest_port = dest_port, + } } + }; + ump_net_send_call(call, payload_size, payload, cb, cb_arg); +} + +void ump_net_udp_listen ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg, + ump_net_udp_listen_callback_t cb, void *cb_arg +) { + if (collections_hash_find(udp_listen_table, dest_port) != NULL) { + cb(cb_arg, LIB_ERR_NET_PORT_IN_USE, 0); + return; + } + struct udp_listen_entry *entry = malloc(sizeof(struct udp_listen_entry)); + if (entry == NULL) { + cb(cb_arg, LIB_ERR_MALLOC_FAIL, 0); + return; + } + entry->recv_handler = recv_handler; + entry->recv_handler_arg = recv_handler_arg; + collections_hash_insert(udp_listen_table, dest_port, entry); + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_UDP_LISTEN, + .d = { .udp_listen = { .dest_port = dest_port } } + }; + ump_net_send_call(call, 0, NULL, cb, cb_arg); +} + +void ump_net_udp_listen_stop ( + struct ump_net_call *call, + uint16_t dest_port, + ump_net_udp_listen_stop_callback_t cb, void *cb_arg +) { + if (collections_hash_find(udp_listen_table, dest_port) == NULL) { + cb(cb_arg, LIB_ERR_NET_NOT_LISTENING); + return; + } + collections_hash_delete(udp_listen_table, dest_port); + + call->out_header = (struct ump_net_out_header){ + .op = UMP_NET_OP_UDP_LISTEN_STOP, + .d = { .udp_listen_stop = { .dest_port = dest_port } } + }; + ump_net_send_call(call, 0, NULL, cb, cb_arg); +} + +static ump_recv_payload_callback_fn_t recv_payload_callback; +static void *recv_payload_callback_arg; + +static void recv_payload_callback_wrap (void *arg, size_t payload_size, void *payload) { + if (recv_payload_callback != NULL) { + recv_payload_callback(recv_payload_callback_arg, payload_size, payload); + } + net_recv_next(); +} + +void ump_net_recv_payload ( + void *payload, + ump_recv_payload_callback_fn_t callback, void *callback_arg +) { + recv_payload_callback = callback; + recv_payload_callback_arg = callback_arg; + ump_recv_payload(net_recv_chan, payload, recv_payload_callback_wrap, NULL); +} diff --git a/lib/netutil/checksum.c b/lib/netutil/checksum.c index 55bd0ca..de95b60 100644 --- a/lib/netutil/checksum.c +++ b/lib/netutil/checksum.c @@ -33,10 +33,7 @@ lwip_standard_chksum(void *dataptr, uint16_t len) if ((acc & 0xffff0000UL) != 0) { acc = (acc >> 16) + (acc & 0x0000ffffUL); } - /* This maybe a little confusing: reorder sum using htons() - instead of ntohs() since it has a little less call overhead. - The caller must invert bits for Internet sum ! */ - return htons((uint16_t)acc); + return (uint16_t)acc; }; /** diff --git a/usr/drivers/enet/Hakefile b/usr/drivers/enet/Hakefile index 1d311d0..c2a8888 100644 --- a/usr/drivers/enet/Hakefile +++ b/usr/drivers/enet/Hakefile @@ -23,8 +23,9 @@ build application { target = "enet", - cFiles = [ - "enet_module.c" + cFiles = [ + "enet_module.c", + "enet_proto.c" ], mackerelDevices = ["imx8x/enet"], addLibraries = libDeps ["devif_backend_enet", "netutil"], diff --git a/usr/drivers/enet/enet.h b/usr/drivers/enet/enet.h index 758b174..3474418 100644 --- a/usr/drivers/enet/enet.h +++ b/usr/drivers/enet/enet.h @@ -10,8 +10,20 @@ #ifndef ENET_H_ #define ENET_H_ +#include +#include +#include +#include +#include +#include +#include +#include + +// enable verbose debug logs //#define ENET_DEBUG_OPTION 1 +// enable warnings when packets are dropped, and other unusual events happen +#define ENET_WARN_OPTION 1 #if defined(ENET_DEBUG_OPTION) #define ENET_DEBUG(x...) debug_printf("[enet] " x); @@ -19,6 +31,12 @@ #define ENET_DEBUG(fmt, ...) ((void)0) #endif +#if defined(ENET_WARN_OPTION) +#define ENET_WARN(x...) debug_printf("[enet] WARN: " x); +#else +#define ENET_WARN(fmt, ...) ((void)0) +#endif + #define ENET_PROMISC @@ -62,7 +80,7 @@ struct enet_queue { size_t size; // stop and wake threashold - uint16_t stop_th; + uint16_t stop_th; uint16_t wake_th; char* tso_hdr; @@ -85,6 +103,83 @@ struct enet_queue { struct region_entry* regions; }; +typedef void (*tx_alloc_callback_t)(void *cb_arg, struct devq_buf *buf, void *vaddr); + +struct tx_alloc_queue_entry { + tx_alloc_callback_t cb; + void *cb_arg; + struct tx_alloc_queue_entry *next; +}; + +#define ARP_TX_QUEUE_LEN 64 +STATIC_ASSERT((ARP_TX_QUEUE_LEN & (ARP_TX_QUEUE_LEN - 1)) == 0, "Must be power of 2"); + +struct arp_tx_queue_entry { + struct tx_alloc_queue_entry tx_entry; + uint64_t eth_dst; + uint32_t ip_dst; +}; + +struct arp_state { + struct arp_tx_queue_entry tx_queue[ARP_TX_QUEUE_LEN]; + size_t tx_queue_len; + size_t tx_queue_tail; + + collections_hash_table* table; +}; + +struct ip_state { + uint16_t next_id; +}; + +struct udp_state { + collections_hash_table* listen_table; + uint16_t next_ephemeral_port; +}; + +struct ump_client; + +struct ump_reply_buf { + struct ump_send_queue_entry ump_entry; + struct ump_net_in_header ump_header; + struct ump_client *client; +}; + +struct ump_client { + struct ump_send_chan *send_chan; + struct ump_recv_chan *recv_chan; + struct tx_alloc_queue_entry tx_entry; + struct devq_buf tx_buf; + struct ip_hdr *tx_ip_hdr; + + // If ump_reply_slab becomes empty, this is set to true and + // UMP receiving is blocked. + bool blocked_on_reply_slab; + struct simpleslab_allocator ump_reply_slab; + uint8_t ump_reply_slab_buf[sizeof(struct ump_reply_buf) * 128]; +}; + +struct icmp_echo_reply_meta { + struct devq_buf rx_buf; + struct tx_alloc_queue_entry tx_entry; + struct icmp_echo_hdr *icmp_echo_hdr; + struct eth_hdr *eth_hdr; + struct ip_hdr *ip_hdr; +}; + +struct udp_recv { + struct devq_buf rx_buf; + struct ump_send_queue_entry ump_entry; + struct ump_net_in_header ump_header; +}; + +// union to obtain max required size +union rx_meta_slab_data { + struct icmp_echo_reply_meta icmp_echo_reply_meta; + struct udp_recv udp_recv; +}; + + struct enet_driver_state { struct bfdriver_instance *bfi; struct capref regs; @@ -99,9 +194,30 @@ struct enet_driver_state { struct capref rx_mem; struct capref tx_mem; + regionid_t tx_rid; + + int tx_init_i; + + ip_addr_t my_ip; + int net_bits; + ip_addr_t net_mask; + + struct tx_alloc_queue_entry *tx_alloc_queue_head; + struct tx_alloc_queue_entry *tx_alloc_queue_tail; + + // There are exactly as many of these blocks as there are RX buffers. + // You can allocate one for as long as you hold an RX buffer. + struct simpleslab_allocator rx_meta_slab; + uint8_t rx_meta_slab_buf[sizeof(union rx_meta_slab_data) * RX_RING_SIZE]; + + struct arp_state arp; + struct ip_state ip; + struct udp_state udp; }; #define ENET_HASH_BITS 6 #define ENET_CRC32_POLY 0xEDB88320 +void enet_loop (void); + #endif // ndef ENET_H_ diff --git a/usr/drivers/enet/enet_devq.c b/usr/drivers/enet/enet_devq.c index f39741e..04b9a2b 100644 --- a/usr/drivers/enet/enet_devq.c +++ b/usr/drivers/enet/enet_devq.c @@ -25,7 +25,7 @@ #include "enet.h" -static struct region_entry* get_region(struct enet_queue* q, regionid_t rid) +struct region_entry* enet_get_region(struct enet_queue* q, regionid_t rid) { struct region_entry* entry = q->regions; while (entry != NULL) { @@ -47,7 +47,7 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid) assert(entry); entry->rid = rid; entry->next = NULL; - + struct frame_identity id; err = frame_identify(cap, &id); if (err_is_fail(err)) { @@ -65,7 +65,7 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid) entry->mem.vbase = (lvaddr_t) va; entry->mem.mem = cap; entry->mem.size = id.bytes; - + ENET_DEBUG("register region id %d base=%lx \n", rid, entry->mem.devaddr); // linked list of regions struct region_entry* cur = queue->regions; @@ -77,10 +77,10 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid) while (cur->next != NULL) { cur = cur->next; } - + cur->next = entry; - ENET_DEBUG("registerd region id %d base=%p len=%ld \n", rid, + ENET_DEBUG("registerd region id %d base=%p len=%ld \n", rid, (void*) entry->mem.vbase, entry->mem.size); return SYS_ERR_OK; } @@ -102,13 +102,13 @@ static inline size_t enet_full_slots(struct enet_queue* q) static void enet_activate_tx_ring(enet_t * d) { // bit is always set to 1 only when ring is empty then it is set to 0 - enet_tdar_tdar_wrf(d, 1); + enet_tdar_tdar_wrf(d, 1); } static void enet_activate_rx_ring(enet_t* d) { // bit is always set to 1 only when ring is empty then it is set to 0 - enet_rdar_rdar_wrf(d, 1); + enet_rdar_rdar_wrf(d, 1); } static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid, @@ -118,7 +118,7 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid, genoffset_t* valid_length, uint64_t* flags) { - struct enet_queue* q = (struct enet_queue*) que; + struct enet_queue* q = (struct enet_queue*) que; enet_bufdesc_t desc = q->ring[q->head]; struct devq_buf* buf = &q->ring_bufs[q->head]; @@ -131,7 +131,7 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid, } /* - ENET_DEBUG("Try dequeue %d RADR %d ENABLED %d STATUS %lx \n", q->head, + ENET_DEBUG("Try dequeue %d RADR %d ENABLED %d STATUS %lx \n", q->head, enet_rdar_rdar_rdf(q->d), enet_ecr_etheren_rdf(q->d), status); */ if (!(status & ENET_RX_EMPTY)) { @@ -153,15 +153,15 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid, status &= ~ENET_RX_STATS; // remove chached stuff in buffer - struct region_entry *entry = get_region(q, *rid); - assert(entry); + struct region_entry *entry = enet_get_region(q, *rid); + assert(entry); lvaddr_t vaddr = (lvaddr_t) entry->mem.vbase + *offset + *valid_data; - cpu_dcache_wb_range(vaddr, *valid_length); + cpu_dcache_wb_range(vaddr, *valid_length); dmb(); - + enet_bufdesc_sc_insert(desc, status); - + q->head = (q->head+1) & (q->size -1); @@ -175,18 +175,18 @@ static errval_t enet_tx_dequeue(struct devq* que, regionid_t* rid, genoffset_t* valid_length, uint64_t* flags) { - struct enet_queue* q = (struct enet_queue*) que; + struct enet_queue* q = (struct enet_queue*) que; if (enet_full_slots(q)) { enet_bufdesc_t desc = q->ring[q->head]; dmb(); - cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head], + cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head], sizeof(enet_bufdesc_t)); desc = q->ring[q->head]; struct devq_buf* buf= &q->ring_bufs[q->head]; if (!(enet_bufdesc_sc_extract(desc) & ENET_TX_READY)) { - ENET_DEBUG("We sent something!! \n"); + // ENET_DEBUG("We sent something!! \n"); *valid_length = buf->valid_length; *offset = buf->offset; *length = buf->length; @@ -210,8 +210,8 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of genoffset_t length, genoffset_t valid_data, genoffset_t valid_length, uint64_t flags) { - - struct enet_queue* q = (struct enet_queue*) que; + + struct enet_queue* q = (struct enet_queue*) que; assert(valid_length > 0 && valid_length < ENET_MAX_PKT_SIZE); @@ -221,11 +221,11 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of lpaddr_t addr = 0; lvaddr_t vaddr = 0; - struct region_entry *entry = get_region(q, rid); - assert(entry); + struct region_entry *entry = enet_get_region(q, rid); + assert(entry); addr = (lpaddr_t) entry->mem.devaddr + offset + valid_data; vaddr = (lvaddr_t) entry->mem.vbase + offset + valid_data; - + struct devq_buf* buf= &q->ring_bufs[q->tail]; buf->offset = offset; buf->length = length; @@ -233,9 +233,9 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of buf->valid_data = valid_data; buf->rid = rid; buf->flags = flags; - + // TODO alignment - + enet_bufdesc_t desc = q->ring[q->tail]; enet_bufdesc_addr_insert(desc, addr); enet_bufdesc_len_insert(desc, valid_length); @@ -244,7 +244,7 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of dmb(); if (q->tail == (q->size -1)) { - enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC | + enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC | ENET_TX_LAST | ENET_TX_WRAP); } else { enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC | ENET_TX_LAST); @@ -277,20 +277,20 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of genoffset_t length, genoffset_t valid_data, genoffset_t valid_length, uint64_t flags) { - struct enet_queue* q = (struct enet_queue*) que; + struct enet_queue* q = (struct enet_queue*) que; //enet_bufdesc_addr_insert(desc, ); - struct region_entry *entry = get_region(q, rid); - assert(entry); - + struct region_entry *entry = enet_get_region(q, rid); + assert(entry); + assert(valid_length > 0 && length <= ENET_MAX_BUF_SIZE); if (enet_full_slots(q) == q->size) { return DEVQ_ERR_QUEUE_FULL; } - + lpaddr_t addr = 0; addr = (lpaddr_t) entry->mem.devaddr + offset; - + struct devq_buf* buf= &q->ring_bufs[q->tail]; buf->offset = offset; buf->length = length; @@ -298,7 +298,7 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of buf->valid_data = valid_data; buf->rid = rid; buf->flags = flags; - + enet_bufdesc_t desc = q->ring[q->tail]; enet_bufdesc_addr_insert(desc, addr); enet_bufdesc_len_insert(desc, 0); @@ -311,7 +311,7 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of } arm64_dcache_wb_range((lvaddr_t) &q->ring[q->tail], sizeof(enet_bufdesc_t)); - /*ENET_DEBUG("enqueue ring_buf[%d]=%p phys=%lx offset=%lx length=%zu\n", q->tail, + /*ENET_DEBUG("enqueue ring_buf[%d]=%p phys=%lx offset=%lx length=%zu\n", q->tail, q->ring[q->tail], addr, offset, length); */ // activate RX (This is only needed if ring is empty) @@ -330,7 +330,7 @@ errval_t enet_rx_queue_create(struct enet_queue ** q, enet_t *dev) assert(rxq); rxq->size = RX_RING_SIZE; - + /* Initialize Mackerel binding */ rxq->d = dev; @@ -413,7 +413,7 @@ errval_t enet_tx_queue_create(struct enet_queue ** q, struct enet_t* dev) struct enet_queue* txq; txq = calloc(1, sizeof(struct enet_queue)); txq->size = TX_RING_SIZE; - + /* Initialize Mackerel binding */ txq->d = dev; assert(txq->d); diff --git a/usr/drivers/enet/enet_module.c b/usr/drivers/enet/enet_module.c index 8518207..bd84d20 100644 --- a/usr/drivers/enet/enet_module.c +++ b/usr/drivers/enet/enet_module.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "enet.h" @@ -446,8 +447,15 @@ static void enet_reg_setup(struct enet_driver_state* st) reg = enet_rcr_mii_mode_insert(reg, 0x1); reg = enet_rcr_fce_insert(reg, 0x1); reg = enet_rcr_max_fl_insert(reg, 1522); + reg = enet_rcr_crcfwd_insert(reg, 1); // strip received Ethernet CRC //reg = enet_rcr_prom_insert(reg, 1); enet_rcr_wr(st->d, reg); + + enet_racc_t racc = 0; + racc = enet_racc_linedis_insert(racc, 1); // discard received packets with invalid Ethernet CRC + enet_racc_wr(st->d, racc); + + enet_tcr_crcfwd_wrf(st->d, 0); // add Ethernet CRC when transmitting } static errval_t enet_open(struct enet_driver_state *st) @@ -563,13 +571,13 @@ static errval_t enet_probe(struct enet_driver_state* st) return SYS_ERR_OK; } +struct enet_driver_state *st; int main(int argc, char *argv[]) { errval_t err; debug_printf("Enet driver started \n"); - struct enet_driver_state * st = (struct enet_driver_state*) - calloc(1, sizeof(struct enet_driver_state)); + st = (struct enet_driver_state*)calloc(1, sizeof(struct enet_driver_state)); assert(st != NULL); /* Net Project: get the capability to the register region @@ -653,17 +661,47 @@ int main(int argc, char *argv[]) { if (err_is_fail(err)) { return err; } - struct devq_buf buf; - while(true) { - err = devq_dequeue((struct devq*) st->rxq, &buf.rid, &buf.offset, - &buf.length, &buf.valid_data, &buf.valid_length, - &buf.flags); - if (err_is_ok(err)) { - debug_printf("Received Packet of size %lu \n", buf.valid_length); - err = devq_enqueue((struct devq*) st->rxq, buf.rid, buf.offset, - buf.length, buf.valid_data, buf.valid_length, - buf.flags); - assert(err_is_ok(err)); - } + st->tx_rid = rid; + + st->tx_init_i = 0; + st->tx_alloc_queue_head = NULL; + st->tx_alloc_queue_tail = NULL; + + if (argc < 3) { + printf("ENET: Error: missing address arguments\n"); + return 1; } + + int a1, a2, a3, a4; + int parsed = sscanf(argv[1], "%d.%d.%d.%d.", &a1, &a2, &a3, &a4); + if (parsed != 4) { + printf("ENET: Error: failed to parse IP address\n"); + return 1; + } + st->my_ip = MK_IP(a1, a2, a3, a4); + + int net_bits = strtol(argv[2], NULL, 10); + if (!(0 <= net_bits && net_bits <= 32)) { + printf("ENET: Error: network bit count out of range\n"); + return 1; + } + st->net_bits = net_bits; + if (net_bits == 0) st->net_mask = 0; // shift by 32 bits is undefined behavior + else st->net_mask = 0xffffffffU << (32 - net_bits); + + struct eth_addr_net mac = eth_addr_wr(st->mac); + debug_printf("my MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac.addr[0], mac.addr[1], mac.addr[2], mac.addr[3], mac.addr[4], mac.addr[5] + ); + struct uint32_net ip = uint32_wr(st->my_ip); + debug_printf("my IP: %d.%d.%d.%d\n", + ip.val[0], ip.val[1], ip.val[2], ip.val[3] + ); + struct uint32_net net = uint32_wr(st->my_ip & st->net_mask); + debug_printf("net: %d.%d.%d.%d/%d\n", + net.val[0], net.val[1], net.val[2], net.val[3], + st->net_bits + ); + + enet_loop(); } diff --git a/usr/drivers/enet/enet_proto.c b/usr/drivers/enet/enet_proto.c new file mode 100644 index 0000000..65172a0 --- /dev/null +++ b/usr/drivers/enet/enet_proto.c @@ -0,0 +1,706 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "enet.h" + +// https://datatracker.ietf.org/doc/html/rfc6335#section-6 +#define EPHEMERAL_PORT_START 49152 +#define EPHEMERAL_PORT_END 65535 + +extern struct enet_driver_state *st; + +static void write_eth_ip_header ( + struct devq_buf *buf, void *vaddr, + uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len +); + +static void tx_alloc (struct tx_alloc_queue_entry *entry, tx_alloc_callback_t cb, void *cb_arg) { + errval_t err = DEVQ_ERR_QUEUE_EMPTY; + struct devq_buf buf; + + // If the queue is not empty, always append to the queue, so that FIFO order is maintained + if (st->tx_alloc_queue_head == NULL) { + err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset, + &buf.length, &buf.valid_data, &buf.valid_length, + &buf.flags); + } + if (err_is_fail(err)) { + if (st->tx_init_i < st->txq->size) { + buf.rid = st->tx_rid; + buf.offset = st->tx_init_i * 2048; + buf.length = 2048; + buf.valid_data = 0; + buf.valid_length = 2048; + buf.flags = 0; + st->tx_init_i++; + } else { + entry->cb = cb; + entry->cb_arg = cb_arg; + entry->next = NULL; + if (st->tx_alloc_queue_tail == NULL) { + st->tx_alloc_queue_head = entry; + } else { + st->tx_alloc_queue_tail->next = entry; + } + st->tx_alloc_queue_tail = entry; + return; + } + } + + struct region_entry *region_entry = enet_get_region(st->txq, buf.rid); + assert(region_entry != NULL); + void *vaddr = (void*)region_entry->mem.vbase + buf.offset; + cb(cb_arg, &buf, vaddr); +} + +static void tx_send (struct devq_buf *buf) { + errval_t err; + err = devq_enqueue((struct devq*) st->txq, buf->rid, buf->offset, + buf->length, buf->valid_data, buf->valid_length, + buf->flags); + assert(err_is_ok(err)); +} + +static void rx_release (struct devq_buf *buf) { + errval_t err; + err = devq_enqueue((struct devq*) st->rxq, buf->rid, buf->offset, + buf->length, buf->valid_data, buf->valid_length, + buf->flags); + assert(err_is_ok(err)); +} + +// ARP: https://datatracker.ietf.org/doc/html/rfc826 + +static void arp_send ( + struct devq_buf *buf, void *vaddr, + uint64_t eth_dst, uint32_t ip_dest, + uint16_t op +) { + buf->valid_data = 0; + buf->valid_length = ETH_HLEN + sizeof(struct arp_hdr); + + struct eth_hdr *eth_hdr = vaddr; + eth_hdr->dst = eth_addr_wr(eth_dst); + eth_hdr->src = eth_addr_wr(st->mac); + eth_hdr->type = uint16_wr(ETH_TYPE_ARP); + + struct arp_hdr *arp_hdr = vaddr + ETH_HLEN; + arp_hdr->hwtype = uint16_wr(ARP_HW_TYPE_ETH); + arp_hdr->proto = uint16_wr(ETH_TYPE_IP); + arp_hdr->hwlen = ETH_ADDR_LEN; + arp_hdr->protolen = IP_ADDR_LEN; + arp_hdr->opcode = uint16_wr(op); + arp_hdr->eth_src = eth_addr_wr(st->mac); + arp_hdr->ip_src = uint32_wr(st->my_ip); + arp_hdr->eth_dst = eth_addr_wr(eth_dst); + arp_hdr->ip_dst = uint32_wr(ip_dest); + + tx_send(buf); +} + +static void arp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { + struct arp_tx_queue_entry *entry = &st->arp.tx_queue[st->arp.tx_queue_tail]; + assert(cb_arg == entry); + + arp_send(buf, vaddr, entry->eth_dst, entry->ip_dst, ARP_OP_REP); + + st->arp.tx_queue_tail = (st->arp.tx_queue_tail + 1) & (ARP_TX_QUEUE_LEN - 1); + st->arp.tx_queue_len--; +} + +static void arp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) { + ENET_DEBUG("Received ARP packet\n"); + + if (buf->valid_length < sizeof(struct arp_hdr)) { + ENET_WARN("Received ARP packet too small\n"); + rx_release(buf); + return; + } + struct arp_hdr *arp_hdr = vaddr + buf->valid_data; + uint16_t opcode = uint16_rd(arp_hdr->opcode); + if ( + uint16_rd(arp_hdr->hwtype) != ARP_HW_TYPE_ETH || + uint16_rd(arp_hdr->proto) != ETH_TYPE_IP || + arp_hdr->hwlen != ETH_ADDR_LEN || + arp_hdr->protolen != IP_ADDR_LEN || + (opcode != ARP_OP_REQ && opcode != ARP_OP_REP) + ) { + ENET_WARN("Unknown ARP packet type\n"); + rx_release(buf); + return; + } + + uint32_t ip_src = uint32_rd(arp_hdr->ip_src); + + if ((st->my_ip & st->net_mask) != (ip_src & st->net_mask)) { + ENET_WARN("Received ARP packet for address outside my network\n"); + rx_release(buf); + return; + } + + uint64_t eth_src = eth_addr_rd(arp_hdr->eth_src); + uint64_t eth_old = (uintptr_t)collections_hash_find(st->arp.table, ip_src); + // check if sender protocol address is in translation table, if so update entry + if (eth_old != 0 && eth_src != eth_old) { + ENET_WARN("MAC address of %d.%d.%d.%d has changed\n", + arp_hdr->ip_src.val[0], + arp_hdr->ip_src.val[1], + arp_hdr->ip_src.val[2], + arp_hdr->ip_src.val[3] + ); + collections_hash_delete(st->arp.table, ip_src); + collections_hash_insert(st->arp.table, ip_src, (void*)eth_src); + } + + if (uint32_rd(arp_hdr->ip_dst) == st->my_ip) { + ENET_DEBUG("Received ARP packet addressed to me\n"); + + // if sender protocol address was not in translation table, add entry + if (eth_old == 0) { + collections_hash_insert(st->arp.table, ip_src, (void*)eth_src); + } + + if (opcode == ARP_OP_REQ) { + if (st->arp.tx_queue_len >= ARP_TX_QUEUE_LEN) { + ENET_WARN("Too many queued ARP replies, not sending reply\n"); + } else { + struct arp_tx_queue_entry *entry = &st->arp.tx_queue[(st->arp.tx_queue_tail + st->arp.tx_queue_len) & (ARP_TX_QUEUE_LEN - 1)]; + st->arp.tx_queue_len++; + entry->eth_dst = eth_src; + entry->ip_dst = ip_src; + tx_alloc(&entry->tx_entry, arp_reply, entry); + } + } + } + + rx_release(buf); +} + +// ICMP: https://datatracker.ietf.org/doc/html/rfc792 + +static void icmp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { + struct icmp_echo_reply_meta *entry = cb_arg; + + write_eth_ip_header( + buf, vaddr, + eth_addr_rd(entry->eth_hdr->src), + uint32_rd(entry->ip_hdr->src), + IP_PROTO_ICMP, + entry->rx_buf.valid_length + ); + + struct icmp_echo_hdr *icmp_echo_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr); + memcpy(icmp_echo_hdr, entry->icmp_echo_hdr, entry->rx_buf.valid_length); + icmp_echo_hdr->type = ICMP_ER; + + // Incremental update: https://datatracker.ietf.org/doc/html/rfc1071 + uint32_t chksum = uint16_rd(icmp_echo_hdr->chksum); + chksum += (ICMP_ECHO << 8) + ((ICMP_ER << 8) ^ 0x0000ffffUL); + chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); + icmp_echo_hdr->chksum = uint16_wr(chksum); + + rx_release(&entry->rx_buf); + simpleslab_free(&st->rx_meta_slab, entry); + tx_send(buf); +} + +static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) { + if (buf->valid_length < 4) { + ENET_WARN("Received ICMP packet too small\n"); + rx_release(buf); + return; + } + + if (inet_checksum(vaddr + buf->valid_data, buf->valid_length) != 0) { + ENET_WARN("Received ICMP packet with bad checksum\n"); + rx_release(buf); + return; + } + + uint8_t type = *(uint8_t*)(vaddr + buf->valid_data); + if (type == ICMP_ECHO) { + if (buf->valid_length < sizeof(struct icmp_echo_hdr)) { + ENET_WARN("Received ICMP packet too small\n"); + rx_release(buf); + return; + } + + struct icmp_echo_hdr *icmp_echo_hdr = vaddr + buf->valid_data; + ENET_DEBUG("Received ICMP echo, seq=%d\n", uint16_rd(icmp_echo_hdr->seqno)); + + struct icmp_echo_reply_meta *meta = simpleslab_alloc(&st->rx_meta_slab); + meta->rx_buf = *buf; + meta->eth_hdr = eth_hdr; + meta->ip_hdr = ip_hdr; + meta->icmp_echo_hdr = icmp_echo_hdr; + + tx_alloc(&meta->tx_entry, icmp_reply, meta); + return; + } else { + ENET_WARN("Received ICMP packet with unknown type %d\n", type); + } + + rx_release(buf); +} + +// UDP: https://datatracker.ietf.org/doc/html/rfc768 + +static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry) { + struct udp_recv *meta = arg; + rx_release(&meta->rx_buf); + simpleslab_free(&st->rx_meta_slab, meta); +} + +static uint16_t udp_checksum (struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr) { + uint16_t udp_len = uint16_rd(udp_hdr->len); + uint32_t chksum = inet_checksum(udp_hdr, udp_len) ^ 0x0000ffffUL; + // add pseudo header + chksum += (ip_hdr->src.val[0] << 8) | ip_hdr->src.val[1]; + chksum += (ip_hdr->src.val[2] << 8) | ip_hdr->src.val[3]; + chksum += (ip_hdr->dest.val[0] << 8) | ip_hdr->dest.val[1]; + chksum += (ip_hdr->dest.val[2] << 8) | ip_hdr->dest.val[3]; + chksum += IP_PROTO_UDP; + chksum += udp_len; + chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); + chksum = (chksum >> 16) + (chksum & 0x0000ffffUL); + return ~(uint16_t)chksum; +} + +static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) { + if (buf->valid_length < UDP_HLEN) { + ENET_WARN("Received UDP packet too small\n"); + rx_release(buf); + return; + } + struct udp_hdr *udp_hdr = vaddr + buf->valid_data; + uint16_t udp_len = uint16_rd(udp_hdr->len); + if (udp_len < UDP_HLEN || buf->valid_length < udp_len) { + ENET_WARN("Received UDP packet too small\n"); + rx_release(buf); + return; + } + + if ( + uint16_rd(udp_hdr->chksum) != 0 && // 0 means no checksum + udp_checksum(ip_hdr, udp_hdr) != 0 + ) { + ENET_WARN("Received UDP packet with bad checksum\n"); + rx_release(buf); + return; + } + + uint16_t src_port = uint16_rd(udp_hdr->src); + uint16_t dest_port = uint16_rd(udp_hdr->dest); + void *payload = (void*)udp_hdr + UDP_HLEN; + uint16_t payload_len = udp_len - UDP_HLEN; + ENET_DEBUG("Received UDP packet from %d to %d, len %d\n", src_port, dest_port, payload_len); + + + struct ump_client *client = collections_hash_find(st->udp.listen_table, dest_port); + if (client == NULL) { + // TODO: send ICMP unreachable + rx_release(buf); + } else { + struct udp_recv *meta = simpleslab_alloc(&st->rx_meta_slab); + meta->rx_buf = *buf; + meta->ump_header = (struct ump_net_in_header){ + .op = UMP_NET_EV_UDP_RECV, + .d = { .udp_recv = { + .src_ip = uint32_rd(ip_hdr->src), + .dest_ip = uint32_rd(ip_hdr->dest), + .src_port = src_port, + .dest_port = dest_port, + } } + }; + ump_send(client->send_chan, &meta->ump_entry, + sizeof(struct ump_net_in_header), &meta->ump_header, + payload_len, payload, + udp_recv_ump_callback, meta); + return; + } +} + +// IP: https://datatracker.ietf.org/doc/html/rfc791#section-3.1 + +static void ip_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) { + if (buf->valid_length < sizeof(struct ip_hdr)) { + ENET_WARN("Received IP packet too small\n"); + rx_release(buf); + return; + } + struct ip_hdr *ip_hdr = vaddr + buf->valid_data; + uint16_t ip_len = uint16_rd(ip_hdr->len); + uint16_t header_len = IPH_HL(ip_hdr) * 4; + if ( + IPH_V(ip_hdr) != 4 || + header_len < 20 || + header_len > ip_len || + ip_len > buf->valid_length + ) { + ENET_WARN("Received bad IP packet\n"); + rx_release(buf); + return; + } + + if (inet_checksum(ip_hdr, header_len) != 0) { + ENET_WARN("Received IP packet with bad checksum\n"); + rx_release(buf); + return; + } + + uint16_t offset_flags = uint16_rd(ip_hdr->offset); + if ( + (offset_flags & IP_MF) != 0 || + (offset_flags & IP_OFFMASK) != 0 + ) { + ENET_WARN("Received IP fragment, dropping\n"); + rx_release(buf); + return; + } + + ENET_DEBUG("Received IP packet from %d.%d.%d.%d to %d.%d.%d.%d, proto %d\n", + ip_hdr->src.val[0], + ip_hdr->src.val[1], + ip_hdr->src.val[2], + ip_hdr->src.val[3], + ip_hdr->dest.val[0], + ip_hdr->dest.val[1], + ip_hdr->dest.val[2], + ip_hdr->dest.val[3], + ip_hdr->proto + ); + + buf->valid_data += header_len; + buf->valid_length = ip_len - header_len; + + if (uint32_rd(ip_hdr->dest) == st->my_ip) { + if (ip_hdr->proto == IP_PROTO_ICMP) { + icmp_handle(buf, vaddr, eth_hdr, ip_hdr); + } else if (ip_hdr->proto == IP_PROTO_UDP) { + udp_handle(buf, vaddr, eth_hdr, ip_hdr); + } else { + ENET_WARN("Received IP packet with unknown protocol %d\n", ip_hdr->proto); + rx_release(buf); + } + } else { + ENET_WARN("Received IP packet for someone else\n"); + rx_release(buf); + } +} + +static void write_eth_ip_header ( + struct devq_buf *buf, void *vaddr, + uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len +) { + buf->valid_data = 0; + buf->valid_length = ETH_HLEN + sizeof(struct ip_hdr) + payload_len; + assert(buf->valid_length <= ETH_HLEN + 1500); + + struct eth_hdr *eth_hdr = vaddr; + eth_hdr->dst = eth_addr_wr(eth_dst); + eth_hdr->src = eth_addr_wr(st->mac); + eth_hdr->type = uint16_wr(ETH_TYPE_IP); + + struct ip_hdr *ip_hdr = vaddr + ETH_HLEN; + IPH_VHL_SET(ip_hdr, 4, 5); + ip_hdr->tos = 0; + ip_hdr->len = uint16_wr(sizeof(struct ip_hdr) + payload_len); + ip_hdr->id = uint16_wr(st->ip.next_id++); + ip_hdr->offset = uint16_wr(IP_DF); + ip_hdr->ttl = 64; + ip_hdr->proto = proto; + ip_hdr->chksum = uint16_wr(0); + ip_hdr->src = uint32_wr(st->my_ip); + ip_hdr->dest = uint32_wr(ip_dest); + ip_hdr->chksum = uint16_wr(inet_checksum(ip_hdr, 20)); +} + +// Ethernet + +static void rx_handle (struct devq_buf *buf) { + struct region_entry *entry = enet_get_region(st->rxq, buf->rid); + assert(entry != NULL); + void *vaddr = (void*)entry->mem.vbase + buf->offset; + void *eth_vaddr = vaddr + buf->valid_data; + + #if defined(ENET_DEBUG_OPTION) + debug_printf("Received Packet of size %lu:", buf->valid_length); + for (size_t i = 0; i < buf->valid_length; i++) { + printf(" %02x", ((uint8_t*)eth_vaddr)[i]); + } + printf("\n"); + #endif + + if (buf->valid_length < ETH_HLEN) { + ENET_WARN("Ethernet packet too small\n"); + rx_release(buf); + return; + } + + struct eth_hdr *eth_hdr = eth_vaddr; + uint16_t type = uint16_rd(eth_hdr->type); + + buf->valid_data += ETH_HLEN; + buf->valid_length -= ETH_HLEN; + + if (type == ETH_TYPE_ARP) { + arp_handle(buf, vaddr, eth_hdr); + } else if (type == ETH_TYPE_IP) { + ip_handle(buf, vaddr, eth_hdr); + } else { + ENET_WARN("Received packet of unknown type: %04x\n", type); + rx_release(buf); + } +} + +// UMP + +static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size); + +static void netump_next (struct ump_client *client) { + if (client->ump_reply_slab.free != 0) { + ump_recv_header(client->recv_chan, netump_header_handle, client); + } else { + client->blocked_on_reply_slab = true; + } +} + +static void netump_reply_callback (void *arg, struct ump_send_queue_entry *entry) { + struct ump_reply_buf *reply = arg; + struct ump_client *client = reply->client; + simpleslab_free(&client->ump_reply_slab, reply); + if (client->blocked_on_reply_slab) { + client->blocked_on_reply_slab = false; + netump_next(client); + } +} + +static void netump_payload_ignore (void *arg, size_t payload_size, void *payload) { + netump_next(arg); +} + +static void netump_send_reply (struct ump_reply_buf *reply, errval_t err) { + reply->ump_header.ret_err = err; + ump_send(reply->client->send_chan, &reply->ump_entry, + sizeof(struct ump_net_in_header), &reply->ump_header, + 0, NULL, + netump_reply_callback, reply); +} + +static void netump_udp_tx_payload (void *arg, size_t payload_size, void *payload) { + struct ump_client *client = arg; + + struct udp_hdr *udp_hdr = (void*)client->tx_ip_hdr + sizeof(struct ip_hdr); + uint16_t checksum = udp_checksum(client->tx_ip_hdr, udp_hdr); + if (checksum == 0) checksum = 0xffff; + udp_hdr->chksum = uint16_wr(checksum); + + tx_send(&client->tx_buf); + netump_next(client); +} + +static void netump_udp_tx_allocated (void *arg, struct devq_buf *buf, void *vaddr) { + struct ump_reply_buf *reply = arg; + struct ump_client *client = reply->client; + struct ump_net_out_header *header = client->recv_chan->header; + size_t payload_size = client->recv_chan->next_payload_size; + + uint64_t eth_dest = (uintptr_t)collections_hash_find(st->arp.table, header->d.udp_send.dest_ip); + if (eth_dest == 0) { + ENET_WARN("IP not in ARP table, dropping packet and sending ARP request\n"); + arp_send(buf, vaddr, 0xffffffffffff, + header->d.udp_send.dest_ip, ARP_OP_REQ); + netump_send_reply(reply, LIB_ERR_NET_ARP_MISS); + ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client); + return; + } + write_eth_ip_header( + buf, vaddr, + eth_dest, + header->d.udp_send.dest_ip, + IP_PROTO_UDP, + UDP_HLEN + payload_size + ); + struct udp_hdr *udp_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr); + udp_hdr->src = uint16_wr(header->d.udp_send.src_port); + udp_hdr->dest = uint16_wr(header->d.udp_send.dest_port); + udp_hdr->len = uint16_wr(UDP_HLEN + payload_size); + udp_hdr->chksum = uint16_wr(0); + void *payload = (void*)udp_hdr + UDP_HLEN; + client->tx_buf = *buf; + client->tx_ip_hdr = vaddr + ETH_HLEN; + + netump_send_reply(reply, SYS_ERR_OK); + ump_recv_payload(client->recv_chan, payload, netump_udp_tx_payload, client); +} + +static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size) { + struct ump_client *client = arg; + struct ump_net_out_header *header = header_raw; + errval_t err; + + struct ump_reply_buf *reply = simpleslab_alloc(&client->ump_reply_slab); + assert(reply != NULL); + reply->client = client; + reply->ump_header.op = header->op; + + if (header->op == UMP_NET_OP_UDP_SEND) { + if (payload_size > 1500 - sizeof(struct ip_hdr) - UDP_HLEN) { + err = LIB_ERR_NET_PACKET_TOO_BIG; + } else { + tx_alloc(&client->tx_entry, netump_udp_tx_allocated, reply); + return; + } + } else if (header->op == UMP_NET_OP_UDP_LISTEN) { + uint16_t port = header->d.udp_listen.dest_port; + if (port == 0) { + // allocate an ephemeral port + uint16_t first = st->udp.next_ephemeral_port; + port = first; + while (true) { + uint16_t next_port; + if (port == EPHEMERAL_PORT_END) { + next_port = EPHEMERAL_PORT_START; + } else { + next_port = port + 1; + } + if (collections_hash_find(st->udp.listen_table, port) == NULL) { + st->udp.next_ephemeral_port = next_port; + break; + } + port = next_port; + if (port == first) { + port = 0; + break; + } + } + } + if (port == 0) { + err = LIB_ERR_NET_ALLOC_PORT; + } else if (collections_hash_find(st->udp.listen_table, port) != NULL) { + err = LIB_ERR_NET_PORT_IN_USE; + } else { + ENET_DEBUG("Listening on UDP port %d\n", port); + reply->ump_header.d.udp_listen.dest_port = port; + collections_hash_insert(st->udp.listen_table, port, client); + err = SYS_ERR_OK; + } + } else if (header->op == UMP_NET_OP_UDP_LISTEN_STOP) { + uint16_t port = header->d.udp_listen_stop.dest_port; + if (collections_hash_find(st->udp.listen_table, port) != client) { + err = LIB_ERR_NET_NOT_LISTENING; + } else { + ENET_DEBUG("Stopped listening on UDP port %d\n", port); + collections_hash_delete(st->udp.listen_table, port); + err = SYS_ERR_OK; + } + } else { + debug_printf("Error: unknown UMP op\n"); + simpleslab_free(&client->ump_reply_slab, reply); + ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client); + return; + } + + netump_send_reply(reply, err); + ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client); +} + +static errval_t netump_connect (void *arg, struct capref cap) { + errval_t err; + + ENET_DEBUG("Incoming UMP connection\n"); + + struct ump_client *client = malloc(sizeof(struct ump_client)); + if (client == NULL) return LIB_ERR_MALLOC_FAIL; + + err = ump_chan_init(UMP_ROLE_SERVER, &client->send_chan, &client->recv_chan, + sizeof(struct ump_net_out_header), cap, get_default_waitset()); + if (err_is_fail(err)) return err; + + simpleslab_init(&client->ump_reply_slab, sizeof(struct ump_reply_buf), + &client->ump_reply_slab_buf, sizeof(client->ump_reply_slab_buf)); + client->blocked_on_reply_slab = false; + + netump_next(client); + + return SYS_ERR_OK; +} + +void enet_loop (void) { + errval_t err; + + st->arp.tx_queue_len = 0; + st->arp.tx_queue_tail = 0; + collections_hash_create(&st->arp.table, NULL); + + st->ip.next_id = 0; + + collections_hash_create_with_buckets(&st->udp.listen_table, 100, NULL); + st->udp.next_ephemeral_port = EPHEMERAL_PORT_START; + + simpleslab_init(&st->rx_meta_slab, sizeof(union rx_meta_slab_data), + &st->rx_meta_slab_buf, sizeof(st->rx_meta_slab_buf)); + + struct ump_binding_server server; + err = ump_binding_register(&server, UMP_SERVER_NET, netump_connect, NULL); + if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server"); + + struct devq_buf buf; + struct waitset *default_ws = get_default_waitset(); + while (true) { + bool made_progress = false; + + err = event_dispatch_non_block(default_ws); + if (err_is_fail(err) && err != LIB_ERR_NO_EVENT) { + DEBUG_ERR(err, "in event_dispatch"); + abort(); + } + // TODO: set made_progress if we handled an event. Currently, + // UMP always registers a callback, so we would always see an event. + // See TODO in poll_channels_disabled. + + err = devq_dequeue((struct devq*) st->rxq, &buf.rid, &buf.offset, + &buf.length, &buf.valid_data, &buf.valid_length, + &buf.flags); + if (err_is_ok(err)) { + made_progress = true; + rx_handle(&buf); + } + + if (st->tx_alloc_queue_head != NULL) { + err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset, + &buf.length, &buf.valid_data, &buf.valid_length, + &buf.flags); + if (err_is_ok(err)) { + made_progress = true; + struct tx_alloc_queue_entry *entry = st->tx_alloc_queue_head; + st->tx_alloc_queue_head = entry->next; + if (entry->next == NULL) { + st->tx_alloc_queue_tail = NULL; + } + struct region_entry *region_entry = enet_get_region(st->txq, buf.rid); + assert(region_entry != NULL); + void *vaddr = (void*)region_entry->mem.vbase + buf.offset; + entry->cb(entry->cb_arg, &buf, vaddr); + } + } + + if (!made_progress) { + thread_yield(); + } + } +} diff --git a/usr/echoserver/main.c b/usr/echoserver/main.c index 59edb3a..d081de1 100644 --- a/usr/echoserver/main.c +++ b/usr/echoserver/main.c @@ -1,9 +1,16 @@ #include #include #include -#include +#include +#include #include +#include #include +#include +#include + +// Echo Protocol: https://datatracker.ietf.org/doc/html/rfc862 +#define PORT_ECHO 7 #define ECHO_TEST_MESSAGE "Hello Client! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." @@ -48,18 +55,108 @@ static errval_t connect_server (void *arg, struct capref cap) { return SYS_ERR_OK; } + +// net echo server + +#define BUF_SIZE 1500 +struct echo_buf { + uint32_t src_ip; + uint16_t src_port; + struct ump_net_call call; + uint8_t payload[BUF_SIZE]; +}; +static struct simpleslab_allocator slabs; +static uint8_t slab_buf[sizeof(struct echo_buf) * 128]; + + +static void net_listen_callback (void *arg, errval_t err, uint16_t src_port) { + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to listen on UDP"); + return; + } + printf("Echo server: Listening on UDP port %d.\n", src_port); +} + +static void net_send_callback (void *arg, errval_t err) { + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to send packet"); + } +} + +static void net_reply_callback (void *arg, errval_t err) { + struct echo_buf *echo_buf = arg; + simpleslab_free(&slabs, echo_buf); + if (err_is_fail(err)) { + DEBUG_ERR(err, "failed to send packet"); + } +} + +static struct ump_net_ev_udp_recv net_udp_recv; + +static void net_recv_packet (void *arg, size_t payload_size, void *payload) { + struct echo_buf *echo_buf = arg; + //printf("Echo server: Received payload: '%.*s'\n", payload_size, &echo_buf->payload); + + // Send reply + ump_net_udp_send( + &echo_buf->call, + net_udp_recv.src_ip, + PORT_ECHO, net_udp_recv.src_port, + payload_size, &echo_buf->payload, + net_reply_callback, echo_buf + ); +} + +static void handle_udp_recv (void *arg, struct ump_net_ev_udp_recv *udp_recv, size_t payload_size) { + if (payload_size > BUF_SIZE) { + printf("Echo server: ERROR: Packet too big\n"); + } else { + struct echo_buf *echo_buf = simpleslab_alloc(&slabs); + if (echo_buf == NULL) { + printf("Echo server: WARN: All buffers full, dropping packet\n"); + } else { + net_udp_recv = *udp_recv; + ump_net_recv_payload(&echo_buf->payload, net_recv_packet, echo_buf); + return; + } + } + ump_net_recv_payload(NULL, NULL, NULL); +} + +static void send_again (void *arg) { + static struct ump_net_call call; + ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 12, "hello again\n", net_send_callback, NULL); +} + int main (int argc, char *argv[]) { errval_t err; - struct ump_binding_server server; + struct waitset *default_ws = get_default_waitset(); printf("Echo server: registering\n"); - + struct ump_binding_server server; err = ump_binding_register(&server, UMP_SERVER_ECHO, connect_server, NULL); if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server"); - printf("Echo server: listening\n"); - struct waitset *default_ws = get_default_waitset(); + + simpleslab_init(&slabs, sizeof(struct echo_buf), slab_buf, sizeof(slab_buf)); + + err = ump_net_init(default_ws); + if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to connect to net server"); + + struct ump_net_call call; + ump_net_udp_listen(&call, PORT_ECHO, handle_udp_recv, NULL, net_listen_callback, NULL); + + struct ump_net_call call2; + ump_net_udp_send(&call2, 0xc0a80204, PORT_ECHO, 7000, 6, "hello\n", net_send_callback, NULL); + + struct deferred_event dev; + deferred_event_init(&dev); + err = deferred_event_register(&dev, default_ws, 1000000, + MKCLOSURE(send_again, NULL)); + if (err_is_fail(err)) return err; + + while (true) { err = event_dispatch(default_ws); if (err_is_fail(err)) { diff --git a/usr/init/main.c b/usr/init/main.c index ae9dd09..f467d65 100644 --- a/usr/init/main.c +++ b/usr/init/main.c @@ -148,6 +148,14 @@ bsp_main(int argc, char *argv[]) { thread_create(urpc_client_loop, &urpc_to_app_ws); + // Spawn enet driver + struct spawninfo enet_si; + domainid_t enet_pid; + err = spawn_load_by_name("enet", &enet_si, &enet_pid); + if (err_is_fail(err)) { + DEBUG_ERR(err, "when spawning enet"); + } + // Grading grading_test_late(); From 50cb528a718b9926b2b910c569759d74fac52c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Sat, 28 May 2022 14:09:17 +0200 Subject: [PATCH 3/6] Add ping source Modified only to make it compile without other files --- performance/net/udpping/.gitignore | 1 + performance/net/udpping/Makefile | 2 + performance/net/udpping/iputils_common.c | 140 ++ performance/net/udpping/iputils_common.h | 73 + performance/net/udpping/ping.c | 1662 ++++++++++++++++++++++ performance/net/udpping/ping.h | 431 ++++++ performance/net/udpping/ping_common.c | 955 +++++++++++++ 7 files changed, 3264 insertions(+) create mode 100644 performance/net/udpping/.gitignore create mode 100644 performance/net/udpping/Makefile create mode 100644 performance/net/udpping/iputils_common.c create mode 100644 performance/net/udpping/iputils_common.h create mode 100644 performance/net/udpping/ping.c create mode 100644 performance/net/udpping/ping.h create mode 100644 performance/net/udpping/ping_common.c diff --git a/performance/net/udpping/.gitignore b/performance/net/udpping/.gitignore new file mode 100644 index 0000000..6b9993f --- /dev/null +++ b/performance/net/udpping/.gitignore @@ -0,0 +1 @@ +udpping diff --git a/performance/net/udpping/Makefile b/performance/net/udpping/Makefile new file mode 100644 index 0000000..578ec67 --- /dev/null +++ b/performance/net/udpping/Makefile @@ -0,0 +1,2 @@ +udpping: *.c *.h + gcc -D_GNU_SOURCE ping.c ping_common.c iputils_common.c -o udpping diff --git a/performance/net/udpping/iputils_common.c b/performance/net/udpping/iputils_common.c new file mode 100644 index 0000000..c41f201 --- /dev/null +++ b/performance/net/udpping/iputils_common.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_GETRANDOM +# include +#endif + +#ifdef HAVE_ERROR_H +# include +#else +void error(int status, int errnum, const char *format, ...) +{ + va_list ap; + + fprintf(stderr, "%s: ", program_invocation_short_name); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + if (errnum) + fprintf(stderr, ": %s\n", strerror(errnum)); + else + fprintf(stderr, "\n"); + if (status) + exit(status); +} +#endif + +int close_stream(FILE *stream) +{ + const int flush_status = fflush(stream); +#ifdef HAVE___FPENDING + const int some_pending = (__fpending(stream) != 0); +#endif + const int prev_fail = (ferror(stream) != 0); + const int fclose_fail = (fclose(stream) != 0); + + if (flush_status || + prev_fail || (fclose_fail && ( +#ifdef HAVE___FPENDING + some_pending || +#endif + errno != EBADF))) { + if (!fclose_fail && !(errno == EPIPE)) + errno = 0; + return EOF; + } + return 0; +} + +void close_stdout(void) +{ + if (close_stream(stdout) != 0 && !(errno == EPIPE)) { + if (errno) + error(0, errno, "write error"); + else + error(0, 0, "write error"); + _exit(EXIT_FAILURE); + } + if (close_stream(stderr) != 0) + _exit(EXIT_FAILURE); +} + +long strtol_or_err(char const *const str, char const *const errmesg, + const long min, const long max) +{ + long num; + char *end = NULL; + + errno = 0; + if (str == NULL || *str == '\0') + goto err; + num = strtol(str, &end, 10); + if (errno || str == end || (end && *end)) + goto err; + if (num < min || max < num) + error(EXIT_FAILURE, 0, "%s: '%s': out of range: %lu <= value <= %lu", + errmesg, str, min, max); + return num; + err: + error(EXIT_FAILURE, errno, "%s: '%s'", errmesg, str); + abort(); +} + +static unsigned int iputil_srand_fallback(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_REALTIME, &ts); + return ((getpid() << 16) ^ getuid() ^ ts.tv_sec ^ ts.tv_nsec); +} + +void iputils_srand(void) +{ + unsigned int i; + +#if HAVE_GETRANDOM + ssize_t ret; + + do { + errno = 0; + ret = getrandom(&i, sizeof(i), GRND_NONBLOCK); + switch (errno) { + case 0: + break; + case EINTR: + continue; + default: + i = iputil_srand_fallback(); + goto done; + } + } while (ret != sizeof(i)); + done: +#else + i = iputil_srand_fallback(); +#endif + srand(i); + /* Consume up to 31 random numbers */ + i = rand() & 0x1F; + while (0 < i) { + rand(); + i--; + } +} + +void timespecsub(struct timespec *a, struct timespec *b, struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += 1000000000L; + } +} diff --git a/performance/net/udpping/iputils_common.h b/performance/net/udpping/iputils_common.h new file mode 100644 index 0000000..26e8f7c --- /dev/null +++ b/performance/net/udpping/iputils_common.h @@ -0,0 +1,73 @@ +#ifndef IPUTILS_COMMON_H +#define IPUTILS_COMMON_H + +#include +#include + +#define ARRAY_SIZE(arr) \ + (sizeof(arr) / sizeof((arr)[0]) + \ + sizeof(__typeof__(int[1 - 2 * \ + !!__builtin_types_compatible_p(__typeof__(arr), \ + __typeof__(&arr[0]))])) * 0) + +#ifdef __GNUC__ +# define iputils_attribute_format(t, n, m) __attribute__((__format__ (t, n, m))) +#else +# define iputils_attribute_format(t, n, m) +#endif + +#if defined(USE_IDN) || defined(ENABLE_NLS) +# include +#endif + +#ifdef ENABLE_NLS +# include +# define _(Text) gettext (Text) +#else +# undef bindtextdomain +# define bindtextdomain(Domain, Directory) /* empty */ +# undef textdomain +# define textdomain(Domain) /* empty */ +# define _(Text) Text +#endif + +#ifdef USE_IDN +# include + +# include +# ifndef AI_IDN +# define AI_IDN 0x0040 +# endif +# ifndef AI_CANONIDN +# define AI_CANONIDN 0x0080 +# endif +# ifndef NI_IDN +# define NI_IDN 32 +# endif +#endif /* #ifdef USE_IDN */ + +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif +#ifndef IP_PMTUDISC_DO +# define IP_PMTUDISC_DO 2 +#endif +#ifndef IPV6_PMTUDISC_DO +# define IPV6_PMTUDISC_DO 2 +#endif + +#ifdef HAVE_ERROR_H +# include +#else +extern void error(int status, int errnum, const char *format, ...); +#endif + +extern int close_stream(FILE *stream); +extern void close_stdout(void); +extern long strtol_or_err(char const *const str, char const *const errmesg, + const long min, const long max); +extern void iputils_srand(void); +extern void timespecsub(struct timespec *a, struct timespec *b, + struct timespec *res); + +#endif /* IPUTILS_COMMON_H */ diff --git a/performance/net/udpping/ping.c b/performance/net/udpping/ping.c new file mode 100644 index 0000000..b5a7616 --- /dev/null +++ b/performance/net/udpping/ping.c @@ -0,0 +1,1662 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * If kernel does not support non-raw ICMP sockets, + * this program has to run SUID to ROOT or with + * net_cap_raw enabled. + */ + +#include "ping.h" + +#include +#include +#include +#include +#include +#include + +/* FIXME: global_rts will be removed in future */ +struct ping_rts *global_rts; + +#ifndef ICMP_FILTER +#define ICMP_FILTER 1 +struct icmp_filter { + uint32_t data; +}; +#endif + +ping_func_set_st ping4_func_set = { + .send_probe = ping4_send_probe, + .receive_error_msg = ping4_receive_error_msg, + .parse_reply = ping4_parse_reply, + .install_filter = ping4_install_filter +}; + +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define NROUTES 9 /* number of record route slots */ +#define TOS_MAX 255 /* 8-bit TOS field */ + +static void create_socket(struct ping_rts *rts, socket_st *sock, int family, + int socktype, int protocol, int requisite) +{ + int do_fallback = 0; + + errno = 0; + + assert(sock->fd == -1); + assert(socktype == SOCK_DGRAM || socktype == SOCK_RAW); + + /* Attempt to create a ping socket if requested. Attempt to create a raw + * socket otherwise or as a fallback. Well known errno values follow. + * + * 1) EACCES + * + * Kernel returns EACCES for all ping socket creation attempts when the + * user isn't allowed to use ping socket. A range of group ids is + * configured using the `net.ipv4.ping_group_range` sysctl. Fallback + * to raw socket is necessary. + * + * Kernel returns EACCES for all raw socket creation attempts when the + * process doesn't have the `CAP_NET_RAW` capability. + * + * 2) EAFNOSUPPORT + * + * Kernel returns EAFNOSUPPORT for IPv6 ping or raw socket creation + * attempts when run with IPv6 support disabled (e.g. via `ipv6.disable=1` + * kernel command-line option. + * + * https://github.com/iputils/iputils/issues/32 + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EAFNOSUPPORT for all IPv4 ping socket creation attempts due to lack + * of support in the kernel. Fallback to raw socket is necessary. + * + * https://github.com/iputils/iputils/issues/54 + * + * 3) EPROTONOSUPPORT + * + * OpenVZ 2.6.32-042stab113.11 and possibly other older kernels return + * EPROTONOSUPPORT for all IPv6 ping socket creation attempts due to lack + * of support in the kernel [1]. Debian 9.5 based container with kernel 4.10 + * returns EPROTONOSUPPORT also for IPv4 [2]. Fallback to raw socket is + * necessary. + * + * [1] https://github.com/iputils/iputils/issues/54 + * [2] https://github.com/iputils/iputils/issues/129 + */ + if (socktype == SOCK_DGRAM) + sock->fd = socket(family, socktype, protocol); + + /* Kernel doesn't support ping sockets. */ + if (sock->fd == -1 && errno == EAFNOSUPPORT && family == AF_INET) + do_fallback = 1; + if (sock->fd == -1 && errno == EPROTONOSUPPORT) + do_fallback = 1; + + /* User is not allowed to use ping sockets. */ + if (sock->fd == -1 && errno == EACCES) + do_fallback = 1; + + if (socktype == SOCK_RAW || do_fallback) { + socktype = SOCK_RAW; + sock->fd = socket(family, SOCK_RAW, protocol); + } + + if (sock->fd == -1) { + /* Report error related to disabled IPv6 only when IPv6 also failed or in + * verbose mode. Report other errors always. + */ + if ((errno == EAFNOSUPPORT && family == AF_INET6 && requisite) || + rts->opt_verbose) + error(0, errno, "socket"); + if (requisite) + exit(2); + } else + sock->socktype = socktype; +} + +static void set_socket_option(socket_st *sock, int level, int optname, + const void *optval, socklen_t olen) +{ + if (sock->fd == -1) + return; + + if (setsockopt(sock->fd, level, optname, optval, olen) == -1) + error(2, errno, "setsockopt"); +} + +/* Much like strtod(3), but will fails if str is not valid number. */ +static double ping_strtod(const char *str, const char *err_msg) +{ + double num; + char *end = NULL; + + if (str == NULL || *str == '\0') + goto err; + errno = 0; + + /* + * Here we always want to use locale regardless USE_IDN or ENABLE_NLS, + * because it handles decimal point of -i/-W input options. + */ + setlocale(LC_ALL, "C"); + num = strtod(str, &end); + setlocale(LC_ALL, ""); + + if (errno || str == end || (end && *end)) { + error(0, 0, _("option argument contains garbage: %s"), end); + error(0, 0, _("this will become fatal error in the future")); + } + switch (fpclassify(num)) { + case FP_NORMAL: + case FP_ZERO: + break; + default: + errno = ERANGE; + goto err; + } + return num; + err: + error(2, errno, "%s: %s", err_msg, str); + abort(); /* cannot be reached, above error() will exit */ + return 0.0; +} + +static int parseflow(char *str) +{ + const char *cp; + unsigned long val; + char *ep; + + /* handle both hex and decimal values */ + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + val = (int)strtoul(cp, &ep, 16); + } else + val = (int)strtoul(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if (*ep != '\0') + error(2, 0, _("bad value for flowinfo: %s"), str); + + if (val & ~IPV6_FLOWINFO_FLOWLABEL) + error(2, 0, _("flow value is greater than 20 bits: %s"), str); + return (val); +} + +/* Set Type of Service (TOS) and other Quality of Service relating bits */ +static int parsetos(char *str) +{ + const char *cp; + int tos; + char *ep; + + /* handle both hex and decimal values */ + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { + cp = str + 2; + tos = (int)strtol(cp, &ep, 16); + } else + tos = (int)strtol(str, &ep, 10); + + /* doesn't look like decimal or hex, eh? */ + if (*ep != '\0') + error(2, 0, _("bad TOS value: %s"), str); + + if (tos > TOS_MAX) + error(2, 0, _("the decimal value of TOS bits must be in range 0-255: %d"), tos); + return (tos); +} + +int +main(int argc, char **argv) +{ + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_protocol = IPPROTO_UDP, + .ai_socktype = SOCK_DGRAM, + .ai_flags = getaddrinfo_flags + }; + struct addrinfo *result, *ai; + int ret_val; + int ch; + socket_st sock4 = { .fd = -1 }; + socket_st sock6 = { .fd = -1 }; + char *target; + char *outpack_fill = NULL; + struct ping_rts rts = { + .interval = 1000, + .preload = 1, + .lingertime = MAXWAIT * 1000, + .confirm_flag = MSG_CONFIRM, + .tmin = LONG_MAX, + .pipesize = -1, + .datalen = DEFDATALEN, + .screen_width = INT_MAX, +#ifdef HAVE_LIBCAP + .cap_raw = CAP_NET_RAW, + .cap_admin = CAP_NET_ADMIN, +#endif + .pmtudisc = -1, + .source.sin_family = AF_INET, + .source6.sin6_family = AF_INET6, + .ni.query = -1, + .ni.subject_type = -1, + }; + /* FIXME: global_rts will be removed in future */ + global_rts = &rts; + + atexit(close_stdout); + limit_capabilities(&rts); + +#if defined(USE_IDN) || defined(ENABLE_NLS) + setlocale(LC_ALL, ""); +#if defined(USE_IDN) + if (!strcmp(setlocale(LC_ALL, NULL), "C")) + hints.ai_flags &= ~ AI_CANONIDN; +#endif +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE_NAME, LOCALEDIR); + textdomain (PACKAGE_NAME); +#endif +#endif + + /* Support being called using `ping4` or `ping6` symlinks */ + if (argv[0][strlen(argv[0]) - 1] == '4') + hints.ai_family = AF_INET; + else if (argv[0][strlen(argv[0]) - 1] == '6') + hints.ai_family = AF_INET6; + + /* Parse command line options */ + while ((ch = getopt(argc, argv, "h?" "4bRT:" "6F:N:" "aABc:dDfi:I:l:Lm:M:nOp:qQ:rs:S:t:UvVw:W:")) != EOF) { + switch(ch) { + /* IPv4 specific options */ + case '4': + if (hints.ai_family == AF_INET6) + error(2, 0, _("only one -4 or -6 option may be specified")); + hints.ai_family = AF_INET; + break; + case 'b': + rts.broadcast_pings = 1; + break; + case 'R': + if (rts.opt_timestamp) + error(2, 0, _("only one of -T or -R may be used")); + rts.opt_rroute = 1; + break; + case 'T': + if (rts.opt_rroute) + error(2, 0, _("only one of -T or -R may be used")); + rts.opt_timestamp = 1; + if (strcmp(optarg, "tsonly") == 0) + rts.ts_type = IPOPT_TS_TSONLY; + else if (strcmp(optarg, "tsandaddr") == 0) + rts.ts_type = IPOPT_TS_TSANDADDR; + else if (strcmp(optarg, "tsprespec") == 0) + rts.ts_type = IPOPT_TS_PRESPEC; + else + error(2, 0, _("invalid timestamp type: %s"), optarg); + break; + /* Common options */ + case 'a': + rts.opt_audible = 1; + break; + case 'A': + rts.opt_adaptive = 1; + break; + case 'B': + rts.opt_strictsource = 1; + break; + case 'c': + rts.npackets = strtol_or_err(optarg, _("invalid argument"), 1, LONG_MAX); + break; + case 'd': + rts.opt_so_debug = 1; + break; + case 'D': + rts.opt_ptimeofday = 1; + break; + case 'i': + { + double optval; + + optval = ping_strtod(optarg, _("bad timing interval")); + if (isgreater(optval, (double)INT_MAX / 1000)) + error(2, 0, _("bad timing interval: %s"), optarg); + rts.interval = (int)(optval * 1000); + rts.opt_interval = 1; + } + break; + case 'I': + /* IPv6 */ + if (strchr(optarg, ':')) { + char *p, *addr = strdup(optarg); + + if (!addr) + error(2, errno, _("cannot copy: %s"), optarg); + + p = strchr(addr, SCOPE_DELIMITER); + if (p) { + *p = '\0'; + rts.device = optarg + (p - addr) + 1; + } + + if (inet_pton(AF_INET6, addr, (char *)&rts.source6.sin6_addr) <= 0) + error(2, 0, _("invalid source address: %s"), optarg); + + rts.opt_strictsource = 1; + + free(addr); + } else if (inet_pton(AF_INET, optarg, &rts.source.sin_addr) > 0) { + rts.opt_strictsource = 1; + } else { + rts.device = optarg; + } + break; + case 'l': + rts.preload = strtol_or_err(optarg, _("invalid argument"), 1, MAX_DUP_CHK); + if (rts.uid && rts.preload > 3) + error(2, 0, _("cannot set preload to value greater than 3: %d"), rts.preload); + break; + case 'L': + rts.opt_noloop = 1; + break; + case 'm': + rts.mark = strtol_or_err(optarg, _("invalid argument"), 0, UINT_MAX); + rts.opt_mark = 1; + break; + case 'M': + if (strcmp(optarg, "do") == 0) + rts.pmtudisc = IP_PMTUDISC_DO; + else if (strcmp(optarg, "dont") == 0) + rts.pmtudisc = IP_PMTUDISC_DONT; + else if (strcmp(optarg, "want") == 0) + rts.pmtudisc = IP_PMTUDISC_WANT; + else + error(2, 0, _("invalid -M argument: %s"), optarg); + break; + case 'n': + rts.opt_numeric = 1; + break; + case 'O': + rts.opt_outstanding = 1; + break; + case 'f': + rts.opt_flood = 1; + /* avoid `getaddrinfo()` during flood */ + rts.opt_numeric = 1; + setbuf(stdout, (char *)NULL); + break; + case 'p': + rts.opt_pingfilled = 1; + outpack_fill = strdup(optarg); + if (!outpack_fill) + error(2, errno, _("memory allocation failed")); + break; + case 'q': + rts.opt_quiet = 1; + break; + case 'Q': + rts.settos = parsetos(optarg); /* IPv4 */ + rts.tclass = rts.settos; /* IPv6 */ + break; + case 'r': + rts.opt_so_dontroute = 1; + break; + case 's': + rts.datalen = strtol_or_err(optarg, _("invalid argument"), 0, INT_MAX); + break; + case 'S': + rts.sndbuf = strtol_or_err(optarg, _("invalid argument"), 1, INT_MAX); + break; + case 't': + rts.ttl = strtol_or_err(optarg, _("invalid argument"), 0, 255); + rts.opt_ttl = 1; + break; + case 'U': + rts.opt_latency = 1; + break; + case 'v': + rts.opt_verbose = 1; + break; + case 'w': + rts.deadline = strtol_or_err(optarg, _("invalid argument"), 0, INT_MAX); + break; + case 'W': + { + double optval; + + optval = ping_strtod(optarg, _("bad linger time")); + if (isless(optval, 0) || isgreater(optval, (double)INT_MAX / 1000)) + error(2, 0, _("bad linger time: %s"), optarg); + /* lingertime will be converted to usec later */ + rts.lingertime = (int)(optval * 1000); + } + break; + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (!argc) + error(1, EDESTADDRREQ, "usage error"); + + iputils_srand(); + + target = argv[argc - 1]; + + rts.outpack = malloc(rts.datalen + 28); + if (!rts.outpack) + error(2, errno, _("memory allocation failed")); + if (outpack_fill) { + fill(&rts, outpack_fill, rts.outpack, rts.datalen); + free(outpack_fill); + } + + /* Create sockets */ + enable_capability_raw(); + if (hints.ai_family != AF_INET6) + create_socket(&rts, &sock4, AF_INET, hints.ai_socktype, IPPROTO_ICMP, + hints.ai_family == AF_INET); + if (hints.ai_family != AF_INET) { + create_socket(&rts, &sock6, AF_INET6, hints.ai_socktype, IPPROTO_ICMPV6, sock4.fd == -1); + /* This may not be needed if both protocol versions always had the same value, but + * since I don't know that, it's better to be safe than sorry. */ + rts.pmtudisc = rts.pmtudisc == IP_PMTUDISC_DO ? IPV6_PMTUDISC_DO : + rts.pmtudisc == IP_PMTUDISC_DONT ? IPV6_PMTUDISC_DONT : + rts.pmtudisc == IP_PMTUDISC_WANT ? IPV6_PMTUDISC_WANT : rts.pmtudisc; + } + disable_capability_raw(); + + /* Limit address family on single-protocol systems */ + if (hints.ai_family == AF_UNSPEC) { + if (sock4.fd == -1) + hints.ai_family = AF_INET6; + else if (sock6.fd == -1) + hints.ai_family = AF_INET; + } + + /* Set socket options */ + if (rts.settos) + set_socket_option(&sock4, IPPROTO_IP, IP_TOS, &rts.settos, sizeof rts.settos); + if (rts.tclass) + set_socket_option(&sock6, IPPROTO_IPV6, IPV6_TCLASS, &rts.tclass, sizeof rts.tclass); + + /* getaddrinfo fails to indicate a scopeid when not used in dual-stack mode. + * Work around by always using dual-stack name resolution. + * + * https://github.com/iputils/iputils/issues/252 + */ + int target_ai_family = hints.ai_family; + hints.ai_family = AF_UNSPEC; + + ret_val = getaddrinfo(target, NULL, &hints, &result); + if (ret_val) + error(2, 0, "%s: %s", target, gai_strerror(ret_val)); + + for (ai = result; ai; ai = ai->ai_next) { + if (target_ai_family != AF_UNSPEC && + target_ai_family != ai->ai_family) { + if (!ai->ai_next) { + /* An address was found, but not of the family we really want. + * Throw the appropriate gai error. + */ + error(2, 0, "%s: %s", target, gai_strerror(EAI_ADDRFAMILY)); + } + continue; + } + switch (ai->ai_family) { + case AF_INET: + ret_val = ping4_run(&rts, argc, argv, ai, &sock4); + break; + default: + error(2, 0, _("unknown protocol family: %d"), ai->ai_family); + } + + if (ret_val >= 0) + break; + /* ret_val < 0 means to go on to next addrinfo result, there + * better be one. */ + assert(ai->ai_next); + } + + freeaddrinfo(result); + free(rts.outpack); + + return ret_val; +} + +static int iface_name2index(struct ping_rts *rts, int fd) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, rts->device, IFNAMSIZ - 1); + + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) + error(2, 0, _("unknown iface: %s"), rts->device); + + return ifr.ifr_ifindex; +} + +static void bind_to_device(struct ping_rts *rts, int fd, in_addr_t addr) +{ + int rc; + int errno_save; + + enable_capability_raw(); + rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, rts->device, + strlen(rts->device) + 1); + errno_save = errno; + disable_capability_raw(); + + if (rc != -1) + return; + + if (IN_MULTICAST(ntohl(addr))) { + struct ip_mreqn imr; + + memset(&imr, 0, sizeof(imr)); + imr.imr_ifindex = iface_name2index(rts, fd); + + if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) + error(2, errno, "IP_MULTICAST_IF"); + } else { + error(2, errno_save, "SO_BINDTODEVICE %s", rts->device); + } +} + +/* return >= 0: exit with this code, < 0: go on to next addrinfo result */ +int ping4_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, + socket_st *sock) +{ + static const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_protocol = IPPROTO_UDP, + .ai_flags = getaddrinfo_flags + }; + int hold, packlen; + unsigned char *packet; + char *target; + char hnamebuf[NI_MAXHOST]; + unsigned char rspace[3 + 4 * NROUTES + 1]; /* record route space */ + uint32_t *tmp_rspace; + + if (argc > 1) { + if (rts->opt_rroute) + usage(); + else if (rts->opt_timestamp) { + if (rts->ts_type != IPOPT_TS_PRESPEC) + usage(); + if (argc > 5) + usage(); + } else { + if (argc > 10) + usage(); + rts->opt_sourceroute = 1; + } + } + while (argc > 0) { + target = *argv; + + memset((char *)&rts->whereto, 0, sizeof(rts->whereto)); + rts->whereto.sin_family = AF_INET; + if (inet_aton(target, &rts->whereto.sin_addr) == 1) { + rts->hostname = target; + if (argc == 1) + rts->opt_numeric = 1; + } else { + struct addrinfo *result = ai; + int ret_val; + + if (argc > 1) { + ret_val = getaddrinfo(target, NULL, &hints, &result); + if (ret_val) + error(2, 0, "%s: %s", target, gai_strerror(ret_val)); + } + + memcpy(&rts->whereto, result->ai_addr, sizeof rts->whereto); + memset(hnamebuf, 0, sizeof hnamebuf); + if (result->ai_canonname) + strncpy(hnamebuf, result->ai_canonname, sizeof hnamebuf - 1); + rts->hostname = hnamebuf; + + if (argc > 1) + freeaddrinfo(result); + } + if (argc > 1) + rts->route[rts->nroute++] = rts->whereto.sin_addr.s_addr; + argc--; + argv++; + } + + if (rts->source.sin_addr.s_addr == 0) { + socklen_t alen; + struct sockaddr_in dst = rts->whereto; + int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (probe_fd < 0) + error(2, errno, "socket"); + + if (rts->device) { + bind_to_device(rts, probe_fd, dst.sin_addr.s_addr); + bind_to_device(rts, sock->fd, dst.sin_addr.s_addr); + } + + if (rts->settos && + setsockopt(probe_fd, IPPROTO_IP, IP_TOS, (char *)&rts->settos, sizeof(int)) < 0) + error(0, errno, _("warning: QOS sockopts")); + + if (rts->opt_mark) + sock_setmark(rts->mark, probe_fd); + + dst.sin_port = htons(1025); + if (rts->nroute) + dst.sin_addr.s_addr = rts->route[0]; + if (connect(probe_fd, (struct sockaddr *)&dst, sizeof(dst)) == -1) { + if (errno == EACCES) { + if (rts->broadcast_pings == 0) + error(2, 0, + _("Do you want to ping broadcast? Then -b. If not, check your local firewall rules")); + fprintf(stderr, _("WARNING: pinging broadcast address\n")); + if (setsockopt(probe_fd, SOL_SOCKET, SO_BROADCAST, + &rts->broadcast_pings, sizeof(rts->broadcast_pings)) < 0) + error(2, errno, _("cannot set broadcasting")); + if (connect(probe_fd, (struct sockaddr *)&dst, sizeof(dst)) == -1) + error(2, errno, "connect"); + } else if ((errno == EHOSTUNREACH || errno == ENETUNREACH) && ai->ai_next) { + close(probe_fd); + return -1; + } else + error(2, errno, "connect"); + } + alen = sizeof(rts->source); + if (getsockname(probe_fd, (struct sockaddr *)&rts->source, &alen) == -1) + error(2, errno, "getsockname"); + rts->source.sin_port = 0; + + if (rts->device) { + struct ifaddrs *ifa0, *ifa; + int ret; + + ret = getifaddrs(&ifa0); + if (ret) + error(2, errno, _("gatifaddrs failed")); + for (ifa = ifa0; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_name || !ifa->ifa_addr || + ifa->ifa_addr->sa_family != AF_INET) + continue; + if (!strcmp(ifa->ifa_name, rts->device) && + !memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + &rts->source.sin_addr, sizeof(rts->source.sin_addr))) + break; + } + if (ifa && !memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + &dst.sin_addr, sizeof(rts->source.sin_addr))) { + enable_capability_raw(); + setsockopt(sock->fd, SOL_SOCKET, SO_BINDTODEVICE, "", 0); + disable_capability_raw(); + } + freeifaddrs(ifa0); + if (!ifa) + error(0, 0, _("Warning: source address might be selected on device other than: %s"), rts->device); + } + close(probe_fd); + + } else if (rts->device) { + bind_to_device(rts, sock->fd, rts->whereto.sin_addr.s_addr); + } + + if (rts->whereto.sin_addr.s_addr == 0) + rts->whereto.sin_addr.s_addr = rts->source.sin_addr.s_addr; + + if (rts->broadcast_pings || IN_MULTICAST(ntohl(rts->whereto.sin_addr.s_addr))) { + rts->multicast = 1; + if (rts->uid) { + if (rts->interval < 1000) + error(2, 0, _("broadcast ping with too short interval: %d"), rts->interval); + if (rts->pmtudisc >= 0 && rts->pmtudisc != IP_PMTUDISC_DO) + error(2, 0, _("broadcast ping does not fragment")); + } + if (rts->pmtudisc < 0) + rts->pmtudisc = IP_PMTUDISC_DO; + } + + if (rts->pmtudisc >= 0) { + if (setsockopt(sock->fd, SOL_IP, IP_MTU_DISCOVER, &rts->pmtudisc, sizeof rts->pmtudisc) == -1) + error(2, errno, "IP_MTU_DISCOVER"); + } + + if (rts->opt_strictsource && + bind(sock->fd, (struct sockaddr *)&rts->source, sizeof rts->source) == -1) + error(2, errno, "bind"); + + if (sock->socktype == SOCK_RAW) { + struct icmp_filter filt; + filt.data = ~((1 << ICMP_SOURCE_QUENCH) | + (1 << ICMP_DEST_UNREACH) | + (1 << ICMP_TIME_EXCEEDED) | + (1 << ICMP_PARAMETERPROB) | + (1 << ICMP_REDIRECT) | + (1 << ICMP_ECHOREPLY)); + if (setsockopt(sock->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof filt) == -1) + error(0, errno, _("WARNING: setsockopt(ICMP_FILTER)")); + } + + hold = 1; + if (setsockopt(sock->fd, SOL_IP, IP_RECVERR, &hold, sizeof hold)) + error(0, 0, _("WARNING: your kernel is veeery old. No problems.")); + + if (sock->socktype == SOCK_DGRAM) { + if (setsockopt(sock->fd, SOL_IP, IP_RECVTTL, &hold, sizeof hold)) + error(0, errno, _("WARNING: setsockopt(IP_RECVTTL)")); + if (setsockopt(sock->fd, SOL_IP, IP_RETOPTS, &hold, sizeof hold)) + error(0, errno, _("WARNING: setsockopt(IP_RETOPTS)")); + } + + /* record route option */ + if (rts->opt_rroute) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOP; + rspace[1 + IPOPT_OPTVAL] = IPOPT_RR; + rspace[1 + IPOPT_OLEN] = sizeof(rspace) - 1; + rspace[1 + IPOPT_OFFSET] = IPOPT_MINOFF; + rts->optlen = 40; + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, sizeof rspace) < 0) + error(2, errno, "record route"); + } + if (rts->opt_timestamp) { + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_TIMESTAMP; + rspace[1] = (rts->ts_type == IPOPT_TS_TSONLY ? 40 : 36); + rspace[2] = 5; + rspace[3] = rts->ts_type; + if (rts->ts_type == IPOPT_TS_PRESPEC) { + int i; + rspace[1] = 4 + rts->nroute * 8; + for (i = 0; i < rts->nroute; i++) { + tmp_rspace = (uint32_t *)&rspace[4 + i * 8]; + *tmp_rspace = rts->route[i]; + } + } + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) { + rspace[3] = 2; + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, rspace[1]) < 0) + error(2, errno, "ts option"); + } + rts->optlen = 40; + } + if (rts->opt_sourceroute) { + int i; + memset(rspace, 0, sizeof(rspace)); + rspace[0] = IPOPT_NOOP; + rspace[1 + IPOPT_OPTVAL] = rts->opt_so_dontroute ? IPOPT_SSRR : IPOPT_LSRR; + rspace[1 + IPOPT_OLEN] = 3 + rts->nroute * 4; + rspace[1 + IPOPT_OFFSET] = IPOPT_MINOFF; + for (i = 0; i < rts->nroute; i++) { + tmp_rspace = (uint32_t *)&rspace[4 + i * 4]; + *tmp_rspace = rts->route[i]; + } + + if (setsockopt(sock->fd, IPPROTO_IP, IP_OPTIONS, rspace, 4 + rts->nroute * 4) < 0) + error(2, errno, "record route"); + rts->optlen = 40; + } + + /* Estimate memory eaten by single packet. It is rough estimate. + * Actually, for small datalen's it depends on kernel side a lot. */ + hold = rts->datalen + 8; + hold += ((hold + 511) / 512) * (rts->optlen + 20 + 16 + 64 + 160); + sock_setbufs(rts, sock, hold); + + if (rts->broadcast_pings) { + if (setsockopt(sock->fd, SOL_SOCKET, SO_BROADCAST, &rts->broadcast_pings, + sizeof rts->broadcast_pings) < 0) + error(2, errno, _("cannot set broadcasting")); + } + + if (rts->opt_noloop) { + int loop = 0; + if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof loop) == -1) + error(2, errno, _("cannot disable multicast loopback")); + } + if (rts->opt_ttl) { + int ittl = rts->ttl; + if (setsockopt(sock->fd, IPPROTO_IP, IP_MULTICAST_TTL, &rts->ttl, sizeof rts->ttl) == -1) + error(2, errno, _("cannot set multicast time-to-live")); + if (setsockopt(sock->fd, IPPROTO_IP, IP_TTL, &ittl, sizeof ittl) == -1) + error(2, errno, _("cannot set unicast time-to-live")); + } + + if (rts->datalen >= (int)sizeof(struct timeval)) /* can we time transfer */ + rts->timing = 1; + packlen = rts->datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (unsigned char *)malloc((unsigned int)packlen))) + error(2, errno, _("memory allocation failed")); + + printf(_("PING %s (%s) "), rts->hostname, inet_ntoa(rts->whereto.sin_addr)); + if (rts->device || rts->opt_strictsource) + printf(_("from %s %s: "), inet_ntoa(rts->source.sin_addr), rts->device ? rts->device : ""); + printf(_("%zu(%zu) bytes of data.\n"), rts->datalen, rts->datalen + 8 + rts->optlen + 20); + + setup(rts, sock); + + hold = main_loop(rts, &ping4_func_set, sock, packet, packlen); + free(packet); + return hold; +} + +static void pr_options(struct ping_rts *rts, unsigned char *cp, int hlen) +{ + int i, j; + int olen, totlen; + unsigned char *optptr; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + + totlen = hlen - sizeof(struct iphdr); + optptr = cp; + + while (totlen > 0) { + if (*optptr == IPOPT_EOL) + break; + if (*optptr == IPOPT_NOP) { + totlen--; + optptr++; + printf(_("\nNOP")); + continue; + } + cp = optptr; + olen = optptr[1]; + if (olen < 2 || olen > totlen) + break; + + switch (*cp) { + case IPOPT_SSRR: + case IPOPT_LSRR: + printf(_("\n%cSRR: "), *cp == IPOPT_SSRR ? 'S' : 'L'); + j = *++cp; + cp++; + if (j > IPOPT_MINOFF) { + for (;;) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr = { + address + } + }; + + printf("\t%s", pr_addr(rts, &sin, sizeof sin)); + } + j -= 4; + putchar('\n'); + if (j <= IPOPT_MINOFF) + break; + } + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + break; + if (i == old_rrlen + && !memcmp(cp, old_rr, i) + && !rts->opt_flood) { + printf(_("\t(same route)")); + break; + } + old_rrlen = i; + memcpy(old_rr, (char *)cp, i); + printf(_("\nRR: ")); + cp++; + for (;;) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr = { + address + } + }; + + printf("\t%s", pr_addr(rts, &sin, sizeof sin)); + } + i -= 4; + putchar('\n'); + if (i <= 0) + break; + } + break; + case IPOPT_TS: + { + int stdtime = 0, nonstdtime = 0; + uint8_t flags; + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + if (i > j) + i = j; + i -= 5; + if (i <= 0) + break; + flags = *++cp; + printf(_("\nTS: ")); + cp++; + for (;;) { + long l; + + if ((flags & 0xF) != IPOPT_TS_TSONLY) { + uint32_t address; + memcpy(&address, cp, 4); + cp += 4; + if (address == 0) + printf("\t0.0.0.0"); + else { + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr = { + address + } + }; + + printf("\t%s", pr_addr(rts, &sin, sizeof sin)); + } + i -= 4; + if (i <= 0) + break; + } + l = *cp++; + l = (l << 8) + *cp++; + l = (l << 8) + *cp++; + l = (l << 8) + *cp++; + + if (l & 0x80000000) { + if (nonstdtime == 0) + printf(_("\t%ld absolute not-standard"), l & 0x7fffffff); + else + printf(_("\t%ld not-standard"), (l & 0x7fffffff) - nonstdtime); + nonstdtime = l & 0x7fffffff; + } else { + if (stdtime == 0) + printf(_("\t%ld absolute"), l); + else + printf("\t%ld", l - stdtime); + stdtime = l; + } + i -= 4; + putchar('\n'); + if (i <= 0) + break; + } + if (flags >> 4) + printf(_("Unrecorded hops: %d\n"), flags >> 4); + break; + } + default: + printf(_("\nunknown option %x"), *cp); + break; + } + totlen -= olen; + optptr += olen; + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +static void pr_iph(struct ping_rts *rts, struct iphdr *ip) +{ + int hlen; + unsigned char *cp; + + hlen = ip->ihl << 2; + cp = (unsigned char *)ip + 20; /* point to options */ + + printf(_("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n")); + printf(_(" %1x %1x %02x %04x %04x"), + ip->version, ip->ihl, ip->tos, ip->tot_len, ip->id); + printf(_(" %1x %04x"), ((ip->frag_off) & 0xe000) >> 13, + (ip->frag_off) & 0x1fff); + printf(_(" %02x %02x %04x"), ip->ttl, ip->protocol, ip->check); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->saddr)); + printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->daddr)); + printf("\n"); + pr_options(rts, cp, hlen); +} + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +static void pr_icmph(struct ping_rts *rts, uint8_t type, uint8_t code, + uint32_t info, struct icmphdr *icp) +{ + switch (type) { + case ICMP_ECHOREPLY: + printf(_("Echo Reply\n")); + /* XXX ID + Seq + Data */ + break; + case ICMP_DEST_UNREACH: + switch (code) { + case ICMP_NET_UNREACH: + printf(_("Destination Net Unreachable\n")); + break; + case ICMP_HOST_UNREACH: + printf(_("Destination Host Unreachable\n")); + break; + case ICMP_PROT_UNREACH: + printf(_("Destination Protocol Unreachable\n")); + break; + case ICMP_PORT_UNREACH: + printf(_("Destination Port Unreachable\n")); + break; + case ICMP_FRAG_NEEDED: + printf(_("Frag needed and DF set (mtu = %u)\n"), info); + break; + case ICMP_SR_FAILED: + printf(_("Source Route Failed\n")); + break; + case ICMP_NET_UNKNOWN: + printf(_("Destination Net Unknown\n")); + break; + case ICMP_HOST_UNKNOWN: + printf(_("Destination Host Unknown\n")); + break; + case ICMP_HOST_ISOLATED: + printf(_("Source Host Isolated\n")); + break; + case ICMP_NET_ANO: + printf(_("Destination Net Prohibited\n")); + break; + case ICMP_HOST_ANO: + printf(_("Destination Host Prohibited\n")); + break; + case ICMP_NET_UNR_TOS: + printf(_("Destination Net Unreachable for Type of Service\n")); + break; + case ICMP_HOST_UNR_TOS: + printf(_("Destination Host Unreachable for Type of Service\n")); + break; + case ICMP_PKT_FILTERED: + printf(_("Packet filtered\n")); + break; + case ICMP_PREC_VIOLATION: + printf(_("Precedence Violation\n")); + break; + case ICMP_PREC_CUTOFF: + printf(_("Precedence Cutoff\n")); + break; + default: + printf(_("Dest Unreachable, Bad Code: %d\n"), code); + break; + } + if (icp && rts->opt_verbose) + pr_iph(rts, (struct iphdr *)(icp + 1)); + break; + case ICMP_SOURCE_QUENCH: + printf(_("Source Quench\n")); + if (icp && rts->opt_verbose) + pr_iph(rts, (struct iphdr *)(icp + 1)); + break; + case ICMP_REDIRECT: + switch (code) { + case ICMP_REDIR_NET: + printf(_("Redirect Network")); + break; + case ICMP_REDIR_HOST: + printf(_("Redirect Host")); + break; + case ICMP_REDIR_NETTOS: + printf(_("Redirect Type of Service and Network")); + break; + case ICMP_REDIR_HOSTTOS: + printf(_("Redirect Type of Service and Host")); + break; + default: + printf(_("Redirect, Bad Code: %d"), code); + break; + } + { + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr = { + icp ? icp->un.gateway : htonl(info) + } + }; + + printf(_("(New nexthop: %s)\n"), pr_addr(rts, &sin, sizeof sin)); + } + if (icp && rts->opt_verbose) + pr_iph(rts, (struct iphdr *)(icp + 1)); + break; + case ICMP_ECHO: + printf(_("Echo Request\n")); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIME_EXCEEDED: + switch(code) { + case ICMP_EXC_TTL: + printf(_("Time to live exceeded\n")); + break; + case ICMP_EXC_FRAGTIME: + printf(_("Frag reassembly time exceeded\n")); + break; + default: + printf(_("Time exceeded, Bad Code: %d\n"), code); + break; + } + if (icp && rts->opt_verbose) + pr_iph(rts, (struct iphdr *)(icp + 1)); + break; + case ICMP_PARAMETERPROB: + printf(_("Parameter problem: pointer = %u\n"), + icp ? (ntohl(icp->un.gateway) >> 24) : info); + if (icp && rts->opt_verbose) + pr_iph(rts, (struct iphdr *)(icp + 1)); + break; + case ICMP_TIMESTAMP: + printf(_("Timestamp\n")); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TIMESTAMPREPLY: + printf(_("Timestamp Reply\n")); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_INFO_REQUEST: + printf(_("Information Request\n")); + /* XXX ID + Seq */ + break; + case ICMP_INFO_REPLY: + printf(_("Information Reply\n")); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + printf(_("Address Mask Request\n")); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + printf(_("Address Mask Reply\n")); + break; +#endif + default: + printf(_("Bad ICMP type: %d\n"), type); + } +} + +int ping4_receive_error_msg(struct ping_rts *rts, socket_st *sock) +{ + ssize_t res; + char cbuf[512]; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsgh; + struct sock_extended_err *e; + struct icmphdr icmph; + struct sockaddr_in target; + int net_errors = 0; + int local_errors = 0; + int saved_errno = errno; + + iov.iov_base = &icmph; + iov.iov_len = sizeof(icmph); + msg.msg_name = (void *)⌖ + msg.msg_namelen = sizeof(target); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + res = recvmsg(sock->fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT); + if (res < 0) { + if (errno == EAGAIN || errno == EINTR) + local_errors++; + goto out; + } + + e = NULL; + for (cmsgh = CMSG_FIRSTHDR(&msg); cmsgh; cmsgh = CMSG_NXTHDR(&msg, cmsgh)) { + if (cmsgh->cmsg_level == SOL_IP) { + if (cmsgh->cmsg_type == IP_RECVERR) + e = (struct sock_extended_err *)CMSG_DATA(cmsgh); + } + } + if (e == NULL) + abort(); + + if (e->ee_origin == SO_EE_ORIGIN_LOCAL) { + local_errors++; + if (rts->opt_quiet) + goto out; + if (rts->opt_flood) + write_stdout("E", 1); + else if (e->ee_errno != EMSGSIZE) + error(0, 0, _("local error: %s"), strerror(e->ee_errno)); + else + error(0, 0, _("local error: message too long, mtu=%u"), e->ee_info); + rts->nerrors++; + } else if (e->ee_origin == SO_EE_ORIGIN_ICMP) { + struct sockaddr_in *sin = (struct sockaddr_in *)(e + 1); + + if (res < (ssize_t) sizeof(icmph) || + target.sin_addr.s_addr != rts->whereto.sin_addr.s_addr || + icmph.type != ICMP_ECHO || + !is_ours(rts, sock, icmph.un.echo.id)) { + /* Not our error, not an error at all. Clear. */ + saved_errno = 0; + goto out; + } + + acknowledge(rts, ntohs(icmph.un.echo.sequence)); + + if (sock->socktype == SOCK_RAW) { + struct icmp_filter filt; + + filt.data = ~((1 << ICMP_SOURCE_QUENCH) | + (1 << ICMP_REDIRECT) | + (1 << ICMP_ECHOREPLY)); + if (setsockopt(sock->fd, SOL_RAW, ICMP_FILTER, (const void *)&filt, + sizeof(filt)) == -1) + error(2, errno, "setsockopt(ICMP_FILTER)"); + } + net_errors++; + rts->nerrors++; + if (rts->opt_quiet) + goto out; + if (rts->opt_flood) { + write_stdout("\bE", 2); + } else { + print_timestamp(rts); + printf(_("From %s icmp_seq=%u "), pr_addr(rts, sin, sizeof *sin), ntohs(icmph.un.echo.sequence)); + pr_icmph(rts, e->ee_type, e->ee_code, e->ee_info, NULL); + fflush(stdout); + } + } + +out: + errno = saved_errno; + return net_errors ? net_errors : -local_errors; +} + +#if BYTE_ORDER == LITTLE_ENDIAN +# define ODDBYTE(v) (v) +#elif BYTE_ORDER == BIG_ENDIAN +# define ODDBYTE(v) ((unsigned short)(v) << 8) +#else +# define ODDBYTE(v) htons((unsigned short)(v) << 8) +#endif + +static unsigned short +in_cksum(const unsigned short *addr, int len, unsigned short csum) +{ + int nleft = len; + const unsigned short *w = addr; + unsigned short answer; + int sum = csum; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), + * we add sequential 16 bit words to it, and at the end, fold + * back all the carry bits from the top 16 bits into the lower + * 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) + sum += ODDBYTE(*(unsigned char *)w); /* le16toh() may be unavailable on old systems */ + + /* + * add back carry outs from top 16 bits to low 16 bits + */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return (answer); +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is a random number, + * and the sequence number is an ascending integer. The first several bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int ping4_send_probe(struct ping_rts *rts, socket_st *sock, void *packet, + unsigned packet_size __attribute__((__unused__))) +{ + struct icmphdr *icp; + int cc; + int i; + + icp = (struct icmphdr *)packet; + icp->type = ICMP_ECHO; + icp->code = 0; + icp->checksum = 0; + icp->un.echo.sequence = htons(rts->ntransmitted + 1); + icp->un.echo.id = rts->ident; /* ID */ + + rcvd_clear(rts, rts->ntransmitted + 1); + + if (rts->timing) { + if (rts->opt_latency) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp + 1, &tmp_tv, sizeof(tmp_tv)); + } else { + memset(icp + 1, 0, sizeof(struct timeval)); + } + } + + cc = rts->datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->checksum = in_cksum((unsigned short *)icp, cc, 0); + + if (rts->timing && !rts->opt_latency) { + struct timeval tmp_tv; + gettimeofday(&tmp_tv, NULL); + memcpy(icp + 1, &tmp_tv, sizeof(tmp_tv)); + icp->checksum = in_cksum((unsigned short *)&tmp_tv, sizeof(tmp_tv), ~icp->checksum); + } + + i = sendto(sock->fd, icp, cc, 0, (struct sockaddr *)&rts->whereto, sizeof(rts->whereto)); + + return (cc == i ? 0 : i); +} + +/* + * parse_reply -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +static +void pr_echo_reply(uint8_t *_icp, int len __attribute__((__unused__))) +{ + struct icmphdr *icp = (struct icmphdr *)_icp; + + printf(_(" icmp_seq=%u"), ntohs(icp->un.echo.sequence)); +} + +int ping4_parse_reply(struct ping_rts *rts, struct socket_st *sock, + struct msghdr *msg, int cc, void *addr, + struct timeval *tv) +{ + struct sockaddr_in *from = addr; + uint8_t *buf = msg->msg_iov->iov_base; + struct icmphdr *icp; + struct iphdr *ip; + int hlen; + int csfailed; + struct cmsghdr *cmsgh; + int reply_ttl; + uint8_t *opts, *tmp_ttl; + int olen; + int wrong_source = 0; + + /* Check the IP header */ + ip = (struct iphdr *)buf; + if (sock->socktype == SOCK_RAW) { + hlen = ip->ihl * 4; + if (cc < hlen + 8 || ip->ihl < 5) { + if (rts->opt_verbose) + error(0, 0, _("packet too short (%d bytes) from %s"), cc, + pr_addr(rts,from, sizeof *from)); + return 1; + } + reply_ttl = ip->ttl; + opts = buf + sizeof(struct iphdr); + olen = hlen - sizeof(struct iphdr); + } else { + hlen = 0; + reply_ttl = 0; + opts = buf; + olen = 0; + for (cmsgh = CMSG_FIRSTHDR(msg); cmsgh; cmsgh = CMSG_NXTHDR(msg, cmsgh)) { + if (cmsgh->cmsg_level != SOL_IP) + continue; + if (cmsgh->cmsg_type == IP_TTL) { + if (cmsgh->cmsg_len < sizeof(int)) + continue; + tmp_ttl = (uint8_t *)CMSG_DATA(cmsgh); + reply_ttl = (int)*tmp_ttl; + } else if (cmsgh->cmsg_type == IP_RETOPTS) { + opts = (uint8_t *)CMSG_DATA(cmsgh); + olen = cmsgh->cmsg_len; + } + } + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmphdr *)(buf + hlen); + csfailed = in_cksum((unsigned short *)icp, cc, 0); + + if (icp->type == ICMP_ECHOREPLY) { + if (!is_ours(rts, sock, icp->un.echo.id)) + return 1; /* 'Twas not our ECHO */ + + if (!rts->broadcast_pings && !rts->multicast && + from->sin_addr.s_addr != rts->whereto.sin_addr.s_addr) + wrong_source = 1; + if (gather_statistics(rts, (uint8_t *)icp, sizeof(*icp), cc, + ntohs(icp->un.echo.sequence), + reply_ttl, 0, tv, pr_addr(rts, from, sizeof *from), + pr_echo_reply, rts->multicast, wrong_source)) { + fflush(stdout); + return 0; + } + } else { + /* We fall here when a redirect or source quench arrived. */ + + switch (icp->type) { + case ICMP_ECHO: + /* MUST NOT */ + return 1; + case ICMP_SOURCE_QUENCH: + case ICMP_REDIRECT: + case ICMP_DEST_UNREACH: + case ICMP_TIME_EXCEEDED: + case ICMP_PARAMETERPROB: + { + struct iphdr *iph = (struct iphdr *)(&icp[1]); + struct icmphdr *icp1 = (struct icmphdr *) + ((unsigned char *)iph + iph->ihl * 4); + int error_pkt; + if (cc < (int)(8 + sizeof(struct iphdr) + 8) || + cc < 8 + iph->ihl * 4 + 8) + return 1; + if (icp1->type != ICMP_ECHO || + iph->daddr != rts->whereto.sin_addr.s_addr || + !is_ours(rts, sock, icp1->un.echo.id)) + return 1; + error_pkt = (icp->type != ICMP_REDIRECT && + icp->type != ICMP_SOURCE_QUENCH); + if (error_pkt) { + acknowledge(rts, ntohs(icp1->un.echo.sequence)); + return 0; + } + if (rts->opt_quiet || rts->opt_flood) + return 1; + print_timestamp(rts); + printf(_("From %s: icmp_seq=%u "), pr_addr(rts, from, sizeof *from), + ntohs(icp1->un.echo.sequence)); + if (csfailed) + printf(_("(BAD CHECKSUM)")); + pr_icmph(rts, icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 1; + } + default: + /* MUST NOT */ + break; + } + if (rts->opt_flood && !(rts->opt_verbose || rts->opt_quiet)) { + if (!csfailed) + write_stdout("!E", 2); + else + write_stdout("!EC", 3); + return 0; + } + if (!rts->opt_verbose || rts->uid) + return 0; + if (rts->opt_ptimeofday) { + struct timeval recv_time; + gettimeofday(&recv_time, NULL); + printf("%lu.%06lu ", (unsigned long)recv_time.tv_sec, (unsigned long)recv_time.tv_usec); + } + printf(_("From %s: "), pr_addr(rts, from, sizeof *from)); + if (csfailed) { + printf(_("(BAD CHECKSUM)\n")); + return 0; + } + pr_icmph(rts, icp->type, icp->code, ntohl(icp->un.gateway), icp); + return 0; + } + + if (rts->opt_audible) { + putchar('\a'); + if (rts->opt_flood) + fflush(stdout); + } + if (!rts->opt_flood) { + pr_options(rts, opts, olen + sizeof(struct iphdr)); + + putchar('\n'); + fflush(stdout); + } + return 0; +} + +/* + * pr_addr -- + * + * Return an ascii host address optionally with a hostname. + */ +char *pr_addr(struct ping_rts *rts, void *sa, socklen_t salen) +{ + static char buffer[4096] = ""; + static struct sockaddr_storage last_sa; + static socklen_t last_salen = 0; + char name[NI_MAXHOST] = ""; + char address[NI_MAXHOST] = ""; + + memset(&last_sa, 0, sizeof(last_sa)); + if (salen == last_salen && !memcmp(sa, &last_sa, salen)) + return buffer; + + memcpy(&last_sa, sa, (last_salen = salen)); + + rts->in_pr_addr = !setjmp(rts->pr_addr_jmp); + + getnameinfo(sa, salen, address, sizeof address, NULL, 0, getnameinfo_flags | NI_NUMERICHOST); + if (!rts->exiting && !rts->opt_numeric) + getnameinfo(sa, salen, name, sizeof name, NULL, 0, getnameinfo_flags); + + if (*name) + snprintf(buffer, sizeof buffer, "%s (%s)", name, address); + else + snprintf(buffer, sizeof buffer, "%s", address); + + rts->in_pr_addr = 0; + + return (buffer); +} + + +void ping4_install_filter(struct ping_rts *rts, socket_st *sock) +{ + static int once; + static struct sock_filter insns[] = { + BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header due BSD, see ping6. */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA, 0, 1), /* Ours? */ + BPF_STMT(BPF_RET | BPF_K, ~0U), /* Yes, it passes. */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */ + BPF_STMT(BPF_RET | BPF_K, 0xFFFFFFF), /* No. It passes. */ + BPF_STMT(BPF_RET | BPF_K, 0) /* Echo with wrong ident. Reject. */ + }; + static struct sock_fprog filter = { + sizeof insns / sizeof(insns[0]), + insns + }; + + if (once) + return; + once = 1; + + /* Patch bpflet for current identifier. */ + insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(rts->ident), 0, 1); + + if (setsockopt(sock->fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) + error(0, errno, _("WARNING: failed to install socket filter")); +} diff --git a/performance/net/udpping/ping.h b/performance/net/udpping/ping.h new file mode 100644 index 0000000..ba7d6d9 --- /dev/null +++ b/performance/net/udpping/ping.h @@ -0,0 +1,431 @@ +#ifndef IPUTILS_PING_H +#define IPUTILS_PING_H + +/* Includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBCAP +# include +# include +#endif + +#include "iputils_common.h" + +#ifdef USE_IDN +# define getaddrinfo_flags (AI_CANONNAME | AI_IDN | AI_CANONIDN) +# define getnameinfo_flags NI_IDN +#else +# define getaddrinfo_flags (AI_CANONNAME) +# define getnameinfo_flags 0 +#endif + +#include +#include +#include +#include +#include +#include +/* All includes done. */ + +#ifndef SCOPE_DELIMITER +# define SCOPE_DELIMITER '%' +#endif + +#define DEFDATALEN (64 - 8) /* default data length */ + +#define MAXWAIT 10 /* max seconds to wait for response */ +#define MININTERVAL 10 /* Minimal interpacket gap */ +#define MINUSERINTERVAL 2 /* Minimal allowed interval for non-root */ + +#define SCHINT(a) (((a) <= MININTERVAL) ? MININTERVAL : (a)) + + +#ifndef MSG_CONFIRM +#define MSG_CONFIRM 0 +#endif + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. + */ +#define MAX_DUP_CHK 0x10000 + +#if defined(__WORDSIZE) && __WORDSIZE == 64 +# define USE_BITMAP64 +#endif + +#ifdef USE_BITMAP64 +typedef uint64_t bitmap_t; +# define BITMAP_SHIFT 6 +#else +typedef uint32_t bitmap_t; +# define BITMAP_SHIFT 5 +#endif + +#if ((MAX_DUP_CHK >> (BITMAP_SHIFT + 3)) << (BITMAP_SHIFT + 3)) != MAX_DUP_CHK +# error Please MAX_DUP_CHK and/or BITMAP_SHIFT +#endif + +struct rcvd_table { + bitmap_t bitmap[MAX_DUP_CHK / (sizeof(bitmap_t) * 8)]; +}; + +typedef struct socket_st { + int fd; + int socktype; +} socket_st; + +struct ping_rts; + +int ping4_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, socket_st *sock); +int ping4_send_probe(struct ping_rts *rts, socket_st *, void *packet, unsigned packet_size); +int ping4_receive_error_msg(struct ping_rts *, socket_st *); +int ping4_parse_reply(struct ping_rts *, socket_st *, struct msghdr *msg, int cc, void *addr, struct timeval *); +void ping4_install_filter(struct ping_rts *rts, socket_st *); + +typedef struct ping_func_set_st { + int (*send_probe)(struct ping_rts *rts, socket_st *, void *packet, unsigned packet_size); + int (*receive_error_msg)(struct ping_rts *rts, socket_st *sock); + int (*parse_reply)(struct ping_rts *rts, socket_st *, struct msghdr *msg, int len, void *addr, struct timeval *); + void (*install_filter)(struct ping_rts *rts, socket_st *); +} ping_func_set_st; + +/* Node Information query */ +struct ping_ni { + int query; + int flag; + void *subject; + int subject_len; + int subject_type; + char *group; +#if PING6_NONCE_MEMORY + uint8_t *nonce_ptr; +#else + struct { + struct timeval tv; + pid_t pid; + } nonce_secret; +#endif +}; + +/*ping runtime state */ +struct ping_rts { + unsigned int mark; + unsigned char *outpack; + + struct rcvd_table rcvd_tbl; + + size_t datalen; + char *hostname; + uid_t uid; + int ident; /* random id to identify our packets */ + + int sndbuf; + int ttl; + + long npackets; /* max packets to transmit */ + long nreceived; /* # of packets we got back */ + long nrepeats; /* number of duplicates */ + long ntransmitted; /* sequence # for outbound packets = #sent */ + long nchecksum; /* replies with bad checksum */ + long nerrors; /* icmp errors */ + int interval; /* interval between packets (msec) */ + int preload; + int deadline; /* time to die */ + int lingertime; + struct timespec start_time, cur_time; + volatile int exiting; + volatile int status_snapshot; + int confirm; + int confirm_flag; + char *device; + int pmtudisc; + + volatile int in_pr_addr; /* pr_addr() is executing */ + jmp_buf pr_addr_jmp; + + /* timing */ + int timing; /* flag to do timing */ + long tmin; /* minimum round trip time */ + long tmax; /* maximum round trip time */ + double tsum; /* sum of all times, for doing average */ + double tsum2; + int rtt; + int rtt_addend; + uint16_t acked; + int pipesize; + + ping_func_set_st ping4_func_set; + ping_func_set_st ping6_func_set; + uint32_t tclass; + uint32_t flowlabel; + struct sockaddr_in6 source6; + struct sockaddr_in6 whereto6; + struct sockaddr_in6 firsthop6; + int multicast; + + /* Used only in ping.c */ + int ts_type; + int nroute; + uint32_t route[10]; + struct sockaddr_in whereto; /* who to ping */ + int optlen; + int settos; /* Set TOS, Precedence or other QOS options */ + int broadcast_pings; + struct sockaddr_in source; + + /* Used only in ping_common.c */ + int screen_width; +#ifdef HAVE_LIBCAP + cap_value_t cap_raw; + cap_value_t cap_admin; +#endif + + /* Used only in ping6_common.c */ + int subnet_router_anycast; /* Subnet-Router anycast (RFC 4291) */ + struct sockaddr_in6 firsthop; + unsigned char cmsgbuf[4096]; + size_t cmsglen; + struct ping_ni ni; + + /* boolean option bits */ + unsigned int + opt_adaptive:1, + opt_audible:1, + opt_flood:1, + opt_flood_poll:1, + opt_flowinfo:1, + opt_interval:1, + opt_latency:1, + opt_mark:1, + opt_noloop:1, + opt_numeric:1, + opt_outstanding:1, + opt_pingfilled:1, + opt_ptimeofday:1, + opt_quiet:1, + opt_rroute:1, + opt_so_debug:1, + opt_so_dontroute:1, + opt_sourceroute:1, + opt_strictsource:1, + opt_tclass:1, + opt_timestamp:1, + opt_ttl:1, + opt_verbose:1; +}; +/* FIXME: global_rts will be removed in future */ +extern struct ping_rts *global_rts; + +#define A(bit) (rts->rcvd_tbl.bitmap[(bit) >> BITMAP_SHIFT]) /* identify word in array */ +#define B(bit) (((bitmap_t)1) << ((bit) & ((1 << BITMAP_SHIFT) - 1))) /* identify bit in word */ + +static inline void rcvd_set(struct ping_rts *rts, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + A(bit) |= B(bit); +} + +static inline void rcvd_clear(struct ping_rts *rts, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + A(bit) &= ~B(bit); +} + +static inline bitmap_t rcvd_test(struct ping_rts *rts, uint16_t seq) +{ + unsigned bit = seq % MAX_DUP_CHK; + return A(bit) & B(bit); +} + +/* + * Write to stdout + */ +static inline void write_stdout(const char *str, size_t len) +{ + size_t o = 0; + ssize_t cc; + do { + cc = write(STDOUT_FILENO, str + o, len - o); + o += cc; + } while (len > o || cc < 0); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +static inline void tvsub(struct timeval *out, struct timeval *in) +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * tssub -- + * Subtract 2 timespec structs: out = out - in. Out is assumed to + * be >= in. + */ +static inline void tssub(struct timespec *out, struct timespec *in) +{ + if ((out->tv_nsec -= in->tv_nsec) < 0) { + --out->tv_sec; + out->tv_nsec += 1000000000; + } + out->tv_sec -= in->tv_sec; +} + +static inline void set_signal(int signo, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + + sa.sa_handler = (void (*)(int))handler; + sigaction(signo, &sa, NULL); +} + +extern int __schedule_exit(int next); + +static inline int schedule_exit(struct ping_rts *rts, int next) +{ + if (rts->npackets && rts->ntransmitted >= rts->npackets && !rts->deadline) + next = __schedule_exit(next); + return next; +} + +static inline int in_flight(struct ping_rts *rts) +{ + uint16_t diff = (uint16_t)rts->ntransmitted - rts->acked; + return (diff <= 0x7FFF) ? diff : rts->ntransmitted - rts->nreceived - rts->nerrors; +} + +static inline void acknowledge(struct ping_rts *rts, uint16_t seq) +{ + uint16_t diff = (uint16_t)rts->ntransmitted - seq; + if (diff <= 0x7FFF) { + if ((int)diff + 1 > rts->pipesize) + rts->pipesize = (int)diff + 1; + if ((int16_t)(seq - rts->acked) > 0 || + (uint16_t)rts->ntransmitted - rts->acked > 0x7FFF) + rts->acked = seq; + } +} + +static inline void advance_ntransmitted(struct ping_rts *rts) +{ + rts->ntransmitted++; + /* Invalidate acked, if 16 bit seq overflows. */ + if ((uint16_t)rts->ntransmitted - rts->acked > 0x7FFF) + rts->acked = (uint16_t)rts->ntransmitted + 1; +} + +extern void usage(void) __attribute__((noreturn)); +extern void limit_capabilities(struct ping_rts *rts); +static int enable_capability_raw(void); +static int disable_capability_raw(void); +static int enable_capability_admin(void); +static int disable_capability_admin(void); +#ifdef HAVE_LIBCAP +extern int modify_capability(cap_value_t, cap_flag_value_t); +static inline int enable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_SET); } +static inline int disable_capability_raw(void) { return modify_capability(CAP_NET_RAW, CAP_CLEAR); } +static inline int enable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_SET); } +static inline int disable_capability_admin(void) { return modify_capability(CAP_NET_ADMIN, CAP_CLEAR); } +#else +extern int modify_capability(int); +static inline int enable_capability_raw(void) { return modify_capability(1); } +static inline int disable_capability_raw(void) { return modify_capability(0); } +static inline int enable_capability_admin(void) { return modify_capability(1); } +static inline int disable_capability_admin(void) { return modify_capability(0); } +#endif +extern void drop_capabilities(void); + +char *pr_addr(struct ping_rts *rts, void *sa, socklen_t salen); + +int is_ours(struct ping_rts *rts, socket_st *sock, uint16_t id); +extern int pinger(struct ping_rts *rts, ping_func_set_st *fset, socket_st *sock); +extern void sock_setbufs(struct ping_rts *rts, socket_st *, int alloc); +extern void sock_setmark(unsigned int mark, int fd); +extern void setup(struct ping_rts *rts, socket_st *); +extern int main_loop(struct ping_rts *rts, ping_func_set_st *fset, socket_st*, + uint8_t *packet, int packlen); +extern int finish(struct ping_rts *rts); +extern void status(struct ping_rts *rts); +extern void common_options(int ch); +extern int gather_statistics(struct ping_rts *rts, uint8_t *icmph, int icmplen, + int cc, uint16_t seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(uint8_t *ptr, int cc), int multicast, + int wrong_source); +extern void print_timestamp(struct ping_rts *rts); +void fill(struct ping_rts *rts, char *patp, unsigned char *packet, size_t packet_size); + +/* IPv6 */ + +int ping6_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, + socket_st *sock); +void ping6_usage(unsigned from_ping); + +int ping6_send_probe(struct ping_rts *rts, socket_st *sockets, void *packet, unsigned packet_size); +int ping6_receive_error_msg(struct ping_rts *rts, socket_st *sockets); +int ping6_parse_reply(struct ping_rts *rts, socket_st *, struct msghdr *msg, int cc, void *addr, struct timeval *); +void ping6_install_filter(struct ping_rts *rts, socket_st *sockets); +int ntohsp(uint16_t *p); + +/* IPv6 node information query */ + +int niquery_is_enabled(struct ping_ni *ni); +void niquery_init_nonce(struct ping_ni *ni); +int niquery_option_handler(struct ping_ni *ni, const char *opt_arg); +int niquery_is_subject_valid(struct ping_ni *ni); +int niquery_check_nonce(struct ping_ni *ni, uint8_t *nonce); +void niquery_fill_nonce(struct ping_ni *ni, uint16_t seq, uint8_t *nonce); + +#define NI_NONCE_SIZE 8 + +struct ni_hdr { + struct icmp6_hdr ni_u; + uint8_t ni_nonce[NI_NONCE_SIZE]; +}; + +#define ni_type ni_u.icmp6_type +#define ni_code ni_u.icmp6_code +#define ni_cksum ni_u.icmp6_cksum +#define ni_qtype ni_u.icmp6_data16[0] +#define ni_flags ni_u.icmp6_data16[1] + +#endif /* IPUTILS_PING_H */ diff --git a/performance/net/udpping/ping_common.c b/performance/net/udpping/ping_common.c new file mode 100644 index 0000000..ba46252 --- /dev/null +++ b/performance/net/udpping/ping_common.c @@ -0,0 +1,955 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "iputils_common.h" +#include "ping.h" + +#ifndef HZ +#define HZ sysconf(_SC_CLK_TCK) +#endif + +#ifndef HAVE_LIBCAP +static uid_t euid; +#endif + +void usage(void) +{ + fprintf(stderr, + "\nUsage\n" + " ping [options] \n" + "\nOptions:\n" + " dns name or ip address\n" + " -a use audible ping\n" + " -A use adaptive ping\n" + " -B sticky source address\n" + " -c stop after replies\n" + " -D print timestamps\n" + " -d use SO_DEBUG socket option\n" + " -f flood ping\n" + " -h print help and exit\n" + " -I either interface name or address\n" + " -i seconds between sending each packet\n" + " -L suppress loopback of multicast packets\n" + " -l send number of packages while waiting replies\n" + " -m tag the packets going out\n" + " -M define mtu discovery, can be one of \n" + " -n no dns name resolution\n" + " -O report outstanding replies\n" + " -p contents of padding byte\n" + " -q quiet output\n" + " -Q use quality of service bits\n" + " -s use as number of data bytes to be sent\n" + " -S use as SO_SNDBUF socket option value\n" + " -t define time to live\n" + " -U print user-to-user latency\n" + " -v verbose output\n" + " -V print version and exit\n" + " -w reply wait in seconds\n" + " -W time to wait for response\n" + "\nIPv4 options:\n" + " -4 use IPv4\n" + " -b allow pinging broadcast\n" + " -R record route\n" + " -T define timestamp, can be one of \n" + "\nIPv6 options:\n" + " -6 use IPv6\n" + " -F define flow label, default is random\n" + " -N use icmp6 node info query, try as argument\n" + "\nFor more details see ping(8).\n" + ); + exit(2); +} + +void limit_capabilities(struct ping_rts *rts) +{ +#ifdef HAVE_LIBCAP + cap_t cap_cur_p; + cap_t cap_p; + cap_flag_value_t cap_ok; + + cap_cur_p = cap_get_proc(); + if (!cap_cur_p) + error(-1, errno, "cap_get_proc"); + cap_p = cap_init(); + if (!cap_p) + error(-1, errno, "cap_init"); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_ADMIN, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &rts->cap_admin, CAP_SET); + cap_ok = CAP_CLEAR; + cap_get_flag(cap_cur_p, CAP_NET_RAW, CAP_PERMITTED, &cap_ok); + if (cap_ok != CAP_CLEAR) + cap_set_flag(cap_p, CAP_PERMITTED, 1, &rts->cap_raw, CAP_SET); + if (cap_set_proc(cap_p) < 0) + error(-1, errno, "cap_set_proc"); + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + error(-1, errno, "prctl"); + if (setuid(getuid()) < 0) + error(-1, errno, "setuid"); + if (prctl(PR_SET_KEEPCAPS, 0) < 0) + error(-1, errno, "prctl"); + cap_free(cap_p); + cap_free(cap_cur_p); +#else + euid = geteuid(); +#endif + rts->uid = getuid(); +#ifndef HAVE_LIBCAP + if (seteuid(rts->uid)) + error(-1, errno, "setuid"); +#endif +} + +#ifdef HAVE_LIBCAP +int modify_capability(cap_value_t cap, cap_flag_value_t on) +{ + cap_t cap_p = cap_get_proc(); + cap_flag_value_t cap_ok; + int rc = -1; + + if (!cap_p) { + error(0, errno, "cap_get_proc"); + goto out; + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); + if (cap_ok == CAP_CLEAR) { + rc = on ? -1 : 0; + goto out; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); + + if (cap_set_proc(cap_p) < 0) { + error(0, errno, "cap_set_proc"); + goto out; + } + + cap_free(cap_p); + cap_p = NULL; + + rc = 0; +out: + if (cap_p) + cap_free(cap_p); + return rc; +} +#else +int modify_capability(int on) +{ + if (seteuid(on ? euid : getuid())) { + error(0, errno, "seteuid"); + return -1; + } + + return 0; +} +#endif + +void drop_capabilities(void) +{ +#ifdef HAVE_LIBCAP + cap_t cap = cap_init(); + if (cap_set_proc(cap) < 0) + error(-1, errno, "cap_set_proc"); + cap_free(cap); +#else + if (setuid(getuid())) + error(-1, errno, "setuid"); +#endif +} + +/* Fills all the outpack, excluding ICMP header, but _including_ + * timestamp area with supplied pattern. + */ +void fill(struct ping_rts *rts, char *patp, unsigned char *packet, size_t packet_size) +{ + int ii, jj; + unsigned int pat[16]; + char *cp; + unsigned char *bp = packet + 8; + +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + + for (cp = patp; *cp; cp++) { + if (!isxdigit(*cp)) + error(2, 0, _("patterns must be specified as hex digits: %s"), cp); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], + &pat[6], &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], + &pat[12], &pat[13], &pat[14], &pat[15]); + + if (ii > 0) { + size_t kk; + size_t max = packet_size < (size_t)ii + 8 ? 0 : packet_size - (size_t)ii + 8; + + for (kk = 0; kk <= max; kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + } + if (!rts->opt_quiet) { + printf(_("PATTERN: 0x")); + for (jj = 0; jj < ii; ++jj) + printf("%02x", bp[jj] & 0xFF); + printf("\n"); + } + +#ifdef USE_IDN + setlocale(LC_ALL, ""); +#endif +} + +static void sigexit(int signo __attribute__((__unused__))) +{ + global_rts->exiting = 1; + if (global_rts->in_pr_addr) + longjmp(global_rts->pr_addr_jmp, 0); +} + +static void sigstatus(int signo __attribute__((__unused__))) +{ + global_rts->status_snapshot = 1; +} + +int __schedule_exit(int next) +{ + static unsigned long waittime; + struct itimerval it; + + if (waittime) + return next; + + if (global_rts->nreceived) { + waittime = 2 * global_rts->tmax; + if (waittime < 1000 * (unsigned long)global_rts->interval) + waittime = 1000 * global_rts->interval; + } else + waittime = global_rts->lingertime * 1000; + + if (next < 0 || (unsigned long)next < waittime / 1000) + next = waittime / 1000; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = waittime / 1000000; + it.it_value.tv_usec = waittime % 1000000; + setitimer(ITIMER_REAL, &it, NULL); + return next; +} + +static inline void update_interval(struct ping_rts *rts) +{ + int est = rts->rtt ? rts->rtt / 8 : rts->interval * 1000; + + rts->interval = (est + rts->rtt_addend + 500) / 1000; + if (rts->uid && rts->interval < MINUSERINTERVAL) + rts->interval = MINUSERINTERVAL; +} + +/* + * Print timestamp + */ +void print_timestamp(struct ping_rts *rts) +{ + if (rts->opt_ptimeofday) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("[%lu.%06lu] ", + (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is a random number, + * and the sequence number is an ascending integer. The first several bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +int pinger(struct ping_rts *rts, ping_func_set_st *fset, socket_st *sock) +{ + static int oom_count; + static int tokens; + int i; + + /* Have we already sent enough? If we have, return an arbitrary positive value. */ + if (rts->exiting || (rts->npackets && rts->ntransmitted >= rts->npackets && !rts->deadline)) + return 1000; + + /* Check that packets < rate*time + preload */ + if (rts->cur_time.tv_sec == 0) { + clock_gettime(CLOCK_MONOTONIC_RAW, &rts->cur_time); + tokens = rts->interval * (rts->preload - 1); + } else { + long ntokens, tmp; + struct timespec tv; + + clock_gettime(CLOCK_MONOTONIC_RAW, &tv); + ntokens = (tv.tv_sec - rts->cur_time.tv_sec) * 1000 + + (tv.tv_nsec - rts->cur_time.tv_nsec) / 1000000; + if (!rts->interval) { + /* Case of unlimited flood is special; + * if we see no reply, they are limited to 100pps */ + if (ntokens < MININTERVAL && in_flight(rts) >= rts->preload) + return MININTERVAL - ntokens; + } + ntokens += tokens; + tmp = (long)rts->interval * (long)rts->preload; + if (tmp < ntokens) + ntokens = tmp; + if (ntokens < rts->interval) + return rts->interval - ntokens; + + rts->cur_time = tv; + tokens = ntokens - rts->interval; + } + + if (rts->opt_outstanding) { + if (rts->ntransmitted > 0 && !rcvd_test(rts, rts->ntransmitted)) { + print_timestamp(rts); + printf(_("no answer yet for icmp_seq=%lu\n"), (rts->ntransmitted % MAX_DUP_CHK)); + fflush(stdout); + } + } + +resend: + i = fset->send_probe(rts, sock, rts->outpack, sizeof(rts->outpack)); + + if (i == 0) { + oom_count = 0; + advance_ntransmitted(rts); + if (!rts->opt_quiet && rts->opt_flood) { + /* Very silly, but without this output with + * high preload or pipe size is very confusing. */ + if ((rts->preload < rts->screen_width && rts->pipesize < rts->screen_width) || + in_flight(rts) < rts->screen_width) + write_stdout(".", 1); + } + return rts->interval - tokens; + } + + /* And handle various errors... */ + if (i > 0) { + /* Apparently, it is some fatal bug. */ + abort(); + } else if (errno == ENOBUFS || errno == ENOMEM) { + int nores_interval; + + /* Device queue overflow or OOM. Packet is not sent. */ + tokens = 0; + /* Slowdown. This works only in adaptive mode (option -A) */ + rts->rtt_addend += (rts->rtt < 8 * 50000 ? rts->rtt / 8 : 50000); + if (rts->opt_adaptive) + update_interval(rts); + nores_interval = SCHINT(rts->interval / 2); + if (nores_interval > 500) + nores_interval = 500; + oom_count++; + if (oom_count * nores_interval < rts->lingertime) + return nores_interval; + i = 0; + /* Fall to hard error. It is to avoid complete deadlock + * on stuck output device even when dealine was not requested. + * Expected timings are screwed up in any case, but we will + * exit some day. :-) */ + } else if (errno == EAGAIN) { + /* Socket buffer is full. */ + tokens += rts->interval; + return MININTERVAL; + } else { + if ((i = fset->receive_error_msg(rts, sock)) > 0) { + /* An ICMP error arrived. In this case, we've received + * an error from sendto(), but we've also received an + * ICMP message, which means the packet did in fact + * send in some capacity. So, in this odd case, report + * the more specific errno as the error, and treat this + * as a hard local error. */ + i = 0; + goto hard_local_error; + } + /* Compatibility with old linuces. */ + if (i == 0 && rts->confirm_flag && errno == EINVAL) { + rts->confirm_flag = 0; + errno = 0; + } + if (!errno) + goto resend; + } + +hard_local_error: + /* Hard local error. Pretend we sent packet. */ + advance_ntransmitted(rts); + + if (i == 0 && !rts->opt_quiet) { + if (rts->opt_flood) + write_stdout("E", 1); + else + error(0, errno, "sendmsg"); + } + tokens = 0; + return SCHINT(rts->interval); +} + +/* Set socket buffers, "alloc" is an estimate of memory taken by single packet. */ + +void sock_setbufs(struct ping_rts *rts, socket_st *sock, int alloc) +{ + int rcvbuf, hold; + socklen_t tmplen = sizeof(hold); + + if (!rts->sndbuf) + rts->sndbuf = alloc; + setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, (char *)&rts->sndbuf, sizeof(rts->sndbuf)); + + rcvbuf = hold = alloc * rts->preload; + if (hold < 65536) + hold = 65536; + setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, sizeof(hold)); + if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&hold, &tmplen) == 0) { + if (hold < rcvbuf) + error(0, 0, _("WARNING: probably, rcvbuf is not enough to hold preload")); + } +} + +void sock_setmark(unsigned int mark, int fd) +{ +#ifdef SO_MARK + int ret; + int errno_save; + + enable_capability_admin(); + ret = setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)); + errno_save = errno; + disable_capability_admin(); + + /* Do not exit, old kernels do not support mark. */ + if (ret == -1) + error(0, errno_save, _("WARNING: failed to set mark: %d"), mark); +#else + error(0, errno_save, _("WARNING: SO_MARK not supported")); +#endif +} + +/* Protocol independent setup and parameter checks. */ + +void setup(struct ping_rts *rts, socket_st *sock) +{ + int hold; + struct timeval tv; + sigset_t sset; + + if (rts->opt_flood && !rts->opt_interval) + rts->interval = 0; + + if (rts->uid && rts->interval < MINUSERINTERVAL) + error(2, 0, _("cannot flood; minimal interval allowed for user is %dms"), MINUSERINTERVAL); + + if (rts->interval >= INT_MAX / rts->preload) + error(2, 0, _("illegal preload and/or interval: %d"), rts->interval); + + hold = 1; + if (rts->opt_so_debug) + setsockopt(sock->fd, SOL_SOCKET, SO_DEBUG, (char *)&hold, sizeof(hold)); + if (rts->opt_so_dontroute) + setsockopt(sock->fd, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, sizeof(hold)); + +#ifdef SO_TIMESTAMP + if (!rts->opt_latency) { + int on = 1; + if (setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + error(0, 0, _("Warning: no SO_TIMESTAMP support, falling back to SIOCGSTAMP")); + } +#endif + + if (rts->opt_mark) + sock_setmark(rts->mark, sock->fd); + + /* Set some SNDTIMEO to prevent blocking forever + * on sends, when device is too slow or stalls. Just put limit + * of one second, or "interval", if it is less. + */ + tv.tv_sec = 1; + tv.tv_usec = 0; + if (rts->interval < 1000) { + tv.tv_sec = 0; + tv.tv_usec = 1000 * SCHINT(rts->interval); + } + setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); + + /* Set RCVTIMEO to "interval". Note, it is just an optimization + * allowing to avoid redundant poll(). */ + tv.tv_sec = SCHINT(rts->interval) / 1000; + tv.tv_usec = 1000 * (SCHINT(rts->interval) % 1000); + if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) + rts->opt_flood_poll = 1; + + if (!rts->opt_pingfilled) { + size_t i; + unsigned char *p = rts->outpack + 8; + + /* Do not forget about case of small datalen, fill timestamp area too! */ + for (i = 0; i < rts->datalen; ++i) + *p++ = i; + } + + if (sock->socktype == SOCK_RAW) + rts->ident = rand() & 0xFFFF; + + set_signal(SIGINT, sigexit); + set_signal(SIGALRM, sigexit); + set_signal(SIGQUIT, sigstatus); + + sigemptyset(&sset); + sigprocmask(SIG_SETMASK, &sset, NULL); + + clock_gettime(CLOCK_MONOTONIC_RAW, &rts->start_time); + + if (rts->deadline) { + struct itimerval it; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = rts->deadline; + it.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &it, NULL); + } + + if (isatty(STDOUT_FILENO)) { + struct winsize w; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) { + if (w.ws_col > 0) + rts->screen_width = w.ws_col; + } + } +} + +int main_loop(struct ping_rts *rts, ping_func_set_st *fset, socket_st *sock, + uint8_t *packet, int packlen) +{ + char addrbuf[128]; + char ans_data[4096]; + struct iovec iov; + struct msghdr msg; + int cc; + int next; + int polling; + int recv_error; + + iov.iov_base = (char *)packet; + + for (;;) { + /* Check exit conditions. */ + if (rts->exiting) + break; + if (rts->npackets && rts->nreceived + rts->nerrors >= rts->npackets) + break; + if (rts->deadline && rts->nerrors) + break; + /* Check for and do special actions. */ + if (rts->status_snapshot) + status(rts); + + /* Send probes scheduled to this time. */ + do { + next = pinger(rts, fset, sock); + next = schedule_exit(rts, next); + } while (next <= 0); + + /* "next" is time to send next probe, if positive. + * If next<=0 send now or as soon as possible. */ + + /* Technical part. Looks wicked. Could be dropped, + * if everyone used the newest kernel. :-) + * Its purpose is: + * 1. Provide intervals less than resolution of scheduler. + * Solution: spinning. + * 2. Avoid use of poll(), when recvmsg() can provide + * timed waiting (SO_RCVTIMEO). */ + polling = 0; + recv_error = 0; + if (rts->opt_adaptive || rts->opt_flood_poll || next < SCHINT(rts->interval)) { + int recv_expected = in_flight(rts); + + /* If we are here, recvmsg() is unable to wait for + * required timeout. */ + if (1000 % HZ == 0 ? next <= 1000 / HZ : (next < INT_MAX / HZ && next * HZ <= 1000)) { + /* Very short timeout... So, if we wait for + * something, we sleep for MININTERVAL. + * Otherwise, spin! */ + if (recv_expected) { + next = MININTERVAL; + } else { + next = 0; + /* When spinning, no reasons to poll. + * Use nonblocking recvmsg() instead. */ + polling = MSG_DONTWAIT; + /* But yield yet. */ + sched_yield(); + } + } + + if (!polling && + (rts->opt_adaptive || rts->opt_flood_poll || rts->interval)) { + struct pollfd pset; + pset.fd = sock->fd; + pset.events = POLLIN; + pset.revents = 0; + if (poll(&pset, 1, next) < 1 || + !(pset.revents & (POLLIN | POLLERR))) + continue; + polling = MSG_DONTWAIT; + recv_error = pset.revents & POLLERR; + } + } + + for (;;) { + struct timeval *recv_timep = NULL; + struct timeval recv_time; + int not_ours = 0; /* Raw socket can receive messages + * destined to other running pings. */ + + iov.iov_len = packlen; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addrbuf; + msg.msg_namelen = sizeof(addrbuf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + cc = recvmsg(sock->fd, &msg, polling); + polling = MSG_DONTWAIT; + + if (cc < 0) { + /* If there was a POLLERR and there is no packet + * on the socket, try to read the error queue. + * Otherwise, give up. + */ + if ((errno == EAGAIN && !recv_error) || + errno == EINTR) + break; + recv_error = 0; + if (!fset->receive_error_msg(rts, sock)) { + if (errno) { + error(0, errno, "recvmsg"); + break; + } + not_ours = 1; + } + } else { + +#ifdef SO_TIMESTAMP + struct cmsghdr *c; + + for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { + if (c->cmsg_level != SOL_SOCKET || + c->cmsg_type != SO_TIMESTAMP) + continue; + if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) + continue; + recv_timep = (struct timeval *)CMSG_DATA(c); + } +#endif + + if (rts->opt_latency || recv_timep == NULL) { + if (rts->opt_latency || + ioctl(sock->fd, SIOCGSTAMP, &recv_time)) + gettimeofday(&recv_time, NULL); + recv_timep = &recv_time; + } + + not_ours = fset->parse_reply(rts, sock, &msg, cc, addrbuf, recv_timep); + } + + /* See? ... someone runs another ping on this host. */ + if (not_ours && sock->socktype == SOCK_RAW) + fset->install_filter(rts, sock); + + /* If nothing is in flight, "break" returns us to pinger. */ + if (in_flight(rts) == 0) + break; + + /* Otherwise, try to recvmsg() again. recvmsg() + * is nonblocking after the first iteration, so that + * if nothing is queued, it will receive EAGAIN + * and return to pinger. */ + } + } + return finish(rts); +} + +int gather_statistics(struct ping_rts *rts, uint8_t *icmph, int icmplen, + int cc, uint16_t seq, int hops, + int csfailed, struct timeval *tv, char *from, + void (*pr_reply)(uint8_t *icmph, int cc), int multicast, + int wrong_source) +{ + int dupflag = 0; + long triptime = 0; + uint8_t *ptr = icmph + icmplen; + + ++rts->nreceived; + if (!csfailed) + acknowledge(rts, seq); + + if (rts->timing && cc >= (int)(8 + sizeof(struct timeval))) { + struct timeval tmp_tv; + memcpy(&tmp_tv, ptr, sizeof(tmp_tv)); + +restamp: + tvsub(tv, &tmp_tv); + triptime = tv->tv_sec * 1000000 + tv->tv_usec; + if (triptime < 0) { + error(0, 0, _("Warning: time of day goes back (%ldus), taking countermeasures"), triptime); + triptime = 0; + if (!rts->opt_latency) { + gettimeofday(tv, NULL); + rts->opt_latency = 1; + goto restamp; + } + } + if (!csfailed) { + rts->tsum += triptime; + rts->tsum2 += (double)((long long)triptime * (long long)triptime); + if (triptime < rts->tmin) + rts->tmin = triptime; + if (triptime > rts->tmax) + rts->tmax = triptime; + if (!rts->rtt) + rts->rtt = triptime * 8; + else + rts->rtt += triptime - rts->rtt / 8; + if (rts->opt_adaptive) + update_interval(rts); + } + } + + if (csfailed) { + ++rts->nchecksum; + --rts->nreceived; + } else if (rcvd_test(rts, seq)) { + ++rts->nrepeats; + --rts->nreceived; + dupflag = 1; + } else { + rcvd_set(rts, seq); + dupflag = 0; + } + rts->confirm = rts->confirm_flag; + + if (rts->opt_quiet) + return 1; + + if (rts->opt_flood) { + if (!csfailed) + write_stdout("\b \b", 3); + else + write_stdout("\bC", 2); + } else { + size_t i; + uint8_t *cp, *dp; + + print_timestamp(rts); + printf(_("%d bytes from %s:"), cc, from); + + if (pr_reply) + pr_reply(icmph, cc); + + if (hops >= 0) + printf(_(" ttl=%d"), hops); + + if ((size_t)cc < rts->datalen + 8) { + printf(_(" (truncated)\n")); + return 1; + } + if (rts->timing) { + if (triptime >= 100000 - 50) + printf(_(" time=%ld ms"), (triptime + 500) / 1000); + else if (triptime >= 10000 - 5) + printf(_(" time=%ld.%01ld ms"), (triptime + 50) / 1000, + ((triptime + 50) % 1000) / 100); + else if (triptime >= 1000) + printf(_(" time=%ld.%02ld ms"), (triptime + 5) / 1000, + ((triptime + 5) % 1000) / 10); + else + printf(_(" time=%ld.%03ld ms"), triptime / 1000, + triptime % 1000); + } + + if (dupflag && (!multicast || rts->opt_verbose)) + printf(_(" (DUP!)")); + if (csfailed) + printf(_(" (BAD CHECKSUM!)")); + if (wrong_source) + printf(_(" (DIFFERENT ADDRESS!)")); + + /* check the data */ + cp = ((unsigned char *)ptr) + sizeof(struct timeval); + dp = &rts->outpack[8 + sizeof(struct timeval)]; + for (i = sizeof(struct timeval); i < rts->datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) { + printf(_("\nwrong data byte #%zu should be 0x%x but was 0x%x"), + i, *dp, *cp); + cp = (unsigned char *)ptr + sizeof(struct timeval); + for (i = sizeof(struct timeval); i < rts->datalen; ++i, ++cp) { + if ((i % 32) == sizeof(struct timeval)) + printf("\n#%zu\t", i); + printf("%x ", *cp); + } + break; + } + } + } + return 0; +} + +static long llsqrt(long long a) +{ + long long prev = LLONG_MAX; + long long x = a; + + if (x > 0) { + while (x < prev) { + prev = x; + x = (x + (a / x)) / 2; + } + } + + return (long)x; +} + +/* + * finish -- + * Print out statistics, and give up. + */ +int finish(struct ping_rts *rts) +{ + struct timespec tv = rts->cur_time; + char *comma = ""; + + tssub(&tv, &rts->start_time); + + putchar('\n'); + fflush(stdout); + printf(_("--- %s ping statistics ---\n"), rts->hostname); + printf(_("%ld packets transmitted, "), rts->ntransmitted); + printf(_("%ld received"), rts->nreceived); + if (rts->nrepeats) + printf(_(", +%ld duplicates"), rts->nrepeats); + if (rts->nchecksum) + printf(_(", +%ld corrupted"), rts->nchecksum); + if (rts->nerrors) + printf(_(", +%ld errors"), rts->nerrors); + + if (rts->ntransmitted) { +#ifdef USE_IDN + setlocale(LC_ALL, "C"); +#endif + printf(_(", %g%% packet loss"), + (float)((((long long)(rts->ntransmitted - rts->nreceived)) * 100.0) / rts->ntransmitted)); + printf(_(", time %ldms"), 1000 * tv.tv_sec + (tv.tv_nsec + 500000) / 1000000); + } + + putchar('\n'); + + if (rts->nreceived && rts->timing) { + double tmdev; + long total = rts->nreceived + rts->nrepeats; + long tmavg = rts->tsum / total; + long long tmvar; + + if (rts->tsum < INT_MAX) + /* This slightly clumsy computation order is important to avoid + * integer rounding errors for small ping times. */ + tmvar = (rts->tsum2 - ((rts->tsum * rts->tsum) / total)) / total; + else + tmvar = (rts->tsum2 / total) - (tmavg * tmavg); + + tmdev = llsqrt(tmvar); + + printf(_("rtt min/avg/max/mdev = %ld.%03ld/%lu.%03ld/%ld.%03ld/%ld.%03ld ms"), + (long)rts->tmin / 1000, (long)rts->tmin % 1000, + (unsigned long)(tmavg / 1000), (long)(tmavg % 1000), + (long)rts->tmax / 1000, (long)rts->tmax % 1000, + (long)tmdev / 1000, (long)tmdev % 1000); + comma = ", "; + } + if (rts->pipesize > 1) { + printf(_("%spipe %d"), comma, rts->pipesize); + comma = ", "; + } + + if (rts->nreceived && (!rts->interval || rts->opt_flood || rts->opt_adaptive) && rts->ntransmitted > 1) { + int ipg = (1000000 * (long long)tv.tv_sec + tv.tv_nsec / 1000) / (rts->ntransmitted - 1); + + printf(_("%sipg/ewma %d.%03d/%d.%03d ms"), + comma, ipg / 1000, ipg % 1000, rts->rtt / 8000, (rts->rtt / 8) % 1000); + } + putchar('\n'); + return (!rts->nreceived || (rts->deadline && rts->nreceived < rts->npackets)); +} + +void status(struct ping_rts *rts) +{ + int loss = 0; + long tavg = 0; + + rts->status_snapshot = 0; + + if (rts->ntransmitted) + loss = (((long long)(rts->ntransmitted - rts->nreceived)) * 100) / rts->ntransmitted; + + fprintf(stderr, "\r"); + fprintf(stderr, _("%ld/%ld packets, %d%% loss"), rts->nreceived, rts->ntransmitted, loss); + + if (rts->nreceived && rts->timing) { + tavg = rts->tsum / (rts->nreceived + rts->nrepeats); + + fprintf(stderr, _(", min/avg/ewma/max = %ld.%03ld/%lu.%03ld/%d.%03d/%ld.%03ld ms"), + (long)rts->tmin / 1000, (long)rts->tmin % 1000, + tavg / 1000, tavg % 1000, + rts->rtt / 8000, (rts->rtt / 8) % 1000, (long)rts->tmax / 1000, (long)rts->tmax % 1000); + } + fprintf(stderr, "\n"); +} + +inline int is_ours(struct ping_rts *rts, socket_st * sock, uint16_t id) +{ + return sock->socktype == SOCK_DGRAM || id == rts->ident; +} From cdba8db5e2aec2c956f53ba3aa2b01644616c182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Sat, 28 May 2022 16:22:21 +0200 Subject: [PATCH 4/6] enet: Use const for pointers to receive buffer --- include/netutil/checksum.h | 30 +++++++++++++++--------------- lib/netutil/checksum.c | 8 ++++---- usr/drivers/enet/enet.h | 6 +++--- usr/drivers/enet/enet_proto.c | 28 ++++++++++++++-------------- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/include/netutil/checksum.h b/include/netutil/checksum.h index 4e19dd5..d8ffced 100644 --- a/include/netutil/checksum.h +++ b/include/netutil/checksum.h @@ -3,9 +3,9 @@ /* * Copyright (c) 2001, 2002 Swedish Institute of Computer Science. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, @@ -14,21 +14,21 @@ * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. + * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. - * + * * Author: Adam Dunkels * */ @@ -39,6 +39,6 @@ /** * Calculate the internet checksum according to RFC1071 */ -uint16_t inet_checksum(void *dataptr, uint16_t len); +uint16_t inet_checksum(const void *dataptr, uint16_t len); #endif diff --git a/lib/netutil/checksum.c b/lib/netutil/checksum.c index de95b60..256a4eb 100644 --- a/lib/netutil/checksum.c +++ b/lib/netutil/checksum.c @@ -3,15 +3,15 @@ static uint16_t -lwip_standard_chksum(void *dataptr, uint16_t len) +lwip_standard_chksum(const void *dataptr, uint16_t len) { uint32_t acc; uint16_t src; - uint8_t *octetptr; + const uint8_t *octetptr; acc = 0; /* dataptr may be at odd or even addresses */ - octetptr = (uint8_t*)dataptr; + octetptr = (const uint8_t*)dataptr; while (len > 1) { /* declare first octet as most significant thus assume network order, ignoring host order */ @@ -39,7 +39,7 @@ lwip_standard_chksum(void *dataptr, uint16_t len) /** * Calculate a short such that ret + dataptr[..] becomes 0 */ -uint16_t inet_checksum(void *dataptr, uint16_t len) +uint16_t inet_checksum(const void *dataptr, uint16_t len) { return ~lwip_standard_chksum(dataptr, len); }; diff --git a/usr/drivers/enet/enet.h b/usr/drivers/enet/enet.h index 3474418..dd47042 100644 --- a/usr/drivers/enet/enet.h +++ b/usr/drivers/enet/enet.h @@ -162,9 +162,9 @@ struct ump_client { struct icmp_echo_reply_meta { struct devq_buf rx_buf; struct tx_alloc_queue_entry tx_entry; - struct icmp_echo_hdr *icmp_echo_hdr; - struct eth_hdr *eth_hdr; - struct ip_hdr *ip_hdr; + const struct icmp_echo_hdr *icmp_echo_hdr; + const struct eth_hdr *eth_hdr; + const struct ip_hdr *ip_hdr; }; struct udp_recv { diff --git a/usr/drivers/enet/enet_proto.c b/usr/drivers/enet/enet_proto.c index 65172a0..4b714e7 100644 --- a/usr/drivers/enet/enet_proto.c +++ b/usr/drivers/enet/enet_proto.c @@ -122,7 +122,7 @@ static void arp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { st->arp.tx_queue_len--; } -static void arp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) { +static void arp_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr) { ENET_DEBUG("Received ARP packet\n"); if (buf->valid_length < sizeof(struct arp_hdr)) { @@ -130,7 +130,7 @@ static void arp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_h rx_release(buf); return; } - struct arp_hdr *arp_hdr = vaddr + buf->valid_data; + const struct arp_hdr *arp_hdr = vaddr + buf->valid_data; uint16_t opcode = uint16_rd(arp_hdr->opcode); if ( uint16_rd(arp_hdr->hwtype) != ARP_HW_TYPE_ETH || @@ -218,7 +218,7 @@ static void icmp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) { tx_send(buf); } -static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) { +static void icmp_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr, const struct ip_hdr *ip_hdr) { if (buf->valid_length < 4) { ENET_WARN("Received ICMP packet too small\n"); rx_release(buf); @@ -231,7 +231,7 @@ static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_ return; } - uint8_t type = *(uint8_t*)(vaddr + buf->valid_data); + uint8_t type = *(const uint8_t*)(vaddr + buf->valid_data); if (type == ICMP_ECHO) { if (buf->valid_length < sizeof(struct icmp_echo_hdr)) { ENET_WARN("Received ICMP packet too small\n"); @@ -239,7 +239,7 @@ static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_ return; } - struct icmp_echo_hdr *icmp_echo_hdr = vaddr + buf->valid_data; + const struct icmp_echo_hdr *icmp_echo_hdr = vaddr + buf->valid_data; ENET_DEBUG("Received ICMP echo, seq=%d\n", uint16_rd(icmp_echo_hdr->seqno)); struct icmp_echo_reply_meta *meta = simpleslab_alloc(&st->rx_meta_slab); @@ -265,7 +265,7 @@ static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry simpleslab_free(&st->rx_meta_slab, meta); } -static uint16_t udp_checksum (struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr) { +static uint16_t udp_checksum (const struct ip_hdr *ip_hdr, const struct udp_hdr *udp_hdr) { uint16_t udp_len = uint16_rd(udp_hdr->len); uint32_t chksum = inet_checksum(udp_hdr, udp_len) ^ 0x0000ffffUL; // add pseudo header @@ -280,13 +280,13 @@ static uint16_t udp_checksum (struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr) { return ~(uint16_t)chksum; } -static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) { +static void udp_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr, const struct ip_hdr *ip_hdr) { if (buf->valid_length < UDP_HLEN) { ENET_WARN("Received UDP packet too small\n"); rx_release(buf); return; } - struct udp_hdr *udp_hdr = vaddr + buf->valid_data; + const struct udp_hdr *udp_hdr = vaddr + buf->valid_data; uint16_t udp_len = uint16_rd(udp_hdr->len); if (udp_len < UDP_HLEN || buf->valid_length < udp_len) { ENET_WARN("Received UDP packet too small\n"); @@ -305,7 +305,7 @@ static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_h uint16_t src_port = uint16_rd(udp_hdr->src); uint16_t dest_port = uint16_rd(udp_hdr->dest); - void *payload = (void*)udp_hdr + UDP_HLEN; + const void *payload = (const void*)udp_hdr + UDP_HLEN; uint16_t payload_len = udp_len - UDP_HLEN; ENET_DEBUG("Received UDP packet from %d to %d, len %d\n", src_port, dest_port, payload_len); @@ -336,13 +336,13 @@ static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_h // IP: https://datatracker.ietf.org/doc/html/rfc791#section-3.1 -static void ip_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) { +static void ip_handle (struct devq_buf *buf, const void *vaddr, const struct eth_hdr *eth_hdr) { if (buf->valid_length < sizeof(struct ip_hdr)) { ENET_WARN("Received IP packet too small\n"); rx_release(buf); return; } - struct ip_hdr *ip_hdr = vaddr + buf->valid_data; + const struct ip_hdr *ip_hdr = vaddr + buf->valid_data; uint16_t ip_len = uint16_rd(ip_hdr->len); uint16_t header_len = IPH_HL(ip_hdr) * 4; if ( @@ -434,8 +434,8 @@ static void write_eth_ip_header ( static void rx_handle (struct devq_buf *buf) { struct region_entry *entry = enet_get_region(st->rxq, buf->rid); assert(entry != NULL); - void *vaddr = (void*)entry->mem.vbase + buf->offset; - void *eth_vaddr = vaddr + buf->valid_data; + const void *vaddr = (void*)entry->mem.vbase + buf->offset; + const void *eth_vaddr = vaddr + buf->valid_data; #if defined(ENET_DEBUG_OPTION) debug_printf("Received Packet of size %lu:", buf->valid_length); @@ -451,7 +451,7 @@ static void rx_handle (struct devq_buf *buf) { return; } - struct eth_hdr *eth_hdr = eth_vaddr; + const struct eth_hdr *eth_hdr = eth_vaddr; uint16_t type = uint16_rd(eth_hdr->type); buf->valid_data += ETH_HLEN; From 9db9053c97496021cc6a5787ed6321f2a1404470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Sat, 28 May 2022 19:49:10 +0200 Subject: [PATCH 5/6] enet: Fix cache bug --- usr/drivers/enet/enet_devq.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/usr/drivers/enet/enet_devq.c b/usr/drivers/enet/enet_devq.c index 04b9a2b..24726e8 100644 --- a/usr/drivers/enet/enet_devq.c +++ b/usr/drivers/enet/enet_devq.c @@ -156,7 +156,9 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid, struct region_entry *entry = enet_get_region(q, *rid); assert(entry); lvaddr_t vaddr = (lvaddr_t) entry->mem.vbase + *offset + *valid_data; - cpu_dcache_wb_range(vaddr, *valid_length); + // I would think that invalidate is the correct thing to do, but that + // causes a fault for some reason. So do clean+invalidate instead. + cpu_dcache_wbinv_range(vaddr, *valid_length); dmb(); @@ -180,9 +182,8 @@ static errval_t enet_tx_dequeue(struct devq* que, regionid_t* rid, if (enet_full_slots(q)) { enet_bufdesc_t desc = q->ring[q->head]; dmb(); - cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head], + cpu_dcache_wbinv_range((lvaddr_t) &q->ring[q->head], sizeof(enet_bufdesc_t)); - desc = q->ring[q->head]; struct devq_buf* buf= &q->ring_bufs[q->head]; if (!(enet_bufdesc_sc_extract(desc) & ENET_TX_READY)) { From 1beb079cb0dd9311c80139a6536f87f4e408381e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Sat, 28 May 2022 19:53:14 +0200 Subject: [PATCH 6/6] Customized ping - Add -u option to send UDP packets - Don't print dots when flooding - Don't require root user --- performance/net/udpping/ping.c | 17 +++++++++++------ performance/net/udpping/ping.h | 1 + performance/net/udpping/ping_common.c | 17 +++++++---------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/performance/net/udpping/ping.c b/performance/net/udpping/ping.c index b5a7616..84825d2 100644 --- a/performance/net/udpping/ping.c +++ b/performance/net/udpping/ping.c @@ -275,6 +275,7 @@ main(int argc, char **argv) .tmin = LONG_MAX, .pipesize = -1, .datalen = DEFDATALEN, + .protocol = IPPROTO_ICMP, .screen_width = INT_MAX, #ifdef HAVE_LIBCAP .cap_raw = CAP_NET_RAW, @@ -311,7 +312,7 @@ main(int argc, char **argv) hints.ai_family = AF_INET6; /* Parse command line options */ - while ((ch = getopt(argc, argv, "h?" "4bRT:" "6F:N:" "aABc:dDfi:I:l:Lm:M:nOp:qQ:rs:S:t:UvVw:W:")) != EOF) { + while ((ch = getopt(argc, argv, "h?" "4bRT:" "6F:N:" "aABc:dDfi:I:l:Lm:M:nOp:qQ:rs:S:t:uUvVw:W:")) != EOF) { switch(ch) { /* IPv4 specific options */ case '4': @@ -398,8 +399,6 @@ main(int argc, char **argv) break; case 'l': rts.preload = strtol_or_err(optarg, _("invalid argument"), 1, MAX_DUP_CHK); - if (rts.uid && rts.preload > 3) - error(2, 0, _("cannot set preload to value greater than 3: %d"), rts.preload); break; case 'L': rts.opt_noloop = 1; @@ -456,6 +455,9 @@ main(int argc, char **argv) rts.ttl = strtol_or_err(optarg, _("invalid argument"), 0, 255); rts.opt_ttl = 1; break; + case 'u': + rts.protocol = IPPROTO_UDP; + break; case 'U': rts.opt_latency = 1; break; @@ -503,7 +505,7 @@ main(int argc, char **argv) /* Create sockets */ enable_capability_raw(); if (hints.ai_family != AF_INET6) - create_socket(&rts, &sock4, AF_INET, hints.ai_socktype, IPPROTO_ICMP, + create_socket(&rts, &sock4, AF_INET, hints.ai_socktype, rts.protocol, hints.ai_family == AF_INET); if (hints.ai_family != AF_INET) { create_socket(&rts, &sock6, AF_INET6, hints.ai_socktype, IPPROTO_ICMPV6, sock4.fd == -1); @@ -676,6 +678,7 @@ int ping4_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, argc--; argv++; } + rts->whereto.sin_port = htons(7); if (rts->source.sin_addr.s_addr == 0) { socklen_t alen; @@ -884,7 +887,9 @@ int ping4_run(struct ping_rts *rts, int argc, char **argv, struct addrinfo *ai, printf(_("PING %s (%s) "), rts->hostname, inet_ntoa(rts->whereto.sin_addr)); if (rts->device || rts->opt_strictsource) printf(_("from %s %s: "), inet_ntoa(rts->source.sin_addr), rts->device ? rts->device : ""); - printf(_("%zu(%zu) bytes of data.\n"), rts->datalen, rts->datalen + 8 + rts->optlen + 20); + int extralen = 0; + if (rts->protocol == IPPROTO_UDP) extralen = 8; + printf(_("%zu(%zu) bytes of data.\n"), rts->datalen, rts->datalen + 8 + rts->optlen + 20 + extralen); setup(rts, sock); @@ -1501,7 +1506,7 @@ int ping4_parse_reply(struct ping_rts *rts, struct socket_st *sock, icp = (struct icmphdr *)(buf + hlen); csfailed = in_cksum((unsigned short *)icp, cc, 0); - if (icp->type == ICMP_ECHOREPLY) { + if (icp->type == ICMP_ECHOREPLY || icp->type == ICMP_ECHO) { if (!is_ours(rts, sock, icp->un.echo.id)) return 1; /* 'Twas not our ECHO */ diff --git a/performance/net/udpping/ping.h b/performance/net/udpping/ping.h index ba7d6d9..b6d2311 100644 --- a/performance/net/udpping/ping.h +++ b/performance/net/udpping/ping.h @@ -145,6 +145,7 @@ struct ping_rts { struct rcvd_table rcvd_tbl; size_t datalen; + int protocol; char *hostname; uid_t uid; int ident; /* random id to identify our packets */ diff --git a/performance/net/udpping/ping_common.c b/performance/net/udpping/ping_common.c index ba46252..99ed7d5 100644 --- a/performance/net/udpping/ping_common.c +++ b/performance/net/udpping/ping_common.c @@ -356,9 +356,9 @@ resend: if (!rts->opt_quiet && rts->opt_flood) { /* Very silly, but without this output with * high preload or pipe size is very confusing. */ - if ((rts->preload < rts->screen_width && rts->pipesize < rts->screen_width) || - in_flight(rts) < rts->screen_width) - write_stdout(".", 1); + // if ((rts->preload < rts->screen_width && rts->pipesize < rts->screen_width) || + // in_flight(rts) < rts->screen_width) + // write_stdout(".", 1); } return rts->interval - tokens; } @@ -476,9 +476,6 @@ void setup(struct ping_rts *rts, socket_st *sock) if (rts->opt_flood && !rts->opt_interval) rts->interval = 0; - if (rts->uid && rts->interval < MINUSERINTERVAL) - error(2, 0, _("cannot flood; minimal interval allowed for user is %dms"), MINUSERINTERVAL); - if (rts->interval >= INT_MAX / rts->preload) error(2, 0, _("illegal preload and/or interval: %d"), rts->interval); @@ -777,10 +774,10 @@ restamp: return 1; if (rts->opt_flood) { - if (!csfailed) - write_stdout("\b \b", 3); - else - write_stdout("\bC", 2); + // if (!csfailed) + // write_stdout("\b \b", 3); + // else + // write_stdout("\bC", 2); } else { size_t i; uint8_t *cp, *dp;