diff --git a/package/gluon-mesh-olsrd/Makefile b/package/gluon-mesh-olsrd/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..55926ea5b794c0187637c1adf0d0554b168b7129
--- /dev/null
+++ b/package/gluon-mesh-olsrd/Makefile
@@ -0,0 +1,19 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-mesh-olsrd
+PKG_VERSION=1
+
+include ../gluon.mk
+
+define Package/gluon-mesh-olsrd
+  TITLE:=olsrd mesh
+  DEPENDS:= \
+		+gluon-core \
+		@IPV6 \
+		+oonf-olsrd2 \
+		+firewall \
+		+gluon-mesh-layer3-common
+  PROVIDES:=gluon-mesh-provider
+endef
+
+$(eval $(call BuildPackageGluon,gluon-mesh-olsrd))
diff --git a/package/gluon-mesh-olsrd/check_site.lua b/package/gluon-mesh-olsrd/check_site.lua
new file mode 100644
index 0000000000000000000000000000000000000000..3e13679068412282bdc3b4eb9ab9a881127d6f65
--- /dev/null
+++ b/package/gluon-mesh-olsrd/check_site.lua
@@ -0,0 +1 @@
+need_table({'mesh', 'olsrd', 'v2', 'config'}, nil, false)
diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr
new file mode 100755
index 0000000000000000000000000000000000000000..7f12096cc64ddea6b87b2cf1ee64cc6295eccf32
--- /dev/null
+++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+reload_running() {
+  if [ -x /etc/init.d/"$1" ] && /etc/init.d/"$1" enabled && /etc/init.d/"$1" running; then
+    echo "(post-setup.d:$IFNAME) Reloading $1..."
+    /etc/init.d/"$1" reload
+  fi
+}
+
+reload_running olsrd2
diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr
new file mode 100755
index 0000000000000000000000000000000000000000..f69bf08dfc7a39d3472a1d5347c5e895cbcebfdd
--- /dev/null
+++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+reload_running() {
+  if [ -x /etc/init.d/"$1" ] && /etc/init.d/"$1" enabled && /etc/init.d/"$1" running; then
+    echo "(teardown.d:$IFNAME) Reloading $1..."
+    /etc/init.d/"$1" reload
+  fi
+}
+
+reload_running olsrd2
diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev b/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev
new file mode 100644
index 0000000000000000000000000000000000000000..202a2374ae5a6d18c57d5f0ed33e89bc87abb6bc
--- /dev/null
+++ b/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev
@@ -0,0 +1 @@
+mmfd
diff --git a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd
new file mode 100755
index 0000000000000000000000000000000000000000..8f39df99445b353599276f54fb1ef3567b0604d0
--- /dev/null
+++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. /lib/gluon/autoupdater/lib.sh
+
+
+start_enabled olsrd2
+wifi up
diff --git a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd
new file mode 100755
index 0000000000000000000000000000000000000000..c9cd9a8ccaba509d9bc38c18196df7a61333b59e
--- /dev/null
+++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. /lib/gluon/autoupdater/lib.sh
+
+
+stop olsrd2
+wifi down
diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf
new file mode 100755
index 0000000000000000000000000000000000000000..880dc31fd4baee8d29abb5133e18ded309fdd895
--- /dev/null
+++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf
@@ -0,0 +1,163 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+local wireless = require 'gluon.wireless'
+local mesh_interfaces = util.get_role_interfaces(uci, 'mesh')
+local uplink_interfaces = util.get_role_interfaces(uci, 'uplink')
+local client_interfaces = util.get_role_interfaces(uci, 'client')
+
+local mesh_interfaces_uplink = {}
+local mesh_interfaces_client = {}
+local mesh_interfaces_other = {}
+for _, iface in ipairs(mesh_interfaces) do
+	if util.contains(uplink_interfaces, iface) then
+		table.insert(mesh_interfaces_uplink, iface)
+	elseif util.contains(client_interfaces, iface) then
+		table.insert(mesh_interfaces_client, iface)
+	else
+		table.insert(mesh_interfaces_other, iface)
+	end
+end
+
+local intf = {
+	wired_mesh = {},
+	vpn_mesh = {},
+	radio_mesh = {},
+}
+
+intf.all_intfs = {}
+
+for _, l in ipairs({ intf.wired_mesh, intf.vpn_mesh, intf.radio_mesh }) do
+	for _, n in ipairs(l) do
+		table.insert(intf.all_intfs, n)
+	end
+end
+
+-- get all mesh radios and mesh lans and then add them to olsrd
+wireless.foreach_radio(uci, function(radio, _, _)
+	local radio_name = radio['.name']
+	table.insert(intf.radio_mesh, 'mesh_' .. radio_name)
+end)
+
+if pcall(function() require 'gluon.mesh-vpn' end) then
+	local vpn_core = require 'gluon.mesh-vpn'
+
+	if vpn_core.enabled() then
+		-- mesh_vpn is a interface that has the right ifname
+		-- we can't use mesh-vpn (dash instead of underscore) since it's not a uci interface
+		table.insert(intf.vpn_mesh, 'mesh_vpn')
+	end
+end
+
+table.insert(intf.wired_mesh, 'loopback')
+
+local has_uplink_mesh = false
+local has_other_mesh = false
+
+for _,i in pairs(mesh_interfaces) do
+	if util.contains(uplink_interfaces, i) then
+		has_uplink_mesh = true
+	else
+		has_other_mesh = true
+	end
+end
+
+if has_uplink_mesh then
+	table.insert(intf.wired_mesh, 'mesh_uplink')
+end
+
+if has_other_mesh then
+	table.insert(intf.wired_mesh, 'mesh_other')
+end
+
+uci:delete_all('olsrd2', 'interface')
+
+if site.mesh.olsrd.v2.enable(true) then
+	os.execute('/etc/init.d/olsrd2 enable')
+
+	local addrs = { }
+	local lan = { }
+	local cfg = site.mesh.olsrd.v2
+	local config = uci:get_first("olsrd2", "olsrv2")
+
+	-- set global config
+	local olsr2Config = {
+		failfast = 'no',
+		pidfile = '/var/run/olsrd2.pid',
+		lockfile = '/var/lock/olsrd2'
+	}
+
+	local extraConf = cfg.config()
+	if extraConf then
+		for k, _ in pairs(extraConf) do
+			olsr2Config[k] = extraConf[k]
+		end
+	end
+
+	uci:delete_all('olsrd2', 'global')
+	uci:section('olsrd2', 'global', 'global', olsr2Config)
+
+	uci:delete_all('olsrd2', 'telnet')
+	uci:section('olsrd2', 'telnet', 'telnet', {
+
+	})
+
+	uci:delete_all('olsrd2', 'http')
+	uci:section('olsrd2', 'http', 'http', {
+
+	})
+
+	if cfg.lan() then
+		lan = cfg.lan()
+	end
+
+	table.insert(addrs, '-127.0.0.1/8')
+	table.insert(addrs, '-::1/128')
+
+	table.insert(addrs, 'default_accept')
+
+	uci:set("olsrd2", config, "originator", addrs)
+	uci:set("olsrd2", config, "lan", lan)
+
+	if #intf.wired_mesh then
+		uci:section('olsrd2', 'interface', 'wired_mesh', {
+			ifname = intf.wired_mesh,
+			bindto = addrs,
+		})
+	end
+
+	if #intf.vpn_mesh then
+		uci:section('olsrd2', 'interface', 'vpn_mesh', {
+			ifname = intf.vpn_mesh,
+			bindto = addrs,
+		})
+	end
+
+	if #intf.radio_mesh then
+		uci:section('olsrd2', 'interface', 'radio_mesh', {
+			ifname = intf.radio_mesh,
+			bindto = addrs,
+		})
+	end
+
+	uci:section('olsrd2', 'interface', 'loopback', {
+		ifname = { 'loopback' },
+		bindto = addrs,
+	})
+
+	uci:section('firewall', 'rule', 'allow_olsr2_mesh', {
+		src = 'mesh',
+		dest_port = '269',
+		proto = 'udp',
+		target = 'ACCEPT',
+	})
+else
+	-- site.mesh.olsrd.v2.enable false
+	os.execute('/etc/init.d/olsrd2 disable')
+	uci:delete('firewall', 'allow_olsr2_mesh')
+end
+uci:save('olsrd2')
+uci:save('firewall')
+uci:save('network')
diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw
new file mode 100755
index 0000000000000000000000000000000000000000..65387e8755b48f16ef92657bfbdad4a7ba22d0d7
--- /dev/null
+++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw
@@ -0,0 +1,10 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+local util = require 'gluon.util'
+
+local networks = uci:get_list('firewall', 'drop', 'network')
+util.remove_from_set(networks, 'client')
+uci:set_list('firewall', 'drop', 'network', networks)
+
+uci:save('firewall')
diff --git a/package/gluon-mesh-olsrd/usr/lib/autoupdater/abort.d/10olsrd b/package/gluon-mesh-olsrd/usr/lib/autoupdater/abort.d/10olsrd
new file mode 100755
index 0000000000000000000000000000000000000000..ae0c4f36dea2fcf97bc31527742f023386ff6fa1
--- /dev/null
+++ b/package/gluon-mesh-olsrd/usr/lib/autoupdater/abort.d/10olsrd
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. /lib/gluon/autoupdater/lib.sh
+
+
+start_enabled olsrd2
diff --git a/package/gluon-mesh-olsrd/usr/lib/autoupdater/upgrade.d/10olsrd b/package/gluon-mesh-olsrd/usr/lib/autoupdater/upgrade.d/10olsrd
new file mode 100755
index 0000000000000000000000000000000000000000..bbb6b54525c5db5d22613fecbe2f558275d9953b
--- /dev/null
+++ b/package/gluon-mesh-olsrd/usr/lib/autoupdater/upgrade.d/10olsrd
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. /lib/gluon/autoupdater/lib.sh
+
+
+stop olsrd2
diff --git a/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch b/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4b8c9aad769d693440b910ebdbbbc6400ff4368d
--- /dev/null
+++ b/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch
@@ -0,0 +1,20 @@
+From: Maciej Krüger <mkg20001@gmail.com>
+Date: Sun, 10 Apr 2022 01:58:41 +0200
+Subject: oonf-olsrd2: add support to check if service is running
+
+diff --git a/oonf-olsrd2/files/olsrd2.init b/oonf-olsrd2/files/olsrd2.init
+index debae9883258b821a5ea0aecebe879ddc84e29eb..b6c1e9a5522788005db850ceaf6699aa1eee6877 100755
+--- a/oonf-olsrd2/files/olsrd2.init
++++ b/oonf-olsrd2/files/olsrd2.init
+@@ -3,4 +3,11 @@
+ START=82
+ DAEMON='olsrd2'
+ 
++running() {
++  test -e "/tmp/run/olsrd2.pid" && test -e "/proc/$(cat "/tmp/run/olsrd2.pid")" && return 0
++  return 1
++}
++
++extra_command "running" "Check if service is running"
++
+ . /lib/functions/oonf_init.sh
diff --git a/targets/generic b/targets/generic
index c9bce1e0d87726b9255c633ad550da37ed4043c6..20111220c6a63bf1831c060111bbcb5351214fb2 100644
--- a/targets/generic
+++ b/targets/generic
@@ -68,6 +68,8 @@ config('KERNEL_SECCOMP', false)
 -- use try_config, so enabling the package is still possible
 try_config('PACKAGE_kmod-mt7915e', false)
 
+try_config('OONF_GENERIC_HTTP', true)
+
 config('COLLECT_KERNEL_DEBUG', true)
 
 config('TARGET_MULTI_PROFILE', true)