diff --git a/Makefile.in b/Makefile.in
index fc7a4ffc69..702351082f 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -119,6 +119,8 @@ PUBHDR = \
 	pcap/vlan.h
 
 HDR = $(PUBHDR) \
+	batadv_legacy_packet.h \
+	batadv_packet.h \
 	diag-control.h \
 	ethertype.h \
 	extract.h \
diff --git a/batadv_legacy_packet.h b/batadv_legacy_packet.h
new file mode 100644
index 0000000000..ff2715c240
--- /dev/null
+++ b/batadv_legacy_packet.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: BSD-3 */
+/* Copyright (C) 2020  Linus Lüssing */
+
+#ifndef _BATADV_LEGACY_PACKET_H_
+#define _BATADV_LEGACY_PACKET_H_
+
+enum batadv_legacy_packettype {
+	BATADV_LEGACY_IV_OGM		= 0x01,
+	BATADV_LEGACY_ICMP		= 0x02,
+	BATADV_LEGACY_UNICAST		= 0x03,
+	BATADV_LEGACY_BCAST		= 0x04,
+	BATADV_LEGACY_VIS		= 0x05,
+	BATADV_LEGACY_UNICAST_FRAG	= 0x06,
+	BATADV_LEGACY_TT_QUERY		= 0x07,
+	BATADV_LEGACY_ROAM_ADV		= 0x08,
+	BATADV_LEGACY_UNICAST_4ADDR	= 0x09,
+	BATADV_LEGACY_CODED		= 0x0a,
+};
+
+#define ETH_ALEN	6
+
+struct batadv_legacy_unicast_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t ttvn;
+	uint8_t dest[ETH_ALEN];
+};
+
+struct batadv_legacy_unicast_4addr_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t src[ETH_ALEN];
+	uint8_t subtype;
+	uint8_t reserved;
+};
+
+struct batadv_legacy_unicast_frag_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t ttvn;
+	uint8_t dest[ETH_ALEN];
+	uint8_t flags;
+	uint8_t align;
+	uint8_t orig[ETH_ALEN];
+	uint8_t seqno[2];		/* 2-byte integral value */
+};
+
+struct batadv_legacy_bcast_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t reserved;
+	uint8_t seqno[4];		/* 4-byte integral value */
+	uint8_t orig[ETH_ALEN];
+};
+
+struct batadv_legacy_coded_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t first_ttvn;
+	uint8_t first_source[ETH_ALEN];
+	uint8_t first_orig_dest[ETH_ALEN];
+	uint8_t first_crc[4];		/* 4-byte integral value */
+	uint8_t second_ttl;
+	uint8_t second_ttvn;
+	uint8_t second_dest[ETH_ALEN];
+	uint8_t second_source[ETH_ALEN];
+	uint8_t second_orig_dest[ETH_ALEN];
+	uint8_t second_crc[4];		/* 4-byte integral value */
+	uint8_t coded_len[2];		/* 2-byte integral value */
+};
+
+#endif /* _BATADV_LEGACY_PACKET_H_ */
diff --git a/batadv_packet.h b/batadv_packet.h
new file mode 100644
index 0000000000..06c0b65974
--- /dev/null
+++ b/batadv_packet.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: BSD-3 */
+/* Copyright (C) 2024  Linus Lüssing */
+
+#ifndef _BATADV_PACKET_H_
+#define _BATADV_PACKET_H_
+
+/* For the definitive and most recent packet format definition,
+ * see the batadv_packet.h in the Linux kernel.
+ */
+
+enum batadv_packettype {
+	BATADV_IV_OGM           = 0x00,
+	BATADV_BCAST            = 0x01,
+	BATADV_CODED            = 0x02,
+	BATADV_ELP		= 0x03,
+	BATADV_OGM2		= 0x04,
+	BATADV_MCAST		= 0x05,
+	BATADV_UNICAST          = 0x40,
+	BATADV_UNICAST_FRAG     = 0x41,
+	BATADV_UNICAST_4ADDR    = 0x42,
+	BATADV_ICMP             = 0x43,
+	BATADV_UNICAST_TVLV     = 0x44,
+};
+
+#define ETH_ALEN	6
+
+struct batadv_unicast_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t ttvn;
+	uint8_t dest[ETH_ALEN];
+};
+
+struct batadv_unicast_4addr_packet {
+	struct batadv_unicast_packet u;
+	uint8_t src[ETH_ALEN];
+	uint8_t subtype;
+	uint8_t reserved;
+};
+
+struct batadv_frag_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t num_pri;	/* number and priority */
+	uint8_t dest[ETH_ALEN];
+	uint8_t orig[ETH_ALEN];
+	uint8_t seqno[2];	/* 2-byte integral value */
+	uint8_t total_size[2];	/* 2-byte integral value */
+};
+
+struct batadv_bcast_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t reserved;
+	uint8_t seqno[4];	/* 4-byte integral value */
+	uint8_t orig[ETH_ALEN];
+};
+
+struct batadv_mcast_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t reserved;
+	uint8_t tvlv_len[2];	/* 2-byte integral value */
+};
+
+struct batadv_coded_packet {
+	uint8_t packet_type;
+	uint8_t version;
+	uint8_t ttl;
+	uint8_t first_ttvn;
+	uint8_t first_source[ETH_ALEN];
+	uint8_t first_orig_dest[ETH_ALEN];
+	uint8_t first_crc[4];	/* 4-byte integral value */
+	uint8_t second_ttl;
+	uint8_t second_ttvn;
+	uint8_t second_dest[ETH_ALEN];
+	uint8_t second_source[ETH_ALEN];
+	uint8_t second_orig_dest[ETH_ALEN];
+	uint8_t second_crc[4];	/* 4-byte integral value */
+	uint8_t coded_len[2];	/* 2-byte integral value */
+};
+
+#endif /* _BATADV_PACKET_H_ */
diff --git a/ethertype.h b/ethertype.h
index e34e07b98d..d8e87e4f55 100644
--- a/ethertype.h
+++ b/ethertype.h
@@ -49,6 +49,9 @@
 #ifndef ETHERTYPE_TRAIL
 #define ETHERTYPE_TRAIL		0x1000
 #endif
