Merge branch 'main' into shelly
This commit is contained in:
commit
97b4f973ee
@ -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 "";
|
||||
};
|
||||
*/
|
||||
|
||||
@ -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()",
|
||||
|
||||
@ -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
|
||||
|
||||
43
include/aos/simpleslab.h
Normal file
43
include/aos/simpleslab.h
Normal file
@ -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 <sys/cdefs.h>
|
||||
|
||||
__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
|
||||
@ -3,10 +3,11 @@
|
||||
|
||||
#include <aos/aos.h>
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
|
||||
@ -17,11 +17,11 @@ 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
|
||||
*
|
||||
*
|
||||
* 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,17 +64,17 @@ 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;
|
||||
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;
|
||||
};
|
||||
@ -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
|
||||
@ -213,16 +213,16 @@ void ump_send (
|
||||
struct ump_send_chan *chan,
|
||||
struct ump_send_queue_entry *entry,
|
||||
size_t header_size,
|
||||
void *header,
|
||||
size_t payload_size,
|
||||
void *payload,
|
||||
const void *header,
|
||||
size_t payload_size,
|
||||
const void *payload,
|
||||
ump_send_callback_fn_t callback,
|
||||
void *callback_arg
|
||||
);
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
59
include/aos/ump_net.h
Normal file
59
include/aos/ump_net.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef _LIB_BARRELFISH_UMP_NET_H
|
||||
#define _LIB_BARRELFISH_UMP_NET_H
|
||||
|
||||
#include <aos/aos.h>
|
||||
|
||||
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
|
||||
87
include/aos/ump_net_client.h
Normal file
87
include/aos/ump_net_client.h
Normal file
@ -0,0 +1,87 @@
|
||||
#ifndef _LIB_BARRELFISH_UMP_NET_CLIENT_H
|
||||
#define _LIB_BARRELFISH_UMP_NET_CLIENT_H
|
||||
|
||||
#include <aos/aos.h>
|
||||
#include <aos/ump_chan.h>
|
||||
#include <aos/ump_net.h>
|
||||
|
||||
// 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
|
||||
@ -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);
|
||||
|
||||
@ -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 <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
@ -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
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <aos/aos.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
|
||||
//#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
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <aos/aos.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
//#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)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <aos/aos.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
//#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__));
|
||||
|
||||
/**
|
||||
|
||||
82
include/netutil/types.h
Normal file
82
include/netutil/types.h
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef _NETUTIL_TYPES_H_
|
||||
#define _NETUTIL_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
@ -2,6 +2,7 @@
|
||||
#define _UDP_H_
|
||||
|
||||
#include <netutil/ip.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
|
||||
//#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__));
|
||||
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
94
lib/aos/simpleslab.c
Normal file
94
lib/aos/simpleslab.c
Normal file
@ -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 <aos/aos.h>
|
||||
#include <aos/simpleslab.h>
|
||||
#include <aos/static_assert.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@ -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);
|
||||
|
||||
207
lib/aos/ump_net_client.c
Normal file
207
lib/aos/ump_net_client.c
Normal file
@ -0,0 +1,207 @@
|
||||
#include <aos/aos.h>
|
||||
#include <aos/deferred.h>
|
||||
#include <aos/aos_rpc.h>
|
||||
#include <aos/ump_binding.h>
|
||||
#include <aos/ump_chan.h>
|
||||
#include <aos/ump_net.h>
|
||||
#include <aos/ump_net_client.h>
|
||||
#include <collections/hash_table.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@ -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 */
|
||||
@ -33,16 +33,13 @@ 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
1
performance/net/udpping/.gitignore
vendored
Normal file
1
performance/net/udpping/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
udpping
|
||||
2
performance/net/udpping/Makefile
Normal file
2
performance/net/udpping/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
udpping: *.c *.h
|
||||
gcc -D_GNU_SOURCE ping.c ping_common.c iputils_common.c -o udpping
|
||||
140
performance/net/udpping/iputils_common.c
Normal file
140
performance/net/udpping/iputils_common.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if HAVE_GETRANDOM
|
||||
# include <sys/random.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ERROR_H
|
||||
# include <error.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
73
performance/net/udpping/iputils_common.h
Normal file
73
performance/net/udpping/iputils_common.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef IPUTILS_COMMON_H
|
||||
#define IPUTILS_COMMON_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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 <locale.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# 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 <idn2.h>
|
||||
|
||||
# include <netdb.h>
|
||||
# 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 <error.h>
|
||||
#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 */
|
||||
1667
performance/net/udpping/ping.c
Normal file
1667
performance/net/udpping/ping.c
Normal file
File diff suppressed because it is too large
Load Diff
432
performance/net/udpping/ping.h
Normal file
432
performance/net/udpping/ping.h
Normal file
@ -0,0 +1,432 @@
|
||||
#ifndef IPUTILS_PING_H
|
||||
#define IPUTILS_PING_H
|
||||
|
||||
/* Includes */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/uio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <setjmp.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <sched.h>
|
||||
#include <math.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <linux/filter.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#ifdef HAVE_LIBCAP
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/capability.h>
|
||||
#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 <ifaddrs.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/in6.h>
|
||||
/* 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;
|
||||
int protocol;
|
||||
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 */
|
||||
952
performance/net/udpping/ping_common.c
Normal file
952
performance/net/udpping/ping_common.c
Normal file
@ -0,0 +1,952 @@
|
||||
/*
|
||||
* 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] <destination>\n"
|
||||
"\nOptions:\n"
|
||||
" <destination> dns name or ip address\n"
|
||||
" -a use audible ping\n"
|
||||
" -A use adaptive ping\n"
|
||||
" -B sticky source address\n"
|
||||
" -c <count> stop after <count> replies\n"
|
||||
" -D print timestamps\n"
|
||||
" -d use SO_DEBUG socket option\n"
|
||||
" -f flood ping\n"
|
||||
" -h print help and exit\n"
|
||||
" -I <interface> either interface name or address\n"
|
||||
" -i <interval> seconds between sending each packet\n"
|
||||
" -L suppress loopback of multicast packets\n"
|
||||
" -l <preload> send <preload> number of packages while waiting replies\n"
|
||||
" -m <mark> tag the packets going out\n"
|
||||
" -M <pmtud opt> define mtu discovery, can be one of <do|dont|want>\n"
|
||||
" -n no dns name resolution\n"
|
||||
" -O report outstanding replies\n"
|
||||
" -p <pattern> contents of padding byte\n"
|
||||
" -q quiet output\n"
|
||||
" -Q <tclass> use quality of service <tclass> bits\n"
|
||||
" -s <size> use <size> as number of data bytes to be sent\n"
|
||||
" -S <size> use <size> as SO_SNDBUF socket option value\n"
|
||||
" -t <ttl> define time to live\n"
|
||||
" -U print user-to-user latency\n"
|
||||
" -v verbose output\n"
|
||||
" -V print version and exit\n"
|
||||
" -w <deadline> reply wait <deadline> in seconds\n"
|
||||
" -W <timeout> time to wait for response\n"
|
||||
"\nIPv4 options:\n"
|
||||
" -4 use IPv4\n"
|
||||
" -b allow pinging broadcast\n"
|
||||
" -R record route\n"
|
||||
" -T <timestamp> define timestamp, can be one of <tsonly|tsandaddr|tsprespec>\n"
|
||||
"\nIPv6 options:\n"
|
||||
" -6 use IPv6\n"
|
||||
" -F <flowlabel> define flow label, default is random\n"
|
||||
" -N <nodeinfo opt> use icmp6 node info query, try <help> 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->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;
|
||||
}
|
||||
@ -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"],
|
||||
|
||||
@ -10,8 +10,20 @@
|
||||
#ifndef ENET_H_
|
||||
#define ENET_H_
|
||||
|
||||
#include <aos/simpleslab.h>
|
||||
#include <aos/ump_chan.h>
|
||||
#include <aos/ump_net.h>
|
||||
#include <collections/hash_table.h>
|
||||
|
||||
#include <netutil/types.h>
|
||||
#include <netutil/etharp.h>
|
||||
#include <netutil/ip.h>
|
||||
#include <netutil/udp.h>
|
||||
|
||||
// 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;
|
||||
const struct icmp_echo_hdr *icmp_echo_hdr;
|
||||
const struct eth_hdr *eth_hdr;
|
||||
const 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_
|
||||
|
||||
@ -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,17 @@ 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);
|
||||
// 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();
|
||||
|
||||
|
||||
enet_bufdesc_sc_insert(desc, status);
|
||||
|
||||
|
||||
q->head = (q->head+1) & (q->size -1);
|
||||
|
||||
|
||||
@ -175,18 +177,17 @@ 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_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)) {
|
||||
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 +211,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 +222,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 +234,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 +245,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 +278,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 +299,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 +312,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 +331,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 +414,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);
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include <dev/imx8x/enet_dev.h>
|
||||
#include <maps/imx8x_map.h>
|
||||
#include <netutil/etharp.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
706
usr/drivers/enet/enet_proto.c
Normal file
706
usr/drivers/enet/enet_proto.c
Normal file
@ -0,0 +1,706 @@
|
||||
#include <devif/queue_interface_backend.h>
|
||||
#include <devif/backends/net/enet_devif.h>
|
||||
#include <aos/aos.h>
|
||||
#include <driverkit/driverkit.h>
|
||||
#include <dev/imx8x/enet_dev.h>
|
||||
|
||||
#include <aos/ump_binding.h>
|
||||
#include <aos/ump_chan.h>
|
||||
#include <aos/ump_net.h>
|
||||
|
||||
#include <netutil/etharp.h>
|
||||
#include <netutil/ip.h>
|
||||
#include <netutil/icmp.h>
|
||||
#include <netutil/udp.h>
|
||||
#include <netutil/checksum.h>
|
||||
#include <netutil/types.h>
|
||||
|
||||
#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, const void *vaddr, const 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;
|
||||
}
|
||||
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 ||
|
||||
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, 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);
|
||||
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 = *(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");
|
||||
rx_release(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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 (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
|
||||
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, 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;
|
||||
}
|
||||
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");
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
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, 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;
|
||||
}
|
||||
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 (
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
const 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <aos/aos.h>
|
||||
#include <aos/ump_binding.h>
|
||||
#include <aos/simpleslab.h>
|
||||
#include <aos/deferred.h>
|
||||
#include <aos/aos_rpc.h>
|
||||
#include <aos/ump_binding.h>
|
||||
#include <aos/ump_chan.h>
|
||||
#include <aos/ump_net.h>
|
||||
#include <aos/ump_net_client.h>
|
||||
|
||||
// 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."
|
||||
|
||||
@ -22,7 +29,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");
|
||||
}
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -155,6 +155,14 @@ bsp_main(int argc, char *argv[]) {
|
||||
DEBUG_ERR(err, "when spawning shelly");
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user