Skip to content

Commit

Permalink
feat: adding ns-binding package
Browse files Browse the repository at this point in the history
  • Loading branch information
Tbaile committed Nov 18, 2024
1 parent b68c938 commit d966319
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions config/ns-binding.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_PACKAGE_ns-binding=y
51 changes: 51 additions & 0 deletions packages/ns-binding/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#
# Copyright (C) 2024 Nethesis
#
# This is free software, licensed under the GNU General Public License v3.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=ns-binding
PKG_VERSION:=0.0.1
PKG_RELEASE:=1

PKG_BUILD_DIR:=$(BUILD_DIR)/ns-binding-$(PKG_VERSION)

PKG_MAINTAINER:=Tommaso Bailetti <tommaso.bailetti@nethesis.it>
PKG_LICENSE:=GPL-3.0-only

include $(INCLUDE_DIR)/package.mk

define Package/ns-binding
SECTION:=base
CATEGORY:=NethSecurity
TITLE:=MAC/IP binding
URL:=https://github.com/NethServer/nethsecurity
DEPENDS:=+python3-nethsec +dnsmasq-full +ns-api
PKGARCH:=all
endef

define Package/ns-binding/description
Tool to force bind MAC addresses to IP addresses
endef

define Build/Compile
endef

define Package/ns-binding/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
/etc/init.d/ns-binding restart
fi
exit 0
endef

define Package/ns-binding/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) ./files/ns-binding.py $(1)/usr/sbin/ns-binding
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/ns-binding.init $(1)/etc/init.d/ns-binding
endef

$(eval $(call BuildPackage,ns-binding))
30 changes: 30 additions & 0 deletions packages/ns-binding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ns-binding

This package provides a way to create IP/MAC bindings in DHCP-managed networks.

## Usage

Once configured a DHCP server through the UI, you can create bindings for the interface setting a `ns_binding` option
in the DHCP server configuration. The IP/MAC bindings will be created off the static leases defined in the
configuration.

For instance, if the DHCP server is named `GREEN_1`, to add the necessary rules for the interface you must:

```bash
uci set dhcp.GREEN_1.ns_binding='1'
uci commit dhcp
reload_config
```

This will create the necessary rules to bind the IP/MAC addresses in the network based off the static leases.

From now on, the IP/MAC bindings will be automatically generated and updated whenever the `dhcp` configuration is
changed.

To disable the binding you can either remove the `ns_binding` option or set it to `0`.

```bash
uci set dhcp.GREEN_1.ns_binding='0'
uci commit dhcp
reload_config
```
27 changes: 27 additions & 0 deletions packages/ns-binding/files/ns-binding.init
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh /etc/rc.common

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

USE_PROCD=1

start_service() {
# Main service
procd_open_instance
procd_set_param command /usr/sbin/ns-binding
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}

reload_service()
{
restart
}

service_triggers()
{
procd_add_reload_trigger dhcp
}
76 changes: 76 additions & 0 deletions packages/ns-binding/files/ns-binding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

from euci import EUci
from nethsec import utils
import subprocess

e_uci = EUci()

# Filter out DHCP servers that are not ns-binding enabled
network_interfaces = {
name: iface.get('device')
for name, iface in utils.get_all_by_type(e_uci, 'network', 'interface').items()
}
dhcp_servers = [
server for server in utils.get_all_by_type(e_uci, 'dhcp', 'dhcp').values()
if server.get('ns_binding', '0') == '1' and server.get('interface') in network_interfaces
]
# generate list of interfaces used by DHCP servers
dhcp_interfaces = [
network_interfaces[server.get('interface')]
for server in dhcp_servers
]

if len(dhcp_interfaces) > 0:
# reservation entry
reservations = {
reservation['mac']: reservation['ip']
for reservation in utils.get_all_by_type(e_uci, 'dhcp', 'host').values()
}
# file generation
binding_items = [f'{mac} . {ip}' for mac, ip in reservations.items()]
other_interfaces = [iface for iface in network_interfaces.values() if iface not in dhcp_interfaces]
file = f"""
table inet ns-binding
delete table inet ns-binding
table inet ns-binding {{
set bindingListV4 {{
type ether_addr . ipv4_addr
policy memory
flags interval
auto-merge
{'elements = {' if len(binding_items) > 0 else ''}
{', '.join(binding_items)}
{'}' if len(binding_items) > 0 else ''}
}}
# if interface is not in other_interfaces, allow dhcp queries and check with bindingListV4 for all rest
chain input {{
type filter hook input priority -110; policy drop;
ct state established,related counter accept
iifname {{ {' , '.join(other_interfaces)} }} counter accept
udp dport 67 counter accept
udp dport 68 counter accept
ether saddr . ip saddr @bindingListV4 counter accept
}}
chain lan-forward {{
type filter hook forward priority -110; policy drop;
ct state established,related counter accept
iifname {{ {' , '.join(other_interfaces)} }} counter accept
ether saddr . ip saddr @bindingListV4 counter accept
}}
}}
"""
subprocess.run(['/usr/sbin/nft', '-f', '-'], input=file.encode(), check=True)
print('ns-binding table created')
else:
subprocess.run(['/usr/sbin/nft', 'delete', 'table', 'inet', 'ns-binding'], capture_output=True)
print('ns-binding table deleted')

0 comments on commit d966319

Please sign in to comment.