+#ifndef ETHERTYPE_BATMAN
+#define ETHERTYPE_BATMAN	0x4305 /* B.A.T.M.A.N. Advanced */
+#endif
 #ifndef	ETHERTYPE_MOPDL
 #define ETHERTYPE_MOPDL		0x6001
 #endif
diff --git a/gencode.c b/gencode.c
index 7e6fea7a16..f51b9c31f9 100644
--- a/gencode.c
+++ b/gencode.c
@@ -45,6 +45,8 @@
 #include "ieee80211.h"
 #include "pflog.h"
 #include "ppp.h"
+#include "batadv_packet.h"
+#include "batadv_legacy_packet.h"
 #include "pcap/sll.h"
 #include "pcap/ipnet.h"
 #include "diag-control.h"
@@ -10107,6 +10109,330 @@ gen_vxlan(compiler_state_t *cstate, bpf_u_int32 vni, int has_vni)
 	return b1;
 }
 
+static struct block *
+gen_batadv_check_version(compiler_state_t *cstate, struct block *b0, bpf_u_int32 version)
+{
+	struct block *b1;
+
+	if (version > UINT8_MAX)
+		bpf_error(cstate,
+			  "batman-adv compatibility version number %u unsupported",
+			  version);
+
+	b1 = gen_cmp(cstate, OR_LINKPL, 1, BPF_B, version);
+	gen_and(b0, b1);
+
+	return b1;
+}
+
+static struct block *
+gen_batadv_check_type(compiler_state_t *cstate, struct block *b0,
+		      bpf_u_int32 version, bpf_u_int32 type)
+{
+	struct block *b1;
+
+	switch (version) {
+	case 14:
+	case 15:
+		if (type > UINT8_MAX)
+			bpf_error(cstate,
+				  "batman-adv packet type %u unsupported for compatibility version %u",
+				  type, version);
+
+		b1 = gen_cmp(cstate, OR_LINKPL, 0, BPF_B, type);
+		gen_and(b0, b1);
+		b0 = b1;
+
+		break;
+	default:
+		bpf_error(cstate,
+			  "batman-adv compatibility version number %u unsupported",
+			  version);
+	}
+
+	return b0;
+}
+
+
+static void gen_batadv_push_offset(compiler_state_t *cstate, u_int offset)
+{
+	PUSH_LINKHDR(cstate, DLT_EN10MB, cstate->off_linkpl.is_variable,
+		     cstate->off_linkpl.constant_part + cstate->off_nl + offset,
+		     cstate->off_linkpl.reg);
+
+	cstate->off_linktype.constant_part += cstate->off_linkhdr.constant_part;
+	cstate->off_linkpl.constant_part += cstate->off_linkhdr.constant_part;
+
+	cstate->off_nl = 0;
+	cstate->off_nl_nosnap = 0;	/* no 802.2 LLC */
+}
+
+static void
+gen_batadv_offsets_v14(compiler_state_t *cstate, bpf_u_int32 type)
+{
+	size_t offset;
+
+	switch (type) {
+	case BATADV_LEGACY_UNICAST:		/* 0x03 */
+		offset = sizeof(struct batadv_legacy_unicast_packet);
+		break;
+	case BATADV_LEGACY_BCAST:		/* 0x04 */
+		offset = sizeof(struct batadv_legacy_bcast_packet);
+		break;
+	case BATADV_LEGACY_UNICAST_FRAG:	/* 0x06 */
+		offset = sizeof(struct batadv_legacy_unicast_frag_packet);
+		break;
+	case BATADV_LEGACY_UNICAST_4ADDR:	/* 0x09 */
+		offset = sizeof(struct batadv_legacy_unicast_4addr_packet);
+		break;
+	case BATADV_LEGACY_CODED:		/* 0x0a */
+		offset = sizeof(struct batadv_legacy_coded_packet);
+		break;
+	default:
+		offset = 0;
+	}
+
+	if (offset)
+		gen_batadv_push_offset(cstate, (u_int)offset);
+}
+
+static void
+gen_prepare_var_offset(compiler_state_t *cstate, bpf_abs_offset *off, struct slist *s)
+{
+	struct slist *s2;
+
+	if (!off->is_variable)
+		off->is_variable = 1;
+	if (off->reg == -1) {
+		off->reg = alloc_reg(cstate);
+
+		s2 = new_stmt(cstate, BPF_ALU|BPF_AND);
+		s2->s.k = 0;
+		s2 = new_stmt(cstate, BPF_ST);
+		s2->s.k = off->reg;
+		sappend(s, s2);
+	}
+}
+
+/**
+ * gen_batadv_loadx_tvlv_len_offset() - load offset to tvlv_len into X
+ * @cstate: our compiler state
+ * @bat_offset: our offset to the start of the batman-adv packet header
+ * @field_offset: offset of tvlv_len field within the batman-adv packet header
+ * @s: instructions list to append to
+ *
+ * Will load: X = bat_offset + field_offset. So that [X] in the packet will
+ * point to the most-significant byte of the tvlv_len in a batman-adv packet.
+ */
+static void
+gen_batadv_loadx_tvlv_len_offset(compiler_state_t *cstate,
+				 bpf_abs_offset *bat_offset,
+				 bpf_u_int32 field_offset, struct slist *s)
+{
+	struct slist *s2;
+
+	s2 = new_stmt(cstate, BPF_LD|BPF_MEM);
+	s2->s.k = bat_offset->reg;
+	sappend(s, s2);
+
+	s2 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_K);
+	s2->s.k = bat_offset->constant_part + field_offset;
+	sappend(s, s2);
+
+	s2 = new_stmt(cstate, BPF_MISC|BPF_TAX);
+	s2->s.k = 0;
+	sappend(s, s2);
+}
+
+/**
+ * gen_batadv_loadx_tvlv_len() - load tvlv_len value into X
+ * @cstate: our compiler state
+ * @bat_offset: our offset to the start of the batman-adv packet header
+ * @field_offset: offset of tvlv_len field within the batman-adv packet header
+ * @s: instructions list to append to
+ *
+ * Loads the value of the 2 byte tvlv_len field in a given batman-adv packet
+ * header into the X register.
+ */
+static void
+gen_batadv_loadx_tvlv_len(compiler_state_t *cstate, bpf_abs_offset *bat_offset,
+			  bpf_u_int32 field_offset, struct slist *s)
+{
+	struct slist *s2;
+
+	/* load offset to tvlv_len field into X register */
+	gen_batadv_loadx_tvlv_len_offset(cstate, bat_offset, field_offset, s);
+
+	/* clear A register */
+	s2 = new_stmt(cstate, BPF_ALU|BPF_AND|BPF_K);
+	s2->s.k = 0;
+	sappend(s, s2);
+
+	/* load most significant byte of tvlv_len */
+	s2 = new_stmt(cstate, BPF_LD|BPF_IND|BPF_B);
+	s2->s.k = 0;
+	sappend(s, s2);
+
+	/* multiply by 2^8 for real value of MSB, make room for LSB */
+	s2 = new_stmt(cstate, BPF_ALU|BPF_LSH|BPF_K);
+	s2->s.k = 8;
+	sappend(s, s2);
+
+	/* load least significant byte of tvlv_len */
+	s2 = new_stmt(cstate, BPF_LD|BPF_IND|BPF_B);
+	s2->s.k = 1;
+	sappend(s, s2);
+
+	s2 = new_stmt(cstate, BPF_MISC|BPF_TAX);
+	s2->s.k = 0;
+	sappend(s, s2);
+}
+
+/**
+ * gen_batadv_offset_addx() - add X register to a variable offset
+ * @cstate: our compiler state
+ * @off: the (variable) offset to add to
+ * @s: instructions list to append to
+ *
+ * Adds the value from the X register to the given variable offset.
+ */
+static void
+gen_batadv_offset_addx(compiler_state_t *cstate, bpf_abs_offset *off,
+		       struct slist *s)
+{
+	struct slist *s2;
+
+	s2 = new_stmt(cstate, BPF_LD|BPF_MEM);
+	s2->s.k = off->reg;
+	sappend(s, s2);
+
+	s2 = new_stmt(cstate, BPF_ALU|BPF_ADD|BPF_X);
+	s2->s.k = 0;
+	sappend(s, s2);
+
+	s2 = new_stmt(cstate, BPF_ST);
+	s2->s.k = off->reg;
+	sappend(s, s2);
+}
+
+/**
+ * gen_batadv_offsets_add_tvlv_len() - add tvlv_len to payload offsets
+ * @cstate: our compiler state
+ * @b0: instructions block to add to
+ * @field_offset: offset of tvlv_len field within the batman-adv packet header
+ *
+ * Adds the tvlv_len value from/in a batman-adv packet header to the offsets
+ * of cstate->off_linkpl and cstate->off_linktype.
+ *
+ * Return: The updated instructions block.
+ */
+static struct block *
+gen_batadv_offsets_add_tvlv_len(compiler_state_t *cstate, struct block *b0,
+				bpf_u_int32 field_offset)
+{
+	struct slist s;
+
+	s.next = NULL;
+
+	/* turn constant-only offsets into variable offsets as we need to add
+	 * variable offset values (tvlv_len) to them later */
+	gen_prepare_var_offset(cstate, &cstate->off_linkpl, &s);
+	gen_prepare_var_offset(cstate, &cstate->off_linktype, &s);
+
+	/* load tvlv_len into X register */
+	gen_batadv_loadx_tvlv_len(cstate, &cstate->off_linkpl, field_offset, &s);
+
+	gen_batadv_offset_addx(cstate, &cstate->off_linkpl, &s);
+	gen_batadv_offset_addx(cstate, &cstate->off_linktype, &s);
+
+	sappend(s.next, b0->head->stmts);
+	b0->head->stmts = s.next;
+
+	return b0;
+}
+
+static struct block *
+gen_batadv_offsets_v15(compiler_state_t *cstate, struct block *b0, bpf_u_int32 type)
+{
+	size_t offset;
+	bpf_u_int32 field_offset;
+
+	switch (type) {
+	case BATADV_BCAST:		/* 0x01 */
+		offset = sizeof(struct batadv_bcast_packet);
+		break;
+	case BATADV_CODED:		/* 0x02 */
+		offset = sizeof(struct batadv_coded_packet);
+		break;
+	case BATADV_MCAST:		/* 0x05 */
+		offset = sizeof(struct batadv_mcast_packet);
+		field_offset = (bpf_u_int32)offsetof(struct batadv_mcast_packet,
+						     tvlv_len);
+
+		b0 = gen_batadv_offsets_add_tvlv_len(cstate, b0, field_offset);
+		break;
+	case BATADV_UNICAST:		/* 0x40 */
+		offset = sizeof(struct batadv_unicast_packet);
+		break;
+	case BATADV_UNICAST_FRAG:	/* 0x41 */
+		offset = sizeof(struct batadv_frag_packet);
+		break;
+	case BATADV_UNICAST_4ADDR:	/* 0x42 */
+		offset = sizeof(struct batadv_unicast_4addr_packet);
+		break;
+	default:
+		offset = 0;
+	}
+
+	if (offset)
+		gen_batadv_push_offset(cstate, (u_int)offset);
+
+	return b0;
+}
+
+static struct block *
+gen_batadv_offsets(compiler_state_t *cstate, struct block *b0, bpf_u_int32 version, bpf_u_int32 type)
+{
+	switch (version) {
+	case 14:
+		gen_batadv_offsets_v14(cstate, type);
+		break;
+	case 15:
+		b0 = gen_batadv_offsets_v15(cstate, b0, type);
+		break;
+	default:
+		break;
+	}
+
+	return b0;
+}
+
+struct block *
+gen_batadv(compiler_state_t *cstate, bpf_u_int32 version, int has_version,
+	   bpf_u_int32 type, int has_type)
+{
+	struct block *b0;
+
+	/*
+	 * Catch errors reported by us and routines below us, and return NULL
+	 * on an error.
+	 */
+	if (setjmp(cstate->top_ctx))
+		return (NULL);
+
+	b0 = gen_linktype(cstate, ETHERTYPE_BATMAN);
+
+	if (has_version)
+		b0 = gen_batadv_check_version(cstate, b0, version);
+
+	if (has_type) {
+		b0 = gen_batadv_check_type(cstate, b0, version, type);
+		b0 = gen_batadv_offsets(cstate, b0, version, type);
+	}
+
+	return b0;
+}
+
 /* Check that the encapsulated frame has a link layer header
  * for Ethernet filters. */
 static struct block *
