[v2,3/4] batman-adv: Introduce packet type independent TVLV handler API

Message ID 20161006064142.20003-4-linus.luessing@c0d3.blue (mailing list archive)
State Superseded, archived
Delegated to: Sven Eckelmann
Headers

Commit Message

Linus Lüssing Oct. 6, 2016, 6:41 a.m. UTC
  Instead of having one TVLV registration function for OGMs and one for
unicast packets, this patch adds a new, generic tvlv handler
registratiorn function, which simply uses a packet type as parameter.

For now, this patch only migrates the multicast and gateway tvlv
handlers to this new API, as these two have been tested and verified
to work by the author.

The benefits of this new API:

* A more unified TVLV handling
* Easier to add TVLV capabilities to any new, upcoming packet type
* Does not rely on linearized skb data

Further ToDos (for later patches):

* Insert handler hooks for unicast_tvlv packets too and
  migrate BATADV_TVLV_{DAT,NC,TT,ROAM} to new API after
  further testing.
* Remove old TVLV handler API

Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>

---

Changes in v2:
* add includes for linux/{bug,printk}.h
* update forward declaration to "struct sk_buff;" in tvlv.h
* kerneldoc:
  - change a "@tvlv_type" to "@packet_type",
  - fixed batadv_tvlv_ogm_unpack_ctx() parameter kerneldoc
(thanks Sven!)
---
 net/batman-adv/bat_iv_ogm.c            |   8 +-
 net/batman-adv/bat_v_ogm.c             |  17 +-
 net/batman-adv/distributed-arp-table.c |   4 +-
 net/batman-adv/gateway_common.c        |  44 +++--
 net/batman-adv/multicast.c             |  37 ++--
 net/batman-adv/network-coding.c        |   4 +-
 net/batman-adv/tvlv.c                  | 326 ++++++++++++++++++++++++++++++---
 net/batman-adv/tvlv.h                  |  24 ++-
 net/batman-adv/types.h                 |  24 ++-
 9 files changed, 408 insertions(+), 80 deletions(-)
  

Comments

Sven Eckelmann Dec. 14, 2016, 2:50 p.m. UTC | #1
On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> @@ -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;
>  }

Why must we skb_reset_network_header before the kfree?

Kind regards,
	Sven
  
Sven Eckelmann Dec. 14, 2016, 8:03 p.m. UTC | #2
On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> +       void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node);
> +       const struct ethhdr *ethhdr = eth_hdr(skb);
> +       unsigned int tvlv_offset = sizeof(*ogm2);

Haven't checked the complete code yet but isn't an increase of the
reference counter for orig_node missing here? Things like
batadv_gw_tvlv_ogm_handler would then have to take care of dropping
the reference.

Kind regards,
	Sven
  
Sven Eckelmann Dec. 14, 2016, 8:12 p.m. UTC | #3
On Mittwoch, 14. Dezember 2016 21:03:05 CET Sven Eckelmann wrote:
> On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> > +       void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node);
> > +       const struct ethhdr *ethhdr = eth_hdr(skb);
> > +       unsigned int tvlv_offset = sizeof(*ogm2);
> 
> Haven't checked the complete code yet but isn't an increase of the
> reference counter for orig_node missing here? Things like
> batadv_gw_tvlv_ogm_handler would then have to take care of dropping
> the reference.

Ok, forget that. batadv_tvlv_containers_process2 (which is the only user of
ctx) is directly calling the function and not delaying anything. I was under
the impression that their might be an delay.

But I am really not sure why you are using batadv_tvlv_ogm_pack_ctx anyway.
You should be able to use "void *ctx" in batadv_tvlv_ogm_pack_ctx parameter
and then give it an struct batadv_orig_node *. Or is is just for the WARN_ON?

Same for batadv_tvlv_ogm_unpack_ctx

Kind regards,
	Sven
  
Linus Lüssing Dec. 19, 2016, 10:29 a.m. UTC | #4
On Wed, Dec 14, 2016 at 03:50:38PM +0100, Sven Eckelmann wrote:
> On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> > @@ -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;
> >  }
> 
> Why must we skb_reset_network_header before the kfree?

Technically, yes, there's no need for it.

Usually I would have prefered keeping the skb network header
pointer just after the ethernet header just like anything else on the
receive path does right now. Would be messy if you could never be
sure where that pointer actually points at.

On the other hand it was too convenient to use it here :).


What do you think about wrapping the WARN_ON, while loop and
skb_reset_network_header() into a new function with a functional,
non-modifying touch regarding the input skb?

