Hi all,
this is the final version of my google summer of code project, "Bandwidth Meter". For a project description you can see the wiki page and gsoc final report:
http://www.open-mesh.org/projects/batman-adv/wiki/Bandwidth_meter
http://www.open-mesh.org/projects/batman-adv/wiki/GSOC2012_BW_report
Any comment is welcome,
Edo
Edo Monticelli (1): batman-adv: bandwidth meter implementation
Makefile.kbuild | 1 + bw_meter.c | 622 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ bw_meter.h | 7 + icmp_socket.c | 18 ++ main.c | 2 + packet.h | 38 +++- routing.c | 15 ++- types.h | 25 +++ 8 files changed, 722 insertions(+), 6 deletions(-) create mode 100644 bw_meter.c create mode 100644 bw_meter.h
The bandwith meter module is a simple, kernel-space replacement for bandwith measurements tool like iperf and netper. It is intended to approximate TCP behaviour.
It is invoked through batctl: the protocol is connection oriented, with cumulative acknowledgment and sliding window. Sender keeps a timeout, which is checked by a worker function regoularly invoked through the workqueue mechanism. If the timeout is expired at the time worker is executed, the whole window is re-transmitted.
Sequence number in packet header is 16 bit and is wrap around safe. BATADV_BW_FIRST_SEQ is the sequence number of the first packet. It is set to 65530 to generate a wrap-around. *Both sides must agree on this value*.
The receiver maintains a bitmap of BATADV_BW_WINDOW_SIZE to account for received packets not in order, so to avoid that they are unecessarly resent. Every time a packet is received, if it is either in order or not, an ack with the seqno equal to the last in order packet is sent back.
The test *can* be interrupted by batctl. A receiver side timeout avoids unlimited waitings for sender packets: after one second of inactivity, the receiver abort the ongoing test. The sender aborts the test after having resent BATADV_BW_MAX_RETRY the same window.
When the test is over, the results are returned to batctl through a call to the function batadv_socket_receive_packet(), before freeing struct batadv_bw_vars. This function only accepts struct batadv_icmp_packet_rr, so that structure is used and a cast is done to struct batadv_bw_result. The function basically sends a particular icmp packet through the local socket. In case any error occured during the test, this is also reported to batctl.
Signed-off-by: Edo Monticelli montik@autistici.org --- Makefile.kbuild | 1 + bw_meter.c | 622 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ bw_meter.h | 7 + icmp_socket.c | 18 ++ main.c | 2 + packet.h | 38 +++- routing.c | 15 ++- types.h | 25 +++ 8 files changed, 722 insertions(+), 6 deletions(-) create mode 100644 bw_meter.c create mode 100644 bw_meter.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index 8676d2b..8c08ff9 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -23,6 +23,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o +batman-adv-y += bw_meter.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/bw_meter.c b/bw_meter.c new file mode 100644 index 0000000..2bc0374 --- /dev/null +++ b/bw_meter.c @@ -0,0 +1,622 @@ +#include "main.h" +#include "send.h" +#include "hash.h" +#include "originator.h" +#include "hard-interface.h" +#include "bw_meter.h" +#include "icmp_socket.h" +#include "types.h" +#include "bw_meter.h" + +#define BATADV_BW_PACKET_LEN 1400 +#define BATADV_BW_WINDOW_SIZE 30 +#define BATADV_BW_CLEAN_RECEIVER_TIMEOUT 2000 +#define BATADV_BW_TIMEOUT 60 +#define BATADV_BW_WORKER_TIMEOUT 30 +#define BATADV_BW_RECV_TIMEOUT 1000 +#define BATADV_BW_TOTAL_TO_SEND 10000 +#define BATADV_BW_MAX_RETRY 3 +#define BATADV_BW_FIRST_SEQ 65530 + +#define batadv_bw_batctl_error_notify(status, uid) \ + batadv_bw_batctl_notify(status, uid, 0) + +static int batadv_bw_queue_sender_worker(struct batadv_bw_vars *bw_vars); +static int batadv_bw_queue_receiver_worker(struct batadv_bw_vars *bw_vars); + +static void batadv_bw_vars_free(struct batadv_bw_vars *bw_vars) +{ + spin_lock_bh(&bw_vars->bat_priv->bw_list_lock); + list_del(&bw_vars->list); + spin_unlock_bh(&bw_vars->bat_priv->bw_list_lock); + kfree(bw_vars); +} + +static int batadv_bw_icmp_send(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + struct batadv_hard_iface *primary_if = NULL; + struct batadv_orig_node *orig_node = NULL; + struct batadv_neigh_node *neigh_node = NULL; + struct batadv_icmp_packet *icmp_packet; + int ret = -1; + + icmp_packet = (struct batadv_icmp_packet *)skb->data; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: no primary if\n"); + goto out; + } + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: mesh inactive\n"); + goto dst_unreach; + } + + orig_node = batadv_orig_hash_find(bat_priv, + icmp_packet->dst); + if (!orig_node) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: no orig node\n"); + goto dst_unreach; + } + + neigh_node = batadv_orig_node_get_router(orig_node); + if (!neigh_node) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: no neigh node\n"); + goto dst_unreach; + } + + if (!neigh_node->if_incoming) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: no if incoming\n"); + goto dst_unreach; + } + + if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter:batadv_bw_icmp_send: status not IF_ACTIVE\n"); + goto dst_unreach; + } + + memcpy(icmp_packet->orig, + primary_if->net_dev->dev_addr, ETH_ALEN); + + batadv_send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); + ret = 0; + goto out; + +dst_unreach: + batadv_bw_stop(bat_priv, icmp_packet->dst, BATADV_BW_DST_UNREACHABLE); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + if (orig_node) + batadv_orig_node_free_ref(orig_node); + return ret; +} + +static struct batadv_bw_vars *batadv_bw_list_find(struct batadv_priv *bat_priv, + void *dst) +{ + struct batadv_bw_vars *pos = NULL, *tmp; + + list_for_each_entry_safe(pos, tmp, &bat_priv->bw_list, list) { + if (memcmp(&pos->other_end, dst, ETH_ALEN) == 0) + return pos; + } + + return NULL; +} + +static int batadv_bw_ack_send(struct batadv_socket_client *socket_client, + struct batadv_icmp_packet *icmp_packet, + uint16_t seq) +{ + struct sk_buff *skb; + struct batadv_icmp_packet *icmp_ack; + struct batadv_priv *bat_priv = socket_client->bat_priv; + int ret = -1; + + bat_priv = socket_client->bat_priv; + skb = dev_alloc_skb(sizeof(*skb) + ETH_HLEN); + if (!skb) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_send_bw_ack cannot allocate skb\n"); + goto out; + } + + skb_reserve(skb, ETH_HLEN); + icmp_ack = (struct batadv_icmp_packet *) + skb_put(skb, sizeof(*icmp_ack)); + icmp_ack->header.packet_type = BATADV_ICMP; + icmp_ack->header.version = BATADV_COMPAT_VERSION; + icmp_ack->header.ttl = 50; + icmp_ack->seqno = htons(seq); + icmp_ack->msg_type = BATADV_BW_ACK; + memcpy(icmp_ack->dst, icmp_packet->orig, ETH_ALEN); + icmp_ack->uid = socket_client->index; + + /* send the ack */ + if (batadv_bw_icmp_send(bat_priv, skb) < 0) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_send_bw_ack cannot send_icmp_packet\n"); + goto out; + } + ret = 0; +out: + return ret; +} + +/* Returns the next zero position, starting from bit_first. If the end of the + * bitmap is reached, another find_next is performed starting from the + * beginning of the bitmap + */ +static int batadv_bw_modular_next_zero_find(struct batadv_bw_vars *bw_vars) +{ + int ret; + + ret = find_next_zero_bit(bw_vars->bw_bits, + BATADV_BW_WINDOW_SIZE, + (bw_vars->bit_first + 1) % + BATADV_BW_WINDOW_SIZE); + if (ret != BATADV_BW_WINDOW_SIZE) + return ret; + + ret = find_next_zero_bit(bw_vars->bw_bits, + BATADV_BW_WINDOW_SIZE, 0); + return ret; +} + +static void batadv_bw_bit_first_move(struct batadv_bw_vars *bw_vars, + uint16_t new_pos) +{ + if (new_pos > BATADV_BW_WINDOW_SIZE) { + batadv_dbg(BATADV_DBG_BATMAN, bw_vars->bat_priv, + "Meter: can't move bit first to inconsistent position\n"); + return; + } + + do { + bw_vars->bit_first = (bw_vars->bit_first + 1) % + BATADV_BW_WINDOW_SIZE; + clear_bit(bw_vars->bit_first, bw_vars->bw_bits); + } while (bw_vars->bit_first != new_pos); +} + +static void batadv_bw_window_slide(struct batadv_bw_vars *bw_vars, + uint16_t seqno, uint16_t len) +{ + uint16_t window_first_16, diff, bit_seqno, new_position; + + /* check if the packet belongs to window */ + spin_lock_bh(&bw_vars->bw_vars_lock); + window_first_16 = (uint16_t) bw_vars->window_first; + diff = seqno - window_first_16; + bw_vars->last_sent_time = jiffies; + + if (diff >= BATADV_BW_WINDOW_SIZE) { + spin_unlock_bh(&bw_vars->bw_vars_lock); + goto out; + } + + /* check for the last packet */ + if (len < BATADV_BW_PACKET_LEN && + bw_vars->status == BATADV_BW_RECEIVER) { + bit_seqno = (bw_vars->bit_first + diff) % BATADV_BW_WINDOW_SIZE; + bw_vars->total_to_send = (bit_seqno + 1) % + BATADV_BW_WINDOW_SIZE; + bw_vars->status = BATADV_BW_LAST_WINDOW; + } + + /* packet is in order */ + if (diff == 0) { + new_position = batadv_bw_modular_next_zero_find(bw_vars); + + /* update bw_vars->window_first */ + if (new_position > bw_vars->bit_first) + bw_vars->window_first += new_position - + bw_vars->bit_first; + else + bw_vars->window_first += new_position + + BATADV_BW_WINDOW_SIZE - + bw_vars->bit_first; + + batadv_bw_bit_first_move(bw_vars, new_position); + } + /* hole in the window */ + else { + bit_seqno = (bw_vars->bit_first + diff) % BATADV_BW_WINDOW_SIZE; + set_bit(bit_seqno, bw_vars->bw_bits); + } + + if (bw_vars->status == BATADV_BW_LAST_WINDOW && + bw_vars->bit_first == bw_vars->total_to_send) { + bw_vars->status = BATADV_BW_COMPLETE; + } + + spin_unlock_bh(&bw_vars->bw_vars_lock); +out: + return; +} + + +void batadv_bw_meter_received(struct batadv_priv *bat_priv, struct sk_buff *skb) +{ + struct batadv_bw_vars *bw_vars; + struct batadv_icmp_packet *icmp_packet; + struct batadv_socket_client *socket_client; + uint16_t seqno; + + socket_client = container_of(&bat_priv, + struct batadv_socket_client, bat_priv); + icmp_packet = (struct batadv_icmp_packet *)skb->data; + + /* search/initialize bw_vars struct */ + spin_lock_bh(&bat_priv->bw_list_lock); + seqno = ntohs(icmp_packet->seqno); + bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->orig); + if (!bw_vars) { + if (seqno != BATADV_BW_FIRST_SEQ) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: seqno != BATADV_BW_FIRST_SEQ cannot initiate connection\n"); + goto out; + } + + bw_vars = kmalloc(sizeof(*bw_vars), GFP_ATOMIC); + if (!bw_vars) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: meter_received cannot allocate bw_vars\n"); + goto out; + } + + memcpy(&bw_vars->other_end, &icmp_packet->orig, ETH_ALEN); + bw_vars->status = BATADV_BW_RECEIVER; + bw_vars->window_first = BATADV_BW_FIRST_SEQ; + bw_vars->bat_priv = bat_priv; + bw_vars->bw_bits = kmalloc(BITS_TO_LONGS(BATADV_BW_WINDOW_SIZE), + GFP_ATOMIC); + if (!bw_vars->bw_bits) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: meter_received cannot allocate window_bitmap\n"); + goto out; + } + + bw_vars->bit_first = 0; + bitmap_zero(bw_vars->bw_bits, BATADV_BW_WINDOW_SIZE); + + spin_lock_init(&bw_vars->bw_vars_lock); + list_add(&bw_vars->list, &bat_priv->bw_list); + + batadv_bw_queue_receiver_worker(bw_vars); + } + + if (bw_vars->status != BATADV_BW_RECEIVER && + bw_vars->status != BATADV_BW_LAST_WINDOW) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: dropping packet: connection is not expecting any\n"); + goto out; + } + + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_bw_window_slide(bw_vars, seqno, skb->len); + batadv_bw_ack_send(socket_client, + (struct batadv_icmp_packet *)icmp_packet, + bw_vars->window_first - 1); +out: + return; +} + +/* Sends all packets that belongs to the window and have not been sent yet + * according to next_to_send and (window_first + BW_WINDOW_SIZE) + */ +static int batadv_bw_multiple_send(struct batadv_priv *bat_priv, + struct batadv_bw_vars *bw_vars) +{ + struct sk_buff *skb; + struct batadv_icmp_packet *icmp_to_send; + struct batadv_socket_client *socket_client; + char *icmp_to_send_char; + int ret, bw_packet_len; + uint16_t window_end, next_to_send; + + ret = -1; + socket_client = container_of(&bat_priv, struct batadv_socket_client, + bat_priv); + + if (!atomic_add_unless(&bw_vars->sending, 1, 1)) + goto out; + + while (1) { + spin_lock_bh(&bw_vars->bw_vars_lock); + window_end = min(bw_vars->window_first + BATADV_BW_WINDOW_SIZE, + bw_vars->total_to_send); + + if (!batadv_seq_before(bw_vars->next_to_send, window_end)) { + atomic_dec(&bw_vars->sending); + spin_unlock_bh(&bw_vars->bw_vars_lock); + break; + } + + bw_packet_len = BATADV_BW_PACKET_LEN; + bw_vars->last_sent_time = jiffies; + next_to_send = bw_vars->next_to_send++; + spin_unlock_bh(&bw_vars->bw_vars_lock); + + if ((bw_vars->window_first + BATADV_BW_WINDOW_SIZE >= + bw_vars->total_to_send) && + bw_vars->next_to_send == (uint16_t)bw_vars->total_to_send) { + bw_packet_len -= 1; + } + + skb = dev_alloc_skb(bw_packet_len + ETH_HLEN); + if (!skb) { + atomic_dec(&bw_vars->sending); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_bw_multiple_send() cannot allocate skb\n"); + goto out; + } + + /* TODO redefine BW_PACKET_LEN */ + skb_reserve(skb, ETH_HLEN); + icmp_to_send_char = skb_put(skb, bw_packet_len); + icmp_to_send = (struct batadv_icmp_packet *)icmp_to_send_char; + + /* fill the icmp header */ + memcpy(&icmp_to_send->dst, &bw_vars->other_end, ETH_ALEN); + icmp_to_send->header.version = BATADV_COMPAT_VERSION; + icmp_to_send->header.packet_type = BATADV_ICMP; + icmp_to_send->msg_type = BATADV_BW_START; + icmp_to_send->seqno = htons(next_to_send); + icmp_to_send->uid = socket_client->index; + + if (batadv_bw_icmp_send(bat_priv, skb) < 0) { + atomic_dec(&bw_vars->sending); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_bw_multiple_send() cannot send_icmp_packet\n"); + goto out; + } + } + ret = 0; +out: + return ret; +} + +void batadv_bw_ack_received(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + struct batadv_icmp_packet *icmp_packet; + struct batadv_bw_vars *bw_vars; + uint16_t seqno, window_end, window_first_16; + + icmp_packet = (struct batadv_icmp_packet *)skb->data; + + /* find the bw_vars */ + spin_lock_bh(&bat_priv->bw_list_lock); + bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->orig); + spin_unlock_bh(&bat_priv->bw_list_lock); + + if (!bw_vars) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: received an ack not related to an open connection\n"); + goto out; + } + + /* slide and send fresh packets */ + spin_lock_bh(&bw_vars->bw_vars_lock); + seqno = ntohs(icmp_packet->seqno); + window_first_16 = (uint16_t) bw_vars->window_first; + window_end = window_first_16 + BATADV_BW_WINDOW_SIZE; + if (!batadv_seq_after(window_first_16, seqno) && + batadv_seq_before(seqno, bw_vars->next_to_send)) { + bw_vars->window_first += (uint16_t) (seqno + 1 - + window_first_16); + } else if (!batadv_bw_is_error(bw_vars->status)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: received unespected ack\n"); + } + + spin_unlock_bh(&bw_vars->bw_vars_lock); + batadv_bw_multiple_send(bat_priv, bw_vars); +out: + return; +} + +static void batadv_bw_receiver_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct batadv_bw_vars *bw_vars; + struct batadv_priv *bat_priv; + + delayed_work = container_of(work, struct delayed_work, work); + bw_vars = container_of(delayed_work, struct batadv_bw_vars, bw_work); + bat_priv = bw_vars->bat_priv; + + if (batadv_has_timed_out(bw_vars->last_sent_time, + BATADV_BW_RECV_TIMEOUT)) { + if (bw_vars->status != BATADV_BW_COMPLETE) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: more than %dms of inactivity: test will be aborted!\n", + BATADV_BW_RECV_TIMEOUT); + } else { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: succesfully completed test with node %02x:%02x:%02x:%02x:%02x:%02x\n", + bw_vars->other_end[0], bw_vars->other_end[1], + bw_vars->other_end[2], bw_vars->other_end[3], + bw_vars->other_end[4], + bw_vars->other_end[5]); + } + + batadv_bw_vars_free(bw_vars); + } else { + batadv_bw_queue_receiver_worker(bw_vars); + } +} + +static void batadv_bw_batctl_notify(uint8_t status, uint8_t uid, + unsigned long int start_time) +{ + struct batadv_bw_result result; + + result.icmp_packet.uid = uid; + + if (!batadv_bw_is_error(status)) { + result.return_value = BATADV_BW_COMPLETE; + result.test_time = ((long)jiffies - + (long)start_time); + result.total_bytes = BATADV_BW_TOTAL_TO_SEND * + BATADV_BW_PACKET_LEN; + } else { + result.return_value = status; + } + + batadv_socket_receive_packet((struct batadv_icmp_packet_rr *)&result, + sizeof(result)); +} + +static void batadv_bw_sender_worker(struct work_struct *work) +{ + struct delayed_work *delayed_work; + struct batadv_bw_vars *bw_vars; + struct batadv_priv *bat_priv; + + delayed_work = container_of(work, struct delayed_work, work); + bw_vars = container_of(delayed_work, struct batadv_bw_vars, bw_work); + bat_priv = bw_vars->bat_priv; + + /* if timedout, resend whole window */ + if (bw_vars->status == BATADV_BW_SENDER && + batadv_has_timed_out(bw_vars->last_sent_time, BATADV_BW_TIMEOUT)) { + /* increase resend counters */ + if (bw_vars->window_first == bw_vars->last_resent_window) { + bw_vars->retry_number += 1; + if (bw_vars->retry_number > BATADV_BW_MAX_RETRY) { + bw_vars->window_first = bw_vars->total_to_send; + bw_vars->status = BATADV_BW_RESEND_LIMIT; + } + } else { + bw_vars->retry_number = 0; + } + + bw_vars->last_resent_window = bw_vars->window_first; + bw_vars->next_to_send = bw_vars->window_first; + batadv_bw_multiple_send(bat_priv, bw_vars); + } + + /* if not finished, re-enqueue worker */ + if (bw_vars->window_first < bw_vars->total_to_send) { + if (batadv_bw_queue_sender_worker(bw_vars) == 0) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_bw_start work already enqueued\n"); + } + } else { + batadv_bw_batctl_notify(bw_vars->status, + bw_vars->socket_client->index, + bw_vars->start_time); + batadv_bw_vars_free(bw_vars); + } +} + +static int batadv_bw_queue_sender_worker(struct batadv_bw_vars *bw_vars) +{ + int ret; + INIT_DELAYED_WORK(&bw_vars->bw_work, batadv_bw_sender_worker); + ret = queue_delayed_work(batadv_event_workqueue, &bw_vars->bw_work, + msecs_to_jiffies(BATADV_BW_WORKER_TIMEOUT)); + + return ret; +} + +static int batadv_bw_queue_receiver_worker(struct batadv_bw_vars *bw_vars) +{ + int ret; + INIT_DELAYED_WORK(&bw_vars->bw_work, batadv_bw_receiver_worker); + ret = queue_delayed_work(batadv_event_workqueue, &bw_vars->bw_work, + msecs_to_jiffies(BATADV_BW_RECV_TIMEOUT)); + + return ret; +} + +void batadv_bw_stop(struct batadv_priv *bat_priv, + uint8_t dst[], uint8_t error_status) +{ + struct batadv_bw_vars *bw_vars; + spin_lock_bh(&bat_priv->bw_list_lock); + bw_vars = batadv_bw_list_find(bat_priv, dst); + if (!bw_vars) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: trying to interrupt an already over connection\n"); + return; + } + spin_unlock_bh(&bat_priv->bw_list_lock); + spin_lock_bh(&bw_vars->bw_vars_lock); + bw_vars->window_first = bw_vars->total_to_send; + bw_vars->status = error_status; + spin_unlock_bh(&bw_vars->bw_vars_lock); +} + +void batadv_bw_start(struct batadv_socket_client *socket_client, + struct batadv_icmp_packet *icmp_packet) +{ + struct batadv_priv *bat_priv = socket_client->bat_priv; + struct batadv_bw_vars *bw_vars; + + /* find bw_vars */ + spin_lock_bh(&bat_priv->bw_list_lock); + bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->dst); + if (bw_vars) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: test to or from the same node already ongoing, aborting\n"); + batadv_bw_batctl_error_notify(BATADV_BW_ALREADY_ONGOING, + socket_client->index); + goto out; + } + + bw_vars = kmalloc(sizeof(*bw_vars), GFP_ATOMIC); + if (!bw_vars) { + spin_unlock_bh(&bat_priv->bw_list_lock); + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_bw_start cannot allocate list elements\n"); + batadv_bw_batctl_error_notify(BATADV_BW_MEMORY_ERROR, + socket_client->index); + goto out; + } + + /* initialize bw_vars */ + memcpy(&bw_vars->other_end, &icmp_packet->dst, ETH_ALEN); + bw_vars->total_to_send = BATADV_BW_TOTAL_TO_SEND + BATADV_BW_FIRST_SEQ; + bw_vars->window_first = BATADV_BW_FIRST_SEQ; + bw_vars->next_to_send = BATADV_BW_FIRST_SEQ; + bw_vars->status = BATADV_BW_SENDER; + bw_vars->last_resent_window = 0; + bw_vars->bat_priv = bat_priv; + bw_vars->socket_client = socket_client; + bw_vars->last_sent_time = jiffies; + bw_vars->start_time = jiffies; + atomic_set(&bw_vars->sending, 0); + spin_lock_init(&bw_vars->bw_vars_lock); + list_add(&bw_vars->list, &bat_priv->bw_list); + spin_unlock_bh(&bat_priv->bw_list_lock); + + /* start worker */ + if (batadv_bw_queue_sender_worker(bw_vars) == 0) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Meter: batadv_bw_start work already enqueued\n"); + } + + batadv_bw_multiple_send(bat_priv, bw_vars); +out: + return; +} diff --git a/bw_meter.h b/bw_meter.h new file mode 100644 index 0000000..9999f7e --- /dev/null +++ b/bw_meter.h @@ -0,0 +1,7 @@ +void batadv_bw_start(struct batadv_socket_client *socket_client, + struct batadv_icmp_packet *icmp_packet_bw); +void batadv_bw_stop(struct batadv_priv *bat_priv, uint8_t dst[], + uint8_t return_value); +void batadv_bw_meter_received(struct batadv_priv *bat_priv, + struct sk_buff *skb); +void batadv_bw_ack_received(struct batadv_priv *bat_priv, struct sk_buff *skb); diff --git a/icmp_socket.c b/icmp_socket.c index bde3cf7..a48e9b5 100644 --- a/icmp_socket.c +++ b/icmp_socket.c @@ -20,11 +20,13 @@ #include "main.h" #include <linux/debugfs.h> #include <linux/slab.h> + #include "icmp_socket.h" #include "send.h" #include "hash.h" #include "originator.h" #include "hard-interface.h" +#include "bw_meter.h"
static struct batadv_socket_client *batadv_socket_client_hash[256];
@@ -152,6 +154,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, struct batadv_hard_iface *primary_if = NULL; struct sk_buff *skb; struct batadv_icmp_packet_rr *icmp_packet; + struct batadv_icmp_packet icmp_packet_bw;
struct batadv_orig_node *orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL; @@ -170,6 +173,21 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, goto out; }
+ if (copy_from_user(&icmp_packet_bw, buff, sizeof(icmp_packet_bw))) { + len = -EFAULT; + goto out; + } + + if (icmp_packet_bw.msg_type == BATADV_BW_START) { + batadv_bw_start(socket_client, &icmp_packet_bw); + goto out; + } + + if (icmp_packet_bw.msg_type == BATADV_BW_STOP) { + batadv_bw_stop(bat_priv, icmp_packet_bw.dst, BATADV_BW_SIGINT); + goto out; + } + if (len >= sizeof(struct batadv_icmp_packet_rr)) packet_len = sizeof(struct batadv_icmp_packet_rr);
diff --git a/main.c b/main.c index b4aa470..23a49e3 100644 --- a/main.c +++ b/main.c @@ -101,6 +101,7 @@ int batadv_mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->gw.list_lock); spin_lock_init(&bat_priv->vis.hash_lock); spin_lock_init(&bat_priv->vis.list_lock); + spin_lock_init(&bat_priv->bw_list_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); @@ -108,6 +109,7 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_LIST_HEAD(&bat_priv->tt.changes_list); INIT_LIST_HEAD(&bat_priv->tt.req_list); INIT_LIST_HEAD(&bat_priv->tt.roam_list); + INIT_LIST_HEAD(&bat_priv->bw_list);
ret = batadv_originator_init(bat_priv); if (ret < 0) diff --git a/packet.h b/packet.h index 2d23a14..568025a 100644 --- a/packet.h +++ b/packet.h @@ -21,6 +21,7 @@ #define _NET_BATMAN_ADV_PACKET_H_
#define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ +#define batadv_bw_is_error(n) ((uint8_t)n > 127 ? 1 : 0)
enum batadv_packettype { BATADV_IV_OGM = 0x01, @@ -44,12 +45,15 @@ enum batadv_iv_flags { };
/* ICMP message types */ -enum batadv_icmp_packettype { - BATADV_ECHO_REPLY = 0, +enum icmp_packettype { + BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3, - BATADV_ECHO_REQUEST = 8, - BATADV_TTL_EXCEEDED = 11, - BATADV_PARAMETER_PROBLEM = 12, + BATADV_ECHO_REQUEST = 8, + BATADV_TTL_EXCEEDED = 11, + BATADV_PARAMETER_PROBLEM = 12, + BATADV_BW_START = 15, + BATADV_BW_ACK = 16, + BATADV_BW_STOP = 17, };
/* vis defines */ @@ -155,6 +159,30 @@ struct batadv_icmp_packet_rr { uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; } __packed;
+enum batadv_bw_meter_status { + BATADV_BW_RECEIVER = 1, + BATADV_BW_SENDER = 2, + BATADV_BW_COMPLETE = 3, + BATADV_BW_LAST_WINDOW = 4, + /* error status >= 128 */ + BATADV_BW_SIGINT = 128, + BATADV_BW_DST_UNREACHABLE = 129, + BATADV_BW_RESEND_LIMIT = 130, + BATADV_BW_ALREADY_ONGOING = 131, + BATADV_BW_MEMORY_ERROR = 132, +}; + +/* structure returned to batctl */ +/* icmp_packet_rr used to keep socket_client's index, + * as function batadv_socket_receive_packet expects it + */ +struct batadv_bw_result { + struct batadv_icmp_packet icmp_packet; + uint32_t test_time; + uint32_t total_bytes; + uint8_t return_value; +} __packed; + struct batadv_unicast_packet { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ diff --git a/routing.c b/routing.c index 939fc01..f82751d 100644 --- a/routing.c +++ b/routing.c @@ -28,6 +28,7 @@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "bw_meter.h"
static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -290,6 +291,16 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
+ if (icmp_packet->msg_type == BATADV_BW_START) { + batadv_bw_meter_received(bat_priv, skb); + goto out; + } + + if (icmp_packet->msg_type == BATADV_BW_ACK) { + batadv_bw_ack_received(bat_priv, skb); + goto out; + } + /* add data to device queue */ if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { batadv_socket_receive_packet(icmp_packet, icmp_len); @@ -427,7 +438,9 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
/* add record route information if not full */ if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) && - (icmp_packet->rr_cur < BATADV_RR_LEN)) { + (icmp_packet->rr_cur < BATADV_RR_LEN) && + (icmp_packet->msg_type != BATADV_BW_START) && + (icmp_packet->msg_type != BATADV_BW_ACK)) { memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), ethhdr->h_dest, ETH_ALEN); icmp_packet->rr_cur++; diff --git a/types.h b/types.h index 2ed82ca..fc7720f 100644 --- a/types.h +++ b/types.h @@ -226,6 +226,27 @@ struct batadv_priv_vis { struct batadv_vis_info *my_info; };
+struct batadv_bw_vars { + struct list_head list; + struct delayed_work bw_work; + struct batadv_priv *bat_priv; + struct batadv_socket_client *socket_client; + unsigned long *bw_bits; /* bitmap */ + unsigned long start_time; + unsigned long last_sent_time; + uint32_t total_to_send; + uint32_t window_first; + uint32_t last_resent_window; + uint16_t next_to_send; + uint16_t bit_first; + uint8_t other_end[ETH_ALEN]; + uint8_t status; /* see bm_meter_status */ + uint8_t retry_number; + uint8_t return_value; + atomic_t sending; /* 1 if in multiple_send, 0 otherwise */ + spinlock_t bw_vars_lock; /* protects bw_vars */ +}; + struct batadv_priv { atomic_t mesh_state; struct net_device_stats stats; @@ -251,12 +272,16 @@ struct batadv_priv { struct dentry *debug_dir; struct hlist_head forw_bat_list; struct hlist_head forw_bcast_list; + + struct list_head bw_list; struct batadv_hashtable *orig_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects */ + spinlock_t bw_list_lock; /* protects bw_list */ struct delayed_work orig_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops; + #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_priv_bla bla; #endif
On Thursday 23 August 2012 15:41:58 Edo Monticelli wrote:
/* ICMP message types */ -enum batadv_icmp_packettype {
BATADV_ECHO_REPLY = 0,
+enum icmp_packettype {
BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3,
I heavily protest against the change of the enum name back to the pre-prefix name.
There is still a TODO inside the diff and you have variables which are assigned but not used.
Kind regards, Sven
On Thu, Aug 23, 2012 at 03:41:58PM +0200, Edo Monticelli wrote:
The bandwith meter module is a simple, kernel-space replacement for bandwith measurements tool like iperf and netper. It is intended to approximate TCP behaviour.
It is invoked through batctl: the protocol is connection oriented, with cumulative acknowledgment and sliding window. Sender keeps a timeout, which is checked by a worker function regoularly invoked through the workqueue mechanism. If the timeout is expired at the time worker is executed, the whole window is re-transmitted.
Sequence number in packet header is 16 bit and is wrap around safe. BATADV_BW_FIRST_SEQ is the sequence number of the first packet. It is set to 65530 to generate a wrap-around. *Both sides must agree on this value*.
I guess we can skip the paragraph, no? No need to bother people with these technical details in a summary. :)
The receiver maintains a bitmap of BATADV_BW_WINDOW_SIZE to account for received packets not in order, so to avoid that they are unecessarly resent. Every time a packet is received, if it is either in order or not, an ack with the seqno equal to the last in order packet is sent back.
The test *can* be interrupted by batctl. A receiver side timeout avoids unlimited waitings for sender packets: after one second of inactivity, the receiver abort the ongoing test. The sender aborts the test after having resent BATADV_BW_MAX_RETRY the same window.
When the test is over, the results are returned to batctl through a call to the function batadv_socket_receive_packet(), before freeing struct batadv_bw_vars. This function only accepts struct batadv_icmp_packet_rr, so that structure is used and a cast is done to struct batadv_bw_result. The function basically sends a particular icmp packet through the local socket. In case any error occured during the test, this is also reported to batctl.
Signed-off-by: Edo Monticelli montik@autistici.org
Makefile.kbuild | 1 + bw_meter.c | 622 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ bw_meter.h | 7 + icmp_socket.c | 18 ++ main.c | 2 + packet.h | 38 +++- routing.c | 15 ++- types.h | 25 +++ 8 files changed, 722 insertions(+), 6 deletions(-) create mode 100644 bw_meter.c create mode 100644 bw_meter.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index 8676d2b..8c08ff9 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -23,6 +23,7 @@ batman-adv-y += bat_iv_ogm.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o +batman-adv-y += bw_meter.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o diff --git a/bw_meter.c b/bw_meter.c new file mode 100644 index 0000000..2bc0374 --- /dev/null +++ b/bw_meter.c @@ -0,0 +1,622 @@ +#include "main.h" +#include "send.h" +#include "hash.h" +#include "originator.h" +#include "hard-interface.h" +#include "bw_meter.h" +#include "icmp_socket.h" +#include "types.h" +#include "bw_meter.h"
Didn't we agree to add some comments to these defines? these are quite a lot and not (always) self-explanatory.
+#define BATADV_BW_PACKET_LEN 1400 +#define BATADV_BW_WINDOW_SIZE 30 +#define BATADV_BW_CLEAN_RECEIVER_TIMEOUT 2000 +#define BATADV_BW_TIMEOUT 60 +#define BATADV_BW_WORKER_TIMEOUT 30 +#define BATADV_BW_RECV_TIMEOUT 1000 +#define BATADV_BW_TOTAL_TO_SEND 10000 +#define BATADV_BW_MAX_RETRY 3 +#define BATADV_BW_FIRST_SEQ 65530
+#define batadv_bw_batctl_error_notify(status, uid) \
- batadv_bw_batctl_notify(status, uid, 0)
+static int batadv_bw_queue_sender_worker(struct batadv_bw_vars *bw_vars); +static int batadv_bw_queue_receiver_worker(struct batadv_bw_vars *bw_vars);
+static void batadv_bw_vars_free(struct batadv_bw_vars *bw_vars) +{
- spin_lock_bh(&bw_vars->bat_priv->bw_list_lock);
- list_del(&bw_vars->list);
- spin_unlock_bh(&bw_vars->bat_priv->bw_list_lock);
- kfree(bw_vars);
+}
+static int batadv_bw_icmp_send(struct batadv_priv *bat_priv,
struct sk_buff *skb)
+{
- struct batadv_hard_iface *primary_if = NULL;
- struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *neigh_node = NULL;
- struct batadv_icmp_packet *icmp_packet;
- int ret = -1;
- icmp_packet = (struct batadv_icmp_packet *)skb->data;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: no primary if\n");
goto out;
- }
- if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: mesh inactive\n");
goto dst_unreach;
- }
- orig_node = batadv_orig_hash_find(bat_priv,
icmp_packet->dst);
- if (!orig_node) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: no orig node\n");
goto dst_unreach;
- }
- neigh_node = batadv_orig_node_get_router(orig_node);
- if (!neigh_node) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: no neigh node\n");
goto dst_unreach;
- }
- if (!neigh_node->if_incoming) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: no if incoming\n");
goto dst_unreach;
- }
- if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter:batadv_bw_icmp_send: status not IF_ACTIVE\n");
goto dst_unreach;
- }
- memcpy(icmp_packet->orig,
primary_if->net_dev->dev_addr, ETH_ALEN);
- batadv_send_skb_packet(skb, neigh_node->if_incoming,
neigh_node->addr);
- ret = 0;
- goto out;
+dst_unreach:
- batadv_bw_stop(bat_priv, icmp_packet->dst, BATADV_BW_DST_UNREACHABLE);
+out:
- if (primary_if)
batadv_hardif_free_ref(primary_if);
- if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
- if (orig_node)
batadv_orig_node_free_ref(orig_node);
- return ret;
+}
+static struct batadv_bw_vars *batadv_bw_list_find(struct batadv_priv *bat_priv,
void *dst)
+{
- struct batadv_bw_vars *pos = NULL, *tmp;
- list_for_each_entry_safe(pos, tmp, &bat_priv->bw_list, list) {
if (memcmp(&pos->other_end, dst, ETH_ALEN) == 0)
return pos;
- }
- return NULL;
+}
+static int batadv_bw_ack_send(struct batadv_socket_client *socket_client,
struct batadv_icmp_packet *icmp_packet,
uint16_t seq)
+{
- struct sk_buff *skb;
- struct batadv_icmp_packet *icmp_ack;
- struct batadv_priv *bat_priv = socket_client->bat_priv;
- int ret = -1;
- bat_priv = socket_client->bat_priv;
- skb = dev_alloc_skb(sizeof(*skb) + ETH_HLEN);
- if (!skb) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_send_bw_ack cannot allocate skb\n");
goto out;
- }
- skb_reserve(skb, ETH_HLEN);
- icmp_ack = (struct batadv_icmp_packet *)
skb_put(skb, sizeof(*icmp_ack));
- icmp_ack->header.packet_type = BATADV_ICMP;
- icmp_ack->header.version = BATADV_COMPAT_VERSION;
- icmp_ack->header.ttl = 50;
- icmp_ack->seqno = htons(seq);
- icmp_ack->msg_type = BATADV_BW_ACK;
- memcpy(icmp_ack->dst, icmp_packet->orig, ETH_ALEN);
- icmp_ack->uid = socket_client->index;
- /* send the ack */
- if (batadv_bw_icmp_send(bat_priv, skb) < 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_send_bw_ack cannot send_icmp_packet\n");
goto out;
- }
- ret = 0;
+out:
- return ret;
+}
+/* Returns the next zero position, starting from bit_first. If the end of the
- bitmap is reached, another find_next is performed starting from the
- beginning of the bitmap
- */
+static int batadv_bw_modular_next_zero_find(struct batadv_bw_vars *bw_vars) +{
- int ret;
- ret = find_next_zero_bit(bw_vars->bw_bits,
BATADV_BW_WINDOW_SIZE,
(bw_vars->bit_first + 1) %
BATADV_BW_WINDOW_SIZE);
- if (ret != BATADV_BW_WINDOW_SIZE)
return ret;
- ret = find_next_zero_bit(bw_vars->bw_bits,
BATADV_BW_WINDOW_SIZE, 0);
- return ret;
+}
+static void batadv_bw_bit_first_move(struct batadv_bw_vars *bw_vars,
uint16_t new_pos)
+{
- if (new_pos > BATADV_BW_WINDOW_SIZE) {
batadv_dbg(BATADV_DBG_BATMAN, bw_vars->bat_priv,
"Meter: can't move bit first to inconsistent position\n");
return;
- }
- do {
bw_vars->bit_first = (bw_vars->bit_first + 1) %
BATADV_BW_WINDOW_SIZE;
clear_bit(bw_vars->bit_first, bw_vars->bw_bits);
- } while (bw_vars->bit_first != new_pos);
+}
+static void batadv_bw_window_slide(struct batadv_bw_vars *bw_vars,
uint16_t seqno, uint16_t len)
+{
- uint16_t window_first_16, diff, bit_seqno, new_position;
- /* check if the packet belongs to window */
- spin_lock_bh(&bw_vars->bw_vars_lock);
- window_first_16 = (uint16_t) bw_vars->window_first;
- diff = seqno - window_first_16;
- bw_vars->last_sent_time = jiffies;
- if (diff >= BATADV_BW_WINDOW_SIZE) {
spin_unlock_bh(&bw_vars->bw_vars_lock);
goto out;
- }
- /* check for the last packet */
- if (len < BATADV_BW_PACKET_LEN &&
bw_vars->status == BATADV_BW_RECEIVER) {
bit_seqno = (bw_vars->bit_first + diff) % BATADV_BW_WINDOW_SIZE;
bw_vars->total_to_send = (bit_seqno + 1) %
BATADV_BW_WINDOW_SIZE;
bw_vars->status = BATADV_BW_LAST_WINDOW;
- }
- /* packet is in order */
- if (diff == 0) {
new_position = batadv_bw_modular_next_zero_find(bw_vars);
/* update bw_vars->window_first */
if (new_position > bw_vars->bit_first)
bw_vars->window_first += new_position -
bw_vars->bit_first;
else
bw_vars->window_first += new_position +
BATADV_BW_WINDOW_SIZE -
bw_vars->bit_first;
batadv_bw_bit_first_move(bw_vars, new_position);
- }
- /* hole in the window */
- else {
bit_seqno = (bw_vars->bit_first + diff) % BATADV_BW_WINDOW_SIZE;
set_bit(bit_seqno, bw_vars->bw_bits);
- }
- if (bw_vars->status == BATADV_BW_LAST_WINDOW &&
bw_vars->bit_first == bw_vars->total_to_send) {
bw_vars->status = BATADV_BW_COMPLETE;
- }
- spin_unlock_bh(&bw_vars->bw_vars_lock);
+out:
- return;
+}
Was it not possible to use existing sliding window mechanisms, e.g. the one used in bitarray.c? Having this functionality twice in batman is pretty redundant, and this kind of code usually brings a lot of bugs in details. ;)
+void batadv_bw_meter_received(struct batadv_priv *bat_priv, struct sk_buff *skb) +{
- struct batadv_bw_vars *bw_vars;
- struct batadv_icmp_packet *icmp_packet;
- struct batadv_socket_client *socket_client;
- uint16_t seqno;
- socket_client = container_of(&bat_priv,
struct batadv_socket_client, bat_priv);
- icmp_packet = (struct batadv_icmp_packet *)skb->data;
- /* search/initialize bw_vars struct */
- spin_lock_bh(&bat_priv->bw_list_lock);
- seqno = ntohs(icmp_packet->seqno);
- bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->orig);
- if (!bw_vars) {
if (seqno != BATADV_BW_FIRST_SEQ) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: seqno != BATADV_BW_FIRST_SEQ cannot initiate connection\n");
goto out;
}
bw_vars = kmalloc(sizeof(*bw_vars), GFP_ATOMIC);
if (!bw_vars) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: meter_received cannot allocate bw_vars\n");
goto out;
}
memcpy(&bw_vars->other_end, &icmp_packet->orig, ETH_ALEN);
bw_vars->status = BATADV_BW_RECEIVER;
bw_vars->window_first = BATADV_BW_FIRST_SEQ;
bw_vars->bat_priv = bat_priv;
bw_vars->bw_bits = kmalloc(BITS_TO_LONGS(BATADV_BW_WINDOW_SIZE),
GFP_ATOMIC);
if (!bw_vars->bw_bits) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: meter_received cannot allocate window_bitmap\n");
goto out;
}
bw_vars->bit_first = 0;
bitmap_zero(bw_vars->bw_bits, BATADV_BW_WINDOW_SIZE);
spin_lock_init(&bw_vars->bw_vars_lock);
list_add(&bw_vars->list, &bat_priv->bw_list);
batadv_bw_queue_receiver_worker(bw_vars);
- }
- if (bw_vars->status != BATADV_BW_RECEIVER &&
bw_vars->status != BATADV_BW_LAST_WINDOW) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: dropping packet: connection is not expecting any\n");
goto out;
- }
- spin_unlock_bh(&bat_priv->bw_list_lock);
- batadv_bw_window_slide(bw_vars, seqno, skb->len);
- batadv_bw_ack_send(socket_client,
(struct batadv_icmp_packet *)icmp_packet,
bw_vars->window_first - 1);
+out:
- return;
+}
+/* Sends all packets that belongs to the window and have not been sent yet
- according to next_to_send and (window_first + BW_WINDOW_SIZE)
- */
+static int batadv_bw_multiple_send(struct batadv_priv *bat_priv,
struct batadv_bw_vars *bw_vars)
+{
- struct sk_buff *skb;
- struct batadv_icmp_packet *icmp_to_send;
- struct batadv_socket_client *socket_client;
- char *icmp_to_send_char;
- int ret, bw_packet_len;
- uint16_t window_end, next_to_send;
- ret = -1;
- socket_client = container_of(&bat_priv, struct batadv_socket_client,
bat_priv);
- if (!atomic_add_unless(&bw_vars->sending, 1, 1))
goto out;
- while (1) {
spin_lock_bh(&bw_vars->bw_vars_lock);
window_end = min(bw_vars->window_first + BATADV_BW_WINDOW_SIZE,
bw_vars->total_to_send);
if (!batadv_seq_before(bw_vars->next_to_send, window_end)) {
atomic_dec(&bw_vars->sending);
spin_unlock_bh(&bw_vars->bw_vars_lock);
break;
}
bw_packet_len = BATADV_BW_PACKET_LEN;
bw_vars->last_sent_time = jiffies;
next_to_send = bw_vars->next_to_send++;
spin_unlock_bh(&bw_vars->bw_vars_lock);
if ((bw_vars->window_first + BATADV_BW_WINDOW_SIZE >=
bw_vars->total_to_send) &&
bw_vars->next_to_send == (uint16_t)bw_vars->total_to_send) {
bw_packet_len -= 1;
}
skb = dev_alloc_skb(bw_packet_len + ETH_HLEN);
if (!skb) {
atomic_dec(&bw_vars->sending);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_bw_multiple_send() cannot allocate skb\n");
goto out;
}
/* TODO redefine BW_PACKET_LEN */
Why this TODO?
skb_reserve(skb, ETH_HLEN);
icmp_to_send_char = skb_put(skb, bw_packet_len);
icmp_to_send = (struct batadv_icmp_packet *)icmp_to_send_char;
/* fill the icmp header */
memcpy(&icmp_to_send->dst, &bw_vars->other_end, ETH_ALEN);
icmp_to_send->header.version = BATADV_COMPAT_VERSION;
icmp_to_send->header.packet_type = BATADV_ICMP;
icmp_to_send->msg_type = BATADV_BW_START;
icmp_to_send->seqno = htons(next_to_send);
icmp_to_send->uid = socket_client->index;
if (batadv_bw_icmp_send(bat_priv, skb) < 0) {
atomic_dec(&bw_vars->sending);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_bw_multiple_send() cannot send_icmp_packet\n");
goto out;
}
- }
- ret = 0;
+out:
- return ret;
+}
+void batadv_bw_ack_received(struct batadv_priv *bat_priv,
struct sk_buff *skb)
+{
- struct batadv_icmp_packet *icmp_packet;
- struct batadv_bw_vars *bw_vars;
- uint16_t seqno, window_end, window_first_16;
- icmp_packet = (struct batadv_icmp_packet *)skb->data;
- /* find the bw_vars */
- spin_lock_bh(&bat_priv->bw_list_lock);
- bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->orig);
- spin_unlock_bh(&bat_priv->bw_list_lock);
- if (!bw_vars) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: received an ack not related to an open connection\n");
goto out;
- }
- /* slide and send fresh packets */
- spin_lock_bh(&bw_vars->bw_vars_lock);
- seqno = ntohs(icmp_packet->seqno);
- window_first_16 = (uint16_t) bw_vars->window_first;
- window_end = window_first_16 + BATADV_BW_WINDOW_SIZE;
- if (!batadv_seq_after(window_first_16, seqno) &&
batadv_seq_before(seqno, bw_vars->next_to_send)) {
bw_vars->window_first += (uint16_t) (seqno + 1 -
window_first_16);
- } else if (!batadv_bw_is_error(bw_vars->status)) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: received unespected ack\n");
- }
- spin_unlock_bh(&bw_vars->bw_vars_lock);
- batadv_bw_multiple_send(bat_priv, bw_vars);
+out:
- return;
+}
+static void batadv_bw_receiver_worker(struct work_struct *work) +{
- struct delayed_work *delayed_work;
- struct batadv_bw_vars *bw_vars;
- struct batadv_priv *bat_priv;
- delayed_work = container_of(work, struct delayed_work, work);
- bw_vars = container_of(delayed_work, struct batadv_bw_vars, bw_work);
- bat_priv = bw_vars->bat_priv;
- if (batadv_has_timed_out(bw_vars->last_sent_time,
BATADV_BW_RECV_TIMEOUT)) {
if (bw_vars->status != BATADV_BW_COMPLETE) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: more than %dms of inactivity: test will be aborted!\n",
BATADV_BW_RECV_TIMEOUT);
} else {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: succesfully completed test with node %02x:%02x:%02x:%02x:%02x:%02x\n",
bw_vars->other_end[0], bw_vars->other_end[1],
bw_vars->other_end[2], bw_vars->other_end[3],
bw_vars->other_end[4],
bw_vars->other_end[5]);
}
batadv_bw_vars_free(bw_vars);
- } else {
batadv_bw_queue_receiver_worker(bw_vars);
- }
+}
+static void batadv_bw_batctl_notify(uint8_t status, uint8_t uid,
unsigned long int start_time)
+{
- struct batadv_bw_result result;
- result.icmp_packet.uid = uid;
- if (!batadv_bw_is_error(status)) {
result.return_value = BATADV_BW_COMPLETE;
result.test_time = ((long)jiffies -
(long)start_time);
result.total_bytes = BATADV_BW_TOTAL_TO_SEND *
BATADV_BW_PACKET_LEN;
- } else {
result.return_value = status;
- }
- batadv_socket_receive_packet((struct batadv_icmp_packet_rr *)&result,
sizeof(result));
+}
This looks much better now. :)
+static void batadv_bw_sender_worker(struct work_struct *work) +{
- struct delayed_work *delayed_work;
- struct batadv_bw_vars *bw_vars;
- struct batadv_priv *bat_priv;
- delayed_work = container_of(work, struct delayed_work, work);
- bw_vars = container_of(delayed_work, struct batadv_bw_vars, bw_work);
- bat_priv = bw_vars->bat_priv;
- /* if timedout, resend whole window */
- if (bw_vars->status == BATADV_BW_SENDER &&
batadv_has_timed_out(bw_vars->last_sent_time, BATADV_BW_TIMEOUT)) {
/* increase resend counters */
if (bw_vars->window_first == bw_vars->last_resent_window) {
bw_vars->retry_number += 1;
if (bw_vars->retry_number > BATADV_BW_MAX_RETRY) {
bw_vars->window_first = bw_vars->total_to_send;
bw_vars->status = BATADV_BW_RESEND_LIMIT;
}
} else {
bw_vars->retry_number = 0;
}
bw_vars->last_resent_window = bw_vars->window_first;
bw_vars->next_to_send = bw_vars->window_first;
batadv_bw_multiple_send(bat_priv, bw_vars);
- }
- /* if not finished, re-enqueue worker */
- if (bw_vars->window_first < bw_vars->total_to_send) {
if (batadv_bw_queue_sender_worker(bw_vars) == 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_bw_start work already enqueued\n");
}
- } else {
batadv_bw_batctl_notify(bw_vars->status,
bw_vars->socket_client->index,
bw_vars->start_time);
batadv_bw_vars_free(bw_vars);
- }
+}
+static int batadv_bw_queue_sender_worker(struct batadv_bw_vars *bw_vars) +{
- int ret;
- INIT_DELAYED_WORK(&bw_vars->bw_work, batadv_bw_sender_worker);
- ret = queue_delayed_work(batadv_event_workqueue, &bw_vars->bw_work,
msecs_to_jiffies(BATADV_BW_WORKER_TIMEOUT));
- return ret;
+}
+static int batadv_bw_queue_receiver_worker(struct batadv_bw_vars *bw_vars) +{
- int ret;
- INIT_DELAYED_WORK(&bw_vars->bw_work, batadv_bw_receiver_worker);
- ret = queue_delayed_work(batadv_event_workqueue, &bw_vars->bw_work,
msecs_to_jiffies(BATADV_BW_RECV_TIMEOUT));
- return ret;
+}
+void batadv_bw_stop(struct batadv_priv *bat_priv,
uint8_t dst[], uint8_t error_status)
+{
- struct batadv_bw_vars *bw_vars;
- spin_lock_bh(&bat_priv->bw_list_lock);
- bw_vars = batadv_bw_list_find(bat_priv, dst);
- if (!bw_vars) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: trying to interrupt an already over connection\n");
return;
- }
- spin_unlock_bh(&bat_priv->bw_list_lock);
- spin_lock_bh(&bw_vars->bw_vars_lock);
- bw_vars->window_first = bw_vars->total_to_send;
- bw_vars->status = error_status;
- spin_unlock_bh(&bw_vars->bw_vars_lock);
+}
+void batadv_bw_start(struct batadv_socket_client *socket_client,
struct batadv_icmp_packet *icmp_packet)
+{
- struct batadv_priv *bat_priv = socket_client->bat_priv;
- struct batadv_bw_vars *bw_vars;
- /* find bw_vars */
- spin_lock_bh(&bat_priv->bw_list_lock);
- bw_vars = batadv_bw_list_find(bat_priv, &icmp_packet->dst);
- if (bw_vars) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: test to or from the same node already ongoing, aborting\n");
batadv_bw_batctl_error_notify(BATADV_BW_ALREADY_ONGOING,
socket_client->index);
goto out;
- }
- bw_vars = kmalloc(sizeof(*bw_vars), GFP_ATOMIC);
- if (!bw_vars) {
spin_unlock_bh(&bat_priv->bw_list_lock);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_bw_start cannot allocate list elements\n");
batadv_bw_batctl_error_notify(BATADV_BW_MEMORY_ERROR,
socket_client->index);
goto out;
- }
- /* initialize bw_vars */
- memcpy(&bw_vars->other_end, &icmp_packet->dst, ETH_ALEN);
- bw_vars->total_to_send = BATADV_BW_TOTAL_TO_SEND + BATADV_BW_FIRST_SEQ;
- bw_vars->window_first = BATADV_BW_FIRST_SEQ;
- bw_vars->next_to_send = BATADV_BW_FIRST_SEQ;
- bw_vars->status = BATADV_BW_SENDER;
- bw_vars->last_resent_window = 0;
- bw_vars->bat_priv = bat_priv;
- bw_vars->socket_client = socket_client;
- bw_vars->last_sent_time = jiffies;
- bw_vars->start_time = jiffies;
- atomic_set(&bw_vars->sending, 0);
- spin_lock_init(&bw_vars->bw_vars_lock);
- list_add(&bw_vars->list, &bat_priv->bw_list);
- spin_unlock_bh(&bat_priv->bw_list_lock);
- /* start worker */
- if (batadv_bw_queue_sender_worker(bw_vars) == 0) {
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Meter: batadv_bw_start work already enqueued\n");
- }
- batadv_bw_multiple_send(bat_priv, bw_vars);
+out:
- return;
+} diff --git a/bw_meter.h b/bw_meter.h new file mode 100644 index 0000000..9999f7e --- /dev/null +++ b/bw_meter.h @@ -0,0 +1,7 @@ +void batadv_bw_start(struct batadv_socket_client *socket_client,
struct batadv_icmp_packet *icmp_packet_bw);
+void batadv_bw_stop(struct batadv_priv *bat_priv, uint8_t dst[],
uint8_t return_value);
+void batadv_bw_meter_received(struct batadv_priv *bat_priv,
struct sk_buff *skb);
+void batadv_bw_ack_received(struct batadv_priv *bat_priv, struct sk_buff *skb); diff --git a/icmp_socket.c b/icmp_socket.c index bde3cf7..a48e9b5 100644 --- a/icmp_socket.c +++ b/icmp_socket.c @@ -20,11 +20,13 @@ #include "main.h" #include <linux/debugfs.h> #include <linux/slab.h>
obsolete newline?
#include "icmp_socket.h" #include "send.h" #include "hash.h" #include "originator.h" #include "hard-interface.h" +#include "bw_meter.h"
static struct batadv_socket_client *batadv_socket_client_hash[256];
@@ -152,6 +154,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, struct batadv_hard_iface *primary_if = NULL; struct sk_buff *skb; struct batadv_icmp_packet_rr *icmp_packet;
struct batadv_icmp_packet icmp_packet_bw;
struct batadv_orig_node *orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL;
@@ -170,6 +173,21 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, goto out; }
- if (copy_from_user(&icmp_packet_bw, buff, sizeof(icmp_packet_bw))) {
len = -EFAULT;
goto out;
- }
- if (icmp_packet_bw.msg_type == BATADV_BW_START) {
batadv_bw_start(socket_client, &icmp_packet_bw);
goto out;
- }
- if (icmp_packet_bw.msg_type == BATADV_BW_STOP) {
batadv_bw_stop(bat_priv, icmp_packet_bw.dst, BATADV_BW_SIGINT);
goto out;
- }
- if (len >= sizeof(struct batadv_icmp_packet_rr)) packet_len = sizeof(struct batadv_icmp_packet_rr);
diff --git a/main.c b/main.c index b4aa470..23a49e3 100644 --- a/main.c +++ b/main.c @@ -101,6 +101,7 @@ int batadv_mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->gw.list_lock); spin_lock_init(&bat_priv->vis.hash_lock); spin_lock_init(&bat_priv->vis.list_lock);
spin_lock_init(&bat_priv->bw_list_lock);
INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list);
@@ -108,6 +109,7 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_LIST_HEAD(&bat_priv->tt.changes_list); INIT_LIST_HEAD(&bat_priv->tt.req_list); INIT_LIST_HEAD(&bat_priv->tt.roam_list);
INIT_LIST_HEAD(&bat_priv->bw_list);
ret = batadv_originator_init(bat_priv); if (ret < 0)
diff --git a/packet.h b/packet.h index 2d23a14..568025a 100644 --- a/packet.h +++ b/packet.h @@ -21,6 +21,7 @@ #define _NET_BATMAN_ADV_PACKET_H_
#define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ +#define batadv_bw_is_error(n) ((uint8_t)n > 127 ? 1 : 0)
enum batadv_packettype { BATADV_IV_OGM = 0x01, @@ -44,12 +45,15 @@ enum batadv_iv_flags { };
/* ICMP message types */ -enum batadv_icmp_packettype {
- BATADV_ECHO_REPLY = 0,
+enum icmp_packettype {
as Sven already pointed out, please keep the batadv_ prefix
- BATADV_ECHO_REPLY = 0, BATADV_DESTINATION_UNREACHABLE = 3,
- BATADV_ECHO_REQUEST = 8,
- BATADV_TTL_EXCEEDED = 11,
- BATADV_PARAMETER_PROBLEM = 12,
- BATADV_ECHO_REQUEST = 8,
- BATADV_TTL_EXCEEDED = 11,
- BATADV_PARAMETER_PROBLEM = 12,
- BATADV_BW_START = 15,
- BATADV_BW_ACK = 16,
- BATADV_BW_STOP = 17,
};
/* vis defines */ @@ -155,6 +159,30 @@ struct batadv_icmp_packet_rr { uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; } __packed;
+enum batadv_bw_meter_status {
- BATADV_BW_RECEIVER = 1,
- BATADV_BW_SENDER = 2,
- BATADV_BW_COMPLETE = 3,
- BATADV_BW_LAST_WINDOW = 4,
- /* error status >= 128 */
- BATADV_BW_SIGINT = 128,
- BATADV_BW_DST_UNREACHABLE = 129,
- BATADV_BW_RESEND_LIMIT = 130,
- BATADV_BW_ALREADY_ONGOING = 131,
- BATADV_BW_MEMORY_ERROR = 132,
+};
+/* structure returned to batctl */ +/* icmp_packet_rr used to keep socket_client's index,
- as function batadv_socket_receive_packet expects it
- */
+struct batadv_bw_result {
- struct batadv_icmp_packet icmp_packet;
- uint32_t test_time;
- uint32_t total_bytes;
- uint8_t return_value;
+} __packed;
struct batadv_unicast_packet { struct batadv_header header; uint8_t ttvn; /* destination translation table version number */ diff --git a/routing.c b/routing.c index 939fc01..f82751d 100644 --- a/routing.c +++ b/routing.c @@ -28,6 +28,7 @@ #include "vis.h" #include "unicast.h" #include "bridge_loop_avoidance.h" +#include "bw_meter.h"
static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -290,6 +291,16 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
icmp_packet = (struct batadv_icmp_packet_rr *)skb->data;
- if (icmp_packet->msg_type == BATADV_BW_START) {
batadv_bw_meter_received(bat_priv, skb);
goto out;
- }
- if (icmp_packet->msg_type == BATADV_BW_ACK) {
batadv_bw_ack_received(bat_priv, skb);
goto out;
- }
- /* add data to device queue */ if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { batadv_socket_receive_packet(icmp_packet, icmp_len);
@@ -427,7 +438,9 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
/* add record route information if not full */ if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) &&
(icmp_packet->rr_cur < BATADV_RR_LEN)) {
(icmp_packet->rr_cur < BATADV_RR_LEN) &&
(icmp_packet->msg_type != BATADV_BW_START) &&
memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), ethhdr->h_dest, ETH_ALEN); icmp_packet->rr_cur++;(icmp_packet->msg_type != BATADV_BW_ACK)) {
diff --git a/types.h b/types.h index 2ed82ca..fc7720f 100644 --- a/types.h +++ b/types.h @@ -226,6 +226,27 @@ struct batadv_priv_vis { struct batadv_vis_info *my_info; };
+struct batadv_bw_vars {
- struct list_head list;
- struct delayed_work bw_work;
- struct batadv_priv *bat_priv;
- struct batadv_socket_client *socket_client;
- unsigned long *bw_bits; /* bitmap */
- unsigned long start_time;
- unsigned long last_sent_time;
- uint32_t total_to_send;
- uint32_t window_first;
- uint32_t last_resent_window;
- uint16_t next_to_send;
- uint16_t bit_first;
- uint8_t other_end[ETH_ALEN];
- uint8_t status; /* see bm_meter_status */
- uint8_t retry_number;
- uint8_t return_value;
- atomic_t sending; /* 1 if in multiple_send, 0 otherwise */
- spinlock_t bw_vars_lock; /* protects bw_vars */
+};
struct batadv_priv { atomic_t mesh_state; struct net_device_stats stats; @@ -251,12 +272,16 @@ struct batadv_priv { struct dentry *debug_dir; struct hlist_head forw_bat_list; struct hlist_head forw_bcast_list;
- struct list_head bw_list; struct batadv_hashtable *orig_hash; spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects */
- spinlock_t bw_list_lock; /* protects bw_list */ struct delayed_work orig_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops;
Why do you have these obsolete newlines here?
I think it would be useful to put the bw_meter variables into a private struct, like batadv_priv_bla for bridge loop avoidance.
#ifdef CONFIG_BATMAN_ADV_BLA struct batadv_priv_bla bla;
#endif
1.7.8.6
b.a.t.m.a.n@lists.open-mesh.org