diff --git a/gencode.h b/gencode.h
index cf9baafed1..d8dfe77855 100644
--- a/gencode.h
+++ b/gencode.h
@@ -359,6 +359,9 @@ struct block *gen_pppoes(compiler_state_t *, bpf_u_int32, int);
 struct block *gen_geneve(compiler_state_t *, bpf_u_int32, int);
 struct block *gen_vxlan(compiler_state_t *, bpf_u_int32, int);
 
+struct block *gen_batadv(compiler_state_t *, bpf_u_int32, int,
+			 bpf_u_int32, int);
+
 struct block *gen_atmfield_code(compiler_state_t *, int, bpf_u_int32,
     int, int);
 struct block *gen_atmtype_abbrev(compiler_state_t *, int);
diff --git a/grammar.y.in b/grammar.y.in
index 578ccfb17a..ffd9be6e2a 100644
--- a/grammar.y.in
+++ b/grammar.y.in
@@ -372,6 +372,7 @@ DIAG_OFF_BISON_BYACC
 %type	<i>	mtp2type
 %type	<blk>	mtp3field
 %type	<blk>	mtp3fieldvalue mtp3value mtp3listvalue
+%type	<rblk>	pbatadv
 
 
 %token  DST SRC HOST GATEWAY
@@ -390,7 +391,7 @@ DIAG_OFF_BISON_BYACC
 %token  LEN
 %token  IPV6 ICMPV6 AH ESP
 %token	VLAN MPLS