Or do you think I should just remove the
skb_reset_network_header() here?
  
Linus Lüssing Dec. 19, 2016, 10:50 a.m. UTC | #5
On Wed, Dec 14, 2016 at 09:12:00PM +0100, Sven Eckelmann wrote:
> On Mittwoch, 14. Dezember 2016 21:03:05 CET Sven Eckelmann wrote:
> > On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> > > +       void *ctx = batadv_tvlv_ogm_pack_ctx(orig_node);
> > > +       const struct ethhdr *ethhdr = eth_hdr(skb);
> > > +       unsigned int tvlv_offset = sizeof(*ogm2);
> > 
> > Haven't checked the complete code yet but isn't an increase of the
> > reference counter for orig_node missing here? Things like
> > batadv_gw_tvlv_ogm_handler would then have to take care of dropping
> > the reference.
> 
> Ok, forget that. batadv_tvlv_containers_process2 (which is the only user of
> ctx) is directly calling the function and not delaying anything. I was under
> the impression that their might be an delay.
> 
> But I am really not sure why you are using batadv_tvlv_ogm_pack_ctx anyway.
> You should be able to use "void *ctx" in batadv_tvlv_ogm_pack_ctx parameter
> and then give it an struct batadv_orig_node *. Or is is just for the WARN_ON?

Hm, no, had nothing to do with the WARN_ON. The idea was to have
the nasty (void *) casting at least at some well-defined place.

And didn't use "void *ctx" as a parameter of _pack_ctx() directly,
to have at least some compile-time verification regarding the
types.


I wanted to have something a little similar to the way icmp_hdr(), icmp6_hdr(),
ipv6_hdr() etc. do the casting, for instance, hm.

(or maybe I should add something like a "struct batadv_tvlv_ctx {
void *ctx };" then, to really get rid of the void pointers in the
functions calling the _pack()/_unpack() ones?)
  
Sven Eckelmann Dec. 19, 2016, 11:37 a.m. UTC | #6
On Montag, 19. Dezember 2016 11:50:27 CET Linus Lüssing wrote:
> And didn't use "void *ctx" as a parameter of _pack_ctx() directly,
> to have at least some compile-time verification regarding the
> types.

I was not talking about the packing functions. I was talking about the 
function which is used to trigger the handlers. It is not necessary (unless I 
miss something) to have some explicit void * casting when its parameter type 
is already void *.

> I wanted to have something a little similar to the way icmp_hdr(),
> icmp6_hdr(), ipv6_hdr() etc. do the casting, for instance, hm.

I think these functions cast from char * to something like struct icmphdr * by 
first retrieving the transport header from the skb. So they do a lot more than 
your more-than-necessary-explicit casting function.

Kind regards,
	Sven
  
Sven Eckelmann Dec. 19, 2016, 11:43 a.m. UTC | #7
On Montag, 19. Dezember 2016 12:37:43 CET Sven Eckelmann wrote:
> I was not talking about the packing functions.

Just read my initial mails. Looks like I've copied the wrong function name 
"batadv_tvlv_ogm_pack_ctx". But I was talking about the parameter in of 
"batadv_tvlv_containers_process2". So I am the reason for the confusion :)

Kind regards,
	Sven
  
Linus Lüssing Dec. 20, 2016, 11:55 a.m. UTC | #8
On Mon, Dec 19, 2016 at 12:43:49PM +0100, Sven Eckelmann wrote:
> On Montag, 19. Dezember 2016 12:37:43 CET Sven Eckelmann wrote:
> > I was not talking about the packing functions.
> 
> Just read my initial mails. Looks like I've copied the wrong function name 
> "batadv_tvlv_ogm_pack_ctx". But I was talking about the parameter in of 
> "batadv_tvlv_containers_process2". So I am the reason for the confusion :)

Just to check, you suggest removing this line:
https://git.open-mesh.org/batman-adv.git/blob/1ddd189528fc332bf286ffd56b629da26a6b149c:/net/batman-adv/tvlv.c#l662

And to replace the passing of "ctx" with an "orig_node" here:
https://git.open-mesh.org/batman-adv.git/blob/1ddd189528fc332bf286ffd56b629da26a6b149c:/net/batman-adv/tvlv.c#l682

(which works as a "void *" parameter accepts any type, without the
need of casting explicitly)


If that's what you mean, hm, not sure. Feels dangerous, such
automatic casting, doesn't it?

