@@ -351,10 +351,12 @@ struct batadv_icmp_tp_packet {
* enum batadv_icmp_tp_subtype - ICMP TP Meter packet subtypes
* @BATADV_TP_MSG: Msg from sender to receiver
* @BATADV_TP_ACK: acknowledgment from receiver to sender
+ * @BATADV_TP_MSG_LINK: Msg from sender to receiver used for link test (one-hop)
*/
enum batadv_icmp_tp_subtype {
BATADV_TP_MSG = 0,
BATADV_TP_ACK,
+ BATADV_TP_MSG_LINK,
};
#define BATADV_RR_LEN 16
@@ -378,7 +378,7 @@ batadv_netlink_tp_meter_start(struct sk_buff *skb, struct genl_info *info)
}
bat_priv = netdev_priv(soft_iface);
- batadv_tp_start(bat_priv, dst, test_length, &cookie,
+ batadv_tp_start(bat_priv, dst, NULL, test_length, &cookie,
BATADV_TP_USERSPACE);
ret = batadv_netlink_tp_meter_put(msg, cookie);
@@ -223,12 +223,14 @@ bool batadv_check_management_packet(struct sk_buff *skb,
/**
* batadv_recv_my_icmp_packet() - receive an icmp packet locally
* @bat_priv: the bat priv with all the soft interface information
+ * @recv_if: interface that the skb is received on
* @skb: icmp packet to process
*
* Return: NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP
* otherwise.
*/
static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *recv_if,
struct sk_buff *skb)
{
struct batadv_hard_iface *primary_if = NULL;
@@ -281,7 +283,7 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
if (!pskb_may_pull(skb, sizeof(struct batadv_icmp_tp_packet)))
goto out;
- batadv_tp_meter_recv(bat_priv, skb);
+ batadv_tp_meter_recv(bat_priv, recv_if, skb);
ret = NET_RX_SUCCESS;
/* skb was consumed */
skb = NULL;
@@ -418,7 +420,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
/* packet for me */
if (batadv_is_my_mac(bat_priv, icmph->dst))
- return batadv_recv_my_icmp_packet(bat_priv, skb);
+ return batadv_recv_my_icmp_packet(bat_priv, recv_if, skb);
/* TTL exceeded */
if (icmph->ttl < 2)
@@ -381,6 +381,9 @@ static void batadv_tp_vars_release(struct kref *ref)
}
spin_unlock_bh(&tp_vars->unacked_lock);
+ if (tp_vars->hardif_neigh)
+ batadv_hardif_neigh_put(tp_vars->hardif_neigh);
+
kfree_rcu(tp_vars, rcu);
}
@@ -579,9 +582,8 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars,
/**
* batadv_tp_send_msg() - send a single message
+ * @bat_priv: the bat priv with all the soft interface information
* @tp_vars: the private TP meter data for this session
- * @src: source mac address
- * @orig_node: the originator of the destination
* @seqno: sequence number of this packet
* @len: length of the entire packet
* @session: session identifier
@@ -594,26 +596,56 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars,
* not reachable, BATADV_TP_REASON_MEMORY_ERROR if the packet couldn't be
* allocated
*/
-static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src,
- struct batadv_orig_node *orig_node,
- u32 seqno, size_t len, const u8 *session,
- int uid, u32 timestamp)
+static int batadv_tp_send_msg(struct batadv_priv *bat_priv,
+ struct batadv_tp_vars *tp_vars, u32 seqno,
+ size_t len, const u8 *session, int uid,
+ u32 timestamp)
{
+ struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_orig_node *orig_node = NULL;
struct batadv_icmp_tp_packet *icmp;
struct sk_buff *skb;
- int r;
- u8 *data;
+ int r, ret = 0;
+ u8 *data, *src, *dst, subtype;
+ struct net_device *netdev;
size_t data_len;
- skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN);
- if (unlikely(!skb))
- return BATADV_TP_REASON_MEMORY_ERROR;
+ /* link test */
+ if (tp_vars->hardif_neigh) {
+ dst = tp_vars->hardif_neigh->addr;
+ src = tp_vars->hardif_neigh->if_incoming->net_dev->dev_addr;
+ subtype = BATADV_TP_MSG_LINK;
+ netdev = tp_vars->hardif_neigh->if_incoming->net_dev;
+ } else {
+ orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
+ if (unlikely(!orig_node)) {
+ ret = BATADV_TP_REASON_DST_UNREACHABLE;
+ goto out;
+ }
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (unlikely(!primary_if)) {
+ ret = BATADV_TP_REASON_DST_UNREACHABLE;
+ goto out;
+ }
+
+ dst = orig_node->orig;
+ src = primary_if->net_dev->dev_addr;
+ subtype = BATADV_TP_MSG;
+ netdev = NULL;
+ }
+
+ skb = netdev_alloc_skb_ip_align(netdev, len + ETH_HLEN);
+ if (unlikely(!skb)) {
+ ret = BATADV_TP_REASON_MEMORY_ERROR;
+ goto out;
+ }
skb_reserve(skb, ETH_HLEN);
icmp = skb_put(skb, sizeof(*icmp));
/* fill the icmp header */
- ether_addr_copy(icmp->dst, orig_node->orig);
+ ether_addr_copy(icmp->dst, dst);
ether_addr_copy(icmp->orig, src);
icmp->version = BATADV_COMPAT_VERSION;
icmp->packet_type = BATADV_ICMP;
@@ -621,7 +653,7 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src,
icmp->msg_type = BATADV_TP;
icmp->uid = uid;
- icmp->subtype = BATADV_TP_MSG;
+ icmp->subtype = subtype;
memcpy(icmp->session, session, sizeof(icmp->session));
icmp->seqno = htonl(seqno);
icmp->timestamp = htonl(timestamp);
@@ -630,11 +662,23 @@ static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src,
data = skb_put(skb, data_len);
batadv_tp_fill_prerandom(tp_vars, data, data_len);
- r = batadv_send_skb_to_orig(skb, orig_node, NULL);
- if (r == NET_XMIT_SUCCESS)
- return 0;
+ if (tp_vars->hardif_neigh)
+ r = batadv_send_skb_packet(skb,
+ tp_vars->hardif_neigh->if_incoming,
+ dst);
+ else
+ r = batadv_send_skb_to_orig(skb, orig_node, NULL);
- return BATADV_TP_REASON_CANT_SEND;
+ if (unlikely(r != NET_XMIT_SUCCESS))
+ ret = BATADV_TP_REASON_CANT_SEND;
+
+out:
+ if (likely(primary_if))
+ batadv_hardif_put(primary_if);
+ if (likely(orig_node))
+ batadv_orig_node_put(orig_node);
+
+ return ret;
}
/**
@@ -653,7 +697,6 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
struct batadv_tp_vars *tp_vars;
size_t packet_len, mss;
u32 rtt, recv_ack, cwnd;
- unsigned char *dev_addr;
packet_len = BATADV_TP_PLEN;
mss = BATADV_TP_PLEN;
@@ -675,13 +718,11 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
(u32)atomic_read(&tp_vars->last_acked)))
goto out;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (unlikely(!primary_if))
- goto out;
-
- orig_node = batadv_orig_hash_find(bat_priv, icmp->orig);
- if (unlikely(!orig_node))
- goto out;
+ if (!tp_vars->hardif_neigh) {
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (unlikely(!primary_if))
+ goto out;
+ }
/* update RTO with the new sampled RTT, if any */
rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp);
@@ -703,8 +744,11 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
goto out;
/* if this is the third duplicate ACK do Fast Retransmit */
- batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
- orig_node, recv_ack, packet_len,
+
+ /* if we have a hardif_neigh, it means that this is a LINK test,
+ * therefore use the according function
+ */
+ batadv_tp_send_msg(bat_priv, tp_vars, recv_ack, packet_len,
icmp->session, icmp->uid,
jiffies_to_msecs(jiffies));
@@ -741,9 +785,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
* immediately as specified by NewReno (see
* Section 3.2 of RFC6582 for details)
*/
- dev_addr = primary_if->net_dev->dev_addr;
- batadv_tp_send_msg(tp_vars, dev_addr,
- orig_node, recv_ack,
+ batadv_tp_send_msg(bat_priv, tp_vars, recv_ack,
packet_len, icmp->session,
icmp->uid,
jiffies_to_msecs(jiffies));
@@ -827,8 +869,6 @@ static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen)
*/
static void batadv_tp_send(struct work_struct *work)
{
- struct batadv_hard_iface *primary_if = NULL;
- struct batadv_orig_node *orig_node = NULL;
struct batadv_tp_vars *tp_vars;
size_t payload_len, packet_len;
struct batadv_priv *bat_priv;
@@ -843,20 +883,6 @@ static void batadv_tp_send(struct work_struct *work)
goto out;
}
- orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
- if (unlikely(!orig_node)) {
- err = BATADV_TP_REASON_DST_UNREACHABLE;
- tp_vars->reason = err;
- goto out;
- }
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (unlikely(!primary_if)) {
- err = BATADV_TP_REASON_DST_UNREACHABLE;
- tp_vars->reason = err;
- goto out;
- }
-
/* assume that all the hard_interfaces have a correctly
* configured MTU, so use the soft_iface MTU as MSS.
* This might not be true and in that case the fragmentation
@@ -883,10 +909,9 @@ static void batadv_tp_send(struct work_struct *work)
*/
packet_len = payload_len + sizeof(struct batadv_unicast_packet);
- err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
- orig_node, tp_vars->last_sent,
- packet_len,
- tp_vars->session, tp_vars->icmp_uid,
+ err = batadv_tp_send_msg(bat_priv, tp_vars, tp_vars->last_sent,
+ packet_len, tp_vars->session,
+ tp_vars->icmp_uid,
jiffies_to_msecs(jiffies));
/* something went wrong during the preparation/transmission */
@@ -908,11 +933,6 @@ static void batadv_tp_send(struct work_struct *work)
}
out:
- if (likely(primary_if))
- batadv_hardif_put(primary_if);
- if (likely(orig_node))
- batadv_orig_node_put(orig_node);
-
batadv_tp_sender_end(bat_priv, tp_vars);
batadv_tp_sender_cleanup(bat_priv, tp_vars);
@@ -934,23 +954,27 @@ static void batadv_tp_start_work(struct batadv_tp_vars *tp_vars)
* batadv_tp_start() - start a new tp meter session
* @bat_priv: the bat priv with all the soft interface information
* @dst: the receiver MAC address
+ * @neigh: neighbour towars which we have to run the test (one-hop test)
* @test_length: test length in milliseconds
* @cookie: session cookie
* @caller: caller of tp meter session (user space or ELP)
*/
void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+ struct batadv_hardif_neigh_node *neigh,
u32 test_length, u32 *cookie,
enum batadv_tp_meter_caller caller)
{
struct batadv_tp_vars *tp_vars;
u8 session_id[2];
u8 icmp_uid;
- u32 session_cookie;
+ u32 session_cookie = 0;
get_random_bytes(session_id, sizeof(session_id));
get_random_bytes(&icmp_uid, 1);
- session_cookie = batadv_tp_session_cookie(session_id, icmp_uid);
- *cookie = session_cookie;
+ if (cookie) {
+ session_cookie = batadv_tp_session_cookie(session_id, icmp_uid);
+ *cookie = session_cookie;
+ }
if (!atomic_add_unless(&bat_priv->tp_num, 1, BATADV_TP_MAX_NUM_QUEUE)) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
@@ -971,6 +995,10 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
/* initialize tp_vars */
ether_addr_copy(tp_vars->other_end, dst);
+ if (neigh) {
+ kref_get(&neigh->refcount);
+ tp_vars->hardif_neigh = neigh;
+ }
kref_init(&tp_vars->refcount);
tp_vars->role = BATADV_TP_SENDER;
tp_vars->caller = caller;
@@ -1132,7 +1160,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
/**
* batadv_tp_send_ack() - send an ACK packet
* @bat_priv: the bat priv with all the soft interface information
- * @dst: the mac address of the destination originator
+ * @tp_vars: the private data of the current TP meter session
* @seq: the sequence number to ACK
* @timestamp: the timestamp to echo back in the ACK
* @session: session identifier
@@ -1141,29 +1169,42 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
* Return: 0 on success, a positive integer representing the reason of the
* failure otherwise
*/
-static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
+static int batadv_tp_send_ack(struct batadv_priv *bat_priv,
+ struct batadv_tp_vars *tp_vars,
u32 seq, __be32 timestamp, const u8 *session,
int socket_index)
{
struct batadv_hard_iface *primary_if = NULL;
- struct batadv_orig_node *orig_node;
+ struct batadv_orig_node *orig_node = NULL;
struct batadv_icmp_tp_packet *icmp;
+ struct net_device *netdev = NULL;
struct sk_buff *skb;
+ u8 *src, *dst;
int r, ret;
- orig_node = batadv_orig_hash_find(bat_priv, dst);
- if (unlikely(!orig_node)) {
- ret = BATADV_TP_REASON_DST_UNREACHABLE;
- goto out;
- }
+ if (tp_vars->hardif_neigh) {
+ dst = tp_vars->hardif_neigh->addr;
+ src = tp_vars->hardif_neigh->if_incoming->net_dev->dev_addr;
+ netdev = tp_vars->hardif_neigh->if_incoming->net_dev;
+ } else {
+ orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
+ if (unlikely(!orig_node)) {
+ ret = BATADV_TP_REASON_DST_UNREACHABLE;
+ goto out;
+ }
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (unlikely(!primary_if)) {
- ret = BATADV_TP_REASON_DST_UNREACHABLE;
- goto out;
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (unlikely(!primary_if)) {
+ ret = BATADV_TP_REASON_DST_UNREACHABLE;
+ goto out;
+ }
+
+ dst = orig_node->orig;
+ src = primary_if->net_dev->dev_addr;
+ netdev = NULL;
}
- skb = netdev_alloc_skb_ip_align(NULL, sizeof(*icmp) + ETH_HLEN);
+ skb = netdev_alloc_skb_ip_align(netdev, sizeof(*icmp) + ETH_HLEN);
if (unlikely(!skb)) {
ret = BATADV_TP_REASON_MEMORY_ERROR;
goto out;
@@ -1175,8 +1216,8 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
icmp->version = BATADV_COMPAT_VERSION;
icmp->ttl = BATADV_TTL;
icmp->msg_type = BATADV_TP;
- ether_addr_copy(icmp->dst, orig_node->orig);
- ether_addr_copy(icmp->orig, primary_if->net_dev->dev_addr);
+ ether_addr_copy(icmp->dst, dst);
+ ether_addr_copy(icmp->orig, src);
icmp->uid = socket_index;
icmp->subtype = BATADV_TP_ACK;
@@ -1185,7 +1226,13 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
icmp->timestamp = timestamp;
/* send the ack */
- r = batadv_send_skb_to_orig(skb, orig_node, NULL);
+ if (tp_vars->hardif_neigh)
+ r = batadv_send_skb_packet(skb,
+ tp_vars->hardif_neigh->if_incoming,
+ dst);
+ else
+ r = batadv_send_skb_to_orig(skb, orig_node, NULL);
+
if (unlikely(r < 0) || r == NET_XMIT_DROP) {
ret = BATADV_TP_REASON_DST_UNREACHABLE;
goto out;
@@ -1313,14 +1360,17 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
/**
* batadv_tp_init_recv() - return matching or create new receiver tp_vars
* @bat_priv: the bat priv with all the soft interface information
+ * @recv_if: interface that the skb is received on
* @icmp: received icmp tp msg
*
* Return: corresponding tp_vars or NULL on errors
*/
static struct batadv_tp_vars *
batadv_tp_init_recv(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *recv_if,
const struct batadv_icmp_tp_packet *icmp)
{
+ struct batadv_hardif_neigh_node *neigh = NULL;
struct batadv_tp_vars *tp_vars;
spin_lock_bh(&bat_priv->tp_list_lock);
@@ -1335,15 +1385,30 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
goto out_unlock;
}
+ /* the sender is starting a LINK test, therefore we have retrieve its
+ * corresponding hardif_neigh_node that we'll use later to send ACKs
+ * back
+ */
+ if (icmp->subtype == BATADV_TP_MSG_LINK) {
+ neigh = batadv_hardif_neigh_get(recv_if, icmp->orig);
+ if (!neigh) {
+ batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
+ "Meter: %s() can't retrieve sender neigh object for %pM\n",
+ __func__, icmp->orig);
+ goto out_unlock;
+ }
+ }
+
tp_vars = kmalloc(sizeof(*tp_vars), GFP_ATOMIC);
if (!tp_vars)
- goto out_unlock;
+ goto err_neigh_put;
ether_addr_copy(tp_vars->other_end, icmp->orig);
tp_vars->role = BATADV_TP_RECEIVER;
memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session));
tp_vars->last_recv = BATADV_TP_FIRST_SEQ;
tp_vars->bat_priv = bat_priv;
+ tp_vars->hardif_neigh = neigh;
kref_init(&tp_vars->refcount);
spin_lock_init(&tp_vars->unacked_lock);
@@ -1356,7 +1421,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
batadv_tp_reset_receiver_timer(tp_vars);
+ goto out_unlock;
+err_neigh_put:
+ batadv_hardif_neigh_put(neigh);
out_unlock:
spin_unlock_bh(&bat_priv->tp_list_lock);
@@ -1366,11 +1434,13 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
/**
* batadv_tp_recv_msg() - process a single data message
* @bat_priv: the bat priv with all the soft interface information
+ * @recv_if: interface that the skb is received on
* @skb: the buffer containing the received packet
*
* Process a received TP MSG packet
*/
static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *recv_if,
const struct sk_buff *skb)
{
const struct batadv_icmp_tp_packet *icmp;
@@ -1385,7 +1455,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* first packet is lost, the tp meter does not work anymore!
*/
if (seqno == BATADV_TP_FIRST_SEQ) {
- tp_vars = batadv_tp_init_recv(bat_priv, icmp);
+ tp_vars = batadv_tp_init_recv(bat_priv, recv_if, icmp);
if (!tp_vars) {
batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
"Meter: seqno != BATADV_TP_FIRST_SEQ cannot initiate connection\n");
@@ -1441,7 +1511,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* is going to be sent is a duplicate (the sender will count them and
* possibly enter Fast Retransmit as soon as it has reached 3)
*/
- batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
+ batadv_tp_send_ack(bat_priv, tp_vars, tp_vars->last_recv,
icmp->timestamp, icmp->session, icmp->uid);
out:
if (likely(tp_vars))
@@ -1451,9 +1521,12 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
/**
* batadv_tp_meter_recv() - main TP Meter receiving function
* @bat_priv: the bat priv with all the soft interface information
+ * @recv_if: interface that the skb is received on
* @skb: the buffer containing the received packet
*/
-void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
+void batadv_tp_meter_recv(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *recv_if,
+ struct sk_buff *skb)
{
struct batadv_icmp_tp_packet *icmp;
@@ -1461,7 +1534,8 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
switch (icmp->subtype) {
case BATADV_TP_MSG:
- batadv_tp_recv_msg(bat_priv, skb);
+ case BATADV_TP_MSG_LINK:
+ batadv_tp_recv_msg(bat_priv, recv_if, skb);
break;
case BATADV_TP_ACK:
batadv_tp_recv_ack(bat_priv, skb);
@@ -28,10 +28,13 @@ struct sk_buff;
int batadv_tp_meter_init(void);
void batadv_tp_meter_destroy(void);
void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
+ struct batadv_hardif_neigh_node *neigh,
u32 test_length, u32 *cookie,
enum batadv_tp_meter_caller caller);
void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
u8 return_value);
-void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
+void batadv_tp_meter_recv(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *recv_if,
+ struct sk_buff *skb);
#endif /* _NET_BATMAN_ADV_TP_METER_H_ */
@@ -1455,6 +1455,9 @@ struct batadv_tp_vars {
/** @rcu: struct used for freeing in an RCU-safe manner */
struct rcu_head rcu;
+
+ /** @hardif_neigh: in case of LINK test, represents the other-end */
+ struct batadv_hardif_neigh_node *hardif_neigh;
};
/**