-%token	PPPOED PPPOES GENEVE VXLAN
+%token	PPPOED PPPOES GENEVE VXLAN BATADV
 %token  ISO ESIS CLNP ISIS L1 L2 IIH LSP SNP CSNP PSNP
 %token  STP
 %token  IPX
@@ -689,11 +690,40 @@ other:	  pqual TK_BROADCAST	{ CHECK_PTR_VAL(($$ = gen_broadcast(cstate, $1))); }
 	| GENEVE		{ CHECK_PTR_VAL(($$ = gen_geneve(cstate, 0, 0))); }
 	| VXLAN pnum		{ CHECK_PTR_VAL(($$ = gen_vxlan(cstate, $2, 1))); }
 	| VXLAN			{ CHECK_PTR_VAL(($$ = gen_vxlan(cstate, 0, 0))); }
+	| BATADV pbatadv	{ $$ = $2; }
 	| pfvar			{ $$ = $1; }
 	| pqual p80211		{ $$ = $2; }
 	| pllc			{ $$ = $1; }
 	;
 
+pbatadv:		{ CHECK_PTR_VAL(($$ = gen_batadv(cstate, 0, 0, 0, 0))); }
+	| pnum		{ CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, 0, 0))); }
+	| pnum pnum	{ CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, $2, 1))); }
+	| pnum ID
+		{
+			int type;
+
+			switch ($1) {
+			case 14:
+				type = pcap_nametobatadvtype_v14($2);
+				break;
+			case 15:
+				type = pcap_nametobatadvtype_v15($2);
+				break;
+			default:
+				bpf_set_error(cstate, "batman-adv compatibility version number %u unsupported", $1);
+				YYABORT;
+			}
+
+			if (type == PROTO_UNDEF) {
+				bpf_set_error(cstate, "invalid batman-adv packet type value \"%s\"", $2);
+				YYABORT;
+			}
+
+			CHECK_PTR_VAL(($$ = gen_batadv(cstate, $1, 1, type, 1)));
+		}
+	;
+
 pfvar:	  PF_IFNAME ID		{ CHECK_PTR_VAL($2); CHECK_PTR_VAL(($$ = gen_pf_ifname(cstate, $2))); }
 	| PF_RSET ID		{ CHECK_PTR_VAL($2); CHECK_PTR_VAL(($$ = gen_pf_ruleset(cstate, $2))); }
 	| PF_RNR NUM		{ CHECK_PTR_VAL(($$ = gen_pf_rnr(cstate, $2))); }
