@@ -1491,7 +1491,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
}
if (if_outgoing == BATADV_IF_DEFAULT)
- batadv_tvlv_ogm_receive(bat_priv, ogm_packet, orig_node);
+ batadv_tvlv_ogm_receive(bat_priv, skb, orig_node);
/* if sender is a direct neighbor the sender mac equals
* originator mac
@@ -1832,6 +1832,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
ogm_offset = 0;
ogm_packet = (struct batadv_ogm_packet *)skb->data;
+ WARN_ON(skb_network_offset(skb) != 0);
+
/* unpack the aggregated packets and process them one by one */
while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb),
ogm_packet->tvlv_len)) {
@@ -1842,8 +1844,12 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
packet_pos = skb->data + ogm_offset;
ogm_packet = (struct batadv_ogm_packet *)packet_pos;
+
+ skb_set_network_header(skb, ogm_offset);
}
+ skb_reset_network_header(skb);
+
kfree_skb(skb);
return NET_RX_SUCCESS;
}
@@ -594,7 +594,7 @@ out:
/**
* batadv_v_ogm_process_per_outif - process a batman v OGM for an outgoing if
* @bat_priv: the bat priv with all the soft interface information
- * @ethhdr: the Ethernet header of the OGM2
+ * @skb: the skb containing the OGM2
* @ogm2: OGM2 structure
* @orig_node: Originator structure for which the OGM has been received
* @neigh_node: the neigh_node through with the OGM has been received
@@ -603,13 +603,16 @@ out:
*/
static void
batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv,
- const struct ethhdr *ethhdr,
+ const struct sk_buff *skb,
const struct batadv_ogm2_packet *ogm2,
struct batadv_orig_node *orig_node,
struct batadv_neigh_node *neigh_node,
struct batadv_hard_iface *if_incoming,
struct batadv_hard_iface *if_outgoing)
{
+ void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node);
+ const struct ethhdr *ethhdr = eth_hdr(skb);
+ unsigned int tvlv_offset = sizeof(*ogm2);
int seqno_age;
bool forward;
@@ -623,11 +626,15 @@ batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv,
return;
/* only unknown & newer OGMs contain TVLVs we are interested in */
- if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT))
+ if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT)) {
+ batadv_tvlv_containers_process2(bat_priv, skb, BATADV_OGM2,
+ tvlv_offset,
+ ntohs(ogm2->tvlv_len), ctx);
batadv_tvlv_containers_process(bat_priv, true, orig_node,
NULL, NULL,
(unsigned char *)(ogm2 + 1),
ntohs(ogm2->tvlv_len));
+ }
/* if the metric update went through, update routes if needed */
forward = batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node,
@@ -728,7 +735,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
path_throughput = min_t(u32, link_throughput, ogm_throughput);
ogm_packet->throughput = htonl(path_throughput);
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, orig_node,
+ batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet, orig_node,
neigh_node, if_incoming,
BATADV_IF_DEFAULT);
@@ -772,7 +779,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
continue;
}
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet,
+ batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet,
orig_node, neigh_node,
if_incoming, hard_iface);
@@ -734,7 +734,7 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
u8 flags,
void *tvlv_value, u16 tvlv_value_len)
{
- if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+ if (flags & BATADV_TVLV_HANDLER_CIFNOTFND)
clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
else
set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities);
@@ -776,7 +776,7 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1,
NULL, BATADV_TVLV_DAT, 1,
- BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ BATADV_TVLV_HANDLER_CIFNOTFND);
batadv_dat_tvlv_container_update(bat_priv);
return 0;
}
@@ -207,25 +207,26 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
}
/**
- * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container
+ * batadv_gw_tvlv_ogm_handler - process incoming gateway tvlv container
* @bat_priv: the bat priv with all the soft interface information
- * @orig: the orig_node of the ogm
- * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
* @tvlv_value: tvlv buffer containing the gateway data
* @tvlv_value_len: tvlv buffer length
+ * @ctx: handler specific context information (here: orig_node)
+ *
+ * Return: Always NET_RX_SUCCESS.
*/
-static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig,
- u8 flags,
- void *tvlv_value, u16 tvlv_value_len)
+static int batadv_gw_tvlv_ogm_handler(struct batadv_priv *bat_priv,
+ void *tvlv_value,
+ u16 tvlv_value_len,
+ void *ctx)
{
+ struct batadv_orig_node *orig_node = batadv_tvlv_ogm_unpack_ctx(ctx);
struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
- /* only fetch the tvlv value if the handler wasn't called via the
- * CIFNOTFND flag and if there is data to fetch
+ /* might either be too small due to a broken packet,
+ * or zero because no matching TVLV was found in the provided OGM
*/
- if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) ||
- (tvlv_value_len < sizeof(gateway))) {
+ if (tvlv_value_len < sizeof(gateway)) {
gateway.bandwidth_down = 0;
gateway.bandwidth_up = 0;
} else {
@@ -239,12 +240,14 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
}
}
- batadv_gw_node_update(bat_priv, orig, &gateway);
+ batadv_gw_node_update(bat_priv, orig_node, &gateway);
/* restart gateway selection */
if ((gateway.bandwidth_down != 0) &&
(atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT))
- batadv_gw_check_election(bat_priv, orig);
+ batadv_gw_check_election(bat_priv, orig_node);
+
+ return NET_RX_SUCCESS;
}
/**
@@ -253,9 +256,12 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
*/
void batadv_gw_init(struct batadv_priv *bat_priv)
{
- batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1,
- NULL, BATADV_TVLV_GW, 1,
- BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler,
+ BATADV_IV_OGM, BATADV_TVLV_GW, 1,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
+ batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler,
+ BATADV_OGM2, BATADV_TVLV_GW, 1,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
}
/**
@@ -265,5 +271,9 @@ void batadv_gw_init(struct batadv_priv *bat_priv)
void batadv_gw_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1);
- batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1);
+
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, BATADV_TVLV_GW,
+ 1);
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, BATADV_TVLV_GW,
+ 1);
}
@@ -1070,23 +1070,23 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv,
/**
* batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container
* @bat_priv: the bat priv with all the soft interface information
- * @orig: the orig_node of the ogm
- * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags)
* @tvlv_value: tvlv buffer containing the multicast data
* @tvlv_value_len: tvlv buffer length
+ * @ctx: handler specific context information (here: orig_node)
+ *
+ * Return: Always NET_RX_SUCCESS.
*/
-static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig,
- u8 flags,
- void *tvlv_value,
- u16 tvlv_value_len)
+static int batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
+ void *tvlv_value,
+ u16 tvlv_value_len,
+ void *ctx)
{
- bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ struct batadv_orig_node *orig = batadv_tvlv_ogm_unpack_ctx(ctx);
+ bool orig_mcast_enabled = !!tvlv_value;
u8 mcast_flags = BATADV_NO_FLAGS;
bool orig_initialized;
- if (orig_mcast_enabled && tvlv_value &&
- (tvlv_value_len >= sizeof(mcast_flags)))
+ if (orig_mcast_enabled && (tvlv_value_len >= sizeof(mcast_flags)))
mcast_flags = *(u8 *)tvlv_value;
spin_lock_bh(&orig->mcast_handler_lock);
@@ -1121,6 +1121,8 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
orig->mcast_flags = mcast_flags;
spin_unlock_bh(&orig->mcast_handler_lock);
+
+ return NET_RX_SUCCESS;
}
/**
@@ -1129,9 +1131,12 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
*/
void batadv_mcast_init(struct batadv_priv *bat_priv)
{
- batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler,
- NULL, BATADV_TVLV_MCAST, 2,
- BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler,
+ BATADV_IV_OGM, BATADV_TVLV_MCAST, 2,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
+ batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler,
+ BATADV_OGM2, BATADV_TVLV_MCAST, 2,
+ BATADV_TVLV_HANDLER_CIFNOTFND);
}
#ifdef CONFIG_BATMAN_ADV_DEBUGFS
@@ -1244,7 +1249,11 @@ int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset)
void batadv_mcast_free(struct batadv_priv *bat_priv)
{
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
- batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2);
+
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM,
+ BATADV_TVLV_MCAST, 2);
+ batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2,
+ BATADV_TVLV_MCAST, 2);
spin_lock_bh(&bat_priv->tt.commit_lock);
batadv_mcast_mla_tt_retract(bat_priv, NULL);
@@ -138,7 +138,7 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
u8 flags,
void *tvlv_value, u16 tvlv_value_len)
{
- if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND)
+ if (flags & BATADV_TVLV_HANDLER_CIFNOTFND)
clear_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities);
else
set_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities);
@@ -177,7 +177,7 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1,
NULL, BATADV_TVLV_NC, 1,
- BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+ BATADV_TVLV_HANDLER_CIFNOTFND);
batadv_nc_tvlv_container_update(bat_priv);
return 0;
@@ -68,23 +68,28 @@ static void batadv_tvlv_handler_put(struct batadv_tvlv_handler *tvlv_handler)
* batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list
* based on the provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
- * @type: tvlv handler type to look for
- * @version: tvlv handler version to look for
+ * @packet_type: packet type to look for
+ * @tvlv_type: tvlv handler type to look for
+ * @tvlv_version: tvlv handler version to look for
*
* Return: tvlv handler if found or NULL otherwise.
*/
static struct batadv_tvlv_handler *
-batadv_tvlv_handler_get(struct batadv_priv *bat_priv, u8 type, u8 version)
+batadv_tvlv_handler_get(struct batadv_priv *bat_priv, int packet_type,
+ u8 tvlv_type, u8 tvlv_version)
{
struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler_tmp,
&bat_priv->tvlv.handler_list, list) {
- if (tvlv_handler_tmp->type != type)
+ if (tvlv_handler_tmp->packet_type != packet_type)
continue;
- if (tvlv_handler_tmp->version != version)
+ if (tvlv_handler_tmp->tvlv_type != tvlv_type)
+ continue;
+
+ if (tvlv_handler_tmp->tvlv_version != tvlv_version)
continue;
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount))
@@ -387,7 +392,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler(bat_priv, orig_node,
BATADV_NO_FLAGS,
tvlv_value, tvlv_value_len);
- tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
+ tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED;
} else {
if (!src)
return NET_RX_SUCCESS;
@@ -407,6 +412,139 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
}
/**
+ * batadv_tvlv_call_handler2 - call the appropriate tvlv handler
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_type: packet type to look and call for
+ * @tvlv_type: tvlv handler type to look and call for
+ * @tvlv_version: tvlv handler version to look and call for
+ * @tvlv_value: tvlv content
+ * @tvlv_value_len: tvlv content length
+ * @ctx: handler specific context information
+ *
+ * Return: NET_RX_SUCCESS if handler was found and called successfully,
+ * NET_RX_DROP otherwise.
+ */
+static int batadv_tvlv_call_handler2(struct batadv_priv *bat_priv,
+ u8 packet_type, u8 tvlv_type,
+ u8 tvlv_version, void *tvlv_value,
+ u16 tvlv_value_len, void *ctx)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+ int ret;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type,
+ tvlv_version);
+ if (!tvlv_handler)
+ return NET_RX_DROP;
+
+ ret = tvlv_handler->handler(bat_priv, tvlv_value, tvlv_value_len, ctx);
+ tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED;
+
+ batadv_tvlv_handler_put(tvlv_handler);
+
+ return ret;
+}
+
+/**
+ * batadv_tvlv_call_unfound_handlers - call any handler not called yet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_type: the packet type to call handlers of unfound TVLVs for
+ * @ctx: handler specific context information
+ *
+ * For any registered TVLV handler with a CIFNOTFND flag: If a matching
+ * tvlv type was not found in a specific packet (type) then this calls the
+ * according handler with an empty (NULL) tvlv_value and tvlv_value_len of
+ * zero now.
+ */
+static void batadv_tvlv_call_unfound_handlers(struct batadv_priv *bat_priv,
+ int packet_type, void *ctx)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tvlv_handler,
+ &bat_priv->tvlv.handler_list, list) {
+ if (tvlv_handler->packet_type != packet_type)
+ continue;
+
+ if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) &&
+ !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED))
+ tvlv_handler->handler(bat_priv, NULL, 0, ctx);
+
+ tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED;
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * batadv_tvlv_containers_process2 - parse and process TVLV content of a packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the packet to parse and process TVLV data from
+ * @packet_type: the packet type to call handlers for
+ * @tvlv_offset: offset from the skb data pointer to the first tvlv header
+ * @tvlv_value_len: total tvlv content length (sum of all tvlv headers+values)
+ * @ctx: handler specific context information
+ *
+ * This function parses TVLV options of the given skb and tries to call the
+ * appropriate, registered handlers.
+ *
+ * In the end, all not yet called handlers (because no appropriate TVLV was
+ * found in the packet) which were registered with a CIFNOTFND flag are
+ * called with empty tvlv_value pointers.
+ *
+ * Return: NET_RX_SUCCESS if all TVLVs were known and parsed, as well as
+ * any TVLV handler called successfully. Returns NET_RX_DROP otherwise.
+ */
+int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv,
+ const struct sk_buff *skb, u8 packet_type,
+ unsigned int tvlv_offset,
+ u16 tvlv_value_len, void *ctx)
+{
+ struct batadv_tvlv_hdr *tvlv_hdr, tvlv_hdr_buff;
+ u8 *tvlv_value, tvlv_value_buff[128];
+ u16 tvlv_value_cont_len;
+ int ret = NET_RX_SUCCESS;
+
+ while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
+ tvlv_hdr = skb_header_pointer(skb, tvlv_offset,
+ sizeof(tvlv_hdr_buff),
+ &tvlv_hdr_buff);
+ if (!tvlv_hdr)
+ return NET_RX_DROP;
+
+ tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+ tvlv_offset += sizeof(*tvlv_hdr);
+ tvlv_value_len -= sizeof(*tvlv_hdr);
+
+ if (tvlv_value_cont_len > sizeof(tvlv_value_buff)) {
+ pr_warn_once("batman-adv: TVLVs greater than 128 bytes unsupported for now, ignoring\n");
+ goto skip_handler_call;
+ }
+
+ if (tvlv_value_cont_len > tvlv_value_len)
+ return NET_RX_DROP;
+
+ tvlv_value = skb_header_pointer(skb, tvlv_offset,
+ tvlv_value_cont_len,
+ tvlv_value_buff);
+ if (!tvlv_value)
+ return NET_RX_DROP;
+
+ ret |= batadv_tvlv_call_handler2(bat_priv, packet_type,
+ tvlv_hdr->type,
+ tvlv_hdr->version, tvlv_value,
+ tvlv_value_cont_len, ctx);
+skip_handler_call:
+ tvlv_offset += tvlv_value_cont_len;
+ tvlv_value_len -= tvlv_value_cont_len;
+ }
+
+ batadv_tvlv_call_unfound_handlers(bat_priv, packet_type, ctx);
+
+ return ret;
+}
+
+/**
* batadv_tvlv_containers_process - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
@@ -429,7 +567,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
struct batadv_tvlv_handler *tvlv_handler;
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
- u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
+ u8 cifnotfound = BATADV_TVLV_HANDLER_CIFNOTFND;
int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
@@ -441,7 +579,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
if (tvlv_value_cont_len > tvlv_value_len)
break;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv,
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1,
tvlv_hdr->type,
tvlv_hdr->version);
@@ -461,12 +599,15 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
rcu_read_lock();
hlist_for_each_entry_rcu(tvlv_handler,
&bat_priv->tvlv.handler_list, list) {
- if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
- !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
+ if (tvlv_handler->packet_type != -1)
+ continue;
+
+ if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) &&
+ !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED))
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
+ tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED;
}
rcu_read_unlock();
@@ -474,30 +615,128 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
}
/**
+ * batadv_tvlv_ogm_pack_ctx - pack context to be passed to OGM TVLV handlers
+ * @orig_node: An orig_node to pack (mandatory, may *not* be NULL!)
+ *
+ * This packs the context, here the orig_node the packet came from, so that
+ * it is later available to the to be called OGM TVLV handlers.
+ *
+ * Return: The wrapped context.
+ */
+void *batadv_tvlv_ogm_pack_ctx(struct batadv_orig_node *orig_node)
+{
+ WARN_ON(!orig_node);
+ return (void *)orig_node;
+}
+
+/**
+ * batadv_tvlv_ogm_unpack_ctx - unpack context received with a TVLV handler call
+ * @orig_node: An orig_node to pack
+ *
+ * This unpacks the context received within an OGM TVLV handler, here the
+ * orig_node the packet came from.
+ *
+ * Return: The orig_node the packet came from.
+ */
+struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx)
+{
+ return (struct batadv_orig_node *)ctx;
+}
+
+/**
* batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate
* handlers
* @bat_priv: the bat priv with all the soft interface information
- * @batadv_ogm_packet: ogm packet containing the tvlv containers
+ * @skb: ogm packet containing the tvlv containers
* @orig_node: orig node emitting the ogm packet
+ *
+ * Caller needs to ensure that the skb network header points to the appropriate
+ * OGM header.
*/
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
- struct batadv_ogm_packet *batadv_ogm_packet,
+ const struct sk_buff *skb,
struct batadv_orig_node *orig_node)
{
+ void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node);
+ struct batadv_ogm_packet *ogm_packet;
+ unsigned int tvlv_offset;
void *tvlv_value;
u16 tvlv_value_len;
- if (!batadv_ogm_packet)
+ ogm_packet = (struct batadv_ogm_packet *)skb_network_header(skb);
+ if (!ogm_packet)
return;
- tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len);
+ tvlv_value_len = ntohs(ogm_packet->tvlv_len);
if (!tvlv_value_len)
return;
- tvlv_value = batadv_ogm_packet + 1;
+ tvlv_offset = skb_network_offset(skb) + sizeof(*ogm_packet);
+ tvlv_value = ogm_packet + 1;
batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL,
tvlv_value, tvlv_value_len);
+ batadv_tvlv_containers_process2(bat_priv, skb, BATADV_IV_OGM,
+ tvlv_offset, tvlv_value_len, ctx);
+}
+
+/**
+ * batadv_tvlv_handler_register - register a tvlv handler
+ * @bat_priv: the bat priv with all the soft interface information
+ * @handler: TVLV handler callback function
+ * @packet_type: packet type to register this handler for
+ * @tvlv_type: tvlv handler type to be registered
+ * @tvlv_version: tvlv handler version to be registered
+ * @flags: flags to enable or disable TVLV API behavior
+ *
+ * Registers a handler for incoming packets of the provided packet type.
+ * When a packet of this type with a matching TVLV (both tvlv type and version)
+ * is received then the registered handler is called with the according TVLV
+ * value, length and packet context.
+ *
+ * If 'flags' is set to BATADV_TVLV_HANDLER_CIFNOTFND:
+ * Then the handler might be called with an empty tvlv_value (NULL) and
+ * tvlv_value_len (zero) if a packet with a matching packet type but no
+ * matching TVLV was received.
+ */
+void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv,
+ int (*handler)(struct batadv_priv *bat_priv,
+ void *tvlv_value,
+ u16 tvlv_value_len,
+ void *ctx),
+ u8 packet_type, u8 tvlv_type,
+ u8 tvlv_version, u8 flags)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type,
+ tvlv_version);
+ if (tvlv_handler) {
+ batadv_tvlv_handler_put(tvlv_handler);
+ return;
+ }
+
+ tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC);
+ if (!tvlv_handler)
+ return;
+
+ tvlv_handler->ogm_handler = NULL;
+ tvlv_handler->unicast_handler = NULL;
+ tvlv_handler->handler = handler;
+ tvlv_handler->packet_type = packet_type;
+ tvlv_handler->tvlv_type = tvlv_type;
+ tvlv_handler->tvlv_version = tvlv_version;
+ tvlv_handler->flags = flags;
+ kref_init(&tvlv_handler->refcount);
+ INIT_HLIST_NODE(&tvlv_handler->list);
+
+ spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+ kref_get(&tvlv_handler->refcount);
+ hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list);
+ spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+
+ /* don't return reference to new tvlv_handler */
+ batadv_tvlv_handler_put(tvlv_handler);
}
/**
@@ -510,8 +749,8 @@ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
* @uptr: unicast tvlv handler callback function. This function receives the
* source & destination of the unicast packet as well as the tvlv content
* to process.
- * @type: tvlv handler type to be registered
- * @version: tvlv handler version to be registered
+ * @tvlv_type: tvlv handler type to be registered
+ * @tvlv_version: tvlv handler version to be registered
* @flags: flags to enable or disable TVLV API behavior
*/
void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
@@ -524,11 +763,13 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
u8 *src, u8 *dst,
void *tvlv_value,
u16 tvlv_value_len),
- u8 type, u8 version, u8 flags)
+ u8 tvlv_type, u8 tvlv_version,
+ u8 flags)
{
struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type,
+ tvlv_version);
if (tvlv_handler) {
batadv_tvlv_handler_put(tvlv_handler);
return;
@@ -540,8 +781,10 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler = optr;
tvlv_handler->unicast_handler = uptr;
- tvlv_handler->type = type;
- tvlv_handler->version = version;
+ tvlv_handler->handler = NULL;
+ tvlv_handler->packet_type = -1;
+ tvlv_handler->tvlv_type = tvlv_type;
+ tvlv_handler->tvlv_version = tvlv_version;
tvlv_handler->flags = flags;
kref_init(&tvlv_handler->refcount);
INIT_HLIST_NODE(&tvlv_handler->list);
@@ -556,18 +799,47 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
}
/**
+ * batadv_tvlv_handler_unregister2 - unregister a tvlv handler
+ * @bat_priv: the bat priv with all the soft interface information
+ * @tvlv_type: packet type to unregistered for
+ * @tvlv_type: tvlv handler type to be unregistered
+ * @tvlv_version: tvlv handler version to be unregistered
+ *
+ * Unregisters a TVLV handler based on the provided packet type, tvlv type
+ * and version (all need to match).
+ */
+void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv,
+ u8 packet_type, u8 tvlv_type,
+ u8 tvlv_version)
+{
+ struct batadv_tvlv_handler *tvlv_handler;
+
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type,
+ tvlv_version);
+ if (!tvlv_handler)
+ return;
+
+ batadv_tvlv_handler_put(tvlv_handler);
+ spin_lock_bh(&bat_priv->tvlv.handler_list_lock);
+ hlist_del_rcu(&tvlv_handler->list);
+ spin_unlock_bh(&bat_priv->tvlv.handler_list_lock);
+ batadv_tvlv_handler_put(tvlv_handler);
+}
+
+/**
* batadv_tvlv_handler_unregister - unregister tvlv handler based on the
* provided type and version (both need to match)
* @bat_priv: the bat priv with all the soft interface information
- * @type: tvlv handler type to be unregistered
- * @version: tvlv handler version to be unregistered
+ * @tvlv_type: tvlv handler type to be unregistered
+ * @tvlv_version: tvlv handler version to be unregistered
*/
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
- u8 type, u8 version)
+ u8 tvlv_type, u8 tvlv_version)
{
struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version);
+ tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type,
+ tvlv_version);
if (!tvlv_handler)
return;
@@ -30,12 +30,21 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv,
unsigned char **packet_buff,
int *packet_buff_len, int packet_min_len);
+void *batadv_tvlv_ogm_pack_ctx(struct batadv_orig_node *orig_node);
+struct batadv_orig_node *batadv_tvlv_ogm_unpack_ctx(void *ctx);
void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv,
- struct batadv_ogm_packet *batadv_ogm_packet,
+ const struct sk_buff *skb,
struct batadv_orig_node *orig_node);
void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv,
u8 type, u8 version);
+void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv,
+ int (*handler)(struct batadv_priv *bat_priv,
+ void *tvlv_value,
+ u16 tvlv_value_len,
+ void *ctx),
+ u8 packet_type, u8 tvlv_type,
+ u8 tvlv_version, u8 flags);
void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
void (*optr)(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig,
@@ -46,14 +55,21 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
u8 *src, u8 *dst,
void *tvlv_value,
u16 tvlv_value_len),
- u8 type, u8 version, u8 flags);
+ u8 tvlv_type, u8 tvlv_version, u8 flags);
+void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv,
+ u8 packet_type, u8 tvlv_type,
+ u8 tvlv_version);
void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,
- u8 type, u8 version);
+ u8 tvlv_type, u8 tvlv_version);
int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
bool ogm_source,
struct batadv_orig_node *orig_node,
u8 *src, u8 *dst,
void *tvlv_buff, u16 tvlv_buff_len);
+int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv,
+ const struct sk_buff *skb, u8 packet_type,
+ unsigned int tvlv_offset,
+ u16 tvlv_value_len, void *ctx);
void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
u8 *dst, u8 type, u8 version,
void *tvlv_value, u16 tvlv_value_len);
@@ -1584,8 +1584,11 @@ struct batadv_tvlv_container {
* incoming OGM packets
* @unicast_handler: handler callback which is given the tvlv payload to process
* on incoming unicast tvlv packets
- * @type: tvlv type this handler feels responsible for
- * @version: tvlv version this handler feels responsible for
+ * @handler: handler callback which is given the tvlv payload to process on
+ * incoming packets of the given packet type
+ * @packet_type: packet type this handler feels responsible for
+ * @tvlv_type: tvlv type this handler feels responsible for
+ * @tvlv_version: tvlv version this handler feels responsible for
* @flags: tvlv handler flags
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
@@ -1598,8 +1601,11 @@ struct batadv_tvlv_handler {
int (*unicast_handler)(struct batadv_priv *bat_priv,
u8 *src, u8 *dst,
void *tvlv_value, u16 tvlv_value_len);
- u8 type;
- u8 version;
+ int (*handler)(struct batadv_priv *bat_priv, void *tvlv_value,
+ u16 tvlv_value_len, void *ctx);
+ int packet_type;
+ u8 tvlv_type;
+ u8 tvlv_version;
u8 flags;
struct kref refcount;
struct rcu_head rcu;
@@ -1607,15 +1613,15 @@ struct batadv_tvlv_handler {
/**
* enum batadv_tvlv_handler_flags - tvlv handler flags definitions
- * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call
+ * @BATADV_TVLV_HANDLER_CIFNOTFND: tvlv processing function will call
* this handler even if its type was not found (with no data)
- * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks
+ * @BATADV_TVLV_HANDLER_CALLED: internal tvlv handling flag - the API marks
* a handler as being called, so it won't be called if the
- * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
+ * BATADV_TVLV_HANDLER_CIFNOTFND flag was set
*/
enum batadv_tvlv_handler_flags {
- BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
- BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
+ BATADV_TVLV_HANDLER_CIFNOTFND = BIT(1),
+ BATADV_TVLV_HANDLER_CALLED = BIT(2),
};
/**