I'm really wondering whether I should go for the approach of
hiding the "void *" in a new type. And only having the "void *"
fiddeling inside _pack()/_unpack() functions.
  
Sven Eckelmann Jan. 22, 2017, 12:40 p.m. UTC | #9
> @@ -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;
>  }

[...]
On Donnerstag, 6. Oktober 2016 08:41:40 CET Linus Lüssing wrote:
> What do you think about wrapping the WARN_ON, while loop and
> skb_reset_network_header() into a new function with a functional,
> non-modifying touch regarding the input skb?
> 
> Or do you think I should just remove the
> skb_reset_network_header() here?

Hm, my point was not actually about the loop or the skb_set_network_header or
the WARN_ON. I only don't know why a skb_reset_network_header would be
necessary before a kfree_skb/consume_skb.

It also doesn't seem to be used in a later patch.

Kind regards,
	Sven
  
Sven Eckelmann Jan. 22, 2017, 12:51 p.m. UTC | #10
On Dienstag, 20. Dezember 2016 12:55:50 CET Linus Lüssing wrote:
> On Mon, Dec 19, 2016 at 12:43:49PM +0100, Sven Eckelmann wrote:
> > On Montag, 19. Dezember 2016 12:37:43 CET Sven Eckelmann wrote:
> > > I was not talking about the packing functions.
> > 
> > Just read my initial mails. Looks like I've copied the wrong function name 
> > "batadv_tvlv_ogm_pack_ctx". But I was talking about the parameter in of 
> > "batadv_tvlv_containers_process2". So I am the reason for the confusion :)
> 
> Just to check, you suggest removing this line:
> https://git.open-mesh.org/batman-adv.git/blob/1ddd189528fc332bf286ffd56b629da26a6b149c:/net/batman-adv/tvlv.c#l662

Yes

> And to replace the passing of "ctx" with an "orig_node" here:
> https://git.open-mesh.org/batman-adv.git/blob/1ddd189528fc332bf286ffd56b629da26a6b149c:/net/batman-adv/tvlv.c#l682

Yes

> (which works as a "void *" parameter accepts any type, without the
> need of casting explicitly)

Yes

> If that's what you mean, hm, not sure. Feels dangerous, such
> automatic casting, doesn't it?

It is not perfect but it is the way it is already done for many things in the
kernel and also in userspace. Think for example about "the argument" when you
generate (k)threads and which you can then then retrieve again inside this
thread.

Your current extra functions also don't prevent that there is a wrong argument
added to this perticular function. They just introduce some weird extra
functions. (At least this is how feel about it - their might be different
opinions about that)

> 
> I'm really wondering whether I should go for the approach of
> hiding the "void *" in a new type. And only having the "void *"
> fiddeling inside _pack()/_unpack() functions.

Hm, I personally don't see a reason for a new type at the moment. But when you
find a good one... at least their could be plenty of reasons which might make
everything a little bit cleaner.

Just keep in mind that there are already many other things in the kernel like
container_of and similar things. So don't try to overengineer it :)

Kind regards,
	Sven
  

Patch

diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 9c723cf..4f09b70 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -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;
 }
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index eaa2e2d..606f899 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -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);
 
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index e257efd..4fd912c 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -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;
 }
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index 2118481..6f7cb51 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -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);
 }
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index 13661f4..9a962eb 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -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);
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index e3baf69..2fe44d8 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -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;
 
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 77654f0..8c097c3 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -17,6 +17,7 @@ 
 
 #include "main.h"
 
+#include <linux/bug.h>
 #include <linux/byteorder/generic.h>
 #include <linux/etherdevice.h>
 #include <linux/fs.h>
@@ -27,6 +28,7 @@ 
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/pkt_sched.h>
+#include <linux/printk.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include <linux/skbuff.h>
@@ -68,23 +70,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 +394,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 +414,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 +569,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 +581,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 +601,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 +617,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
+ * @ctx: handler specific context information (here: orig_node)
+ *
+ * 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 +751,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 +765,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 +783,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 +801,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
+ * @packet_type: packet type to unregister 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;
 
diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h
index e4369b5..0d2d586 100644
--- a/net/batman-adv/tvlv.h
+++ b/net/batman-adv/tvlv.h
@@ -22,7 +22,7 @@ 
 
 #include <linux/types.h>
 
-struct batadv_ogm_packet;
+struct sk_buff;
 
 void batadv_tvlv_container_register(struct batadv_priv *bat_priv,
 				    u8 type, u8 version,
@@ -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);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 731bdf5..6a71522 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -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),
 };
 
 /**