diff --git a/nametoaddr.c b/nametoaddr.c
index cc4ab86806..d9b6cb9d67 100644
--- a/nametoaddr.c
+++ b/nametoaddr.c
@@ -104,8 +104,12 @@
 
 #include "diag-control.h"
 
+#include "batadv_packet.h"
+#include "batadv_legacy_packet.h"
+
 #include "gencode.h"
 #include <pcap/namedb.h>
+
 #include "nametoaddr.h"
 
 #include "thread-local.h"
@@ -560,6 +564,7 @@ PCAP_API_DEF struct eproto eproto_db[] = {
 	{ "moprc", ETHERTYPE_MOPRC },
 	{ "rarp", ETHERTYPE_REVARP },
 	{ "sca", ETHERTYPE_SCA },
+	{ "batadv", ETHERTYPE_BATMAN },
 	{ (char *)0, 0 }
 };
 
@@ -600,6 +605,61 @@ pcap_nametollc(const char *s)
 	return PROTO_UNDEF;
 }
 
+/* Static data base of batman-adv v14 packet type values. */
+static struct eproto batadv_type_db_v14[] = {
+	{ "iv-ogm",		BATADV_LEGACY_IV_OGM },
+	{ "icmp",		BATADV_LEGACY_ICMP },
+	{ "unicast",		BATADV_LEGACY_UNICAST },
+	{ "bcast",		BATADV_LEGACY_BCAST },
+	{ "vis",		BATADV_LEGACY_VIS },
+	{ "unicast-frag",	BATADV_LEGACY_UNICAST_FRAG },
+	{ "tt-query",		BATADV_LEGACY_TT_QUERY },
+	{ "roam-adv",		BATADV_LEGACY_ROAM_ADV },
+	{ "unicast-4addr",	BATADV_LEGACY_UNICAST_4ADDR },
+	{ "coded",		BATADV_LEGACY_CODED },
+	{ (char *)0, 0 }
+};
+
+int pcap_nametobatadvtype_v14(const char *s)
+{
+	struct eproto *p = batadv_type_db_v14;
+
+	while (p->s != 0) {
+		if (strcmp(p->s, s) == 0)
+			return p->p;
+		p += 1;
+	}
+	return PROTO_UNDEF;
+}
+
+/* Static data base of batman-adv v15 packet type values. */
+static struct eproto batadv_type_db_v15[] = {
+	{ "iv-ogm",		BATADV_IV_OGM },
+	{ "bcast",		BATADV_BCAST },
+	{ "coded",		BATADV_CODED },
+	{ "elp",		BATADV_ELP },
+	{ "ogm2",		BATADV_OGM2 },
+	{ "mcast",		BATADV_MCAST },
+	{ "unicast",		BATADV_UNICAST },
+	{ "unicast-frag",	BATADV_UNICAST_FRAG },
+	{ "unicast-4addr",	BATADV_UNICAST_4ADDR },
+	{ "icmp",		BATADV_ICMP },
+	{ "unicast-tvlv",	BATADV_UNICAST_TVLV },
+	{ (char *)0, 0 }
+};
+
+int pcap_nametobatadvtype_v15(const char *s)
+{
+	struct eproto *p = batadv_type_db_v15;
+
+	while (p->s != 0) {
+		if (strcmp(p->s, s) == 0)
+			return p->p;
+		p += 1;
+	}
+	return PROTO_UNDEF;
+}
+
 /* Hex digit to 8-bit unsigned integer. */
 static inline u_char
 xdtoi(u_char c)
