Update to add kekyl

This commit is contained in:
Jeremy McGee 2025-09-11 14:57:54 -06:00
parent fee78c7344
commit ef38f076c6
28 changed files with 23388 additions and 11 deletions

View File

@ -1,12 +1,13 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
# Build and deploy Jekyll site from /docs to GitHub Pages
name: Deploy Jekyll (docs subdir)
on:
# Runs on pushes targeting the default branch
push:
branches: ["jekyll"]
# Allows you to run this workflow manually from the Actions tab
branches:
- "main"
paths:
- "docs/**"
# Allows manual runs from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
@ -15,8 +16,7 @@ permissions:
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
# Allow only one concurrent deployment; don't cancel in-progress runs
concurrency:
group: "pages"
cancel-in-progress: false
@ -28,15 +28,22 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
# Build Jekyll from the docs/ directory
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
source: ./docs
destination: ./docs/_site
# Upload the built site from docs/_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/_site/
# Deployment job
deploy:
@ -48,4 +55,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v4

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
.site-nav ul li a {
background-image: linear-gradient(-90deg, rgb(31.6333333333, 30.8222222222, 34.8777777778) 0%, rgba(31.6333333333, 30.8222222222, 34.8777777778, 0.8) 80%, rgba(31.6333333333, 30.8222222222, 34.8777777778, 0) 100%);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,574 @@
(function (jtd, undefined) {
// Event handling
jtd.addEvent = function(el, type, handler) {
if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
}
jtd.removeEvent = function(el, type, handler) {
if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
}
jtd.onReady = function(ready) {
// in case the document is already rendered
if (document.readyState!='loading') ready();
// modern browsers
else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready);
// IE <= 8
else document.attachEvent('onreadystatechange', function(){
if (document.readyState=='complete') ready();
});
}
// Show/hide mobile menu
function initNav() {
jtd.addEvent(document, 'click', function(e){
var target = e.target;
while (target && !(target.classList && target.classList.contains('nav-list-expander'))) {
target = target.parentNode;
}
if (target) {
e.preventDefault();
target.ariaPressed = target.parentNode.classList.toggle('active');
}
});
const siteNav = document.getElementById('site-nav');
const mainHeader = document.getElementById('main-header');
const menuButton = document.getElementById('menu-button');
disableHeadStyleSheets();
jtd.addEvent(menuButton, 'click', function(e){
e.preventDefault();
if (menuButton.classList.toggle('nav-open')) {
siteNav.classList.add('nav-open');
mainHeader.classList.add('nav-open');
menuButton.ariaPressed = true;
} else {
siteNav.classList.remove('nav-open');
mainHeader.classList.remove('nav-open');
menuButton.ariaPressed = false;
}
});
}
// The <head> element is assumed to include the following stylesheets:
// - a <link> to /assets/css/just-the-docs-head-nav.css,
// with id 'jtd-head-nav-stylesheet'
// - a <style> containing the result of _includes/css/activation.scss.liquid.
// To avoid relying on the order of stylesheets (which can change with HTML
// compression, user-added JavaScript, and other side effects), stylesheets
// are only interacted with via ID
function disableHeadStyleSheets() {
const headNav = document.getElementById('jtd-head-nav-stylesheet');
if (headNav) {
headNav.disabled = true;
}
const activation = document.getElementById('jtd-nav-activation');
if (activation) {
activation.disabled = true;
}
}
// Site search
function initSearch() {
var request = new XMLHttpRequest();
request.open('GET', '/assets/js/search-data.json', true);
request.onload = function(){
if (request.status >= 200 && request.status < 400) {
var docs = JSON.parse(request.responseText);
lunr.tokenizer.separator = /[\s\-/]+/
var index = lunr(function(){
this.ref('id');
this.field('title', { boost: 200 });
this.field('content', { boost: 2 });
this.field('relUrl');
this.metadataWhitelist = ['position']
for (var i in docs) {
this.add({
id: i,
title: docs[i].title,
content: docs[i].content,
relUrl: docs[i].relUrl
});
}
});
searchLoaded(index, docs);
} else {
console.log('Error loading ajax request. Request status:' + request.status);
}
};
request.onerror = function(){
console.log('There was a connection error');
};
request.send();
}
function searchLoaded(index, docs) {
var index = index;
var docs = docs;
var searchInput = document.getElementById('search-input');
var searchResults = document.getElementById('search-results');
var mainHeader = document.getElementById('main-header');
var currentInput;
var currentSearchIndex = 0;
function showSearch() {
document.documentElement.classList.add('search-active');
}
function hideSearch() {
document.documentElement.classList.remove('search-active');
}
function update() {
currentSearchIndex++;
var input = searchInput.value;
if (input === '') {
hideSearch();
} else {
showSearch();
// scroll search input into view, workaround for iOS Safari
window.scroll(0, -1);
setTimeout(function(){ window.scroll(0, 0); }, 0);
}
if (input === currentInput) {
return;
}
currentInput = input;
searchResults.innerHTML = '';
if (input === '') {
return;
}
var results = index.query(function (query) {
var tokens = lunr.tokenizer(input)
query.term(tokens, {
boost: 10
});
query.term(tokens, {
wildcard: lunr.Query.wildcard.TRAILING
});
});
if ((results.length == 0) && (input.length > 2)) {
var tokens = lunr.tokenizer(input).filter(function(token, i) {
return token.str.length < 20;
})
if (tokens.length > 0) {
results = index.query(function (query) {
query.term(tokens, {
editDistance: Math.round(Math.sqrt(input.length / 2 - 1))
});
});
}
}
if (results.length == 0) {
var noResultsDiv = document.createElement('div');
noResultsDiv.classList.add('search-no-result');
noResultsDiv.innerText = 'No results found';
searchResults.appendChild(noResultsDiv);
} else {
var resultsList = document.createElement('ul');
resultsList.classList.add('search-results-list');
searchResults.appendChild(resultsList);
addResults(resultsList, results, 0, 10, 100, currentSearchIndex);
}
function addResults(resultsList, results, start, batchSize, batchMillis, searchIndex) {
if (searchIndex != currentSearchIndex) {
return;
}
for (var i = start; i < (start + batchSize); i++) {
if (i == results.length) {
return;
}
addResult(resultsList, results[i]);
}
setTimeout(function() {
addResults(resultsList, results, start + batchSize, batchSize, batchMillis, searchIndex);
}, batchMillis);
}
function addResult(resultsList, result) {
var doc = docs[result.ref];
var resultsListItem = document.createElement('li');
resultsListItem.classList.add('search-results-list-item');
resultsList.appendChild(resultsListItem);
var resultLink = document.createElement('a');
resultLink.classList.add('search-result');
resultLink.setAttribute('href', doc.url);
resultsListItem.appendChild(resultLink);
var resultTitle = document.createElement('div');
resultTitle.classList.add('search-result-title');
resultLink.appendChild(resultTitle);
// note: the SVG svg-doc is only loaded as a Jekyll include if site.search_enabled is true; see _includes/icons/icons.html
var resultDoc = document.createElement('div');
resultDoc.classList.add('search-result-doc');
resultDoc.innerHTML = '<svg viewBox="0 0 24 24" class="search-result-icon"><use xlink:href="#svg-doc"></use></svg>';
resultTitle.appendChild(resultDoc);
var resultDocTitle = document.createElement('div');
resultDocTitle.classList.add('search-result-doc-title');
resultDocTitle.innerHTML = doc.doc;
resultDoc.appendChild(resultDocTitle);
var resultDocOrSection = resultDocTitle;
if (doc.doc != doc.title) {
resultDoc.classList.add('search-result-doc-parent');
var resultSection = document.createElement('div');
resultSection.classList.add('search-result-section');
resultSection.innerHTML = doc.title;
resultTitle.appendChild(resultSection);
resultDocOrSection = resultSection;
}
var metadata = result.matchData.metadata;
var titlePositions = [];
var contentPositions = [];
for (var j in metadata) {
var meta = metadata[j];
if (meta.title) {
var positions = meta.title.position;
for (var k in positions) {
titlePositions.push(positions[k]);
}
}
if (meta.content) {
var positions = meta.content.position;
for (var k in positions) {
var position = positions[k];
var previewStart = position[0];
var previewEnd = position[0] + position[1];
var ellipsesBefore = true;
var ellipsesAfter = true;
for (var k = 0; k < 5; k++) {
var nextSpace = doc.content.lastIndexOf(' ', previewStart - 2);
var nextDot = doc.content.lastIndexOf('. ', previewStart - 2);
if ((nextDot >= 0) && (nextDot > nextSpace)) {
previewStart = nextDot + 1;
ellipsesBefore = false;
break;
}
if (nextSpace < 0) {
previewStart = 0;
ellipsesBefore = false;
break;
}
previewStart = nextSpace + 1;
}
for (var k = 0; k < 10; k++) {
var nextSpace = doc.content.indexOf(' ', previewEnd + 1);
var nextDot = doc.content.indexOf('. ', previewEnd + 1);
if ((nextDot >= 0) && (nextDot < nextSpace)) {
previewEnd = nextDot;
ellipsesAfter = false;
break;
}
if (nextSpace < 0) {
previewEnd = doc.content.length;
ellipsesAfter = false;
break;
}
previewEnd = nextSpace;
}
contentPositions.push({
highlight: position,
previewStart: previewStart, previewEnd: previewEnd,
ellipsesBefore: ellipsesBefore, ellipsesAfter: ellipsesAfter
});
}
}
}
if (titlePositions.length > 0) {
titlePositions.sort(function(p1, p2){ return p1[0] - p2[0] });
resultDocOrSection.innerHTML = '';
addHighlightedText(resultDocOrSection, doc.title, 0, doc.title.length, titlePositions);
}
if (contentPositions.length > 0) {
contentPositions.sort(function(p1, p2){ return p1.highlight[0] - p2.highlight[0] });
var contentPosition = contentPositions[0];
var previewPosition = {
highlight: [contentPosition.highlight],
previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd,
ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter
};
var previewPositions = [previewPosition];
for (var j = 1; j < contentPositions.length; j++) {
contentPosition = contentPositions[j];
if (previewPosition.previewEnd < contentPosition.previewStart) {
previewPosition = {
highlight: [contentPosition.highlight],
previewStart: contentPosition.previewStart, previewEnd: contentPosition.previewEnd,
ellipsesBefore: contentPosition.ellipsesBefore, ellipsesAfter: contentPosition.ellipsesAfter
}
previewPositions.push(previewPosition);
} else {
previewPosition.highlight.push(contentPosition.highlight);
previewPosition.previewEnd = contentPosition.previewEnd;
previewPosition.ellipsesAfter = contentPosition.ellipsesAfter;
}
}
var resultPreviews = document.createElement('div');
resultPreviews.classList.add('search-result-previews');
resultLink.appendChild(resultPreviews);
var content = doc.content;
for (var j = 0; j < Math.min(previewPositions.length, 3); j++) {
var position = previewPositions[j];
var resultPreview = document.createElement('div');
resultPreview.classList.add('search-result-preview');
resultPreviews.appendChild(resultPreview);
if (position.ellipsesBefore) {
resultPreview.appendChild(document.createTextNode('... '));
}
addHighlightedText(resultPreview, content, position.previewStart, position.previewEnd, position.highlight);
if (position.ellipsesAfter) {
resultPreview.appendChild(document.createTextNode(' ...'));
}
}
}
var resultRelUrl = document.createElement('span');
resultRelUrl.classList.add('search-result-rel-url');
resultRelUrl.innerText = doc.relUrl;
resultTitle.appendChild(resultRelUrl);
}
function addHighlightedText(parent, text, start, end, positions) {
var index = start;
for (var i in positions) {
var position = positions[i];
var span = document.createElement('span');
span.innerHTML = text.substring(index, position[0]);
parent.appendChild(span);
index = position[0] + position[1];
var highlight = document.createElement('span');
highlight.classList.add('search-result-highlight');
highlight.innerHTML = text.substring(position[0], index);
parent.appendChild(highlight);
}
var span = document.createElement('span');
span.innerHTML = text.substring(index, end);
parent.appendChild(span);
}
}
jtd.addEvent(searchInput, 'focus', function(){
setTimeout(update, 0);
});
jtd.addEvent(searchInput, 'keyup', function(e){
switch (e.keyCode) {
case 27: // When esc key is pressed, hide the results and clear the field
searchInput.value = '';
break;
case 38: // arrow up
case 40: // arrow down
case 13: // enter
e.preventDefault();
return;
}
update();
});
jtd.addEvent(searchInput, 'keydown', function(e){
switch (e.keyCode) {
case 38: // arrow up
e.preventDefault();
var active = document.querySelector('.search-result.active');
if (active) {
active.classList.remove('active');
if (active.parentElement.previousSibling) {
var previous = active.parentElement.previousSibling.querySelector('.search-result');
previous.classList.add('active');
}
}
return;
case 40: // arrow down
e.preventDefault();
var active = document.querySelector('.search-result.active');
if (active) {
if (active.parentElement.nextSibling) {
var next = active.parentElement.nextSibling.querySelector('.search-result');
active.classList.remove('active');
next.classList.add('active');
}
} else {
var next = document.querySelector('.search-result');
if (next) {
next.classList.add('active');
}
}
return;
case 13: // enter
e.preventDefault();
var active = document.querySelector('.search-result.active');
if (active) {
active.click();
} else {
var first = document.querySelector('.search-result');
if (first) {
first.click();
}
}
return;
}
});
jtd.addEvent(document, 'click', function(e){
if (e.target != searchInput) {
hideSearch();
}
});
}
// Switch theme
jtd.getTheme = function() {
var cssFileHref = document.querySelector('[rel="stylesheet"]').getAttribute('href');
return cssFileHref.substring(cssFileHref.lastIndexOf('-') + 1, cssFileHref.length - 4);
}
jtd.setTheme = function(theme) {
var cssFile = document.querySelector('[rel="stylesheet"]');
cssFile.setAttribute('href', '/assets/css/just-the-docs-' + theme + '.css');
}
// Note: pathname can have a trailing slash on a local jekyll server
// and not have the slash on GitHub Pages
function navLink() {
var pathname = document.location.pathname;
var navLink = document.getElementById('site-nav').querySelector('a[href="' + pathname + '"]');
if (navLink) {
return navLink;
}
// The `permalink` setting may produce navigation links whose `href` ends with `/` or `.html`.
// To find these links when `/` is omitted from or added to pathname, or `.html` is omitted:
if (pathname.endsWith('/') && pathname != '/') {
pathname = pathname.slice(0, -1);
}
if (pathname != '/') {
navLink = document.getElementById('site-nav').querySelector('a[href="' + pathname + '"], a[href="' + pathname + '/"], a[href="' + pathname + '.html"]');
if (navLink) {
return navLink;
}
}
return null; // avoids `undefined`
}
// Scroll site-nav to ensure the link to the current page is visible
function scrollNav() {
const targetLink = navLink();
if (targetLink) {
targetLink.scrollIntoView({ block: "center" });
targetLink.removeAttribute('href');
}
}
// Find the nav-list-link that refers to the current page
// then make it and all enclosing nav-list-item elements active.
function activateNav() {
var target = navLink();
if (target) {
target.classList.toggle('active', true);
}
while (target) {
while (target && !(target.classList && target.classList.contains('nav-list-item'))) {
target = target.parentNode;
}
if (target) {
target.classList.toggle('active', true);
target = target.parentNode;
}
}
}
// Document ready
jtd.onReady(function(){
if (document.getElementById('site-nav')) {
initNav();
activateNav();
scrollNav();
}
initSearch();
});
// Copy button on code
jtd.onReady(function(){
if (!window.isSecureContext) {
console.log('Window does not have a secure context, therefore code clipboard copy functionality will not be available. For more details see https://web.dev/async-clipboard/#security-and-permissions');
return;
}
var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock > div.content, figure.highlight');
// note: the SVG svg-copied and svg-copy is only loaded as a Jekyll include if site.enable_copy_code_button is true; see _includes/icons/icons.html
var svgCopied = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copied"></use></svg>';
var svgCopy = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copy"></use></svg>';
codeBlocks.forEach(codeBlock => {
var copyButton = document.createElement('button');
var timeout = null;
copyButton.type = 'button';
copyButton.ariaLabel = 'Copy code to clipboard';
copyButton.innerHTML = svgCopy;
codeBlock.append(copyButton);
copyButton.addEventListener('click', function () {
if(timeout === null) {
var code = (codeBlock.querySelector('pre:not(.lineno, .highlight)') || codeBlock.querySelector('code')).innerText;
window.navigator.clipboard.writeText(code);
copyButton.innerHTML = svgCopied;
var timeoutSetting = 4000;
timeout = setTimeout(function () {
copyButton.innerHTML = svgCopy;
timeout = null;
}, timeoutSetting);
}
});
});
});
})(window.jtd = window.jtd || {});