diff --git a/pcap-filter.manmisc.in b/pcap-filter.manmisc.in
index 6e3269fb9c..e5ee0ed473 100644
--- a/pcap-filter.manmisc.in
+++ b/pcap-filter.manmisc.in
@@ -98,6 +98,7 @@ protocols are:
 .BR arp ,
 .BR rarp ,
 .BR decnet ,
+.BR batadv ,
 .BR sctp ,
 .B tcp
 and
@@ -436,7 +437,7 @@ True if the packet is an IPv6 multicast packet.
 .IP  "\fBether proto \fIprotocol\fR"
 True if the packet is of ether type \fIprotocol\fR.
 \fIProtocol\fP can be a number or one of the names
-\fBaarp\fP, \fBarp\fP, \fBatalk\fP, \fBdecnet\fP, \fBip\fP, \fBip6\fP,
+\fBaarp\fP, \fBarp\fP, \fBatalk\fP, \fBbatadv\fP, \fBdecnet\fP, \fBip\fP, \fBip6\fP,
 \fBipx\fP, \fBiso\fP, \fBlat\fP, \fBloopback\fP, \fBmopdl\fP, \fBmoprc\fP, \fBnetbeui\fP,
 \fBrarp\fP, \fBsca\fP or \fBstp\fP.
 Note these identifiers (except \fBloopback\fP) are also keywords
@@ -490,7 +491,7 @@ the filter checks for the IPX etype in an Ethernet frame, the IPX
 DSAP in the LLC header, the 802.3-with-no-LLC-header encapsulation of
 IPX, and the IPX etype in a SNAP frame.
 .RE
-.IP "\fBip\fR, \fBip6\fR, \fBarp\fR, \fBrarp\fR, \fBatalk\fR, \fBaarp\fR, \fBdecnet\fR, \fBiso\fR, \fBstp\fR, \fBipx\fR, \fBnetbeui\fP"
+.IP "\fBip\fR, \fBip6\fR, \fBarp\fR, \fBrarp\fR, \fBatalk\fR, \fBaarp\fR, \fBdecnet\fR, \fBiso\fR, \fBstp\fR, \fBipx\fR, \fBnetbeui\fP, \fBbatadv\fP"
 Abbreviations for:
 .in +.5i
 .nf
@@ -857,6 +858,37 @@ For example:
 .fi
 .in -.5i
 filters IPv6 protocol encapsulated in VXLAN with VNI 0x7.
+.IP "\fBbatadv \fI[version] \fI[type]\fR"
+True if the packet is a B.A.T.M.A.N. Advanced packet (Ethernet type 0x4305).
+If the optional \fIversion\fR is specified, only true if the packet has the
+specified batman-adv compatibility \fIversion\fR. If the optional \fIversion\fR
+and \fItype\fR are specified, only true if the packet has both the specified
+batman-adv compatibility \fIversion\fR and batman-adv packet \fItype\fR.
+.IP
+\fIversion\fR may be a number from 0 to 255, though only compatibility version
+14 and 15 were actually deployed in the wild. Version 15 is the current version,
+14 is considered deprecated.
+.IP
+\fItype\fR is currently only defined for compatibility \fIversion\fR 14 and 15.
+\fItype\fR may be a number from 0 to 255 for compatibility \fIversion\fR 14 and 15.
+.IP
+The following packet \fItype\fR aliases are available for compat \fIversion\fR 14:
+\fBiv-ogm\fP, \fBicmp\fP, \fBunicast\fP, \fBbcast\fP, \fBvis\fP, \fBunicast-frag\fP,
+\fBtt-query\fP, \fBroam-adv\fP, \fBunicast-4addr\fP, \fPcoded\fP.
+.IP
+The following packet \fItype\fR aliases are available for compat \fIversion\fR 15:
+\fBiv-ogm\fP, \fBbcast\fP, \fBcoded\fP, \fBelp\fP, \fBogm2\fP, \fBmcast\fP,
+\fBunicast\fP, \fBunicast-frag\fP, \fBunicast-4addr\fP, \fBicmp\fP,
+\fPunicast-tvlv\fP.
+.IP
+Note that when the \fBbatadv\fR keyword is encountered in an expression and
+a batman-adv packet \fItype\fR is provided which specifies an encapsulating
+packet type then it changes the decoding offsets for the remainder of the
+expression on the assumption that the packet is a batman-adv packet. For compat
+\fIversion\fR 14 these are packet \fItype\fRs \fBunicast\fP, \fBbcast\fP,
+\fBunicast-frag\fP, \fBunicast-4addr\fP and \fBcoded\fP. For compat \fIversion\fR
+15 these are currently packet \fItype\fRs \fBbcast\fP, \fBcoded\fP, \fBmcast\fP,
+\fBunicast\fP, \fBunicast-frag\fP and \fBunicast-4addr\fP.
 .IP "\fBiso proto \fIprotocol\fR"
 True if the packet is an OSI packet of protocol type \fIprotocol\fP.
 \fIProtocol\fP can be a number or one of the names
diff --git a/pcap/namedb.h b/pcap/namedb.h
index 28a60a4c71..1a81406dfa 100644
--- a/pcap/namedb.h
+++ b/pcap/namedb.h
@@ -95,6 +95,11 @@ PCAP_API int	pcap_nametoeproto(const char *);
 PCAP_AVAILABLE_0_9
 PCAP_API int	pcap_nametollc(const char *);
 
+PCAP_AVAILABLE_1_11
+PCAP_API int	pcap_nametobatadvtype_v14(const char *);
+
+PCAP_AVAILABLE_1_11
+PCAP_API int	pcap_nametobatadvtype_v15(const char *);
 /*
  * If a protocol is unknown, PROTO_UNDEF is returned.
  * Also, pcap_nametoport() returns the protocol along with the port number.
diff --git a/scanner.l b/scanner.l
index 0fa8f40724..c219a0aa62 100644
--- a/scanner.l
+++ b/scanner.l
@@ -339,6 +339,7 @@ pppoed		return PPPOED;
 pppoes		return PPPOES;
 geneve		return GENEVE;
 vxlan		return VXLAN;
+batadv		return BATADV;
 
 lane		return LANE;
 llc		return LLC;