View File

@ -0,0 +1,2 @@
{
}

61
docs/_site/assets/js/vendor/lunr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,154 @@
# Setup batman-adv
B.A.T.M.A.N. advanced (often referenced as batman-adv) is an implementation of the B.A.T.M.A.N. routing protocol in form of a linux kernel module operating on layer 2.
B.A.T.M.A.N. is a proactive routing protocol for Wireless Ad-hoc Mesh Networks, including (but not limited to) Mobile Ad-hoc Networks (MANETs). The protocol proactively maintains information about the existence of all nodes in the mesh that are accessible via single-hop or multi-hop communication links. The strategy of B.A.T.M.A.N. is to determine for each destination in the mesh one single-hop neighbor, which can be utilized as best gateway to communicate with the destination node. In order to perform multi-hop IP-based routing, the routing table of a node must contain a link-local gateway for each host or network route. To learn about the best next-hop for each destination is all that the B.A.T.M.A.N. algorithm cares about. There is no need to find out or calculate the complete route, which makes a very fast and efficient implementation possible.
**Important: Follow the 802.11s Wizard through the WebUI BEFORE setting up batman-adv**
## Setup Server Gateway
The "Server" Gateway node type is used to uplink internet access or connect another non mesh network to your 802.11s mesh. You will want to follow the 802.11s Mesh Wizard using the `Mesh Gate` mode.
Navigate to the LuCI web interface in your web browser, typically at http://10.42.0.1
From the sidebar menu goto **Network > Interfaces**
![Add Bat0 Device](img/Add-bat0-device.png)
Click on **Add New Interface** Button
![Add Bat0 Device Interface](img/bat0-interface.png)
From the drop-down menu `Protocol` select `Batman Device`. Give the device a name like **bat0**. Then click Create interface. This will create a tunnel device called `bat0`. OSI Layer 2 packets/frames sent over the the tunnel will be routed using the batman protocol. Next, we will be prompted to configure some batman-specific options. You can edit these options again later by clicking the Edit button next to the bat0 interface.
![Configure Bat0 Device](img/bat0-device-config.png)
You can accept most of the default options. Navigate to the `Mesh Routing` tab to make a few recommended changes.
![Mesh Routing Options](img/bat0-mesh-routing-server.png)
First, select the Routing Algorithm you want. You want to use BATMAN_V. All mesh nodes must be configured to use the same version of the batman protocol. Next, check the box to Avoid Bridge Loops. Set Gateway Mode to `Server` to explicitly tell batman that this router will be a gateway to the Internet. (Later, when configuring client nodes, we will set Gateway Mode to Client.)
Save your changes. You are finished setting up the bat0 device. Next we will create an alias interface for associating a radio with the batman mesh. Click Add new interface... again.
![Create Batman Interface](img/batman-interface-creation.png)
Select Batman Interface from the dropdown menu. Give the interface a name like batmesh. Important: do not associate the interface with a device in this menu. You will associate the interface with the bat0 batman device in the next step. For now, leave Device as unspecified and click Create Interface.
![Batman Interface Detail](img/batman-inferface-create-detail.png)
After you create the `batmesh_radio0` interface you will be prompted to configure its settings. Here you can set the Batman Device to bat0. You should not need to change any of the other default options.
`Save & Apply` your interface changes.
![Save & Apply](img/batman-save-interfaces.png)
### Bridging batman to the mesh
You will most likely want to bridge the `bat0` tunnel interface with the halow network. This is required to have batman-adv handle all of the routing across the 802.11s network.
Navigate to `Network > Interaces` and then click on the `Devices` tab.
![Bridge batman to Mesh](img/bridge-lan-to-mesh.png)
**Note**: if the `bat0` device does not appear in the list of devices and you have just created it in the previous step, you may have to click `Save & Apply` for the `bat0` device to actually be created. Once you can see the `bat0` device in the list, click the Configure... button next to the `br-ahwlan` bridge device.
![bridge bat0 to ethernet](img/bridge-bat0-to-halow.png)
Check boxes next to the interfaces you want to be bridged. Due to an idiosyncracy of OpenWRT, you will not see any wireless interfaces here. Don't worry, we will connect them to the LAN in a later step. For now, as in this example, check the `bat0` device in addition to any lan ethernet ports. Then click `Save`.
### Configurating a radio for mesh
Navigate to `Network > Wireless`
![Wireless Overview](img/wireless-overview.png)
Create a **New** Wireless network on your 802.11ah wireless card. This will most likely be the wireless card labeled `radio0`
Configure your mesh mode, Mesh ID, and the network to associate it with. Mode should be `802.11s`, Your `Mesh ID` can be anything you want, but all nodes in your mesh **must** be the same `Mesh ID`. Your network should be `batmesh_radio0` or whatever you called your interface.
You can also configure your operating frequency bandwith and channel from here. **Your bandwidth and channel must match on all mesh nodes**
![Configure Wireless For Mesh](img/configure-mesh-wifi-for-batman.png)
Click on the `Wireless Security` tab to configure your `Encryption` type and `Key`. I strongly recommend using WPA3-SAE.
![Configure Wireless Security](img/configure-mesh-security.png)
Click on the `Mesh Settings` tab. You will want to **uncheck** `Forward mesh peer traffic`. This is required to allow traffic to be routed by batman-adv. You also want to check `Mesh Gate` since this is your gateway node.
![Configure Mesh Settings](img/configure-mesh-settings-server.png)
`Save & Apply` all of your settings.
## Setup Client Node
On your client node follow the 802.11s Mesh setup wizard for a `Mesh Point`
From the sidebar menu goto **Network > Interfaces**
![Add Bat0 Device](img/Add-bat0-device.png)
Click on **Add New Interface** Button
![Add Bat0 Device Interface](img/bat0-interface.png)
From the drop-down menu `Protocol` select `Batman Device`. Give the device a name like **bat0**. Then click Create interface. This will create a tunnel device called `bat0`. OSI Layer 2 packets/frames sent over the the tunnel will be routed using the batman protocol. Next, we will be prompted to configure some batman-specific options. You can edit these options again later by clicking the Edit button next to the bat0 interface.
![Configure Bat0 Device](img/bat0-device-config.png)
You can accept most of the default options. Navigate to the `Mesh Routing` tab to make a few recommended changes.
![Mesh Routing Options](img/bat0-mesh-routing-client.png)
First, select the Routing Algorithm you want. You want to use BATMAN_V. All mesh nodes must be configured to use the same version of the batman protocol. Next, check the box to Avoid Bridge Loops. Set Gateway Mode to `Client` to explicitly tell batman that this is a client node.
Save your changes. You are finished setting up the bat0 device. Next we will create an alias interface for associating a radio with the batman mesh. Click Add new interface... again.
![Create Batman Interface](img/batman-interface-creation.png)
Select Batman Interface from the dropdown menu. Give the interface a name like batmesh. Important: do not associate the interface with a device in this menu. You will associate the interface with the bat0 batman device in the next step. For now, leave Device as unspecified and click Create Interface.
![Batman Interface Detail](img/batman-inferface-create-detail.png)
After you create the `batmesh_radio0` interface you will be prompted to configure its settings. Here you can set the Batman Device to bat0. You should not need to change any of the other default options.
`Save & Apply` your interface changes.
![Save & Apply](img/batman-save-interfaces.png)
### Bridging batman to the mesh
You will most likely want to bridge the `bat0` tunnel interface with the halow network. This is required to have batman-adv handle all of the routing across the 802.11s network.
Navigate to `Network > Interaces` and then click on the `Devices` tab.
![Bridge batman to Mesh](img/bridge-lan-to-mesh.png)
**Note**: if the `bat0` device does not appear in the list of devices and you have just created it in the previous step, you may have to click `Save & Apply` for the `bat0` device to actually be created. Once you can see the `bat0` device in the list, click the Configure... button next to the `br-ahwlan` bridge device.
![bridge bat0 to ethernet](img/bridge-bat0-to-halow.png)
Check boxes next to the interfaces you want to be bridged. Due to an idiosyncracy of OpenWRT, you will not see any wireless interfaces here. Don't worry, we will connect them to the LAN in a later step. For now, as in this example, check the `bat0` device in addition to any lan ethernet ports. Then click `Save`.
### Configurating a radio for mesh
Navigate to `Network > Wireless`
![Wireless Overview](img/wireless-overview.png)
Create a **New** Wireless network on your 802.11ah wireless card. This will most likely be the wireless card labeled `radio0`
Configure your mesh mode, Mesh ID, and the network to associate it with. Mode should be `802.11s`, Your `Mesh ID` can be anything you want, but all nodes in your mesh **must** be the same `Mesh ID`. Your network should be `batmesh_radio0` or whatever you called your interface.
You can also configure your operating frequency bandwith and channel from here. **Your bandwidth and channel must match on all mesh nodes**
![Configure Wireless For Mesh](img/configure-mesh-wifi-for-batman.png)
Click on the `Wireless Security` tab to configure your `Encryption` type and `Key`. I strongly recommend using WPA3-SAE.
![Configure Wireless Security](img/configure-mesh-security.png)
Click on the `Mesh Settings` tab. You will want to **uncheck** `Forward mesh peer traffic`. This is required to allow traffic to be routed by batman-adv. You also want to check `Mesh Gate` since this is your gateway node.
![Configure Mesh Settings](img/configure-mesh-settings-client.png)
`Save & Apply` all of your settings.
Once you have both a server and client node configured and assuming they are in range you should see them automatically associate with each other and you have successfully created an 802.11s mesh with B.A.T.M.A.N ADV. Congratulations!

1
docs/_site/feed.xml Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="http://localhost:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" /><updated>2025-09-11T14:57:38-06:00</updated><id>http://localhost:4000/feed.xml</id><title type="html">OpenMANET</title><subtitle>OpenMANET is a Raspberry Pibased MANET radio built on Wi-Fi HaLow (802.11ah). Designed around Morse Micro HATs/SDIO boards and OpenWrt, it supports 802.11s + batman-adv, GPSD-driven range tests, and a PTT app—aiming for a rugged, field-ready kit. A custom BCF enables ~27 dBm TX power vs Seeed defaults.</subtitle></feed>

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB