summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2024-09-08 08:13:39 +0200
committerGitHub <noreply@github.com>2024-09-08 08:13:39 +0200
commit917325fafc9365a722235222f8635653079a5854 (patch)
tree26d03e228ca55acc5fc29d4ad18e8331b79e8605
parent70122746469e930a6775e0c42c4d1fd30bc6d514 (diff)
parentc9a5a93834fce33f1e999dd50381cb7ff9e5fabb (diff)
downloadvyos-build-917325fafc9365a722235222f8635653079a5854.tar.gz
vyos-build-917325fafc9365a722235222f8635653079a5854.zip
Merge pull request #756 from sever-sever/T6674-circ
T6674: Add build-scrips for packages without Jenkins
-rw-r--r--.github/workflows/trigger_rebuild_packages.yml198
-rw-r--r--scripts/package-build/aws-gwlbtun/.gitignore8
l---------scripts/package-build/aws-gwlbtun/build.py1
-rw-r--r--scripts/package-build/aws-gwlbtun/package.toml59
-rwxr-xr-xscripts/package-build/build.py184
-rw-r--r--scripts/package-build/ddclient/.gitignore7
l---------scripts/package-build/ddclient/build.py1
-rw-r--r--scripts/package-build/ddclient/package.toml4
-rw-r--r--scripts/package-build/dropbear/.gitignore7
l---------scripts/package-build/dropbear/build.py1
-rw-r--r--scripts/package-build/dropbear/package.toml7
-rw-r--r--scripts/package-build/dropbear/patches/0001-Enable-PAM-support.patch61
-rw-r--r--scripts/package-build/ethtool/.gitignore7
l---------scripts/package-build/ethtool/build.py1
-rw-r--r--scripts/package-build/ethtool/package.toml4
-rw-r--r--scripts/package-build/frr/.gitignore8
l---------scripts/package-build/frr/build.py1
-rw-r--r--scripts/package-build/frr/package.toml36
-rw-r--r--scripts/package-build/hostap/.gitignore7
-rw-r--r--scripts/package-build/hostap/README.md4
l---------scripts/package-build/hostap/build.py1
-rwxr-xr-xscripts/package-build/hostap/build.sh38
-rw-r--r--scripts/package-build/hostap/package.toml12
-rw-r--r--scripts/package-build/hsflowd/.gitignore6
l---------scripts/package-build/hsflowd/build.py1
-rw-r--r--scripts/package-build/hsflowd/package.toml8
-rw-r--r--scripts/package-build/isc-dhcp/.gitignore7
l---------scripts/package-build/isc-dhcp/build.py1
-rw-r--r--scripts/package-build/isc-dhcp/package.toml7
-rw-r--r--scripts/package-build/isc-dhcp/patches/0001-Add-support-for-raw-IP-interface-type.patch248
-rw-r--r--scripts/package-build/isc-dhcp/patches/0002-Checkpoint-improved-patch.patch170
-rw-r--r--scripts/package-build/isc-dhcp/patches/0003-fix-compilation-errors.patch48
-rw-r--r--scripts/package-build/isc-dhcp/patches/0004-add-support-for-ARPHRD_NONE-interface-type.patch29
-rw-r--r--scripts/package-build/kea/.gitignore7
l---------scripts/package-build/kea/build.py1
-rw-r--r--scripts/package-build/kea/package.toml4
-rw-r--r--scripts/package-build/keepalived/.gitignore7
l---------scripts/package-build/keepalived/build.py1
-rw-r--r--scripts/package-build/keepalived/package.toml4
-rw-r--r--scripts/package-build/keepalived/patches/0001-vrrp-Set-sysctl-arp_ignore-to-1-on-IPv6-VMACs.patch129
-rw-r--r--scripts/package-build/linux-kernel/.gitignore32
-rw-r--r--scripts/package-build/linux-kernel/README.md41
l---------scripts/package-build/linux-kernel/arch1
-rwxr-xr-xscripts/package-build/linux-kernel/build-accel-ppp.sh42
-rwxr-xr-xscripts/package-build/linux-kernel/build-intel-ixgbe.sh107
-rwxr-xr-xscripts/package-build/linux-kernel/build-intel-ixgbevf.sh100
-rwxr-xr-xscripts/package-build/linux-kernel/build-intel-qat.sh111
-rwxr-xr-xscripts/package-build/linux-kernel/build-jool.py98
-rwxr-xr-xscripts/package-build/linux-kernel/build-kernel.sh56
-rwxr-xr-xscripts/package-build/linux-kernel/build-linux-firmware.sh98
-rwxr-xr-xscripts/package-build/linux-kernel/build-nat-rtsp.sh38
-rwxr-xr-xscripts/package-build/linux-kernel/build-openvpn-dco.sh33
-rwxr-xr-xscripts/package-build/linux-kernel/build.py239
-rw-r--r--scripts/package-build/linux-kernel/package.toml62
l---------scripts/package-build/linux-kernel/patches1
-rw-r--r--scripts/package-build/ndppd/.gitignore7
l---------scripts/package-build/ndppd/build.py1
-rw-r--r--scripts/package-build/ndppd/package.toml4
-rw-r--r--scripts/package-build/ndppd/patches/0001-skip-route-table-if-there-is-no-auto-rule.patch83
-rw-r--r--scripts/package-build/ndppd/patches/0002-set-vyos-version.patch25
-rw-r--r--scripts/package-build/net-snmp/.gitignore6
l---------scripts/package-build/net-snmp/build.py1
-rw-r--r--scripts/package-build/net-snmp/package.toml5
-rw-r--r--scripts/package-build/net-snmp/patches/add-linux-6.7-compatibility-parsing.patch119
-rw-r--r--scripts/package-build/netfilter/.gitignore3
-rwxr-xr-xscripts/package-build/netfilter/build.py189
-rw-r--r--scripts/package-build/netfilter/package.toml11
-rw-r--r--scripts/package-build/netfilter/patches/pkg-nftables/0001-meta-fix-hour-decoding.patch118
-rw-r--r--scripts/package-build/opennhrp/.gitignore6
l---------scripts/package-build/opennhrp/build.py1
-rw-r--r--scripts/package-build/opennhrp/package.toml21
-rw-r--r--scripts/package-build/openvpn-otp/.gitignore6
l---------scripts/package-build/openvpn-otp/build.py1
-rw-r--r--scripts/package-build/openvpn-otp/package.toml19
-rw-r--r--scripts/package-build/owamp/.gitignore6
l---------scripts/package-build/owamp/build.py1
-rw-r--r--scripts/package-build/owamp/package.toml4
-rw-r--r--scripts/package-build/pam_tacplus/.gitignore7
l---------scripts/package-build/pam_tacplus/build.py1
-rw-r--r--scripts/package-build/pam_tacplus/package.toml19
-rw-r--r--scripts/package-build/pmacct/.gitignore6
l---------scripts/package-build/pmacct/build.py1
-rw-r--r--scripts/package-build/pmacct/package.toml4
-rw-r--r--scripts/package-build/pmacct/patches/0001-fix-pmacctd-SEGV-when-ICMP-ICMPv6-traffic-was-proces.patch49
-rw-r--r--scripts/package-build/podman/.gitignore7
l---------scripts/package-build/podman/build.py1
-rw-r--r--scripts/package-build/podman/package.toml27
-rw-r--r--scripts/package-build/pyhumps/.gitignore7
l---------scripts/package-build/pyhumps/build.py1
-rw-r--r--scripts/package-build/pyhumps/package.toml5
-rw-r--r--scripts/package-build/radvd/.gitignore6
l---------scripts/package-build/radvd/build.py1
-rw-r--r--scripts/package-build/radvd/package.toml23
-rw-r--r--scripts/package-build/strongswan/.gitignore6
-rwxr-xr-xscripts/package-build/strongswan/build-vici.sh57
l---------scripts/package-build/strongswan/build.py1
-rw-r--r--scripts/package-build/strongswan/package.toml45
-rw-r--r--scripts/package-build/strongswan/patches/0001-charon-add-optional-source-and-remote-overrides-for-.patch579
-rw-r--r--scripts/package-build/strongswan/patches/0002-vici-send-certificates-for-ike-sa-events.patch140
-rw-r--r--scripts/package-build/strongswan/patches/0003-vici-add-support-for-individual-sa-state-changes.patch159
-rw-r--r--scripts/package-build/strongswan/patches/0004-VyOS-disable-options-enabled-by-Debian-that-are-unus.patch115
-rw-r--r--scripts/package-build/telegraf/.gitignore6
-rw-r--r--scripts/package-build/telegraf/README.md4
l---------scripts/package-build/telegraf/build.py1
-rwxr-xr-xscripts/package-build/telegraf/build.sh27
-rw-r--r--scripts/package-build/telegraf/package.toml5
-rw-r--r--scripts/package-build/telegraf/plugins/inputs/all/all.go72
-rw-r--r--scripts/package-build/telegraf/plugins/outputs/all/all.go9
-rw-r--r--scripts/package-build/waagent/.gitignore8
l---------scripts/package-build/waagent/build.py1
-rw-r--r--scripts/package-build/waagent/package.toml7
-rw-r--r--scripts/package-build/wide-dhcpv6/.gitignore7
l---------scripts/package-build/wide-dhcpv6/build.py1
-rw-r--r--scripts/package-build/wide-dhcpv6/package.toml4
-rw-r--r--scripts/package-build/wide-dhcpv6/patches/0023-dhcpc6-support-per-interface-client-DUIDs.patch230
-rw-r--r--scripts/package-build/wide-dhcpv6/patches/0024-bind-to-single-socket.patch17
-rw-r--r--scripts/package-build/wide-dhcpv6/patches/0025-option-to-prevent-ia-release.patch155
117 files changed, 4852 insertions, 0 deletions
diff --git a/.github/workflows/trigger_rebuild_packages.yml b/.github/workflows/trigger_rebuild_packages.yml
new file mode 100644
index 00000000..0ca9cc28
--- /dev/null
+++ b/.github/workflows/trigger_rebuild_packages.yml
@@ -0,0 +1,198 @@
+name: Trigger to build package
+
+on:
+ push:
+ branches:
+ - circinus
+
+jobs:
+ changes:
+ runs-on: ubuntu-latest
+
+ env:
+ REF: main # Used for curl to trigger build package
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.ref_name }}
+
+ - uses: dorny/paths-filter@v3
+ id: changes
+ with:
+ base: ${{ github.ref_name }}
+ filters: |
+ aws-gwlbtun:
+ - 'scripts/package-build/aws-gwlbtun/**'
+ ddclient:
+ - 'scripts/package-build/ddclient/**'
+ dropbear:
+ - 'scripts/package-build/dropbear/**'
+ ethtool:
+ - 'scripts/package-build/ethtool/**'
+ frr:
+ - 'scripts/package-build/frr/**'
+ hostap:
+ - 'scripts/package-build/hostap/**'
+ hsflowd:
+ - 'scripts/package-build/hsflowd/**'
+ isc-dhcp:
+ - 'scripts/package-build/isc-dhcp/**'
+ kea:
+ - 'scripts/package-build/kea/**'
+ keepalived:
+ - 'scripts/package-build/keepalived/**'
+ linux-kernel:
+ - 'data/defaults.toml'
+ - 'scripts/package-build/linux-kernel/**'
+ ndppd:
+ - 'scripts/package-build/ndppd/**'
+ net-snmp:
+ - 'scripts/package-build/net-snmp/**'
+ netfilter:
+ - 'scripts/package-build/netfilter/**'
+ opennhrp:
+ - 'scripts/package-build/opennhrp/**'
+ openvpn-otp:
+ - 'scripts/package-build/openvpn-otp/**'
+ owamp:
+ - 'scripts/package-build/owamp/**'
+ pam_tacplus:
+ - 'scripts/package-build/pam_tacplus/**'
+ pmacct:
+ - 'scripts/package-build/pmacct/**'
+ podman:
+ - 'scripts/package-build/podman/**'
+ pyhumps:
+ - 'scripts/package-build/pyhumps/**'
+ radvd:
+ - 'scripts/package-build/radvd/**'
+ strongswan:
+ - 'scripts/package-build/strongswan/**'
+ telegraf:
+ - 'scripts/package-build/telegraf/**'
+ waagent:
+ - 'scripts/package-build/waagent/**'
+ wide-dhcpv6:
+ - 'scripts/package-build/wide-dhcpv6/**'
+
+ - name: Trigger builds for changed packages
+ run: |
+ set -eux
+ function trigger_build() {
+ PACKAGE_NAME=$1
+ echo "${PACKAGE_NAME} change detected!"
+ curl -L \
+ -X POST \
+ -H "Accept: application/vnd.github+json" \
+ -H "Authorization: Bearer ${{ secrets.PAT }}" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/${{ secrets.REMOTE_OWNER }}/${{ secrets.REMOTE_REUSE_REPO }}/actions/workflows/build-package.yml/dispatches \
+ -d '{"ref": "${{ env.REF }}", "inputs":{"package_name":"'"$PACKAGE_NAME"'", "gpg_key_id": "${{ secrets.GPG_KEY_ID }}", "package_branch": "${{ github.ref_name }}"}}'
+ }
+
+ # Trigger builds based on detected changes
+ if [ "${{ steps.changes.outputs.aws-gwlbtun }}" == "true" ]; then
+ trigger_build "aws-gwlbtun"
+ fi
+
+ if [ "${{ steps.changes.outputs.ddclient }}" == "true" ]; then
+ trigger_build "ddclient"
+ fi
+
+ if [ "${{ steps.changes.outputs.dropbear }}" == "true" ]; then
+ trigger_build "dropbear"
+ fi
+
+ if [ "${{ steps.changes.outputs.ethtool }}" == "true" ]; then
+ trigger_build "ethtool"
+ fi
+
+ if [ "${{ steps.changes.outputs.frr }}" == "true" ]; then
+ trigger_build "frr"
+ fi
+
+ if [ "${{ steps.changes.outputs.hostap }}" == "true" ]; then
+ trigger_build "hostap"
+ fi
+
+ if [ "${{ steps.changes.outputs.hsflowd }}" == "true" ]; then
+ trigger_build "hsflowd"
+ fi
+
+ if [ "${{ steps.changes.outputs.isc-dhcp }}" == "true" ]; then
+ trigger_build "isc-dhcp"
+ fi
+
+ if [ "${{ steps.changes.outputs.kea }}" == "true" ]; then
+ trigger_build "kea"
+ fi
+
+ if [ "${{ steps.changes.outputs.keepalived }}" == "true" ]; then
+ trigger_build "keepalived"
+ fi
+
+ if [ "${{ steps.changes.outputs.linux-kernel }}" == "true" ]; then
+ trigger_build "linux-kernel"
+ fi
+
+ if [ "${{ steps.changes.outputs.ndppd }}" == "true" ]; then
+ trigger_build "ndppd"
+ fi
+
+ if [ "${{ steps.changes.outputs.net-snmp }}" == "true" ]; then
+ trigger_build "net-snmp"
+ fi
+
+ if [ "${{ steps.changes.outputs.netfilter }}" == "true" ]; then
+ trigger_build "netfilter"
+ fi
+
+ if [ "${{ steps.changes.outputs.opennhrp }}" == "true" ]; then
+ trigger_build "opennhrp"
+ fi
+
+ if [ "${{ steps.changes.outputs.openvpn-otp }}" == "true" ]; then
+ trigger_build "openvpn-otp"
+ fi
+
+ if [ "${{ steps.changes.outputs.owamp }}" == "true" ]; then
+ trigger_build "owamp"
+ fi
+
+ if [ "${{ steps.changes.outputs.pam_tacplus }}" == "true" ]; then
+ trigger_build "pam_tacplus"
+ fi
+
+ if [ "${{ steps.changes.outputs.pmacct }}" == "true" ]; then
+ trigger_build "pmacct"
+ fi
+
+ if [ "${{ steps.changes.outputs.podman }}" == "true" ]; then
+ trigger_build "podman"
+ fi
+
+ if [ "${{ steps.changes.outputs.pyhumps }}" == "true" ]; then
+ trigger_build "pyhumps"
+ fi
+
+ if [ "${{ steps.changes.outputs.radvd }}" == "true" ]; then
+ trigger_build "radvd"
+ fi
+
+ if [ "${{ steps.changes.outputs.strongswan }}" == "true" ]; then
+ trigger_build "strongswan"
+ fi
+
+ if [ "${{ steps.changes.outputs.telegraf }}" == "true" ]; then
+ trigger_build "telegraf"
+ fi
+
+ if [ "${{ steps.changes.outputs.waagent }}" == "true" ]; then
+ trigger_build "waagent"
+ fi
+
+ if [ "${{ steps.changes.outputs.wide-dhcpv6 }}" == "true" ]; then
+ trigger_build "ethtool"
+ fi
diff --git a/scripts/package-build/aws-gwlbtun/.gitignore b/scripts/package-build/aws-gwlbtun/.gitignore
new file mode 100644
index 00000000..0fe7946f
--- /dev/null
+++ b/scripts/package-build/aws-gwlbtun/.gitignore
@@ -0,0 +1,8 @@
+aws-gwlbtun*/
+*.tar.gz
+*.tar.xz
+*.deb
+*.dsc
+*.buildinfo
+*.build
+*.changes \ No newline at end of file
diff --git a/scripts/package-build/aws-gwlbtun/build.py b/scripts/package-build/aws-gwlbtun/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/aws-gwlbtun/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/aws-gwlbtun/package.toml b/scripts/package-build/aws-gwlbtun/package.toml
new file mode 100644
index 00000000..1c4e53f6
--- /dev/null
+++ b/scripts/package-build/aws-gwlbtun/package.toml
@@ -0,0 +1,59 @@
+[[packages]]
+name = "aws-gwlbtun"
+commit_id = "f78058a"
+scm_url = "https://github.com/aws-samples/aws-gateway-load-balancer-tunnel-handler"
+
+
+## Build cmd start
+build_cmd = '''\
+mkdir -p debian
+echo 'obj-*-linux-gnu/gwlbtun usr/sbin' > debian/install
+
+# changelog
+cat <<EOF > debian/changelog
+aws-gwlbtun (1.0-1) unstable; urgency=low
+
+ * Initial release
+
+ -- Your Name <your.email@example.com> Wed, 29 Aug 2024 09:00:00 +0000
+
+EOF
+
+# control
+/bin/bash -c "cat <<EOF > debian/control
+Source: aws-gwlbtun
+Section: net
+Priority: optional
+Maintainer: VyOS Package Maintainers <maintainers@vyos.net>
+Build-Depends: debhelper-compat (= 13)
+Standards-Version: 4.5.1
+Homepage: https://github.com/aws-samples/aws-gateway-load-balancer-tunnel-handler
+
+Package: aws-gwlbtun
+Architecture: any
+Depends: \${shlibs:Depends}, \${misc:Depends}
+Description: AWS Gateway Load Balancer Tunnel Handler
+ This is a simple package that handles tunneling for the AWS Gateway Load Balancer.
+EOF
+"
+
+# rules
+cat <<EOF > debian/rules
+#!/usr/bin/make -f
+
+%: dh $@
+
+build:
+ dh build
+
+binary:
+ dh binary
+
+clean:
+ dh clean
+EOF
+
+chmod +x debian/rules
+debuild -us -uc -b
+'''
+## Build cmd end
diff --git a/scripts/package-build/build.py b/scripts/package-build/build.py
new file mode 100755
index 00000000..99180e17
--- /dev/null
+++ b/scripts/package-build/build.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import glob
+import shutil
+import toml
+import os
+
+from argparse import ArgumentParser
+from pathlib import Path
+from subprocess import run, CalledProcessError
+
+
+def ensure_dependencies(dependencies: list) -> None:
+ """Ensure Debian build dependencies are met"""
+ if not dependencies:
+ print("I: No additional dependencies to install")
+ return
+
+ print("I: Ensure Debian build dependencies are met")
+ run(['sudo', 'apt-get', 'update'], check=True)
+ run(['sudo', 'apt-get', 'install', '-y'] + dependencies, check=True)
+
+
+def apply_patches(repo_dir: Path, patch_dir: Path) -> None:
+ """Apply patches from the patch directory to the repository"""
+ if not patch_dir.exists() or not patch_dir.is_dir():
+ print(f"I: Patch directory {patch_dir} does not exist, skipping patch application")
+ return
+
+ patches = sorted(patch_dir.glob('*'))
+ if not patches:
+ print(f"I: No patches found in {patch_dir}")
+ return
+
+ debian_patches_dir = repo_dir / 'debian/patches'
+ debian_patches_dir.mkdir(parents=True, exist_ok=True)
+
+ series_file = debian_patches_dir / 'series'
+ with series_file.open('a') as series:
+ for patch in patches:
+ patch_dest = debian_patches_dir / patch.name
+ shutil.copy(patch, patch_dest)
+ series.write(patch.name + '\n')
+ print(f"I: Applied patch: {patch.name}")
+
+
+def prepare_package(repo_dir: Path, install_data: str) -> None:
+ """Prepare a package"""
+ if not install_data:
+ print("I: No install data provided, skipping package preparation")
+ return
+
+ try:
+ install_file = repo_dir / 'debian/install'
+ install_file.parent.mkdir(parents=True, exist_ok=True)
+ install_file.write_text(install_data)
+ print("I: Prepared package")
+ except Exception as e:
+ print(f"Failed to prepare package: {e}")
+ raise
+
+
+def build_package(package: list, dependencies: list, patch_dir: Path) -> None:
+ """Build a package from the repository
+
+ Args:
+ package (list): List of Packages from toml
+ dependencies (list): List of additional dependencies
+ patch_dir (Path): Directory containing patches
+ """
+ repo_name = package['name']
+ repo_dir = Path(repo_name)
+
+ try:
+ # Clone the repository if it does not exist
+ if not repo_dir.exists():
+ run(['git', 'clone', package['scm_url'], str(repo_dir)], check=True)
+
+ # Check out the specific commit
+ run(['git', 'checkout', package['commit_id']], cwd=repo_dir, check=True)
+
+ # Ensure dependencies
+ ensure_dependencies(dependencies)
+
+ # Apply patches if any
+ if (repo_dir / 'patches'):
+ apply_patches(repo_dir, patch_dir)
+
+ # Prepare the package if required
+ if package.get('prepare_package', False):
+ prepare_package(repo_dir, package.get('install_data', ''))
+
+ # Build dependency package and install it
+ if (repo_dir / 'debian/control').exists():
+ try:
+ run('sudo mk-build-deps --install --tool "apt-get --yes --no-install-recommends"', cwd=repo_dir, check=True, shell=True)
+ run('sudo dpkg -i *build-deps*.deb', cwd=repo_dir, check=True, shell=True)
+ except CalledProcessError as e:
+ print(f"Failed to build package {repo_name}: {e}")
+
+ # Build the package, check if we have build_cmd in the package.toml
+ try:
+ build_cmd = package.get('build_cmd', 'dpkg-buildpackage -uc -us -tc -F')
+ run(build_cmd, cwd=repo_dir, check=True, shell=True)
+ except CalledProcessError as e:
+ print(e)
+ print("I: Source packages build failed, ignoring - building binaries only")
+ build_cmd = package.get('build_cmd', 'dpkg-buildpackage -uc -us -tc -b')
+ run(build_cmd, cwd=repo_dir, check=True, shell=True)
+
+ except CalledProcessError as e:
+ print(f"Failed to build package {repo_name}: {e}")
+ finally:
+ # Clean up repository directory
+ # shutil.rmtree(repo_dir, ignore_errors=True)
+ pass
+
+
+def cleanup_build_deps(repo_dir: Path) -> None:
+ """Clean up build dependency packages"""
+ try:
+ if repo_dir.exists():
+ for file in glob.glob(str(repo_dir / '*build-deps*.deb')):
+ os.remove(file)
+ print("I: Cleaned up build dependency packages")
+ except Exception as e:
+ print(f"Error cleaning up build dependencies: {e}")
+
+
+def copy_packages(repo_dir: Path) -> None:
+ """Copy generated .deb packages to the parent directory"""
+ try:
+ deb_files = glob.glob(str(repo_dir / '*.deb'))
+ for deb_file in deb_files:
+ shutil.copy(deb_file, repo_dir.parent)
+ print(f'I: copy generated "{deb_file}" package')
+ except Exception as e:
+ print(f"Error copying packages: {e}")
+
+
+if __name__ == '__main__':
+ # Prepare argument parser
+ arg_parser = ArgumentParser()
+ arg_parser.add_argument('--config',
+ default='package.toml',
+ help='Path to the package configuration file')
+ arg_parser.add_argument('--patch-dir',
+ default='patches',
+ help='Path to the directory containing patches')
+ args = arg_parser.parse_args()
+
+ # Load package configuration
+ with open(args.config, 'r') as file:
+ config = toml.load(file)
+
+ packages = config['packages']
+ patch_dir = Path(args.patch_dir)
+
+ for package in packages:
+ dependencies = package.get('dependencies', {}).get('packages', [])
+
+ # Build the package
+ build_package(package, dependencies, patch_dir)
+
+ # Clean up build dependency packages after build
+ cleanup_build_deps(Path(package['name']))
+
+ # Copy generated .deb packages to parent directory
+ copy_packages(Path(package['name']))
diff --git a/scripts/package-build/ddclient/.gitignore b/scripts/package-build/ddclient/.gitignore
new file mode 100644
index 00000000..aeb8af66
--- /dev/null
+++ b/scripts/package-build/ddclient/.gitignore
@@ -0,0 +1,7 @@
+ddclient/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/ddclient/build.py b/scripts/package-build/ddclient/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/ddclient/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/ddclient/package.toml b/scripts/package-build/ddclient/package.toml
new file mode 100644
index 00000000..e2cd3f0a
--- /dev/null
+++ b/scripts/package-build/ddclient/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "ddclient"
+commit_id = "debian/3.11.2-1"
+scm_url = "https://salsa.debian.org/debian/ddclient"
diff --git a/scripts/package-build/dropbear/.gitignore b/scripts/package-build/dropbear/.gitignore
new file mode 100644
index 00000000..6e8cff9c
--- /dev/null
+++ b/scripts/package-build/dropbear/.gitignore
@@ -0,0 +1,7 @@
+dropbear/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/dropbear/build.py b/scripts/package-build/dropbear/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/dropbear/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/dropbear/package.toml b/scripts/package-build/dropbear/package.toml
new file mode 100644
index 00000000..cbb885ee
--- /dev/null
+++ b/scripts/package-build/dropbear/package.toml
@@ -0,0 +1,7 @@
+[[packages]]
+name = "dropbear"
+commit_id = "debian/2022.83-1+deb12u1"
+scm_url = "https://salsa.debian.org/debian/dropbear.git"
+
+[packages.dependencies]
+packages = ["libpam0g-dev"] \ No newline at end of file
diff --git a/scripts/package-build/dropbear/patches/0001-Enable-PAM-support.patch b/scripts/package-build/dropbear/patches/0001-Enable-PAM-support.patch
new file mode 100644
index 00000000..fa6cf620
--- /dev/null
+++ b/scripts/package-build/dropbear/patches/0001-Enable-PAM-support.patch
@@ -0,0 +1,61 @@
+From 861bfb53de5909e25a952a83654c63de61af02b5 Mon Sep 17 00:00:00 2001
+From: Christian Breunig <christian@breunig.cc>
+Date: Sun, 28 May 2023 15:45:32 +0200
+Subject: [PATCH] Enable PAM support
+
+---
+ debian/control | 1 +
+ debian/rules | 2 +-
+ default_options.h | 4 ++--
+ 3 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/debian/control b/debian/control
+index 77ea036..b252b97 100644
+--- a/debian/control
++++ b/debian/control
+@@ -6,6 +6,7 @@ Build-Depends: debhelper,
+ debhelper-compat (= 13),
+ libtomcrypt-dev (>= 1.18.2~),
+ libtommath-dev (>= 1.2.0~),
++ libpam0g-dev,
+ libz-dev
+ Rules-Requires-Root: no
+ Standards-Version: 4.6.1
+diff --git a/debian/rules b/debian/rules
+index 7dab64c..ce11aa4 100755
+--- a/debian/rules
++++ b/debian/rules
+@@ -24,7 +24,7 @@ endif
+ dh $@
+
+ override_dh_auto_configure:
+- dh_auto_configure -- --disable-bundled-libtom \
++ dh_auto_configure -- --disable-bundled-libtom --enable-pam \
+ CC='$(CC)' CFLAGS='$(CFLAGS)' $(CONFFLAGS)
+
+ execute_before_dh_auto_build:
+diff --git a/default_options.h b/default_options.h
+index 5132775..e7d274c 100644
+--- a/default_options.h
++++ b/default_options.h
+@@ -223,7 +223,7 @@ group1 in Dropbear server too */
+
+ /* Authentication Types - at least one required.
+ RFC Draft requires pubkey auth, and recommends password */
+-#define DROPBEAR_SVR_PASSWORD_AUTH 1
++#define DROPBEAR_SVR_PASSWORD_AUTH 0
+
+ /* Note: PAM auth is quite simple and only works for PAM modules which just do
+ * a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c).
+@@ -231,7 +231,7 @@ group1 in Dropbear server too */
+ * but there's an interface via a PAM module. It won't work for more complex
+ * PAM challenge/response.
+ * You can't enable both PASSWORD and PAM. */
+-#define DROPBEAR_SVR_PAM_AUTH 0
++#define DROPBEAR_SVR_PAM_AUTH 1
+
+ /* ~/.ssh/authorized_keys authentication.
+ * You must define DROPBEAR_SVR_PUBKEY_AUTH in order to use plugins. */
+--
+2.30.2
+
diff --git a/scripts/package-build/ethtool/.gitignore b/scripts/package-build/ethtool/.gitignore
new file mode 100644
index 00000000..f964bd07
--- /dev/null
+++ b/scripts/package-build/ethtool/.gitignore
@@ -0,0 +1,7 @@
+ethtool/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/ethtool/build.py b/scripts/package-build/ethtool/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/ethtool/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/ethtool/package.toml b/scripts/package-build/ethtool/package.toml
new file mode 100644
index 00000000..9468ed82
--- /dev/null
+++ b/scripts/package-build/ethtool/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "ethtool"
+commit_id = "debian/1%6.6-1"
+scm_url = "https://salsa.debian.org/kernel-team/ethtool"
diff --git a/scripts/package-build/frr/.gitignore b/scripts/package-build/frr/.gitignore
new file mode 100644
index 00000000..590895c0
--- /dev/null
+++ b/scripts/package-build/frr/.gitignore
@@ -0,0 +1,8 @@
+frr/
+rtrlib/
+libyang/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/frr/build.py b/scripts/package-build/frr/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/frr/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/frr/package.toml b/scripts/package-build/frr/package.toml
new file mode 100644
index 00000000..48d51ae6
--- /dev/null
+++ b/scripts/package-build/frr/package.toml
@@ -0,0 +1,36 @@
+[[packages]]
+name = "libyang"
+commit_id = "v2.1.148"
+scm_url = "https://github.com/CESNET/libyang.git"
+build_cmd = "pipx run apkg build -i && find pkg/pkgs -type f -name *.deb -exec mv -t .. {} +"
+
+[[packages]]
+name = "rtrlib"
+commit_id = "v0.8.0"
+scm_url = "https://github.com/rtrlib/rtrlib.git"
+build_cmd = "sudo mk-build-deps --install --tool 'apt-get --yes --no-install-recommends'; dpkg-buildpackage -uc -us -tc -b"
+
+[[packages]]
+name = "frr"
+commit_id = "stable/9.1"
+scm_url = "https://github.com/FRRouting/frr.git"
+build_cmd = "sudo dpkg -i ../*.deb; sudo dpkg-buildpackage -us -uc -tc -b -Ppkg.frr.rtrlib,pkg.frr.lua"
+
+[packages.dependencies]
+packages = [
+ "chrpath",
+ "gawk",
+ "install-info",
+ "libcap-dev",
+ "libjson-c-dev",
+ "librtr-dev",
+ "libpam-dev",
+ "libprotobuf-c-dev",
+ "libpython3-dev:native",
+ "python3-sphinx:native",
+ "libsnmp-dev",
+ "protobuf-c-compiler",
+ "python3-dev:native",
+ "texinfo",
+ "lua5.3"
+]
diff --git a/scripts/package-build/hostap/.gitignore b/scripts/package-build/hostap/.gitignore
new file mode 100644
index 00000000..f9c7eb32
--- /dev/null
+++ b/scripts/package-build/hostap/.gitignore
@@ -0,0 +1,7 @@
+hostap/
+wpa/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/hostap/README.md b/scripts/package-build/hostap/README.md
new file mode 100644
index 00000000..63119c0c
--- /dev/null
+++ b/scripts/package-build/hostap/README.md
@@ -0,0 +1,4 @@
+# build
+```
+python3 build.py
+```
diff --git a/scripts/package-build/hostap/build.py b/scripts/package-build/hostap/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/hostap/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/hostap/build.sh b/scripts/package-build/hostap/build.sh
new file mode 100755
index 00000000..c356672a
--- /dev/null
+++ b/scripts/package-build/hostap/build.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+CWD=$(pwd)
+set -e
+
+SRC=hostap
+SRC_DEB=wpa
+
+if [ ! -d ${SRC} ]; then
+ echo "${SRC} directory does not exists, please 'git clone'"
+ exit 1
+fi
+if [ ! -d ${SRC_DEB} ]; then
+ echo "${SRC_DEB} directory does not exists, please 'git clone'"
+ exit 1
+fi
+
+echo "I: Copy Debian build instructions"
+cp -a ${SRC_DEB}/debian ${SRC}
+# Preserve Debian's default of allowing TLSv1.0 and legacy renegotiation for
+# compatibility with networks that use legacy crypto
+cat > ${SRC}/debian/patches/series << EOF
+allow-tlsv1.patch
+allow-legacy-renegotiation.patch
+EOF
+
+# Build Debian package
+cd ${SRC}
+
+echo "I: Ensure Debian build dependencies are met"
+sudo mk-build-deps --install --tool "apt-get --yes --no-install-recommends" -Ppkg.wpa.nogui,noudeb
+
+echo "I: Create new Debian Package version"
+version="$(git describe --tags | tr _ .)"
+dch -v ${version:7} "New version to support AES-GCM-256 for MACsec" -b
+
+echo "I: Build Debian hostap Package"
+DEB_CPPFLAGS_SET="-Wno-use-after-free -Wno-deprecated-declarations" \
+ dpkg-buildpackage -us -uc -tc -b -Ppkg.wpa.nogui,noudeb
diff --git a/scripts/package-build/hostap/package.toml b/scripts/package-build/hostap/package.toml
new file mode 100644
index 00000000..cb40710b
--- /dev/null
+++ b/scripts/package-build/hostap/package.toml
@@ -0,0 +1,12 @@
+[[packages]]
+name = "wpa"
+commit_id = "debian/2%2.10-12"
+scm_url = "https://salsa.debian.org/debian/wpa"
+build_cmd = "/bin/true"
+
+[[packages]]
+name = "hostap"
+commit_id = "e7172e26d"
+scm_url = "git://w1.fi/srv/git/hostap.git"
+build_cmd = "cd ..; y | ./build.sh"
+
diff --git a/scripts/package-build/hsflowd/.gitignore b/scripts/package-build/hsflowd/.gitignore
new file mode 100644
index 00000000..d0964b29
--- /dev/null
+++ b/scripts/package-build/hsflowd/.gitignore
@@ -0,0 +1,6 @@
+host-sflow/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/hsflowd/build.py b/scripts/package-build/hsflowd/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/hsflowd/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/hsflowd/package.toml b/scripts/package-build/hsflowd/package.toml
new file mode 100644
index 00000000..75d320a7
--- /dev/null
+++ b/scripts/package-build/hsflowd/package.toml
@@ -0,0 +1,8 @@
+[[packages]]
+name = "host-sflow"
+commit_id = "v2.0.55-1"
+scm_url = "https://github.com/sflow/host-sflow.git"
+build_cmd = "make deb FEATURES='PCAP DROPMON DBUS'"
+
+[packages.dependencies]
+packages = ["libpcap0.8-dev"]
diff --git a/scripts/package-build/isc-dhcp/.gitignore b/scripts/package-build/isc-dhcp/.gitignore
new file mode 100644
index 00000000..66d17cc8
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/.gitignore
@@ -0,0 +1,7 @@
+isc-dhcp/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/isc-dhcp/build.py b/scripts/package-build/isc-dhcp/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/isc-dhcp/package.toml b/scripts/package-build/isc-dhcp/package.toml
new file mode 100644
index 00000000..76a0e4a1
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/package.toml
@@ -0,0 +1,7 @@
+[[packages]]
+name = "isc-dhcp"
+commit_id = "debian/4.4.3-P1-4"
+scm_url = "https://salsa.debian.org/debian/isc-dhcp"
+
+[packages.dependencies]
+packages = ["libpam0g-dev"]
diff --git a/scripts/package-build/isc-dhcp/patches/0001-Add-support-for-raw-IP-interface-type.patch b/scripts/package-build/isc-dhcp/patches/0001-Add-support-for-raw-IP-interface-type.patch
new file mode 100644
index 00000000..c13569ad
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/patches/0001-Add-support-for-raw-IP-interface-type.patch
@@ -0,0 +1,248 @@
+From 8d9e8ace96ad9e2dba9f2d4069228dee5daf6772 Mon Sep 17 00:00:00 2001
+From: Loic Poulain <loic.poulain@linaro.org>
+Date: Mon, 2 Nov 2020 06:42:12 -0500
+Subject: [PATCH 1/4] Add support for raw IP interface type
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Under linux some devices can expose raw IP interfaces, such as WWAN
+modems. In that case IP data is not encapsulated in any lower level
+protocol.
+
+dhclient does not support this currently and this patch adds support
+for such pure IP interfaces.
+
+The original patch comes from Bjørn Mork on Network-Manage mailing list:
+https://mail.gnome.org/archives/networkmanager-list/2015-December/msg00044.html
+
+---
+ common/bpf.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ common/lpf.c | 59 +++++++++++++++++++++++++++++++++++++-----------
+ common/packet.c | 7 ++++++
+ includes/dhcp.h | 1 +
+ 4 files changed, 113 insertions(+), 14 deletions(-)
+
+diff --git a/common/bpf.c b/common/bpf.c
+index 658e5db..0c08574 100644
+--- a/common/bpf.c
++++ b/common/bpf.c
+@@ -198,6 +198,34 @@ struct bpf_insn dhcp_bpf_filter [] = {
+ BPF_STMT (BPF_RET + BPF_K, 0),
+ };
+
++int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
++
++struct bpf_insn dhcp_bpf_pureip_filter [] = {
++ /* Make sure it's a UDP packet... */
++ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 9),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
++
++ /* Make sure this isn't a fragment... */
++ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 6),
++ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
++
++ /* Get the IP header length... */
++ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 0),
++
++ /* Make sure it's to the right port... */
++ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 2),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 0, 1), /* patch */
++
++ /* If we passed all the tests, ask for the whole packet. */
++ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
++
++ /* Otherwise, drop it. */
++ BPF_STMT(BPF_RET+BPF_K, 0),
++};
++
++int dhcp_bpf_pureip_filter_len =
++ sizeof dhcp_bpf_pureip_filter / sizeof (struct bpf_insn);
++
+ #if defined(RELAY_PORT)
+ /*
+ * For relay port extension
+@@ -235,13 +263,43 @@ struct bpf_insn dhcp_bpf_relay_filter [] = {
+
+ int dhcp_bpf_relay_filter_len =
+ sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn);
++
++struct bpf_insn dhcp_bpf_pureip_relay_filter [] = {
++ /* Make sure it's a UDP packet... */
++ BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 9),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
++
++ /* Make sure this isn't a fragment... */
++ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 6),
++ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0),
++
++ /* Get the IP header length... */
++ BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 0),
++
++ /* Make sure it's to the right port... */
++ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 2, 0), /* patch */
++
++ /* relay can have an alternative port... */
++ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 0, 1), /* patch */
++
++ /* If we passed all the tests, ask for the whole packet. */
++ BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
++
++ /* Otherwise, drop it. */
++ BPF_STMT (BPF_RET + BPF_K, 0),
++};
++
++int dhcp_bpf_pureip_relay_filter_len =
++ sizeof dhcp_bpf_pureip_relay_filter / sizeof (struct bpf_insn);
++
+ #endif
+
+ #if defined (DEC_FDDI)
+ struct bpf_insn *bpf_fddi_filter = NULL;
+ #endif
+
+-int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
+ #if defined (HAVE_TR_SUPPORT)
+ struct bpf_insn dhcp_bpf_tr_filter [] = {
+ /* accept all token ring packets due to variable length header */
+diff --git a/common/lpf.c b/common/lpf.c
+index bb8822a..d8f34a4 100644
+--- a/common/lpf.c
++++ b/common/lpf.c
+@@ -177,9 +177,15 @@ void if_deregister_send (info)
+ extern struct sock_filter dhcp_bpf_filter [];
+ extern int dhcp_bpf_filter_len;
+
++extern struct sock_filter dhcp_bpf_pureip_filter [];
++extern int dhcp_bpf_pureip_filter_len;
++
+ #if defined(RELAY_PORT)
+ extern struct sock_filter dhcp_bpf_relay_filter [];
+ extern int dhcp_bpf_relay_filter_len;
++
++extern struct sock_filter dhcp_bpf_pureip_relay_filter [];
++extern int dhcp_bpf_pureip_relay_filter_len;
+ #endif
+
+ #if defined (HAVE_TR_SUPPORT)
+@@ -249,31 +255,52 @@ void if_deregister_receive (info)
+ static void lpf_gen_filter_setup (info)
+ struct interface_info *info;
+ {
++ int pure_ip = info -> hw_address.hbuf [0] == HTYPE_PUREIP;
+ struct sock_fprog p;
+
+ memset(&p, 0, sizeof(p));
+
+- /* Set up the bpf filter program structure. This is defined in
+- bpf.c */
+- p.len = dhcp_bpf_filter_len;
+- p.filter = dhcp_bpf_filter;
++ /* Set up the bpf filter program structure and patch port(s).
++ *
++ * This is defined in bpf.c, XXX changes to filter program may
++ * require changes to the insn number(s) used below! XXX
++ */
++
++ if (pure_ip) {
++ p.len = dhcp_bpf_pureip_filter_len;
++ p.filter = dhcp_bpf_pureip_filter;
++
++ /* patch port */
++ dhcp_bpf_pureip_filter [6].k = ntohs (local_port);
++ } else {
++ p.len = dhcp_bpf_filter_len;
++ p.filter = dhcp_bpf_filter;
++
++ /* patch port */
++ dhcp_bpf_filter [8].k = ntohs (local_port);
++ }
+
+- /* Patch the server port into the LPF program...
+- XXX changes to filter program may require changes
+- to the insn number(s) used below! XXX */
+ #if defined(RELAY_PORT)
+- if (relay_port) {
+- /*
+- * If user defined relay UDP port, we need to filter
+- * also on the user UDP port.
+- */
++ /*
++ * If user defined relay UDP port, we need to filter
++ * also on the user UDP port.
++ */
++ if (relay_port && pure_ip) {
++ p.len = dhcp_bpf_pureip_relay_filter_len;
++ p.filter = dhcp_bpf_pureip_relay_filter;
++
++ /* patch ports */
++ dhcp_bpf_pureip_relay_filter [6].k = ntohs (local_port);
++ dhcp_bpf_pureip_relay_filter [8].k = ntohs (relay_port);
++ } else if (relay_port) {
+ p.len = dhcp_bpf_relay_filter_len;
+ p.filter = dhcp_bpf_relay_filter;
+
++ /* patch ports */
++ dhcp_bpf_relay_filter [8].k = ntohs (local_port);
+ dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
+ }
+ #endif
+- dhcp_bpf_filter [8].k = ntohs (local_port);
+
+ if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
+ sizeof p) < 0) {
+@@ -578,6 +605,12 @@ get_hw_addr(const char *name, struct hardware *hw) {
+ hw->hbuf[3] = 0xbe;
+ hw->hbuf[4] = 0xef;
+ break;
++#endif
++#ifdef ARPHRD_RAWIP
++ case ARPHRD_RAWIP:
++ hw->hlen = 1;
++ hw->hbuf[0] = HTYPE_PUREIP;
++ break;
+ #endif
+ default:
+ log_fatal("Unsupported device type %ld for \"%s\"",
+diff --git a/common/packet.c b/common/packet.c
+index 49795c4..6745db7 100644
+--- a/common/packet.c
++++ b/common/packet.c
+@@ -119,6 +119,10 @@ void assemble_hw_header (interface, buf, bufix, to)
+ case HTYPE_INFINIBAND:
+ log_error("Attempt to assemble hw header for infiniband");
+ break;
++ case HTYPE_PUREIP:
++ /* Nothing to do, there is no hw header */
++ *bufix = 0;
++ break;
+ case HTYPE_ETHER:
+ default:
+ assemble_ethernet_header(interface, buf, bufix, to);
+@@ -219,6 +223,9 @@ ssize_t decode_hw_header (interface, buf, bufix, from)
+ case HTYPE_INFINIBAND:
+ log_error("Attempt to decode hw header for infiniband");
+ return (0);
++ case HTYPE_PUREIP:
++ /* Nothing to do, there is no hw header */
++ return 0;
+ case HTYPE_ETHER:
+ default:
+ return (decode_ethernet_header(interface, buf, bufix, from));
+diff --git a/includes/dhcp.h b/includes/dhcp.h
+index d519821..75be1fb 100644
+--- a/includes/dhcp.h
++++ b/includes/dhcp.h
+@@ -76,6 +76,7 @@ struct dhcp_packet {
+ #define HTYPE_IEEE802 6 /* IEEE 802.2 Token Ring... */
+ #define HTYPE_FDDI 8 /* FDDI... */
+ #define HTYPE_INFINIBAND 32 /* IP over Infiniband */
++#define HTYPE_PUREIP 35 /* Pure IP */
+ #define HTYPE_IPMP 255 /* IPMP - random hw address - there
+ * is no standard for this so we
+ * just steal a type */
+--
+2.39.2
+
diff --git a/scripts/package-build/isc-dhcp/patches/0002-Checkpoint-improved-patch.patch b/scripts/package-build/isc-dhcp/patches/0002-Checkpoint-improved-patch.patch
new file mode 100644
index 00000000..60b693f6
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/patches/0002-Checkpoint-improved-patch.patch
@@ -0,0 +1,170 @@
+From e67d1b6b4178f412084459c4cb7e54a8c0019bd2 Mon Sep 17 00:00:00 2001
+From: Francis Dupont <fdupont@isc.org>
+Date: Fri, 6 Nov 2020 10:46:09 +0100
+Subject: [PATCH 2/4] Checkpoint: improved patch
+
+---
+ common/bpf.c | 10 +++---
+ common/lpf.c | 89 +++++++++++++++++++++++++++++++++++-----------------
+ 2 files changed, 65 insertions(+), 34 deletions(-)
+
+diff --git a/common/bpf.c b/common/bpf.c
+index 0c08574..30dcaa5 100644
+--- a/common/bpf.c
++++ b/common/bpf.c
+@@ -214,13 +214,13 @@ struct bpf_insn dhcp_bpf_pureip_filter [] = {
+
+ /* Make sure it's to the right port... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 2),
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 0, 1), /* patch */
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+- BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
++ BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+- BPF_STMT(BPF_RET+BPF_K, 0),
++ BPF_STMT(BPF_RET + BPF_K, 0),
+ };
+
+ int dhcp_bpf_pureip_filter_len =
+@@ -278,11 +278,11 @@ struct bpf_insn dhcp_bpf_pureip_relay_filter [] = {
+
+ /* Make sure it's to the right port... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 2, 0), /* patch */
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 2, 0), /* patch */
+
+ /* relay can have an alternative port... */
+ BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
+- BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 37, 0, 1), /* patch */
++ BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT (BPF_RET + BPF_K, (u_int)-1),
+diff --git a/common/lpf.c b/common/lpf.c
+index d8f34a4..75609f5 100644
+--- a/common/lpf.c
++++ b/common/lpf.c
+@@ -221,6 +221,9 @@ void if_register_receive (info)
+ lpf_tr_filter_setup (info);
+ else
+ #endif
++ if (info -> hw_address.hbuf [0] == HTYPE_PUREIP)
++ lpf_pureip_filter_setup (info);
++ else
+ lpf_gen_filter_setup (info);
+
+ if (!quiet_interface_discovery)
+@@ -255,50 +258,78 @@ void if_deregister_receive (info)
+ static void lpf_gen_filter_setup (info)
+ struct interface_info *info;
+ {
+- int pure_ip = info -> hw_address.hbuf [0] == HTYPE_PUREIP;
+ struct sock_fprog p;
+
+ memset(&p, 0, sizeof(p));
+
+- /* Set up the bpf filter program structure and patch port(s).
+- *
+- * This is defined in bpf.c, XXX changes to filter program may
+- * require changes to the insn number(s) used below! XXX
+- */
++ /* Set up the bpf filter program structure. This is defined in
++ bpf.c */
++ p.len = dhcp_bpf_filter_len;
++ p.filter = dhcp_bpf_filter;
++
++ dhcp_bpf_filter [8].k = ntohs (local_port);
+
+- if (pure_ip) {
+- p.len = dhcp_bpf_pureip_filter_len;
+- p.filter = dhcp_bpf_pureip_filter;
++ /* Patch the server port into the LPF program...
++ XXX changes to filter program may require changes
++ to the insn number(s) used below! XXX */
++#if defined(RELAY_PORT)
++ if (relay_port) {
++ /*
++ * If user defined relay UDP port, we need to filter
++ * also on the user UDP port.
++ */
++ p.len = dhcp_bpf_relay_filter_len;
++ p.filter = dhcp_bpf_relay_filter;
+
+- /* patch port */
+- dhcp_bpf_pureip_filter [6].k = ntohs (local_port);
+- } else {
+- p.len = dhcp_bpf_filter_len;
+- p.filter = dhcp_bpf_filter;
++ dhcp_bpf_relay_filter [8].k = ntohs (local_port);
++ dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
++ }
++#endif
+
+- /* patch port */
+- dhcp_bpf_filter [8].k = ntohs (local_port);
++ if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
++ sizeof p) < 0) {
++ if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
++ errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
++ errno == EAFNOSUPPORT) {
++ log_error ("socket: %m - make sure");
++ log_error ("CONFIG_PACKET (Packet socket) %s",
++ "and CONFIG_FILTER");
++ log_error ("(Socket Filtering) are enabled %s",
++ "in your kernel");
++ log_fatal ("configuration!");
++ }
++ log_fatal ("Can't install packet filter program: %m");
+ }
++}
++
++static void lpf_pureip_gen_filter_setup (info)
++ struct interface_info *info;
++{
++ struct sock_fprog p;
++
++ memset(&p, 0, sizeof(p));
++
++ /* Set up the bpf filter program structure. This is defined in
++ bpf.c */
++ p.len = dhcp_bpf_pureip_filter_len;
++ p.filter = dhcp_bpf_pureip_filter;
++
++ dhcp_bpf_pureip_filter [6].k = ntohs (local_port);
+
++ /* Patch the server port into the LPF program...
++ XXX changes to filter program may require changes
++ to the insn number(s) used below! XXX */
+ #if defined(RELAY_PORT)
+- /*
+- * If user defined relay UDP port, we need to filter
+- * also on the user UDP port.
+- */
+- if (relay_port && pure_ip) {
++ if (relay_port) {
++ /*
++ * If user defined relay UDP port, we need to filter
++ * also on the user UDP port.
++ */
+ p.len = dhcp_bpf_pureip_relay_filter_len;
+ p.filter = dhcp_bpf_pureip_relay_filter;
+
+- /* patch ports */
+ dhcp_bpf_pureip_relay_filter [6].k = ntohs (local_port);
+ dhcp_bpf_pureip_relay_filter [8].k = ntohs (relay_port);
+- } else if (relay_port) {
+- p.len = dhcp_bpf_relay_filter_len;
+- p.filter = dhcp_bpf_relay_filter;
+-
+- /* patch ports */
+- dhcp_bpf_relay_filter [8].k = ntohs (local_port);
+- dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
+ }
+ #endif
+
+--
+2.39.2
+
diff --git a/scripts/package-build/isc-dhcp/patches/0003-fix-compilation-errors.patch b/scripts/package-build/isc-dhcp/patches/0003-fix-compilation-errors.patch
new file mode 100644
index 00000000..c66e0c7c
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/patches/0003-fix-compilation-errors.patch
@@ -0,0 +1,48 @@
+From 58e0d3317795987b2f1ca788645196d0e3543f88 Mon Sep 17 00:00:00 2001
+From: Adam Smith <zero1three@gmail.com>
+Date: Tue, 23 Jan 2024 21:47:00 -0500
+Subject: [PATCH 3/4] fix compilation errors
+
+---
+ common/lpf.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/common/lpf.c b/common/lpf.c
+index 75609f5..1561d71 100644
+--- a/common/lpf.c
++++ b/common/lpf.c
+@@ -195,6 +195,7 @@ static void lpf_tr_filter_setup (struct interface_info *);
+ #endif
+
+ static void lpf_gen_filter_setup (struct interface_info *);
++static void lpf_pureip_gen_filter_setup (struct interface_info *);
+
+ void if_register_receive (info)
+ struct interface_info *info;
+@@ -215,14 +216,13 @@ void if_register_receive (info)
+ }
+ #endif
+
+-
+ #if defined (HAVE_TR_SUPPORT)
+ if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
+ lpf_tr_filter_setup (info);
+ else
+ #endif
+ if (info -> hw_address.hbuf [0] == HTYPE_PUREIP)
+- lpf_pureip_filter_setup (info);
++ lpf_pureip_gen_filter_setup (info);
+ else
+ lpf_gen_filter_setup (info);
+
+@@ -349,6 +349,7 @@ static void lpf_pureip_gen_filter_setup (info)
+ }
+ }
+
++
+ #if defined (HAVE_TR_SUPPORT)
+ static void lpf_tr_filter_setup (info)
+ struct interface_info *info;
+--
+2.39.2
+
diff --git a/scripts/package-build/isc-dhcp/patches/0004-add-support-for-ARPHRD_NONE-interface-type.patch b/scripts/package-build/isc-dhcp/patches/0004-add-support-for-ARPHRD_NONE-interface-type.patch
new file mode 100644
index 00000000..32089b4d
--- /dev/null
+++ b/scripts/package-build/isc-dhcp/patches/0004-add-support-for-ARPHRD_NONE-interface-type.patch
@@ -0,0 +1,29 @@
+From fd96a11b31cd05aae450ec65fde0b5c6e0b718c2 Mon Sep 17 00:00:00 2001
+From: Adam Smith <zero1three@gmail.com>
+Date: Tue, 23 Jan 2024 22:35:54 -0500
+Subject: [PATCH 4/4] add support for ARPHRD_NONE interface type
+
+---
+ common/lpf.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/common/lpf.c b/common/lpf.c
+index 1561d71..f7e84b1 100644
+--- a/common/lpf.c
++++ b/common/lpf.c
+@@ -643,6 +643,12 @@ get_hw_addr(const char *name, struct hardware *hw) {
+ hw->hlen = 1;
+ hw->hbuf[0] = HTYPE_PUREIP;
+ break;
++#endif
++#ifdef ARPHRD_NONE
++ case ARPHRD_NONE:
++ hw->hlen = 1;
++ hw->hbuf[0] = HTYPE_PUREIP;
++ break;
+ #endif
+ default:
+ log_fatal("Unsupported device type %ld for \"%s\"",
+--
+2.39.2
+
diff --git a/scripts/package-build/kea/.gitignore b/scripts/package-build/kea/.gitignore
new file mode 100644
index 00000000..1f9d42c9
--- /dev/null
+++ b/scripts/package-build/kea/.gitignore
@@ -0,0 +1,7 @@
+isc-kea/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/kea/build.py b/scripts/package-build/kea/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/kea/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/kea/package.toml b/scripts/package-build/kea/package.toml
new file mode 100644
index 00000000..0bfce21e
--- /dev/null
+++ b/scripts/package-build/kea/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "isc-kea"
+commit_id = "debian/2.4.1-3"
+scm_url = "https://salsa.debian.org/debian/isc-kea"
diff --git a/scripts/package-build/keepalived/.gitignore b/scripts/package-build/keepalived/.gitignore
new file mode 100644
index 00000000..fa96cd3f
--- /dev/null
+++ b/scripts/package-build/keepalived/.gitignore
@@ -0,0 +1,7 @@
+keepalived/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/keepalived/build.py b/scripts/package-build/keepalived/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/keepalived/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/keepalived/package.toml b/scripts/package-build/keepalived/package.toml
new file mode 100644
index 00000000..ad1008e6
--- /dev/null
+++ b/scripts/package-build/keepalived/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "keepalived"
+commit_id = "debian/1%2.2.8-1"
+scm_url = "https://salsa.debian.org/debian/pkg-keepalived.git"
diff --git a/scripts/package-build/keepalived/patches/0001-vrrp-Set-sysctl-arp_ignore-to-1-on-IPv6-VMACs.patch b/scripts/package-build/keepalived/patches/0001-vrrp-Set-sysctl-arp_ignore-to-1-on-IPv6-VMACs.patch
new file mode 100644
index 00000000..b099dc7b
--- /dev/null
+++ b/scripts/package-build/keepalived/patches/0001-vrrp-Set-sysctl-arp_ignore-to-1-on-IPv6-VMACs.patch
@@ -0,0 +1,129 @@
+From af4aa758c3512bec8233549e138b03741c5404f9 Mon Sep 17 00:00:00 2001
+From: Quentin Armitage <quentin@armitage.org.uk>
+Date: Sat, 14 Oct 2023 15:37:19 +0100
+Subject: [PATCH] vrrp: Set sysctl arp_ignore to 1 on IPv6 VMACs
+
+Setting arp_ignore to 1 ensures that the VMAC interface does not respond
+to ARP requests for IPv4 addresses not configured on the VMAC.
+
+Signed-off-by: Quentin Armitage <quentin@armitage.org.uk>
+---
+ keepalived/include/vrrp_if_config.h | 2 +-
+ keepalived/vrrp/vrrp_if_config.c | 28 ++++++++++++++++++++--------
+ keepalived/vrrp/vrrp_vmac.c | 5 ++---
+ 3 files changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/keepalived/include/vrrp_if_config.h b/keepalived/include/vrrp_if_config.h
+index 35465cd..c35e56e 100644
+--- a/keepalived/include/vrrp_if_config.h
++++ b/keepalived/include/vrrp_if_config.h
+@@ -34,7 +34,7 @@ extern void set_promote_secondaries(interface_t*);
+ extern void reset_promote_secondaries(interface_t*);
+ #ifdef _HAVE_VRRP_VMAC_
+ extern void restore_rp_filter(void);
+-extern void set_interface_parameters(const interface_t*, interface_t*);
++extern void set_interface_parameters(const interface_t*, interface_t*, sa_family_t);
+ extern void reset_interface_parameters(interface_t*);
+ extern void link_set_ipv6(const interface_t*, bool);
+ #endif
+diff --git a/keepalived/vrrp/vrrp_if_config.c b/keepalived/vrrp/vrrp_if_config.c
+index cfce7e2..fbfd34c 100644
+--- a/keepalived/vrrp/vrrp_if_config.c
++++ b/keepalived/vrrp/vrrp_if_config.c
+@@ -81,6 +81,11 @@ static sysctl_opts_t vmac_sysctl[] = {
+ { 0, 0}
+ };
+
++static sysctl_opts_t vmac_sysctl_6[] = {
++ { IPV4_DEVCONF_ARP_IGNORE, 1 },
++ { 0, 0}
++};
++
+ #endif
+ #endif
+
+@@ -216,11 +221,14 @@ netlink_set_interface_flags(unsigned ifindex, const sysctl_opts_t *sys_opts)
+
+ #ifdef _HAVE_VRRP_VMAC_
+ static inline int
+-netlink_set_interface_parameters(const interface_t *ifp, interface_t *base_ifp)
++netlink_set_interface_parameters(const interface_t *ifp, interface_t *base_ifp, sa_family_t family)
+ {
+- if (netlink_set_interface_flags(ifp->ifindex, vmac_sysctl))
++ if (netlink_set_interface_flags(ifp->ifindex, family == AF_INET6 ? vmac_sysctl_6 : vmac_sysctl))
+ return -1;
+
++ if (family == AF_INET6)
++ return 0;
++
+ /* If the underlying interface is a MACVLAN that has been moved into
+ * a separate network namespace from the parent, we can't access the
+ * parent. */
+@@ -271,9 +279,9 @@ netlink_reset_interface_parameters(const interface_t* ifp)
+ }
+
+ static inline void
+-set_interface_parameters_devconf(const interface_t *ifp, interface_t *base_ifp)
++set_interface_parameters_devconf(const interface_t *ifp, interface_t *base_ifp, sa_family_t family)
+ {
+- if (netlink_set_interface_parameters(ifp, base_ifp))
++ if (netlink_set_interface_parameters(ifp, base_ifp, family))
+ log_message(LOG_INFO, "Unable to set parameters for %s", ifp->ifname);
+ }
+
+@@ -310,11 +318,15 @@ reset_promote_secondaries_devconf(interface_t *ifp)
+
+ #ifdef _HAVE_VRRP_VMAC_
+ static inline void
+-set_interface_parameters_sysctl(const interface_t *ifp, interface_t *base_ifp)
++set_interface_parameters_sysctl(const interface_t *ifp, interface_t *base_ifp, sa_family_t family)
+ {
+ unsigned val;
+
+ set_sysctl("net/ipv4/conf", ifp->ifname, "arp_ignore", 1);
++
++ if (family == AF_INET6)
++ return;
++
+ set_sysctl("net/ipv4/conf", ifp->ifname, "accept_local", 1);
+ set_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter", 0);
+
+@@ -524,15 +536,15 @@ restore_rp_filter(void)
+ }
+
+ void
+-set_interface_parameters(const interface_t *ifp, interface_t *base_ifp)
++set_interface_parameters(const interface_t *ifp, interface_t *base_ifp, sa_family_t family)
+ {
+ if (all_rp_filter == UINT_MAX)
+ clear_rp_filter();
+
+ #ifdef _HAVE_IPV4_DEVCONF_
+- set_interface_parameters_devconf(ifp, base_ifp);
++ set_interface_parameters_devconf(ifp, base_ifp, family);
+ #else
+- set_interface_parameters_sysctl(ifp, base_ifp);
++ set_interface_parameters_sysctl(ifp, base_ifp, family);
+ #endif
+ }
+
+diff --git a/keepalived/vrrp/vrrp_vmac.c b/keepalived/vrrp/vrrp_vmac.c
+index e5ff0e9..021953a 100644
+--- a/keepalived/vrrp/vrrp_vmac.c
++++ b/keepalived/vrrp/vrrp_vmac.c
+@@ -407,10 +407,9 @@ netlink_link_add_vmac(vrrp_t *vrrp, const interface_t *old_interface)
+ if (!ifp->ifindex)
+ return false;
+
+- if (vrrp->family == AF_INET && create_interface) {
++ if (create_interface) {
+ /* Set the necessary kernel parameters to make macvlans work for us */
+-// If this saves current base_ifp's settings, we need to be careful if multiple VMACs on same i/f
+- set_interface_parameters(ifp, ifp->base_ifp);
++ set_interface_parameters(ifp, ifp->base_ifp, vrrp->family);
+ }
+
+ #ifdef _WITH_FIREWALL_
+--
+2.34.1
+
diff --git a/scripts/package-build/linux-kernel/.gitignore b/scripts/package-build/linux-kernel/.gitignore
new file mode 100644
index 00000000..0a18ea8c
--- /dev/null
+++ b/scripts/package-build/linux-kernel/.gitignore
@@ -0,0 +1,32 @@
+/linux*
+/wireguard
+/wireguard-linux-compat
+/accel-ppp
+/intel-qat
+/linux-firmware
+/vyos-drivers-intel*
+/vyos-drivers-realtek*
+/ovpn-dco
+/nat-rtsp*
+/jool*
+/qat*
+/QAT*
+*.tar.gz
+*.tar.xz
+/*.postinst
+
+# Intel Driver source
+i40e-*/
+igb-*/
+ixgbe-*/
+ixgbevf-*/
+vyos-intel-*/
+vyos-linux-firmware*/
+kernel-vars
+r8152-*.tar.bz2
+
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/linux-kernel/README.md b/scripts/package-build/linux-kernel/README.md
new file mode 100644
index 00000000..56954e5a
--- /dev/null
+++ b/scripts/package-build/linux-kernel/README.md
@@ -0,0 +1,41 @@
+# Build
+```
+./build.py --config package.toml --packages linux-kernel accel-ppp xxx
+```
+
+# About
+
+VyOS runs on a custom Linux Kernel (which is 4.19) at the time of this writing.
+This repository holds a Jenkins Pipeline which is used to build the Custom
+Kernel (x86_64/amd64 at the moment) and all required out-of tree modules.
+
+VyOS does not utilize the build in Intel Kernel drivers for its NICs as those
+Kernels sometimes lack features e.g. configurable receive-side-scaling queues.
+On the other hand we ship additional not mainlined features as WireGuard VPN.
+
+## Kernel
+
+The Kernel is build from the vanilla repositories hosted at https://git.kernel.org.
+VyOS requires two additional patches to work which are stored in the patches/kernel
+folder.
+
+### Config
+
+The Kernel configuration used is [x86_64_vyos_defconfig](x86_64_vyos_defconfig)
+which will be copied on demand during the Pipeline run into the `arch/x86/configs`i
+direcotry of the Kernel source tree.
+
+Other configurations can be added in the future easily.
+
+### Modules
+
+VyOS utilizes several Out-of-Tree modules (e.g. WireGuard, Accel-PPP and Intel
+network interface card drivers). Module source code is retrieved from the
+upstream repository and - when needed - patched so it can be build using this
+pipeline.
+
+In the past VyOS maintainers had a fork of the Linux Kernel, WireGuard and
+Accel-PPP. This is fine but increases maintenance effort. By utilizing vanilla
+repositories upgrading to new versions is very easy - only the branch/commit/tag
+used when cloning the repository via [Jenkinsfile](Jenkinsfile) needs to be
+adjusted.
diff --git a/scripts/package-build/linux-kernel/arch b/scripts/package-build/linux-kernel/arch
new file mode 120000
index 00000000..f5f81fdc
--- /dev/null
+++ b/scripts/package-build/linux-kernel/arch
@@ -0,0 +1 @@
+../../../packages/linux-kernel/arch \ No newline at end of file
diff --git a/scripts/package-build/linux-kernel/build-accel-ppp.sh b/scripts/package-build/linux-kernel/build-accel-ppp.sh
new file mode 100755
index 00000000..1685ff8d
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-accel-ppp.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+ACCEL_SRC=${CWD}/accel-ppp
+if [ ! -d ${ACCEL_SRC} ]; then
+ echo "Accel-PPP source not found"
+ exit 1
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+PATCH_DIR=${CWD}/patches/accel-ppp
+if [ -d $PATCH_DIR ]; then
+ cd ${ACCEL_SRC}
+ for patch in $(ls ${PATCH_DIR})
+ do
+ echo "I: Apply patch: ${PATCH_DIR}/${patch}"
+ patch -p1 < ${PATCH_DIR}/${patch}
+ done
+fi
+
+. ${KERNEL_VAR_FILE}
+mkdir -p ${ACCEL_SRC}/build
+cd ${ACCEL_SRC}/build
+
+echo "I: Build Accel-PPP Debian package"
+cmake -DBUILD_IPOE_DRIVER=TRUE \
+ -DBUILD_VLAN_MON_DRIVER=TRUE \
+ -DCMAKE_INSTALL_PREFIX=/usr \
+ -DKDIR=${KERNEL_DIR} \
+ -DLUA=5.3 \
+ -DMODULES_KDIR=${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ -DCPACK_TYPE=Debian12 ..
+make
+cpack -G DEB
+
+# rename resulting Debian package according git description
+mv accel-ppp*.deb ${CWD}/accel-ppp_$(git describe --always --tags)_$(dpkg --print-architecture).deb
diff --git a/scripts/package-build/linux-kernel/build-intel-ixgbe.sh b/scripts/package-build/linux-kernel/build-intel-ixgbe.sh
new file mode 100755
index 00000000..5f45c62a
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-intel-ixgbe.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+if ! dpkg-architecture -iamd64; then
+ echo "Intel ixgbe is only buildable on amd64 platforms"
+ exit 0
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+url="https://sourceforge.net/projects/e1000/files/ixgbe%20stable/5.20.3/ixgbe-5.20.3.tar.gz"
+
+cd ${CWD}
+
+DRIVER_FILE=$(basename ${url} | sed -e s/tar_0/tar/)
+DRIVER_DIR="${DRIVER_FILE%.tar.gz}"
+DRIVER_NAME="ixgbe"
+DRIVER_VERSION=$(echo ${DRIVER_DIR} | awk -F${DRIVER_NAME} '{print $2}' | sed 's/^-//')
+DRIVER_VERSION_EXTRA=""
+
+# Build up Debian related variables required for packaging
+DEBIAN_ARCH=$(dpkg --print-architecture)
+DEBIAN_DIR="${CWD}/vyos-intel-${DRIVER_NAME}_${DRIVER_VERSION}_${DEBIAN_ARCH}"
+DEBIAN_CONTROL="${DEBIAN_DIR}/DEBIAN/control"
+DEBIAN_POSTINST="${CWD}/vyos-intel-ixgbe.postinst"
+
+# Fetch Intel driver source from SourceForge
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+curl -L -o ${DRIVER_FILE} ${url}
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+# Unpack archive
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+mkdir -p ${DRIVER_DIR}
+tar -C ${DRIVER_DIR} --strip-components=1 -xf ${DRIVER_FILE}
+
+cd ${DRIVER_DIR}/src
+if [ -z $KERNEL_DIR ]; then
+ echo "KERNEL_DIR not defined"
+ exit 1
+fi
+
+# See https://lore.kernel.org/lkml/f90837d0-810e-5772-7841-28d47c44d260@intel.com/
+echo "I: remove pci_enable_pcie_error_reporting() code no longer present in Kernel"
+sed -i '/.*pci_disable_pcie_error_reporting(pdev);/d' ixgbe_main.c
+sed -i '/.*pci_enable_pcie_error_reporting(pdev);/d' ixgbe_main.c
+
+# See https://vyos.dev/T6155
+echo "I: always enable allow_unsupported_sfp for all NICs by default"
+patch -l -p1 < ../../patches/ixgbe/allow_unsupported_sfp.patch
+
+# See https://vyos.dev/T6162
+echo "I: add 1000BASE-BX support"
+patch -l -p1 < ../../patches/ixgbe/add_1000base-bx_support.patch
+
+echo "I: Compile Kernel module for Intel ${DRIVER_NAME} driver"
+make KSRC=${KERNEL_DIR} INSTALL_MOD_PATH=${DEBIAN_DIR} INSTALL_FW_PATH=${DEBIAN_DIR} -j $(getconf _NPROCESSORS_ONLN) install
+
+if [ "x$?" != "x0" ]; then
+ exit 1
+fi
+
+if [ -f ${DEBIAN_DIR}.deb ]; then
+ rm ${DEBIAN_DIR}.deb
+fi
+
+# build Debian package
+echo "I: Building Debian package vyos-intel-${DRIVER_NAME}"
+cd ${CWD}
+
+# delete non required files which are also present in the kernel package
+# und thus lead to duplicated files
+find ${DEBIAN_DIR} -name "modules.*" | xargs rm -f
+
+echo "#!/bin/sh" > ${DEBIAN_POSTINST}
+echo "/sbin/depmod -a ${KERNEL_VERSION}${KERNEL_SUFFIX}" >> ${DEBIAN_POSTINST}
+
+fpm --input-type dir --output-type deb --name vyos-intel-${DRIVER_NAME} \
+ --version ${DRIVER_VERSION} --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Vendor based driver for Intel ${DRIVER_NAME}" \
+ --depends linux-image-${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ --license "GPL2" -C ${DEBIAN_DIR} --after-install ${DEBIAN_POSTINST}
+
+echo "I: Cleanup ${DRIVER_NAME} source"
+cd ${CWD}
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+if [ -d ${DEBIAN_DIR} ]; then
+ rm -rf ${DEBIAN_DIR}
+fi
diff --git a/scripts/package-build/linux-kernel/build-intel-ixgbevf.sh b/scripts/package-build/linux-kernel/build-intel-ixgbevf.sh
new file mode 100755
index 00000000..a965e0de
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-intel-ixgbevf.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+if ! dpkg-architecture -iamd64; then
+ echo "Intel ixgbevf is only buildable on amd64 platforms"
+ exit 0
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+url="https://sourceforge.net/projects/e1000/files/ixgbevf%20stable/4.18.9/ixgbevf-4.18.9.tar.gz"
+
+cd ${CWD}
+
+DRIVER_FILE=$(basename ${url} | sed -e s/tar_0/tar/)
+DRIVER_DIR="${DRIVER_FILE%.tar.gz}"
+DRIVER_NAME="ixgbevf"
+DRIVER_VERSION=$(echo ${DRIVER_DIR} | awk -F${DRIVER_NAME} '{print $2}' | sed 's/^-//')
+DRIVER_VERSION_EXTRA=""
+
+# Build up Debian related variables required for packaging
+DEBIAN_ARCH=$(dpkg --print-architecture)
+DEBIAN_DIR="${CWD}/vyos-intel-${DRIVER_NAME}_${DRIVER_VERSION}_${DEBIAN_ARCH}"
+DEBIAN_CONTROL="${DEBIAN_DIR}/DEBIAN/control"
+DEBIAN_POSTINST="${CWD}/vyos-intel-ixgbevf.postinst"
+
+# Fetch Intel driver source from SourceForge
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+curl -L -o ${DRIVER_FILE} ${url}
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+# Unpack archive
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+mkdir -p ${DRIVER_DIR}
+tar -C ${DRIVER_DIR} --strip-components=1 -xf ${DRIVER_FILE}
+
+cd ${DRIVER_DIR}/src
+if [ -z $KERNEL_DIR ]; then
+ echo "KERNEL_DIR not defined"
+ exit 1
+fi
+
+# See https://lore.kernel.org/lkml/f90837d0-810e-5772-7841-28d47c44d260@intel.com/
+echo "I: remove pci_enable_pcie_error_reporting() code no longer present in Kernel"
+sed -i '/.*pci_disable_pcie_error_reporting(pdev);/d' ixgbevf_main.c
+sed -i '/.*pci_enable_pcie_error_reporting(pdev);/d' ixgbevf_main.c
+
+echo "I: Compile Kernel module for Intel ${DRIVER_NAME} driver"
+make KSRC=${KERNEL_DIR} INSTALL_MOD_PATH=${DEBIAN_DIR} INSTALL_FW_PATH=${DEBIAN_DIR} -j $(getconf _NPROCESSORS_ONLN) install
+
+if [ "x$?" != "x0" ]; then
+ exit 1
+fi
+
+if [ -f ${DEBIAN_DIR}.deb ]; then
+ rm ${DEBIAN_DIR}.deb
+fi
+
+# build Debian package
+echo "I: Building Debian package vyos-intel-${DRIVER_NAME}"
+cd ${CWD}
+
+# delete non required files which are also present in the kernel package
+# und thus lead to duplicated files
+find ${DEBIAN_DIR} -name "modules.*" | xargs rm -f
+
+echo "#!/bin/sh" > ${DEBIAN_POSTINST}
+echo "/sbin/depmod -a ${KERNEL_VERSION}${KERNEL_SUFFIX}" >> ${DEBIAN_POSTINST}
+
+fpm --input-type dir --output-type deb --name vyos-intel-${DRIVER_NAME} \
+ --version ${DRIVER_VERSION} --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Vendor based driver for Intel ${DRIVER_NAME}" \
+ --depends linux-image-${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ --license "GPL2" -C ${DEBIAN_DIR} --after-install ${DEBIAN_POSTINST}
+
+echo "I: Cleanup ${DRIVER_NAME} source"
+cd ${CWD}
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+if [ -d ${DEBIAN_DIR} ]; then
+ rm -rf ${DEBIAN_DIR}
+fi
+
diff --git a/scripts/package-build/linux-kernel/build-intel-qat.sh b/scripts/package-build/linux-kernel/build-intel-qat.sh
new file mode 100755
index 00000000..765cea3f
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-intel-qat.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+if ! dpkg-architecture -iamd64; then
+ echo "Intel-QAT is only buildable on amd64 platforms"
+ exit 0
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+url="https://dev.packages.vyos.net/source-mirror/QAT.L.4.24.0-00005.tar.gz"
+
+cd ${CWD}
+
+DRIVER_FILE=$(basename ${url} | sed -e s/tar_0/tar/)
+DRIVER_DIR="${DRIVER_FILE%.tar.gz}"
+DRIVER_NAME="QAT"
+DRIVER_NAME_EXTRA="L."
+DRIVER_VERSION=$(echo ${DRIVER_DIR} | awk -F${DRIVER_NAME} '{print $2}' | awk -F${DRIVER_NAME_EXTRA} '{print $2}')
+DRIVER_VERSION_EXTRA="-0"
+
+# Build up Debian related variables required for packaging
+DEBIAN_ARCH=$(dpkg --print-architecture)
+DEBIAN_DIR="${CWD}/vyos-intel-${DRIVER_NAME}_${DRIVER_VERSION}${DRIVER_VERSION_EXTRA}_${DEBIAN_ARCH}"
+DEBIAN_CONTROL="${DEBIAN_DIR}/DEBIAN/control"
+DEBIAN_POSTINST="${CWD}/vyos-intel-qat.postinst"
+
+# Fetch Intel driver source from SourceForge
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+curl -L -o ${DRIVER_FILE} ${url}
+if [ "$?" -ne "0" ]; then
+ exit 1
+fi
+
+# Unpack archive
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+mkdir -p ${DRIVER_DIR}
+tar -C ${DRIVER_DIR} -xf ${DRIVER_FILE}
+
+cd ${DRIVER_DIR}
+if [ -z $KERNEL_DIR ]; then
+ echo "KERNEL_DIR not defined"
+ exit 1
+fi
+
+echo "I: Compile Kernel module for Intel ${DRIVER_NAME} driver"
+mkdir -p \
+ ${DEBIAN_DIR}/lib/firmware \
+ ${DEBIAN_DIR}/usr/sbin \
+ ${DEBIAN_DIR}/usr/lib/x86_64-linux-gnu \
+ ${DEBIAN_DIR}/etc/init.d
+KERNEL_SOURCE_ROOT=${KERNEL_DIR} ./configure --enable-kapi --enable-qat-lkcf
+make -j $(getconf _NPROCESSORS_ONLN) all
+make INSTALL_MOD_PATH=${DEBIAN_DIR} INSTALL_FW_PATH=${DEBIAN_DIR} \
+ qat-driver-install adf-ctl-all
+
+if [ "x$?" != "x0" ]; then
+ exit 1
+fi
+
+cp quickassist/qat/fw/*.bin ${DEBIAN_DIR}/lib/firmware
+cp build/*.so ${DEBIAN_DIR}/usr/lib/x86_64-linux-gnu
+cp build/adf_ctl ${DEBIAN_DIR}/usr/sbin
+cp quickassist/build_system/build_files/qat_service ${DEBIAN_DIR}/etc/init.d
+cp build/usdm_drv.ko ${DEBIAN_DIR}/lib/modules/${KERNEL_VERSION}${KERNEL_SUFFIX}/updates/drivers
+chmod 644 ${DEBIAN_DIR}/lib/firmware/*
+chmod 755 ${DEBIAN_DIR}/etc/init.d/* ${DEBIAN_DIR}/usr/local/bin/*
+
+if [ -f ${DEBIAN_DIR}.deb ]; then
+ rm ${DEBIAN_DIR}.deb
+fi
+
+# build Debian package
+echo "I: Building Debian package vyos-intel-${DRIVER_NAME}"
+cd ${CWD}
+
+# delete non required files which are also present in the kernel package
+# und thus lead to duplicated files
+find ${DEBIAN_DIR} -name "modules.*" | xargs rm -f
+
+echo "#!/bin/sh" > ${DEBIAN_POSTINST}
+echo "/sbin/depmod -a ${KERNEL_VERSION}${KERNEL_SUFFIX}" >> ${DEBIAN_POSTINST}
+
+fpm --input-type dir --output-type deb --name vyos-intel-${DRIVER_NAME} \
+ --version ${DRIVER_VERSION}${DRIVER_VERSION_EXTRA} --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Vendor based driver for Intel ${DRIVER_NAME}" \
+ --depends linux-image-${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ --license "GPL2" -C ${DEBIAN_DIR} --after-install ${DEBIAN_POSTINST}
+
+echo "I: Cleanup ${DRIVER_NAME} source"
+cd ${CWD}
+if [ -e ${DRIVER_FILE} ]; then
+ rm -f ${DRIVER_FILE}
+fi
+if [ -d ${DRIVER_DIR} ]; then
+ rm -rf ${DRIVER_DIR}
+fi
+if [ -d ${DEBIAN_DIR} ]; then
+ rm -rf ${DEBIAN_DIR}
+fi
diff --git a/scripts/package-build/linux-kernel/build-jool.py b/scripts/package-build/linux-kernel/build-jool.py
new file mode 100755
index 00000000..570293f5
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-jool.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+from tomllib import loads as toml_loads
+from requests import get
+from pathlib import Path
+from subprocess import run
+
+def find_arch() -> str:
+ tmp=run(['dpkg-architecture', '-q', 'DEB_HOST_ARCH'], capture_output=True)
+ return tmp.stdout.decode().strip()
+
+# dependency modifier
+def add_depends(package_dir: str, package_name: str,
+ depends: list[str]) -> None:
+ """Add dependencies to a package
+
+ Args:
+ package_dir (str): a directory where package sources are located
+ package_name (str): a name of package
+ depends (list[str]): a list of dependencies to add
+ """
+ depends_list: str = ', '.join(depends)
+ depends_line: str = f'misc:Depends={depends_list}\n'
+
+ substvars_file = Path(f'{package_dir}/debian/{package_name}.substvars')
+ substvars_file.write_text(depends_line)
+
+
+# find kernel version and source path
+arch: str = find_arch()
+defaults_file: str = Path('../../../data/defaults.toml').read_text()
+architecture_file: str = Path(f'../../../data/architectures/{arch}.toml').read_text()
+KERNEL_VER: str = toml_loads(defaults_file).get('kernel_version')
+KERNEL_FLAVOR: str = toml_loads(architecture_file).get('kernel_flavor')
+KERNEL_SRC: str = Path.cwd().as_posix() + '/linux'
+
+# define variables
+PACKAGE_NAME: str = 'jool'
+PACKAGE_VERSION: str = '4.1.9+bf4c7e3669'
+PACKAGE_DIR: str = f'{PACKAGE_NAME}-{PACKAGE_VERSION}'
+SOURCES_ARCHIVE: str = 'jool-4.1.9+bf4c7e3669.tar.gz'
+SOURCES_URL: str = f'https://github.com/NICMx/Jool/archive/7f08c42c615ed63cf0fdc1522d91aa0809f6d990.tar.gz'
+
+# download sources
+sources_archive = Path(SOURCES_ARCHIVE)
+sources_archive.write_bytes(get(SOURCES_URL).content)
+
+# prepare sources
+debmake_cmd: list[str] = [
+ 'debmake', '-e', 'support@vyos.io', '-f', 'VyOS Support', '-p',
+ PACKAGE_NAME, '-u', PACKAGE_VERSION, '-a', SOURCES_ARCHIVE
+]
+run(debmake_cmd)
+
+# add kernel to dependencies
+add_depends(PACKAGE_DIR, PACKAGE_NAME,
+ [f'linux-image-{KERNEL_VER}-{KERNEL_FLAVOR}'])
+
+# configure build rules
+build_rules_text: str = f'''#!/usr/bin/make -f
+# config
+export KERNEL_DIR := {KERNEL_SRC}
+PACKAGE_BUILD_DIR := debian/{PACKAGE_NAME}
+KVER := {KERNEL_VER}-{KERNEL_FLAVOR}
+MODULES_DIR := extra
+
+# main packaging script based on dh7 syntax
+%:
+ dh $@
+
+override_dh_clean:
+ dh_clean --exclude=debian/{PACKAGE_NAME}.substvars
+
+override_dh_prep:
+ dh_prep --exclude=debian/{PACKAGE_NAME}.substvars
+
+# override_dh_auto_clean:
+# make -C src/mod clean
+
+override_dh_auto_build:
+ dh_auto_build $@
+ make -C ${{KERNEL_DIR}} M=$$PWD/src/mod/common modules
+ make -C ${{KERNEL_DIR}} M=$$PWD/src/mod/nat64 modules
+ make -C ${{KERNEL_DIR}} M=$$PWD/src/mod/siit modules
+
+override_dh_auto_install:
+ dh_auto_install $@
+ install -D -m 644 src/mod/common/jool_common.ko ${{PACKAGE_BUILD_DIR}}/lib/modules/${{KVER}}/${{MODULES_DIR}}/jool_common.ko
+ install -D -m 644 src/mod/nat64/jool.ko ${{PACKAGE_BUILD_DIR}}/lib/modules/${{KVER}}/${{MODULES_DIR}}/jool.ko
+ install -D -m 644 src/mod/siit/jool_siit.ko ${{PACKAGE_BUILD_DIR}}/lib/modules/${{KVER}}/${{MODULES_DIR}}/jool_siit.ko
+
+'''
+bild_rules = Path(f'{PACKAGE_DIR}/debian/rules')
+bild_rules.write_text(build_rules_text)
+
+# build a package
+debuild_cmd: list[str] = ['debuild']
+run(debuild_cmd, cwd=PACKAGE_DIR)
diff --git a/scripts/package-build/linux-kernel/build-kernel.sh b/scripts/package-build/linux-kernel/build-kernel.sh
new file mode 100755
index 00000000..2c02f5c3
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-kernel.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+CWD=$(pwd)
+KERNEL_SRC=linux
+
+set -e
+
+if [ ! -d ${KERNEL_SRC} ]; then
+ echo "Linux Kernel source directory does not exists, please 'git clone'"
+ exit 1
+fi
+
+echo "I: Copy Kernel config (x86_64_vyos_defconfig) to Kernel Source"
+cp -rv arch/ ${KERNEL_SRC}/
+
+cd ${KERNEL_SRC}
+
+echo "I: clean modified files"
+git reset --hard HEAD
+
+KERNEL_VERSION=$(make kernelversion)
+KERNEL_SUFFIX=-$(dpkg --print-architecture)-vyos
+
+# VyOS requires some small Kernel Patches - apply them here
+# It's easier to habe them here and make use of the upstream
+# repository instead of maintaining a full Kernel Fork.
+# Saving time/resources is essential :-)
+PATCH_DIR=${CWD}/patches/kernel
+for patch in $(ls ${PATCH_DIR})
+do
+ echo "I: Apply Kernel patch: ${PATCH_DIR}/${patch}"
+ patch -p1 < ${PATCH_DIR}/${patch}
+done
+
+echo "I: make vyos_defconfig"
+# Select Kernel configuration - currently there is only one
+make vyos_defconfig
+
+echo "I: Generate environment file containing Kernel variable"
+cat << EOF >${CWD}/kernel-vars
+#!/bin/sh
+export KERNEL_VERSION=${KERNEL_VERSION}
+export KERNEL_SUFFIX=${KERNEL_SUFFIX}
+export KERNEL_DIR=${CWD}/${KERNEL_SRC}
+EOF
+
+echo "I: Build Debian Kernel package"
+touch .scmversion
+make bindeb-pkg BUILD_TOOLS=1 LOCALVERSION=${KERNEL_SUFFIX} KDEB_PKGVERSION=${KERNEL_VERSION}-1 -j $(getconf _NPROCESSORS_ONLN)
+
+cd $CWD
+if [[ $? == 0 ]]; then
+ for package in $(ls linux-*.deb)
+ do
+ ln -sf linux-kernel/$package ..
+ done
+fi
diff --git a/scripts/package-build/linux-kernel/build-linux-firmware.sh b/scripts/package-build/linux-kernel/build-linux-firmware.sh
new file mode 100755
index 00000000..2b1fa7b7
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-linux-firmware.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+
+# All selected drivers are then precomfiled "make drivers/foo/bar.i" and we grep for
+# the magic word "UNIQUE_ID_firmware" which identifies firmware files.
+
+CWD=$(pwd)
+LINUX_SRC="linux"
+LINUX_FIRMWARE="linux-firmware"
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+if [ ! -d ${LINUX_SRC} ]; then
+ echo "Kernel source missing"
+ exit 1
+fi
+
+if [ ! -d ${LINUX_FIRMWARE} ]; then
+ echo "Linux firmware repository missing"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+result=()
+# Retrieve firmware blobs from source files
+FW_FILES=$(find ${LINUX_SRC}/debian/linux-image/lib/modules/${KERNEL_VERSION}${KERNEL_SUFFIX}/kernel/drivers/net -name *.ko | xargs modinfo | grep "^firmware:" | awk '{print $2}')
+
+# Debian package will use the descriptive Git commit as version
+GIT_COMMIT=$(cd ${CWD}/${LINUX_FIRMWARE}; git describe --always)
+VYOS_FIRMWARE_NAME="vyos-linux-firmware"
+VYOS_FIRMWARE_DIR="${VYOS_FIRMWARE_NAME}_${GIT_COMMIT}-0_all"
+if [ -d ${VYOS_FIRMWARE_DIR} ]; then
+ # remove Debian package folder and deb file from previous runs
+ rm -rf ${VYOS_FIRMWARE_DIR}*
+fi
+mkdir -p ${VYOS_FIRMWARE_DIR}
+
+# Install firmware files to build directory
+LINUX_FIRMWARE_BUILD_DIR="${LINUX_FIRMWARE}_${GIT_COMMIT}"
+
+if [ -d ${LINUX_FIRMWARE_BUILD_DIR} ]; then
+ rm -rf "${LINUX_FIRMWARE_BUILD_DIR}"
+fi
+
+mkdir -p "${LINUX_FIRMWARE_BUILD_DIR}"
+
+(
+ cd ${LINUX_FIRMWARE}
+ ./copy-firmware.sh "${CWD}/${LINUX_FIRMWARE_BUILD_DIR}"
+)
+
+# Copy firmware file from linux firmware build directory into
+# assembly folder for the vyos-firmware package
+SED_REPLACE="s@${CWD}/${LINUX_FIRMWARE}/@@"
+for FILE_PATTERN in ${FW_FILES}; do
+ find "${LINUX_FIRMWARE_BUILD_DIR}" -path "*/${FILE_PATTERN}" -print0 | while IFS= read -r -d $'\0' FILE; do
+ TARGET="$(echo "${FILE}" | sed "s/${LINUX_FIRMWARE_BUILD_DIR}\///g")"
+ TARGET_DIR="${VYOS_FIRMWARE_DIR}/lib/firmware/$(dirname "${TARGET}")"
+ # If file is a symlink install the symlink target as well
+ if [ -h "${FILE}" ]; then
+ if [ ! -f "${TARGET_DIR}/$(basename "${TARGET}")" ]; then
+ if [ -f "${LINUX_FIRMWARE_BUILD_DIR}/${TARGET}" ]; then
+ mkdir -p "${TARGET_DIR}"
+
+ echo "I: install firmware: ${TARGET}"
+ cp "${CWD}/${LINUX_FIRMWARE_BUILD_DIR}/${TARGET}" "${TARGET_DIR}"
+ # If file links to other folder which this script not cover. Create folder and copy together.
+ if [ -L "${LINUX_FIRMWARE_BUILD_DIR}/${TARGET}" ]; then
+ REALPATH_TARGET=$(realpath --relative-to="${CWD}/${LINUX_FIRMWARE_BUILD_DIR}" "${CWD}/${LINUX_FIRMWARE_BUILD_DIR}/${TARGET}")
+ REALPATH_TARGET_DIR="${VYOS_FIRMWARE_DIR}/lib/firmware/$(dirname "${REALPATH_TARGET}")"
+ mkdir -p "${REALPATH_TARGET_DIR}"
+ echo "I: install firmware: ${REALPATH_TARGET}"
+ cp "${CWD}/${LINUX_FIRMWARE_BUILD_DIR}/${REALPATH_TARGET}" "${REALPATH_TARGET_DIR}"
+ fi
+ else
+ echo "I: firmware file not found: ${TARGET}"
+ fi
+ fi
+ fi
+
+ if [ -f "${FILE}" ]; then
+ mkdir -p "${TARGET_DIR}"
+ echo "I: install firmware: ${TARGET}"
+ cp -P "${CWD}/${LINUX_FIRMWARE_BUILD_DIR}/${TARGET}" "${TARGET_DIR}"
+ else
+ echo "I: firmware file not found: ${TARGET}"
+ fi
+ done
+done
+
+echo "I: Create linux-firmware package"
+rm -f ${VYOS_FIRMWARE_NAME}_*.deb
+fpm --input-type dir --output-type deb --name ${VYOS_FIRMWARE_NAME} \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Binary firmware for various drivers in the Linux kernel" \
+ --architecture all --version ${GIT_COMMIT} --deb-compression gz -C ${VYOS_FIRMWARE_DIR}
+
+rm -rf "${LINUX_FIRMWARE_BUILD_DIR}"
+rm -rf ${VYOS_FIRMWARE_DIR}
diff --git a/scripts/package-build/linux-kernel/build-nat-rtsp.sh b/scripts/package-build/linux-kernel/build-nat-rtsp.sh
new file mode 100755
index 00000000..ec7d19a6
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-nat-rtsp.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+SRC=${CWD}/nat-rtsp
+if [ ! -d ${SRC} ]; then
+ echo "nat-rtsp source not found"
+ exit 1
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+cd ${SRC} && make KERNELDIR=$KERNEL_DIR
+
+# Copy binary to package directory
+DEBIAN_DIR=tmp/lib/modules/${KERNEL_VERSION}${KERNEL_SUFFIX}/extra
+mkdir -p ${DEBIAN_DIR}
+cp nf_conntrack_rtsp.ko nf_nat_rtsp.ko ${DEBIAN_DIR}
+
+DEBIAN_POSTINST="${CWD}/vyos-nat-rtsp.postinst"
+echo "#!/bin/sh" > ${DEBIAN_POSTINST}
+echo "/sbin/depmod -a ${KERNEL_VERSION}${KERNEL_SUFFIX}" >> ${DEBIAN_POSTINST}
+
+# Build Debian Package
+fpm --input-type dir --output-type deb --name nat-rtsp \
+ --version $(git describe --tags --always) --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Connection tracking and NAT support for RTSP" \
+ --depends linux-image-${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ --after-install ${DEBIAN_POSTINST} \
+ --license "GPL2" --chdir tmp
+
+mv *.deb ..
diff --git a/scripts/package-build/linux-kernel/build-openvpn-dco.sh b/scripts/package-build/linux-kernel/build-openvpn-dco.sh
new file mode 100755
index 00000000..fd427825
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build-openvpn-dco.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+CWD=$(pwd)
+KERNEL_VAR_FILE=${CWD}/kernel-vars
+
+SRC=${CWD}/ovpn-dco
+if [ ! -d ${SRC} ]; then
+ echo "OpenVPN DCO source not found"
+ exit 1
+fi
+
+if [ ! -f ${KERNEL_VAR_FILE} ]; then
+ echo "Kernel variable file '${KERNEL_VAR_FILE}' does not exist, run ./build_kernel.sh first"
+ exit 1
+fi
+
+. ${KERNEL_VAR_FILE}
+
+cd ${SRC} && make KERNEL_SRC=$KERNEL_DIR
+
+# Copy binary to package directory
+DEBIAN_DIR=tmp/lib/modules/${KERNEL_VERSION}${KERNEL_SUFFIX}/extra
+mkdir -p ${DEBIAN_DIR}
+cp drivers/net/ovpn-dco/ovpn-dco-v2.ko ${DEBIAN_DIR}
+
+# Build Debian Package
+fpm --input-type dir --output-type deb --name openvpn-dco \
+ --version $(git describe | sed s/^v//) --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "OpenVPN Data Channel Offload" \
+ --depends linux-image-${KERNEL_VERSION}${KERNEL_SUFFIX} \
+ --license "GPL2" --chdir tmp
+
+mv *.deb ..
diff --git a/scripts/package-build/linux-kernel/build.py b/scripts/package-build/linux-kernel/build.py
new file mode 100755
index 00000000..1bcab686
--- /dev/null
+++ b/scripts/package-build/linux-kernel/build.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import datetime
+import glob
+import shutil
+import toml
+import os
+import subprocess
+
+from argparse import ArgumentParser
+from pathlib import Path
+from subprocess import run, CalledProcessError
+
+# Relative path to defaults.toml
+defaults_path = "../../../data/defaults.toml"
+
+
+def ensure_dependencies(dependencies: list) -> None:
+ """Ensure Debian build dependencies are met"""
+ if not dependencies:
+ print("I: No additional dependencies to install")
+ return
+
+ print("I: Ensure Debian build dependencies are met")
+ run(['sudo', 'apt-get', 'install', '-y'] + dependencies, check=True)
+
+
+def prepare_package(repo_dir: Path, install_data: str) -> None:
+ """Prepare a package"""
+ if not install_data:
+ print("I: No install data provided, skipping package preparation")
+ return
+
+ install_file = repo_dir / 'debian/install'
+ install_file.parent.mkdir(parents=True, exist_ok=True)
+ install_file.write_text(install_data)
+ print("I: Prepared package")
+
+
+def clone_or_update_repo(repo_dir: Path, scm_url: str, commit_id: str) -> None:
+ """Clone the repository if it does not exist, otherwise update it"""
+ if repo_dir.exists():
+ #run(['git', 'fetch'], cwd=repo_dir, check=True)
+ run(['git', 'checkout', commit_id], cwd=repo_dir, check=True)
+ #run(['git', 'pull'], cwd=repo_dir, check=True)
+ else:
+ run(['git', 'clone', scm_url, str(repo_dir)], check=True)
+ run(['git', 'checkout', commit_id], cwd=repo_dir, check=True)
+
+
+def build_package(package: dict, dependencies: list) -> None:
+ """Build a package from the repository
+
+ Args:
+ package (dict): Package information
+ dependencies (list): List of additional dependencies
+ """
+ timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+ repo_name = package['name']
+ repo_dir = Path(repo_name)
+
+ try:
+ # Clone or update the repository
+ #clone_or_update_repo(repo_dir, package['scm_url'], package['commit_id'])
+
+ # Ensure dependencies
+ #ensure_dependencies(dependencies)
+
+ # Prepare the package if required
+ #if package.get('prepare_package', False):
+ # prepare_package(repo_dir, package.get('install_data', ''))
+
+ # Execute the build command
+ if package['build_cmd'] == 'build_kernel':
+ build_kernel(package['kernel_version'])
+ elif package['build_cmd'] == 'build_linux_firmware':
+ build_linux_firmware(package['commit_id'], package['scm_url'])
+ elif package['build_cmd'] == 'build_accel_ppp':
+ build_accel_ppp(package['commit_id'], package['scm_url'])
+ elif package['build_cmd'] == 'build_intel_qat':
+ build_intel_qat()
+ elif package['build_cmd'] == 'build_intel_ixgbe':
+ build_intel_ixgbe()
+ elif package['build_cmd'] == 'build_intel_ixgbevf':
+ build_intel_ixgbevf()
+ elif package['build_cmd'] == 'build_jool':
+ build_jool()
+ elif package['build_cmd'] == 'build_openvpn_dco':
+ build_openvpn_dco(package['commit_id'], package['scm_url'])
+ elif package['build_cmd'] == 'build_nat_rtsp':
+ build_nat_rtsp(package['commit_id'], package['scm_url'])
+ else:
+ run(package['build_cmd'], cwd=repo_dir, check=True, shell=True)
+
+ except CalledProcessError as e:
+ print(f"Failed to build package {repo_name}: {e}")
+ finally:
+ # Clean up repository directory
+ # shutil.rmtree(repo_dir, ignore_errors=True)
+ pass
+
+
+def cleanup_build_deps(repo_dir: Path) -> None:
+ """Clean up build dependency packages"""
+ try:
+ if repo_dir.exists():
+ for file in glob.glob(str(repo_dir / '*build-deps*.deb')):
+ os.remove(file)
+ print("Cleaned up build dependency packages")
+ except Exception as e:
+ print(f"Error cleaning up build dependencies: {e}")
+
+
+def copy_packages(repo_dir: Path) -> None:
+ """Copy generated .deb packages to the parent directory"""
+ try:
+ deb_files = glob.glob(str(repo_dir / '*.deb'))
+ for deb_file in deb_files:
+ shutil.copy(deb_file, repo_dir.parent)
+ print("Copied generated .deb packages")
+ except Exception as e:
+ print(f"Error copying packages: {e}")
+
+
+def merge_dicts(defaults, package):
+ return {**defaults, **package}
+
+
+def build_kernel(kernel_version):
+ """Build the Linux kernel"""
+ run(['gpg2', '--locate-keys', 'torvalds@kernel.org', 'gregkh@kernel.org'], check=True)
+ run(['curl', '-OL', f'https://www.kernel.org/pub/linux/kernel/v6.x/linux-{kernel_version}.tar.xz'], check=True)
+ run(['curl', '-OL', f'https://www.kernel.org/pub/linux/kernel/v6.x/linux-{kernel_version}.tar.sign'], check=True)
+ # Using pipes to handle decompression and verification
+ with subprocess.Popen(['xz', '-cd', f'linux-{kernel_version}.tar.xz'], stdout=subprocess.PIPE) as proc_xz:
+ run(['gpg2', '--verify', f'linux-{kernel_version}.tar.sign', '-'], stdin=proc_xz.stdout, check=True)
+ run(['tar', 'xf', f'linux-{kernel_version}.tar.xz'], check=True)
+ os.symlink(f'linux-{kernel_version}', 'linux')
+ run(['./build-kernel.sh'], check=True)
+
+
+def build_linux_firmware(commit_id, scm_url):
+ """Build Linux firmware"""
+ repo_dir = Path('linux-firmware')
+ clone_or_update_repo(repo_dir, scm_url, commit_id)
+ run(['./build-linux-firmware.sh'], check=True)
+
+
+def build_accel_ppp(commit_id, scm_url):
+ """Build accel-ppp"""
+ repo_dir = Path('accel-ppp')
+ clone_or_update_repo(repo_dir, scm_url, commit_id)
+ run(['./build-accel-ppp.sh'], check=True)
+
+
+def build_intel_qat():
+ """Build Intel QAT"""
+ run(['./build-intel-qat.sh'], check=True)
+
+
+def build_intel_ixgbe():
+ """Build Intel IXGBE"""
+ run(['./build-intel-ixgbe.sh'], check=True)
+
+
+def build_intel_ixgbevf():
+ """Build Intel IXGBEVF"""
+ run(['./build-intel-ixgbevf.sh'], check=True)
+
+
+def build_jool():
+ """Build Jool"""
+ run(['echo y | ./build-jool.py'], check=True, shell=True)
+
+
+def build_openvpn_dco(commit_id, scm_url):
+ """Build OpenVPN DCO"""
+ repo_dir = Path('ovpn-dco')
+ clone_or_update_repo(repo_dir, scm_url, commit_id)
+ run(['./build-openvpn-dco.sh'], check=True)
+
+
+def build_nat_rtsp(commit_id, scm_url):
+ """Build RTSP netfilter helper"""
+ repo_dir = Path('nat-rtsp')
+ clone_or_update_repo(repo_dir, scm_url, commit_id)
+ run(['./build-nat-rtsp.sh'], check=True)
+
+
+if __name__ == '__main__':
+ # Prepare argument parser
+ arg_parser = ArgumentParser()
+ arg_parser.add_argument('--config', default='package.toml', help='Path to the package configuration file')
+ arg_parser.add_argument('--packages', nargs='+', help='Names of packages to build (default: all)', default=[])
+ args = arg_parser.parse_args()
+
+ # Load package configuration
+ with open(args.config, 'r') as file:
+ config = toml.load(file)
+
+ # Extract defaults and packages
+ with open(defaults_path, 'r') as file:
+ defaults = toml.load(file)
+
+ packages = config['packages']
+
+ # Filter packages if specific packages are specified in the arguments
+ if args.packages:
+ packages = [pkg for pkg in packages if pkg['name'] in args.packages]
+
+ # Merge defaults into each package
+ packages = [merge_dicts(defaults, pkg) for pkg in packages]
+
+ for package in packages:
+ dependencies = package.get('dependencies', {}).get('packages', [])
+
+ # Build the package
+ build_package(package, dependencies)
+
+ # Clean up build dependency packages after build
+ cleanup_build_deps(Path(package['name']))
+
+ # Copy generated .deb packages to parent directory
+ copy_packages(Path(package['name']))
diff --git a/scripts/package-build/linux-kernel/package.toml b/scripts/package-build/linux-kernel/package.toml
new file mode 100644
index 00000000..8b030da0
--- /dev/null
+++ b/scripts/package-build/linux-kernel/package.toml
@@ -0,0 +1,62 @@
+# [defaults]
+# We get the kernel_version from vyos-build/data/defaults.toml
+# kernel_version = "6.6.47"
+# kernel_flavor = "amd64-vyos"
+
+
+[[packages]]
+name = "linux-kernel"
+commit_id = "" # Uses defaults.kernel_version
+scm_url = ""
+build_cmd = "build_kernel"
+
+[[packages]]
+name = "linux-firmware"
+commit_id = "20240610"
+scm_url = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git"
+build_cmd = "build_linux_firmware"
+
+[[packages]]
+name = "accel-ppp"
+commit_id = "1.13.0"
+scm_url = "https://github.com/accel-ppp/accel-ppp.git"
+build_cmd = "build_accel_ppp"
+
+
+[[packages]]
+name = "ovpn-dco"
+commit_id = "v0.2.20231117"
+scm_url = "https://github.com/OpenVPN/ovpn-dco"
+build_cmd = "build_openvpn_dco"
+
+[[packages]]
+name = "nat-rtsp"
+commit_id = "475af0a"
+scm_url = "https://github.com/maru-sama/rtsp-linux.git"
+build_cmd = "build_nat_rtsp"
+
+
+[[packages]]
+name = "qat"
+commit_id = ""
+scm_url = ""
+build_cmd = "build_intel_qat"
+
+[[packages]]
+name = "ixgbe"
+commit_id = ""
+scm_url = ""
+build_cmd = "build_intel_ixgbe"
+
+[[packages]]
+name = "ixgbevf"
+commit_id = ""
+scm_url = ""
+build_cmd = "build_intel_ixgbevf"
+
+[[packages]]
+name = "jool"
+commit_id = ""
+scm_url = ""
+build_cmd = "build_jool"
+
diff --git a/scripts/package-build/linux-kernel/patches b/scripts/package-build/linux-kernel/patches
new file mode 120000
index 00000000..fd016d35
--- /dev/null
+++ b/scripts/package-build/linux-kernel/patches
@@ -0,0 +1 @@
+../../../packages/linux-kernel/patches \ No newline at end of file
diff --git a/scripts/package-build/ndppd/.gitignore b/scripts/package-build/ndppd/.gitignore
new file mode 100644
index 00000000..2b71e9fb
--- /dev/null
+++ b/scripts/package-build/ndppd/.gitignore
@@ -0,0 +1,7 @@
+ndppd/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/ndppd/build.py b/scripts/package-build/ndppd/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/ndppd/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/ndppd/package.toml b/scripts/package-build/ndppd/package.toml
new file mode 100644
index 00000000..58ce6511
--- /dev/null
+++ b/scripts/package-build/ndppd/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "ndppd"
+commit_id = "debian/0.2.5-6"
+scm_url = "https://salsa.debian.org/debian/ndppd"
diff --git a/scripts/package-build/ndppd/patches/0001-skip-route-table-if-there-is-no-auto-rule.patch b/scripts/package-build/ndppd/patches/0001-skip-route-table-if-there-is-no-auto-rule.patch
new file mode 100644
index 00000000..df6d2e5c
--- /dev/null
+++ b/scripts/package-build/ndppd/patches/0001-skip-route-table-if-there-is-no-auto-rule.patch
@@ -0,0 +1,83 @@
+From b148ba055245cec5007ee91dd3ffbfeb58d49c5a Mon Sep 17 00:00:00 2001
+From: Henning Surmeier <me@hensur.de>
+Date: Sun, 9 Jan 2022 20:35:15 +0100
+Subject: [PATCH 1/2] skip route table if there is no auto rule
+
+---
+ src/ndppd.cc | 3 ++-
+ src/rule.cc | 8 ++++++++
+ src/rule.h | 4 ++++
+ 3 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/src/ndppd.cc b/src/ndppd.cc
+index bec9656..b303721 100644
+--- a/src/ndppd.cc
++++ b/src/ndppd.cc
+@@ -304,7 +304,8 @@ int main(int argc, char* argv[], char* env[])
+ t1.tv_sec = t2.tv_sec;
+ t1.tv_usec = t2.tv_usec;
+
+- route::update(elapsed_time);
++ if (rule::any_auto())
++ route::update(elapsed_time);
+ session::update_all(elapsed_time);
+ }
+
+diff --git a/src/rule.cc b/src/rule.cc
+index 9e72480..a1e8376 100644
+--- a/src/rule.cc
++++ b/src/rule.cc
+@@ -24,6 +24,8 @@
+
+ NDPPD_NS_BEGIN
+
++bool rule::_any_aut = false;
++
+ rule::rule()
+ {
+ }
+@@ -49,6 +51,7 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, bool aut)
+ ru->_pr = pr;
+ ru->_addr = addr;
+ ru->_aut = aut;
++ _any_aut = _any_aut || aut;
+
+ logger::debug()
+ << "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr
+@@ -57,6 +60,11 @@ ptr<rule> rule::create(const ptr<proxy>& pr, const address& addr, bool aut)
+ return ru;
+ }
+
++bool rule::any_auto()
++{
++ return _any_aut;
++}
++
+ const address& rule::addr() const
+ {
+ return _addr;
+diff --git a/src/rule.h b/src/rule.h
+index 6663066..ca2aa36 100644
+--- a/src/rule.h
++++ b/src/rule.h
+@@ -42,6 +42,8 @@ public:
+
+ bool check(const address& addr) const;
+
++ static bool any_auto();
++
+ private:
+ weak_ptr<rule> _ptr;
+
+@@ -53,6 +55,8 @@ private:
+
+ bool _aut;
+
++ static bool _any_aut;
++
+ rule();
+ };
+
+--
+2.34.1
+
diff --git a/scripts/package-build/ndppd/patches/0002-set-vyos-version.patch b/scripts/package-build/ndppd/patches/0002-set-vyos-version.patch
new file mode 100644
index 00000000..3fef87c4
--- /dev/null
+++ b/scripts/package-build/ndppd/patches/0002-set-vyos-version.patch
@@ -0,0 +1,25 @@
+From b0789cf679b0179d37e22f5a936af273d982abeb Mon Sep 17 00:00:00 2001
+From: Henning Surmeier <me@hensur.de>
+Date: Tue, 11 Jan 2022 13:05:47 +0100
+Subject: [PATCH 2/2] set -vyos version
+
+---
+ src/ndppd.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ndppd.h b/src/ndppd.h
+index 008726c..61ed950 100644
+--- a/src/ndppd.h
++++ b/src/ndppd.h
+@@ -21,7 +21,7 @@
+ #define NDPPD_NS_BEGIN namespace ndppd {
+ #define NDPPD_NS_END }
+
+-#define NDPPD_VERSION "0.2.4"
++#define NDPPD_VERSION "0.2.5-vyos"
+
+ #include <assert.h>
+
+--
+2.34.1
+
diff --git a/scripts/package-build/net-snmp/.gitignore b/scripts/package-build/net-snmp/.gitignore
new file mode 100644
index 00000000..67811e63
--- /dev/null
+++ b/scripts/package-build/net-snmp/.gitignore
@@ -0,0 +1,6 @@
+net-snmp/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/net-snmp/build.py b/scripts/package-build/net-snmp/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/net-snmp/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/net-snmp/package.toml b/scripts/package-build/net-snmp/package.toml
new file mode 100644
index 00000000..414f5e24
--- /dev/null
+++ b/scripts/package-build/net-snmp/package.toml
@@ -0,0 +1,5 @@
+[[packages]]
+name = "net-snmp"
+commit_id = "debian/5.9.4+dfsg-1"
+scm_url = "https://salsa.debian.org/debian/net-snmp"
+build_cmd = "dpkg-buildpackage -us -uc -tc -b || true"
diff --git a/scripts/package-build/net-snmp/patches/add-linux-6.7-compatibility-parsing.patch b/scripts/package-build/net-snmp/patches/add-linux-6.7-compatibility-parsing.patch
new file mode 100644
index 00000000..b6dcd77a
--- /dev/null
+++ b/scripts/package-build/net-snmp/patches/add-linux-6.7-compatibility-parsing.patch
@@ -0,0 +1,119 @@
+From f5ae6baf0018abda9dedc368fe6d52c0d7a8ab8f Mon Sep 17 00:00:00 2001
+From: Philippe Troin <phil+github-commits@fifi.org>
+Date: Sat, 3 Feb 2024 10:30:30 -0800
+Subject: [PATCH] Add Linux 6.7 compatibility parsing /proc/net/snmp
+
+Linux 6.7 adds a new OutTransmits field to Ip in /proc/net/snmp.
+This breaks the hard-coded assumptions about the Ip line length.
+Add compatibility to parse Linux 6.7 Ip header while keep support
+for previous versions.
+---
+ .../ip-mib/data_access/systemstats_linux.c | 46 +++++++++++++++----
+ 1 file changed, 37 insertions(+), 9 deletions(-)
+
+diff --git a/agent/mibgroup/ip-mib/data_access/systemstats_linux.c b/agent/mibgroup/ip-mib/data_access/systemstats_linux.c
+index 49e0a34d5c..f04e828a94 100644
+--- a/agent/mibgroup/ip-mib/data_access/systemstats_linux.c
++++ b/agent/mibgroup/ip-mib/data_access/systemstats_linux.c
+@@ -36,7 +36,7 @@ netsnmp_access_systemstats_arch_init(void)
+ }
+
+ /*
+- /proc/net/snmp
++ /proc/net/snmp - Linux 6.6 and lower
+
+ Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates
+ Ip: 2 64 7083534 0 0 0 0 0 6860233 6548963 0 0 1 286623 63322 1 259920 0 0
+@@ -49,6 +49,26 @@ netsnmp_access_systemstats_arch_init(void)
+
+ Udp: InDatagrams NoPorts InErrors OutDatagrams
+ Udp: 1491094 122 0 1466178
++*
++ /proc/net/snmp - Linux 6.7 and higher
++
++ Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates OutTransmits
++ Ip: 1 64 50859058 496 0 37470604 0 0 20472980 7515791 1756 0 0 7264 3632 0 3548 0 7096 44961424
++
++ Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutRateLimitGlobal OutRateLimitHost OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
++ Icmp: 114447 2655 0 17589 0 0 0 0 66905 29953 0 0 0 0 143956 0 0 572 16610 484 0 0 0 59957 66905 0 0 0 0
++
++ IcmpMsg: InType0 InType3 InType8 OutType0 OutType3 OutType8 OutType11
++ IcmpMsg: 29953 17589 66905 66905 16610 59957 484
++
++ Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
++ Tcp: 1 200 120000 -1 17744 13525 307 3783 6 18093137 9277788 3499 8 7442 0
++
++ Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
++ Udp: 2257832 1422 0 2252835 0 0 0 84 0
++
++ UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors IgnoredMulti MemErrors
++ UdpLite: 0 0 0 0 0 0 0 0 0
+ */
+
+
+@@ -101,10 +121,10 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
+ FILE *devin;
+ char line[1024];
+ netsnmp_systemstats_entry *entry = NULL;
+- int scan_count;
++ int scan_count, expected_scan_count;
+ char *stats, *start = line;
+ int len;
+- unsigned long long scan_vals[19];
++ unsigned long long scan_vals[20];
+
+ DEBUGMSGTL(("access:systemstats:container:arch", "load v4 (flags %x)\n",
+ load_flags));
+@@ -126,10 +146,17 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
+ */
+ NETSNMP_IGNORE_RESULT(fgets(line, sizeof(line), devin));
+ len = strlen(line);
+- if (224 != len) {
++ switch (len) {
++ case 224:
++ expected_scan_count = 19;
++ break;
++ case 237:
++ expected_scan_count = 20;
++ break;
++ default:
+ fclose(devin);
+ snmp_log(LOG_ERR, "systemstats_linux: unexpected header length in /proc/net/snmp."
+- " %d != 224\n", len);
++ " %d not in { 224, 237 } \n", len);
+ return -4;
+ }
+
+@@ -178,20 +205,20 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
+ memset(scan_vals, 0x0, sizeof(scan_vals));
+ scan_count = sscanf(stats,
+ "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
+- "%llu %llu %llu %llu %llu %llu %llu %llu %llu",
++ "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
+ &scan_vals[0],&scan_vals[1],&scan_vals[2],
+ &scan_vals[3],&scan_vals[4],&scan_vals[5],
+ &scan_vals[6],&scan_vals[7],&scan_vals[8],
+ &scan_vals[9],&scan_vals[10],&scan_vals[11],
+ &scan_vals[12],&scan_vals[13],&scan_vals[14],
+ &scan_vals[15],&scan_vals[16],&scan_vals[17],
+- &scan_vals[18]);
++ &scan_vals[18],&scan_vals[19]);
+ DEBUGMSGTL(("access:systemstats", " read %d values\n", scan_count));
+
+- if(scan_count != 19) {
++ if(scan_count != expected_scan_count) {
+ snmp_log(LOG_ERR,
+ "error scanning systemstats data (expected %d, got %d)\n",
+- 19, scan_count);
++ expected_scan_count, scan_count);
+ netsnmp_access_systemstats_entry_free(entry);
+ return -4;
+ }
+@@ -223,6 +250,7 @@ _systemstats_v4(netsnmp_container* container, u_int load_flags)
+ entry->stats.HCOutFragFails.high = scan_vals[17] >> 32;
+ entry->stats.HCOutFragCreates.low = scan_vals[18] & 0xffffffff;
+ entry->stats.HCOutFragCreates.high = scan_vals[18] >> 32;
++ /* entry->stats. = scan_vals[19]; / * OutTransmits */
+
+ entry->stats.columnAvail[IPSYSTEMSTATSTABLE_HCINRECEIVES] = 1;
+ entry->stats.columnAvail[IPSYSTEMSTATSTABLE_INHDRERRORS] = 1;
diff --git a/scripts/package-build/netfilter/.gitignore b/scripts/package-build/netfilter/.gitignore
new file mode 100644
index 00000000..8518afb9
--- /dev/null
+++ b/scripts/package-build/netfilter/.gitignore
@@ -0,0 +1,3 @@
+/pkg-libnftnl/
+/pkg-nftables/
+
diff --git a/scripts/package-build/netfilter/build.py b/scripts/package-build/netfilter/build.py
new file mode 100755
index 00000000..9737b7d3
--- /dev/null
+++ b/scripts/package-build/netfilter/build.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import glob
+import shutil
+import toml
+import os
+
+from argparse import ArgumentParser
+from pathlib import Path
+from subprocess import run, CalledProcessError
+
+
+def ensure_dependencies(dependencies: list) -> None:
+ """Ensure Debian build dependencies are met"""
+ if not dependencies:
+ print("I: No additional dependencies to install")
+ return
+
+ print("I: Ensure Debian build dependencies are met")
+ run(['sudo', 'apt-get', 'update'], check=True)
+ run(['sudo', 'apt-get', 'install', '-y'] + dependencies, check=True)
+
+
+def apply_patches(repo_dir: Path, patch_dir: Path, package_name: str) -> None:
+ """Apply patches from the patch directory to the repository"""
+ package_patch_dir = patch_dir / package_name
+ if package_patch_dir.exists() and package_patch_dir.is_dir():
+ patches = list(package_patch_dir.glob('*'))
+ else:
+ print(f"I: No patch directory found for {package_name} in {patch_dir}")
+ return
+
+ # Filter out directories from patches list
+ patches = [patch for patch in patches if patch.is_file()]
+
+ if not patches:
+ print(f"I: No patches found in {package_patch_dir}")
+ return
+
+ debian_patches_dir = repo_dir / 'debian/patches'
+ debian_patches_dir.mkdir(parents=True, exist_ok=True)
+
+ series_file = debian_patches_dir / 'series'
+ with series_file.open('a') as series:
+ for patch in patches:
+ patch_dest = debian_patches_dir / patch.name
+ try:
+ # Ensure the patch file exists before copying
+ if patch.exists():
+ shutil.copy(patch, patch_dest)
+ series.write(patch.name + '\n')
+ print(f"I: Applied patch: {patch.name}")
+ else:
+ print(f"W: Patch file {patch} not found, skipping")
+ except FileNotFoundError:
+ print(f"W: Patch file {patch} not found, skipping")
+
+
+def prepare_package(repo_dir: Path, install_data: str) -> None:
+ """Prepare a package"""
+ if not install_data:
+ print("I: No install data provided, skipping package preparation")
+ return
+
+ try:
+ install_file = repo_dir / 'debian/install'
+ install_file.parent.mkdir(parents=True, exist_ok=True)
+ install_file.write_text(install_data)
+ print("I: Prepared package")
+ except Exception as e:
+ print(f"Failed to prepare package: {e}")
+ raise
+
+
+def build_package(package: dict, dependencies: list, patch_dir: Path) -> None:
+ """Build a package from the repository
+
+ Args:
+ package (dict): Package information
+ dependencies (list): List of additional dependencies
+ patch_dir (Path): Directory containing patches
+ """
+ repo_name = package['name']
+ repo_dir = Path(repo_name)
+
+ try:
+ # Clone the repository if it does not exist
+ if not repo_dir.exists():
+ run(['git', 'clone', package['scm_url'], str(repo_dir)], check=True)
+
+ # Check out the specific commit
+ run(['git', 'checkout', package['commit_id']], cwd=repo_dir, check=True)
+
+ # Ensure dependencies
+ ensure_dependencies(dependencies)
+
+ # Apply patches if any
+ apply_patches(repo_dir, patch_dir, repo_name)
+
+ # Prepare the package if required
+ if package.get('prepare_package', False):
+ prepare_package(repo_dir, package.get('install_data', ''))
+
+ # Build dependency package and install it
+ if (repo_dir / 'debian/control').exists():
+ try:
+ run('sudo mk-build-deps --install --tool "apt-get --yes --no-install-recommends"', cwd=repo_dir, check=True, shell=True)
+ run('sudo dpkg -i *build-deps*.deb', cwd=repo_dir, check=True, shell=True)
+ except CalledProcessError as e:
+ print(f"Failed to build package {repo_name}: {e}")
+
+ # Build the package, check if we have build_cmd in the package.toml
+ build_cmd = package.get('build_cmd', 'dpkg-buildpackage -uc -us -tc -b')
+ run(build_cmd, cwd=repo_dir, check=True, shell=True)
+
+ except CalledProcessError as e:
+ print(f"Failed to build package {repo_name}: {e}")
+ finally:
+ # Clean up repository directory
+ # shutil.rmtree(repo_dir, ignore_errors=True)
+ pass
+
+
+def cleanup_build_deps(repo_dir: Path) -> None:
+ """Clean up build dependency packages"""
+ try:
+ if repo_dir.exists():
+ for file in glob.glob(str(repo_dir / '*build-deps*.deb')):
+ os.remove(file)
+ print("Cleaned up build dependency packages")
+ except Exception as e:
+ print(f"Error cleaning up build dependencies: {e}")
+
+
+def copy_packages(repo_dir: Path) -> None:
+ """Copy generated .deb packages to the parent directory"""
+ try:
+ deb_files = glob.glob(str(repo_dir / '*.deb'))
+ for deb_file in deb_files:
+ shutil.copy(deb_file, repo_dir.parent)
+ print(f'I: copy generated "{deb_file}" package')
+ except Exception as e:
+ print(f"Error copying packages: {e}")
+
+
+if __name__ == '__main__':
+ # Prepare argument parser
+ arg_parser = ArgumentParser()
+ arg_parser.add_argument('--config',
+ default='package.toml',
+ help='Path to the package configuration file')
+ arg_parser.add_argument('--patch-dir',
+ default='patches',
+ help='Path to the directory containing patches')
+ args = arg_parser.parse_args()
+
+ # Load package configuration
+ with open(args.config, 'r') as file:
+ config = toml.load(file)
+
+ packages = config['packages']
+ patch_dir = Path(args.patch_dir)
+
+ for package in packages:
+ dependencies = package.get('dependencies', {}).get('packages', [])
+
+ # Build the package
+ build_package(package, dependencies, patch_dir)
+
+ # Clean up build dependency packages after build
+ cleanup_build_deps(Path(package['name']))
+
+ # Copy generated .deb packages to parent directory
+ copy_packages(Path(package['name']))
diff --git a/scripts/package-build/netfilter/package.toml b/scripts/package-build/netfilter/package.toml
new file mode 100644
index 00000000..45752d08
--- /dev/null
+++ b/scripts/package-build/netfilter/package.toml
@@ -0,0 +1,11 @@
+[[packages]]
+name = "pkg-libnftnl"
+commit_id = "debian/1.2.6-2"
+scm_url = "https://salsa.debian.org/pkg-netfilter-team/pkg-libnftnl.git"
+build_cmd = "sudo mk-build-deps --install --tool 'apt-get --yes --no-install-recommends'; dpkg-buildpackage -uc -us -tc -b"
+
+[[packages]]
+name = "pkg-nftables"
+commit_id = "debian/1.0.9-1"
+scm_url = "https://salsa.debian.org/pkg-netfilter-team/pkg-nftables.git"
+build_cmd = "sudo dpkg -i ../libnftnl*.deb; dpkg-buildpackage -uc -us -tc -b"
diff --git a/scripts/package-build/netfilter/patches/pkg-nftables/0001-meta-fix-hour-decoding.patch b/scripts/package-build/netfilter/patches/pkg-nftables/0001-meta-fix-hour-decoding.patch
new file mode 100644
index 00000000..dd466f1a
--- /dev/null
+++ b/scripts/package-build/netfilter/patches/pkg-nftables/0001-meta-fix-hour-decoding.patch
@@ -0,0 +1,118 @@
+From d392ddf243dcbf8a34726c777d2c669b1e8bfa85 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Thu, 2 Nov 2023 15:34:13 +0100
+Subject: meta: fix hour decoding when timezone offset is negative
+
+Brian Davidson says:
+
+ meta hour rules don't display properly after being created when the
+ hour is on or after 00:00 UTC. The netlink debug looks correct for
+ seconds past midnight UTC, but displaying the rules looks like an
+ overflow or a byte order problem. I am in UTC-0400, so today, 20:00
+ and later exhibits the problem, while 19:00 and earlier hours are
+ fine.
+
+meta.c only ever worked when the delta to UTC is positive.
+We need to add in case the second counter turns negative after
+offset adjustment.
+
+Also add a test case for this.
+
+Fixes: f8f32deda31d ("meta: Introduce new conditions 'time', 'day' and 'hour'")
+Reported-by: Brian Davidson <davidson.brian@gmail.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+---
+ src/meta.c | 11 ++++-
+ .../shell/testcases/listing/dumps/meta_time.nodump | 0
+ tests/shell/testcases/listing/meta_time | 52 ++++++++++++++++++++++
+ 3 files changed, 61 insertions(+), 2 deletions(-)
+ create mode 100644 tests/shell/testcases/listing/dumps/meta_time.nodump
+ create mode 100755 tests/shell/testcases/listing/meta_time
+
+diff --git a/src/meta.c b/src/meta.c
+index b578d5e2..7846aefe 100644
+--- a/src/meta.c
++++ b/src/meta.c
+@@ -495,9 +495,16 @@ static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
+
+ /* Obtain current tm, so that we can add tm_gmtoff */
+ ts = time(NULL);
+- if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm))
+- seconds = (seconds + cur_tm.tm_gmtoff) % SECONDS_PER_DAY;
++ if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) {
++ int32_t adj = seconds + cur_tm.tm_gmtoff;
+
++ if (adj < 0)
++ adj += SECONDS_PER_DAY;
++ else if (adj >= SECONDS_PER_DAY)
++ adj -= SECONDS_PER_DAY;
++
++ seconds = adj;
++ }
+ minutes = seconds / 60;
+ seconds %= 60;
+ hours = minutes / 60;
+diff --git a/tests/shell/testcases/listing/dumps/meta_time.nodump b/tests/shell/testcases/listing/dumps/meta_time.nodump
+new file mode 100644
+index 00000000..e69de29b
+diff --git a/tests/shell/testcases/listing/meta_time b/tests/shell/testcases/listing/meta_time
+new file mode 100755
+index 00000000..a9761998
+--- /dev/null
++++ b/tests/shell/testcases/listing/meta_time
+@@ -0,0 +1,52 @@
++#!/bin/bash
++
++set -e
++
++TMP1=$(mktemp)
++TMP2=$(mktemp)
++
++cleanup()
++{
++ rm -f "$TMP1"
++ rm -f "$TMP2"
++}
++
++check_decode()
++{
++ TZ=$1 $NFT list chain t c | grep meta > "$TMP2"
++ diff -u "$TMP1" "$TMP2"
++}
++
++trap cleanup EXIT
++
++$NFT -f - <<EOF
++table t {
++ chain c {
++ }
++}
++EOF
++
++for i in $(seq -w 0 23); do
++ TZ=UTC $NFT add rule t c meta hour "$i:00"-"$i:59"
++done
++
++# Check decoding in UTC, this mirrors 1:1 what should have been added.
++for i in $(seq 0 23); do
++ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
++done
++
++check_decode UTC
++
++printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 23 0 23 59 > "$TMP1"
++for i in $(seq 0 22); do
++ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
++done
++check_decode UTC+1
++
++printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 1 0 1 59 > "$TMP1"
++for i in $(seq 2 23); do
++ printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" $i 0 $i 59 >> "$TMP1"
++done
++printf "\t\tmeta hour \"%02d:%02d\"-\"%02d:%02d\"\n" 0 0 0 59 >> "$TMP1"
++
++check_decode UTC-1
+--
+cgit v1.2.3
+
diff --git a/scripts/package-build/opennhrp/.gitignore b/scripts/package-build/opennhrp/.gitignore
new file mode 100644
index 00000000..65d0752b
--- /dev/null
+++ b/scripts/package-build/opennhrp/.gitignore
@@ -0,0 +1,6 @@
+opennhrp/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/opennhrp/build.py b/scripts/package-build/opennhrp/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/opennhrp/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/opennhrp/package.toml b/scripts/package-build/opennhrp/package.toml
new file mode 100644
index 00000000..d647c072
--- /dev/null
+++ b/scripts/package-build/opennhrp/package.toml
@@ -0,0 +1,21 @@
+[[packages]]
+name = "opennhrp"
+commit_id = "613277f"
+scm_url = "https://git.code.sf.net/p/opennhrp/code"
+
+build_cmd = """
+make clean
+make
+
+install --directory debian/etc debian/usr/sbin
+install --mode 0644 etc/racoon-ph1dead.sh debian/etc
+install --mode 0644 etc/racoon-ph1down.sh debian/etc
+install --strip --mode 0755 nhrp/opennhrp debian/usr/sbin
+install --strip --mode 0755 nhrp/opennhrpctl debian/usr/sbin
+
+fpm --input-type dir --output-type deb --name opennhrp \
+ --version $(git describe --always | cut -c2-) --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "NBMA Next Hop Resolution Protocol daemon" \
+ --license "MIT" -C debian --package ..
+"""
diff --git a/scripts/package-build/openvpn-otp/.gitignore b/scripts/package-build/openvpn-otp/.gitignore
new file mode 100644
index 00000000..7f89da2b
--- /dev/null
+++ b/scripts/package-build/openvpn-otp/.gitignore
@@ -0,0 +1,6 @@
+openvpn-otp/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/openvpn-otp/build.py b/scripts/package-build/openvpn-otp/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/openvpn-otp/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/openvpn-otp/package.toml b/scripts/package-build/openvpn-otp/package.toml
new file mode 100644
index 00000000..72209ad1
--- /dev/null
+++ b/scripts/package-build/openvpn-otp/package.toml
@@ -0,0 +1,19 @@
+[[packages]]
+name = "openvpn-otp"
+commit_id = "master"
+scm_url = "https://github.com/evgeny-gridasov/openvpn-otp"
+
+# build_cmd = "cd ..; ./build-openvpn-otp.sh"
+build_cmd = """
+./autogen.sh
+./configure --prefix=/usr
+make
+mkdir -p usr/lib/openvpn
+cp src/.libs/openvpn-otp.so usr/lib/openvpn
+
+fpm --input-type dir --output-type deb --name openvpn-otp \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "OpenVPN OTP Authentication support." \
+ --depends openvpn --architecture $(dpkg --print-architecture) \
+ --version $(git describe --tags --always | cut -c2-) --deb-compression gz usr
+"""
diff --git a/scripts/package-build/owamp/.gitignore b/scripts/package-build/owamp/.gitignore
new file mode 100644
index 00000000..4a97524e
--- /dev/null
+++ b/scripts/package-build/owamp/.gitignore
@@ -0,0 +1,6 @@
+owamp/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/owamp/build.py b/scripts/package-build/owamp/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/owamp/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/owamp/package.toml b/scripts/package-build/owamp/package.toml
new file mode 100644
index 00000000..8bc78afa
--- /dev/null
+++ b/scripts/package-build/owamp/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "owamp"
+commit_id = "v4.4.6"
+scm_url = "https://github.com/perfsonar/owamp"
diff --git a/scripts/package-build/pam_tacplus/.gitignore b/scripts/package-build/pam_tacplus/.gitignore
new file mode 100644
index 00000000..04e8d4e9
--- /dev/null
+++ b/scripts/package-build/pam_tacplus/.gitignore
@@ -0,0 +1,7 @@
+pam_tacplus/
+pam_tacplus-debian/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/pam_tacplus/build.py b/scripts/package-build/pam_tacplus/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/pam_tacplus/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/pam_tacplus/package.toml b/scripts/package-build/pam_tacplus/package.toml
new file mode 100644
index 00000000..79b28544
--- /dev/null
+++ b/scripts/package-build/pam_tacplus/package.toml
@@ -0,0 +1,19 @@
+[[packages]]
+name = "pam_tacplus-debian"
+commit_id = "50c6fd7"
+scm_url = "https://github.com/kravietz/pam_tacplus-debian"
+build_cmd = "/bin/true"
+
+[[packages]]
+name = "pam_tacplus"
+#commit_id = "4f91b0d" # This commit cannot build the package
+commit_id = "b839c44"
+scm_url = "https://github.com/kravietz/pam_tacplus"
+
+#build_cmd = "sudo mk-build-deps --install --tool 'apt-get --yes --no-install-recommends'; cd ..; ./build.sh"
+build_cmd = """
+cp -a ../pam_tacplus-debian debian
+rm -f debian/compat
+sudo mk-build-deps --install --tool 'apt-get --yes --no-install-recommends'
+dpkg-buildpackage -uc -us -tc -b -d
+"""
diff --git a/scripts/package-build/pmacct/.gitignore b/scripts/package-build/pmacct/.gitignore
new file mode 100644
index 00000000..7007417a
--- /dev/null
+++ b/scripts/package-build/pmacct/.gitignore
@@ -0,0 +1,6 @@
+pmacct/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/pmacct/build.py b/scripts/package-build/pmacct/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/pmacct/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/pmacct/package.toml b/scripts/package-build/pmacct/package.toml
new file mode 100644
index 00000000..6f5961be
--- /dev/null
+++ b/scripts/package-build/pmacct/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "pmacct"
+commit_id = "debian/1.7.7-1"
+scm_url = "https://salsa.debian.org/debian/pmacct.git"
diff --git a/scripts/package-build/pmacct/patches/0001-fix-pmacctd-SEGV-when-ICMP-ICMPv6-traffic-was-proces.patch b/scripts/package-build/pmacct/patches/0001-fix-pmacctd-SEGV-when-ICMP-ICMPv6-traffic-was-proces.patch
new file mode 100644
index 00000000..cb5f7399
--- /dev/null
+++ b/scripts/package-build/pmacct/patches/0001-fix-pmacctd-SEGV-when-ICMP-ICMPv6-traffic-was-proces.patch
@@ -0,0 +1,49 @@
+From 58900c9d0f98f224577c28dc2323061d33823f39 Mon Sep 17 00:00:00 2001
+From: Paolo Lucente <pl+github@pmacct.net>
+Date: Fri, 4 Mar 2022 22:07:29 +0000
+Subject: [PATCH] * fix, pmacctd: SEGV when ICMP/ICMPv6 traffic was processed
+ and 'flows' primitive was enabled. To address Issue #586
+
+---
+ src/nl.c | 12 +++---------
+ 1 file changed, 3 insertions(+), 9 deletions(-)
+
+diff --git a/src/nl.c b/src/nl.c
+index c42689ed..6a3da94b 100644
+--- a/src/nl.c
++++ b/src/nl.c
+@@ -1,6 +1,6 @@
+ /*
+ pmacct (Promiscuous mode IP Accounting package)
+- pmacct is Copyright (C) 2003-2021 by Paolo Lucente
++ pmacct is Copyright (C) 2003-2022 by Paolo Lucente
+ */
+
+ /*
+@@ -293,10 +293,7 @@ int ip_handler(register struct packet_ptrs *pptrs)
+ }
+ }
+ else {
+- if (pptrs->l4_proto != IPPROTO_ICMP) {
+- pptrs->tlh_ptr = dummy_tlhdr;
+- }
+-
++ pptrs->tlh_ptr = dummy_tlhdr;
+ if (off < caplen) pptrs->payload_ptr = ptr;
+ }
+
+@@ -479,10 +476,7 @@ int ip6_handler(register struct packet_ptrs *pptrs)
+ }
+ }
+ else {
+- if (pptrs->l4_proto != IPPROTO_ICMPV6) {
+- pptrs->tlh_ptr = dummy_tlhdr;
+- }
+-
++ pptrs->tlh_ptr = dummy_tlhdr;
+ if (off < caplen) pptrs->payload_ptr = ptr;
+ }
+
+--
+2.34.1
+
diff --git a/scripts/package-build/podman/.gitignore b/scripts/package-build/podman/.gitignore
new file mode 100644
index 00000000..22c40b0e
--- /dev/null
+++ b/scripts/package-build/podman/.gitignore
@@ -0,0 +1,7 @@
+podman/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/podman/build.py b/scripts/package-build/podman/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/podman/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/podman/package.toml b/scripts/package-build/podman/package.toml
new file mode 100644
index 00000000..952af518
--- /dev/null
+++ b/scripts/package-build/podman/package.toml
@@ -0,0 +1,27 @@
+[[packages]]
+name = "podman"
+commit_id = "v4.9.5"
+scm_url = "https://github.com/containers/podman"
+
+#build_cmd = "cd ..; ./build.sh"
+build_cmd = """
+make install.tools
+make podman-release
+
+tar xf podman-release-$(dpkg --print-architecture).tar.gz
+VERSION=$(ls -d podman-v* | cut -c9-)
+
+fpm --input-type dir --output-type deb --name podman \
+ --version $VERSION --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "Engine to run OCI-based containers in Pods" \
+ --depends conmon --depends crun --depends netavark --depends libgpgme11 \
+ --depends fuse-overlayfs --depends golang-github-containers-common \
+ --license "Apache License 2.0" -C podman-v$VERSION --package ..
+"""
+
+[packages.dependencies]
+packages = [
+ "libseccomp-dev",
+ "libgpgme-dev"
+]
diff --git a/scripts/package-build/pyhumps/.gitignore b/scripts/package-build/pyhumps/.gitignore
new file mode 100644
index 00000000..6a90d1c9
--- /dev/null
+++ b/scripts/package-build/pyhumps/.gitignore
@@ -0,0 +1,7 @@
+humps/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+
diff --git a/scripts/package-build/pyhumps/build.py b/scripts/package-build/pyhumps/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/pyhumps/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/pyhumps/package.toml b/scripts/package-build/pyhumps/package.toml
new file mode 100644
index 00000000..a358d128
--- /dev/null
+++ b/scripts/package-build/pyhumps/package.toml
@@ -0,0 +1,5 @@
+[[packages]]
+name = "humps"
+commit_id = "v3.8.0"
+scm_url = "https://github.com/nficano/humps.git"
+build_cmd = "python3 setup.py --command-packages=stdeb.command bdist_deb; cp deb_dist/*.deb .."
diff --git a/scripts/package-build/radvd/.gitignore b/scripts/package-build/radvd/.gitignore
new file mode 100644
index 00000000..9c37832b
--- /dev/null
+++ b/scripts/package-build/radvd/.gitignore
@@ -0,0 +1,6 @@
+radvd/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/radvd/build.py b/scripts/package-build/radvd/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/radvd/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/radvd/package.toml b/scripts/package-build/radvd/package.toml
new file mode 100644
index 00000000..e44afa18
--- /dev/null
+++ b/scripts/package-build/radvd/package.toml
@@ -0,0 +1,23 @@
+[[packages]]
+name = "radvd"
+commit_id = "f2de4764559"
+scm_url = "https://github.com/radvd-project/radvd"
+
+#build_cmd = "cd ..; ./build.sh"
+build_cmd = """
+./autogen.sh
+./configure
+make
+
+install --directory debian/lib/systemd/system debian/usr/sbin
+install --mode 0644 radvd.service debian/lib/systemd/system
+install --strip --mode 0755 radvd debian/usr/sbin
+
+# Version' field value 'v0.14-20-g613277f': version number does not start with digit
+# "cut" first character from version string
+fpm --input-type dir --output-type deb --name radvd \
+ --version $(git describe --always | cut -c2- | tr _ -) --deb-compression gz \
+ --maintainer "VyOS Package Maintainers <maintainers@vyos.net>" \
+ --description "RADVD router advertisement daemon" \
+ --license "RADVD" -C debian --package ..
+"""
diff --git a/scripts/package-build/strongswan/.gitignore b/scripts/package-build/strongswan/.gitignore
new file mode 100644
index 00000000..ec612740
--- /dev/null
+++ b/scripts/package-build/strongswan/.gitignore
@@ -0,0 +1,6 @@
+strongswan/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/strongswan/build-vici.sh b/scripts/package-build/strongswan/build-vici.sh
new file mode 100755
index 00000000..5ad0ee80
--- /dev/null
+++ b/scripts/package-build/strongswan/build-vici.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+CWD=$(pwd)
+set -e
+
+SRC="strongswan/src/libcharon/plugins/vici/python"
+if [ ! -d ${SRC} ]; then
+ echo "Source directory does not exists, please 'git clone'"
+ exit 1
+fi
+
+cd ${SRC}
+
+mkdir -p debian
+
+# Create control file
+echo "I: create $SRC/debian/control"
+cat <<EOF > debian/control
+Source: strongswan
+Section: python
+Priority: optional
+Maintainer: VyOS Package Maintainers <maintainers@vyos.net>
+Build-Depends: debhelper (>= 9), python3, python3-setuptools
+Standards-Version: 3.9.6
+
+Package: python3-vici
+Architecture: all
+Depends: \${misc:Depends}, \${python3:Depends}
+Description: Native Python interface for strongSwan's VICI protocol
+EOF
+
+
+# Create rules file
+echo "I: create $SRC/rules"
+cat <<EOF > debian/rules
+#!/usr/bin/make -f
+
+%:
+ dh \$@ --with python3
+EOF
+# Make the rules file executable
+chmod +x debian/rules
+
+echo '10' > debian/compat
+
+# Copy changelog
+cp ../../../../../debian/changelog debian/
+
+
+ls -la
+pwd
+
+
+echo "I: Build Debian Package"
+dpkg-buildpackage -uc -us -tc -b -d
+
+echo "I: copy packages"
+cp ../*.deb ../../../../../../
diff --git a/scripts/package-build/strongswan/build.py b/scripts/package-build/strongswan/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/strongswan/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/strongswan/package.toml b/scripts/package-build/strongswan/package.toml
new file mode 100644
index 00000000..8cedd4ac
--- /dev/null
+++ b/scripts/package-build/strongswan/package.toml
@@ -0,0 +1,45 @@
+[[packages]]
+name = "strongswan"
+commit_id = "debian/5.9.11-2"
+scm_url = "https://salsa.debian.org/debian/strongswan.git"
+
+# build_cmd = "cd ..; yes | ./build.sh; ./build-vici.sh"
+build_cmd = """
+export DEBEMAIL="maintainers@vyos.net"
+export DEBFULLNAME="VyOS Package Maintainers"
+
+dch -v "5.9.11-2+vyos0" "Patchset for DMVPN support" -b
+dpkg-buildpackage -uc -us -tc -b -d
+cd ..; ./build-vici.sh
+"""
+
+[packages.dependencies]
+packages = [
+ "bison",
+ "bzip2",
+ "debhelper-compat",
+ "dh-apparmor",
+ "dpkg-dev",
+ "flex",
+ "gperf",
+ "libiptc-dev",
+ "libcap-dev",
+ "libcurl3-dev",
+ "libgcrypt20-dev",
+ "libgmp3-dev",
+ "libkrb5-dev",
+ "libldap2-dev",
+ "libnm-dev",
+ "libpam0g-dev",
+ "libsqlite3-dev",
+ "libssl-dev",
+ "libsystemd-dev",
+ "libtool",
+ "libtss2-dev",
+ "libxml2-dev",
+ "pkg-config",
+ "po-debconf",
+ "systemd",
+ "libsystemd-dev",
+ "tzdata"
+]
diff --git a/scripts/package-build/strongswan/patches/0001-charon-add-optional-source-and-remote-overrides-for-.patch b/scripts/package-build/strongswan/patches/0001-charon-add-optional-source-and-remote-overrides-for-.patch
new file mode 100644
index 00000000..ceb47350
--- /dev/null
+++ b/scripts/package-build/strongswan/patches/0001-charon-add-optional-source-and-remote-overrides-for-.patch
@@ -0,0 +1,579 @@
+From db627ec8a8e72bc6b23dc8ab00f4e6b4f448d01c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Mon, 21 Sep 2015 13:41:58 +0300
+Subject: [PATCH 1/3] charon: add optional source and remote overrides for
+ initiate
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This introduces support for specifying optional IKE SA specific
+source and remote address for child sa initiation. This allows
+to initiate wildcard connection for known address via vici.
+
+In addition this allows impler implementation of trap-any patches
+and is a prerequisite for dmvpn support.
+
+Signed-off-by: Timo Teräs <timo.teras@iki.fi>
+---
+ src/charon-cmd/cmd/cmd_connection.c | 2 +-
+ src/libcharon/control/controller.c | 42 +++++++++++-
+ src/libcharon/control/controller.h | 3 +
+ src/libcharon/plugins/stroke/stroke_control.c | 5 +-
+ src/libcharon/plugins/vici/vici_config.c | 2 +-
+ src/libcharon/plugins/vici/vici_control.c | 64 ++++++++++++++++---
+ .../processing/jobs/start_action_job.c | 2 +-
+ src/libcharon/sa/ike_sa_manager.c | 50 ++++++++++++++-
+ src/libcharon/sa/ike_sa_manager.h | 8 ++-
+ src/libcharon/sa/trap_manager.c | 44 +++++--------
+ src/swanctl/commands/initiate.c | 40 +++++++++++-
+ 11 files changed, 215 insertions(+), 47 deletions(-)
+
+diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c
+index 2e2cb3c..b9369a8 100644
+--- a/src/charon-cmd/cmd/cmd_connection.c
++++ b/src/charon-cmd/cmd/cmd_connection.c
+@@ -439,7 +439,7 @@ static job_requeue_t initiate(private_cmd_connection_t *this)
+ child_cfg = create_child_cfg(this, peer_cfg);
+
+ if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+- controller_cb_empty, NULL, LEVEL_SILENT, 0, FALSE) != SUCCESS)
++ NULL, NULL, controller_cb_empty, NULL, LEVEL_SILENT, 0, FALSE) != SUCCESS)
+ {
+ terminate(pid);
+ }
+diff --git a/src/libcharon/control/controller.c b/src/libcharon/control/controller.c
+index 027f48e..4ce8616 100644
+--- a/src/libcharon/control/controller.c
++++ b/src/libcharon/control/controller.c
+@@ -15,6 +15,28 @@
+ * for more details.
+ */
+
++/*
++ * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
+ #include "controller.h"
+
+ #include <sys/types.h>
+@@ -107,6 +129,16 @@ struct interface_listener_t {
+ */
+ ike_sa_t *ike_sa;
+
++ /**
++ * Our host hint.
++ */
++ host_t *my_host;
++
++ /**
++ * Other host hint.
++ */
++ host_t *other_host;
++
+ /**
+ * unique ID, used for various methods
+ */
+@@ -417,10 +449,15 @@ METHOD(job_t, initiate_execute, job_requeue_t,
+ ike_sa_t *ike_sa;
+ interface_listener_t *listener = &job->listener;
+ peer_cfg_t *peer_cfg = listener->peer_cfg;
++ host_t *my_host = listener->my_host;
++ host_t *other_host = listener->other_host;
+
+ ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
+- peer_cfg);
++ peer_cfg, my_host, other_host);
+ peer_cfg->destroy(peer_cfg);
++ DESTROY_IF(my_host);
++ DESTROY_IF(other_host);
++
+ if (!ike_sa)
+ {
+ DESTROY_IF(listener->child_cfg);
+@@ -499,6 +536,7 @@ METHOD(job_t, initiate_execute, job_requeue_t,
+
+ METHOD(controller_t, initiate, status_t,
+ private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
++ host_t *my_host, host_t *other_host,
+ controller_cb_t callback, void *param, level_t max_level, u_int timeout,
+ bool limits)
+ {
+@@ -523,6 +561,8 @@ METHOD(controller_t, initiate, status_t,
+ .status = FAILED,
+ .child_cfg = child_cfg,
+ .peer_cfg = peer_cfg,
++ .my_host = my_host ? my_host->clone(my_host) : NULL,
++ .other_host = other_host ? other_host->clone(other_host) : NULL,
+ .lock = spinlock_create(),
+ .options.limits = limits,
+ },
+diff --git a/src/libcharon/control/controller.h b/src/libcharon/control/controller.h
+index 36a1d46..a130fbb 100644
+--- a/src/libcharon/control/controller.h
++++ b/src/libcharon/control/controller.h
+@@ -81,6 +81,8 @@ struct controller_t {
+ *
+ * @param peer_cfg peer_cfg to use for IKE_SA setup
+ * @param child_cfg optional child_cfg to set up CHILD_SA from
++ * @param my_host optional address hint for source
++ * @param other_host optional address hint for destination
+ * @param cb logging callback
+ * @param param parameter to include in each call of cb
+ * @param max_level maximum log level for which cb is invoked
+@@ -95,6 +97,7 @@ struct controller_t {
+ */
+ status_t (*initiate)(controller_t *this,
+ peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
++ host_t *my_host, host_t *other_host,
+ controller_cb_t callback, void *param,
+ level_t max_level, u_int timeout, bool limits);
+
+diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c
+index 2824c93..21ff6b3 100644
+--- a/src/libcharon/plugins/stroke/stroke_control.c
++++ b/src/libcharon/plugins/stroke/stroke_control.c
+@@ -109,7 +109,7 @@ static void charon_initiate(private_stroke_control_t *this, peer_cfg_t *peer_cfg
+ if (msg->output_verbosity < 0)
+ {
+ charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+- NULL, NULL, 0, 0, FALSE);
++ NULL, NULL, NULL, NULL, 0, 0, FALSE);
+ }
+ else
+ {
+@@ -117,7 +117,8 @@ static void charon_initiate(private_stroke_control_t *this, peer_cfg_t *peer_cfg
+ status_t status;
+
+ status = charon->controller->initiate(charon->controller,
+- peer_cfg, child_cfg, (controller_cb_t)stroke_log,
++ peer_cfg, child_cfg, NULL, NULL,
++ (controller_cb_t)stroke_log,
+ &info, msg->output_verbosity, this->timeout, FALSE);
+ switch (status)
+ {
+diff --git a/src/libcharon/plugins/vici/vici_config.c b/src/libcharon/plugins/vici/vici_config.c
+index 5221225..b1486e3 100644
+--- a/src/libcharon/plugins/vici/vici_config.c
++++ b/src/libcharon/plugins/vici/vici_config.c
+@@ -2252,7 +2252,7 @@ static void run_start_action(private_vici_config_t *this, peer_cfg_t *peer_cfg,
+ DBG1(DBG_CFG, "initiating '%s'", child_cfg->get_name(child_cfg));
+ charon->controller->initiate(charon->controller,
+ peer_cfg->get_ref(peer_cfg), child_cfg->get_ref(child_cfg),
+- NULL, NULL, 0, 0, FALSE);
++ NULL, NULL, NULL, NULL, 0, 0, FALSE);
+ }
+ }
+
+diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c
+index 1c236d2..811d8db 100644
+--- a/src/libcharon/plugins/vici/vici_control.c
++++ b/src/libcharon/plugins/vici/vici_control.c
+@@ -15,6 +15,28 @@
+ * for more details.
+ */
+
++/*
++ * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
+ #include "vici_control.h"
+ #include "vici_builder.h"
+
+@@ -173,9 +195,11 @@ static child_cfg_t* find_child_cfg(char *name, char *pname, peer_cfg_t **out)
+ CALLBACK(initiate, vici_message_t*,
+ private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
+ {
++ vici_message_t* msg;
+ peer_cfg_t *peer_cfg = NULL;
+ child_cfg_t *child_cfg;
+- char *child, *ike, *type, *sa;
++ host_t *my_host = NULL, *other_host = NULL;
++ char *child, *ike, *type, *sa, *my_host_str, *other_host_str;
+ int timeout;
+ bool limits;
+ controller_cb_t log_cb = NULL;
+@@ -189,6 +213,8 @@ CALLBACK(initiate, vici_message_t*,
+ timeout = request->get_int(request, 0, "timeout");
+ limits = request->get_bool(request, FALSE, "init-limits");
+ log.level = request->get_int(request, 1, "loglevel");
++ my_host_str = request->get_str(request, NULL, "my-host");
++ other_host_str = request->get_str(request, NULL, "other-host");
+
+ if (!child && !ike)
+ {
+@@ -199,31 +225,52 @@ CALLBACK(initiate, vici_message_t*,
+ log_cb = (controller_cb_t)log_vici;
+ }
+
++ if (my_host_str)
++ {
++ my_host = host_create_from_string(my_host_str, 0);
++ }
++ if (other_host_str)
++ {
++ other_host = host_create_from_string(other_host_str, 0);
++ }
++
++
+ type = child ? "CHILD_SA" : "IKE_SA";
+ sa = child ?: ike;
+
+ child_cfg = find_child_cfg(child, ike, &peer_cfg);
+
+- DBG1(DBG_CFG, "vici initiate %s '%s'", type, sa);
++ DBG1(DBG_CFG, "vici initiate %s '%s', me %H, other %H, limits %d", type, sa, my_host, other_host, limits);
+ if (!peer_cfg)
+ {
+- return send_reply(this, "%s config '%s' not found", type, sa);
++ msg = send_reply(this, "%s config '%s' not found", type, sa);
++ goto ret;
+ }
+- switch (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
+- log_cb, &log, log.level, timeout, limits))
++ switch (charon->controller->initiate(charon->controller,
++ peer_cfg, child_cfg,
++ my_host, other_host,
++ log_cb, &log, log.level, timeout, limits))
+ {
+ case SUCCESS:
+- return send_reply(this, NULL);
++ msg = send_reply(this, NULL);
++ break;
+ case OUT_OF_RES:
+- return send_reply(this, "%s '%s' not established after %dms", type,
++ msg = send_reply(this, "%s '%s' not established after %dms", type,
+ sa, timeout);
++ break;
+ case INVALID_STATE:
+- return send_reply(this, "establishing %s '%s' not possible at the "
++ msg = send_reply(this, "establishing %s '%s' not possible at the "
+ "moment due to limits", type, sa);
++ break;
+ case FAILED:
+ default:
+- return send_reply(this, "establishing %s '%s' failed", type, sa);
++ msg = send_reply(this, "establishing %s '%s' failed", type, sa);
++ break;
+ }
++ret:
++ if (my_host) my_host->destroy(my_host);
++ if (other_host) other_host->destroy(other_host);
++ return msg;
+ }
+
+ /**
+diff --git a/src/libcharon/processing/jobs/start_action_job.c b/src/libcharon/processing/jobs/start_action_job.c
+index 122e5ce..dec458c 100644
+--- a/src/libcharon/processing/jobs/start_action_job.c
++++ b/src/libcharon/processing/jobs/start_action_job.c
+@@ -84,7 +84,7 @@ METHOD(job_t, execute, job_requeue_t,
+ charon->controller->initiate(charon->controller,
+ peer_cfg->get_ref(peer_cfg),
+ child_cfg->get_ref(child_cfg),
+- NULL, NULL, 0, 0, FALSE);
++ NULL, NULL, NULL, NULL, 0, 0, FALSE);
+ }
+ }
+ children->destroy(children);
+diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
+index fc31c2a..51e28bc 100644
+--- a/src/libcharon/sa/ike_sa_manager.c
++++ b/src/libcharon/sa/ike_sa_manager.c
+@@ -16,6 +16,28 @@
+ * for more details.
+ */
+
++/*
++ * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
+ #include <string.h>
+ #include <inttypes.h>
+
+@@ -1497,7 +1519,8 @@ typedef struct {
+ } config_entry_t;
+
+ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
+- private_ike_sa_manager_t *this, peer_cfg_t *peer_cfg)
++ private_ike_sa_manager_t *this, peer_cfg_t *peer_cfg,
++ host_t *my_host, host_t *other_host)
+ {
+ enumerator_t *enumerator;
+ entry_t *entry;
+@@ -1508,7 +1531,17 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
+ u_int segment;
+ int i;
+
+- DBG2(DBG_MGR, "checkout IKE_SA by config");
++ if (my_host && my_host->get_port(my_host) == 0)
++ {
++ my_host->set_port(my_host, IKEV2_UDP_PORT);
++ }
++ if (other_host && other_host->get_port(other_host) == 0)
++ {
++ other_host->set_port(other_host, IKEV2_UDP_PORT);
++ }
++
++ DBG2(DBG_MGR, "checkout IKE_SA by config '%s', me %H, other %H",
++ peer_cfg->get_name(peer_cfg), my_host, other_host);
+
+ if (!this->reuse_ikesa && peer_cfg->get_ike_version(peer_cfg) != IKEV1)
+ { /* IKE_SA reuse disabled by config (not possible for IKEv1) */
+@@ -1566,6 +1599,15 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
+ continue;
+ }
+
++ if (my_host && !my_host->ip_equals(my_host, entry->ike_sa->get_my_host(entry->ike_sa)))
++ {
++ continue;
++ }
++ if (other_host && !other_host->ip_equals(other_host, entry->ike_sa->get_other_host(entry->ike_sa)))
++ {
++ continue;
++ }
++
+ current_peer = entry->ike_sa->get_peer_cfg(entry->ike_sa);
+ if (current_peer && current_peer->equals(current_peer, peer_cfg))
+ {
+@@ -1592,6 +1634,10 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
+ {
+ ike_sa->set_peer_cfg(ike_sa, peer_cfg);
+ checkout_new(this, ike_sa);
++ if (my_host || other_host)
++ {
++ ike_sa->update_hosts(ike_sa, my_host, other_host, TRUE);
++ }
+ }
+ }
+ charon->bus->set_sa(charon->bus, ike_sa);
+diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h
+index 004cc22..50f8246 100644
+--- a/src/libcharon/sa/ike_sa_manager.h
++++ b/src/libcharon/sa/ike_sa_manager.h
+@@ -123,7 +123,8 @@ struct ike_sa_manager_t {
+ ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message);
+
+ /**
+- * Checkout an IKE_SA for initiation by a peer_config.
++ * Checkout an IKE_SA for initiation by a peer_config and optional
++ * source and remote host addresses.
+ *
+ * To initiate, a CHILD_SA may be established within an existing IKE_SA.
+ * This call checks for an existing IKE_SA by comparing the configuration.
+@@ -136,9 +137,12 @@ struct ike_sa_manager_t {
+ * @note The peer_config is always set on the returned IKE_SA.
+ *
+ * @param peer_cfg configuration used to find an existing IKE_SA
++ * @param my_host source host address for wildcard peer_cfg
++ * @param other_host remote host address for wildcard peer_cfg
+ * @return checked out/created IKE_SA
+ */
+- ike_sa_t *(*checkout_by_config)(ike_sa_manager_t* this, peer_cfg_t *peer_cfg);
++ ike_sa_t *(*checkout_by_config)(ike_sa_manager_t* this, peer_cfg_t *peer_cfg,
++ host_t *my_host, host_t *other_host);
+
+ /**
+ * Reset initiator SPI.
+diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
+index d8d8a42..e7c906e 100644
+--- a/src/libcharon/sa/trap_manager.c
++++ b/src/libcharon/sa/trap_manager.c
+@@ -523,7 +523,7 @@ METHOD(trap_manager_t, acquire, void,
+ peer_cfg_t *peer;
+ child_cfg_t *child;
+ ike_sa_t *ike_sa;
+- host_t *host;
++ host_t *host, *my_host = NULL, *other_host = NULL;
+ bool wildcard, ignore = FALSE;
+
+ this->lock->read_lock(this->lock);
+@@ -600,37 +600,27 @@ METHOD(trap_manager_t, acquire, void,
+ this->lock->unlock(this->lock);
+
+ if (wildcard)
+- { /* the peer config would match IKE_SAs with other peers */
+- ike_sa = charon->ike_sa_manager->create_new(charon->ike_sa_manager,
+- peer->get_ike_version(peer), TRUE);
+- if (ike_sa)
+- {
+- ike_cfg_t *ike_cfg;
+- uint16_t port;
+- uint8_t mask;
+-
+- ike_sa->set_peer_cfg(ike_sa, peer);
+- ike_cfg = ike_sa->get_ike_cfg(ike_sa);
+-
+- port = ike_cfg->get_other_port(ike_cfg);
+- data->dst->to_subnet(data->dst, &host, &mask);
+- host->set_port(host, port);
+- ike_sa->set_other_host(ike_sa, host);
+-
+- port = ike_cfg->get_my_port(ike_cfg);
+- data->src->to_subnet(data->src, &host, &mask);
+- host->set_port(host, port);
+- ike_sa->set_my_host(ike_sa, host);
+-
+- charon->bus->set_sa(charon->bus, ike_sa);
+- }
+- }
+- else
+ {
+- ike_sa = charon->ike_sa_manager->checkout_by_config(
+- charon->ike_sa_manager, peer);
++ ike_cfg_t *ike_cfg;
++ uint16_t port;
++ uint8_t mask;
++
++ ike_cfg = peer->get_ike_cfg(peer);
++
++ port = ike_cfg->get_other_port(ike_cfg);
++ data->dst->to_subnet(data->dst, &other_host, &mask);
++ other_host->set_port(other_host, port);
++
++ port = ike_cfg->get_my_port(ike_cfg);
++ data->src->to_subnet(data->src, &my_host, &mask);
++ my_host->set_port(my_host, port);
+ }
++ ike_sa = charon->ike_sa_manager->checkout_by_config(
++ charon->ike_sa_manager, peer,
++ my_host, other_host);
+ peer->destroy(peer);
++ DESTROY_IF(my_host);
++ DESTROY_IF(other_host);
+
+ if (ike_sa)
+ {
+diff --git a/src/swanctl/commands/initiate.c b/src/swanctl/commands/initiate.c
+index e0fffb9..dcaded5 100644
+--- a/src/swanctl/commands/initiate.c
++++ b/src/swanctl/commands/initiate.c
+@@ -14,6 +14,28 @@
+ * for more details.
+ */
+
++/*
++ * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
+ #include "command.h"
+
+ #include <errno.h>
+@@ -38,7 +60,7 @@ static int initiate(vici_conn_t *conn)
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+- char *arg, *child = NULL, *ike = NULL;
++ char *arg, *child = NULL, *ike = NULL, *my_host = NULL, *other_host = NULL;
+ int ret = 0, timeout = 0, level = 1;
+
+ while (TRUE)
+@@ -65,6 +87,12 @@ static int initiate(vici_conn_t *conn)
+ case 'l':
+ level = atoi(arg);
+ continue;
++ case 'S':
++ my_host = arg;
++ continue;
++ case 'R':
++ other_host = arg;
++ continue;
+ case EOF:
+ break;
+ default:
+@@ -88,6 +116,14 @@ static int initiate(vici_conn_t *conn)
+ {
+ vici_add_key_valuef(req, "ike", "%s", ike);
+ }
++ if (my_host)
++ {
++ vici_add_key_valuef(req, "my-host", "%s", my_host);
++ }
++ if (other_host)
++ {
++ vici_add_key_valuef(req, "other-host", "%s", other_host);
++ }
+ if (timeout)
+ {
+ vici_add_key_valuef(req, "timeout", "%d", timeout * 1000);
+@@ -134,6 +170,8 @@ static void __attribute__ ((constructor))reg()
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "initiate a CHILD_SA configuration"},
+ {"ike", 'i', 1, "initiate an IKE_SA, or name of child's parent"},
++ {"source", 'S', 1, "override source address"},
++ {"remote", 'R', 1, "override remote address"},
+ {"timeout", 't', 1, "timeout in seconds before detaching"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
diff --git a/scripts/package-build/strongswan/patches/0002-vici-send-certificates-for-ike-sa-events.patch b/scripts/package-build/strongswan/patches/0002-vici-send-certificates-for-ike-sa-events.patch
new file mode 100644
index 00000000..13e657e9
--- /dev/null
+++ b/scripts/package-build/strongswan/patches/0002-vici-send-certificates-for-ike-sa-events.patch
@@ -0,0 +1,140 @@
+From 39d537b875e907c63a54d5de8ba6d2ea0ede4604 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Mon, 21 Sep 2015 13:42:05 +0300
+Subject: [PATCH 2/3] vici: send certificates for ike-sa events
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Timo Teräs <timo.teras@iki.fi>
+---
+ src/libcharon/plugins/vici/vici_query.c | 50 +++++++++++++++++++++----
+ 1 file changed, 42 insertions(+), 8 deletions(-)
+
+diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c
+index bacb7b101..19acc0789 100644
+--- a/src/libcharon/plugins/vici/vici_query.c
++++ b/src/libcharon/plugins/vici/vici_query.c
+@@ -402,7 +402,7 @@ static void list_vips(private_vici_query_t *this, vici_builder_t *b,
+ * List details of an IKE_SA
+ */
+ static void list_ike(private_vici_query_t *this, vici_builder_t *b,
+- ike_sa_t *ike_sa, time_t now)
++ ike_sa_t *ike_sa, time_t now, bool add_certs)
+ {
+ time_t t;
+ ike_sa_id_t *id;
+@@ -411,6 +411,8 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b,
+ uint32_t if_id;
+ uint16_t alg, ks;
+ host_t *host;
++ auth_cfg_t *auth_cfg;
++ enumerator_t *enumerator;
+
+ b->add_kv(b, "uniqueid", "%u", ike_sa->get_unique_id(ike_sa));
+ b->add_kv(b, "version", "%u", ike_sa->get_version(ike_sa));
+@@ -420,11 +422,43 @@ static void list_ike(private_vici_query_t *this, vici_builder_t *b,
+ b->add_kv(b, "local-host", "%H", host);
+ b->add_kv(b, "local-port", "%d", host->get_port(host));
+ b->add_kv(b, "local-id", "%Y", ike_sa->get_my_id(ike_sa));
++ if (add_certs)
++ {
++ enumerator = ike_sa->create_auth_cfg_enumerator(ike_sa, TRUE);
++ if (enumerator->enumerate(enumerator, &auth_cfg))
++ {
++ certificate_t *cert = auth_cfg->get(auth_cfg, AUTH_RULE_SUBJECT_CERT);
++ chunk_t encoding;
++
++ if (cert && cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
++ {
++ b->add(b, VICI_KEY_VALUE, "local-cert-data", encoding);
++ free(encoding.ptr);
++ }
++ }
++ enumerator->destroy(enumerator);
++ }
+
+ host = ike_sa->get_other_host(ike_sa);
+ b->add_kv(b, "remote-host", "%H", host);
+ b->add_kv(b, "remote-port", "%d", host->get_port(host));
+ b->add_kv(b, "remote-id", "%Y", ike_sa->get_other_id(ike_sa));
++ if (add_certs)
++ {
++ enumerator = ike_sa->create_auth_cfg_enumerator(ike_sa, FALSE);
++ if (enumerator->enumerate(enumerator, &auth_cfg))
++ {
++ certificate_t *cert = auth_cfg->get(auth_cfg, AUTH_RULE_SUBJECT_CERT);
++ chunk_t encoding;
++
++ if (cert && cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
++ {
++ b->add(b, VICI_KEY_VALUE, "remote-cert-data", encoding);
++ free(encoding.ptr);
++ }
++ }
++ enumerator->destroy(enumerator);
++ }
+
+ eap = ike_sa->get_other_eap_id(ike_sa);
+
+@@ -556,7 +590,7 @@ CALLBACK(list_sas, vici_message_t*,
+ b = vici_builder_create();
+ b->begin_section(b, ike_sa->get_name(ike_sa));
+
+- list_ike(this, b, ike_sa, now);
++ list_ike(this, b, ike_sa, now, TRUE);
+
+ b->begin_section(b, "child-sas");
+ csas = ike_sa->create_child_sa_enumerator(ike_sa);
+@@ -1774,7 +1808,7 @@ METHOD(listener_t, ike_updown, bool,
+ }
+
+ b->begin_section(b, ike_sa->get_name(ike_sa));
+- list_ike(this, b, ike_sa, now);
++ list_ike(this, b, ike_sa, now, up);
+ b->end_section(b);
+
+ this->dispatcher->raise_event(this->dispatcher,
+@@ -1799,10 +1833,10 @@ METHOD(listener_t, ike_rekey, bool,
+ b = vici_builder_create();
+ b->begin_section(b, old->get_name(old));
+ b->begin_section(b, "old");
+- list_ike(this, b, old, now);
++ list_ike(this, b, old, now, TRUE);
+ b->end_section(b);
+ b->begin_section(b, "new");
+- list_ike(this, b, new, now);
++ list_ike(this, b, new, now, TRUE);
+ b->end_section(b);
+ b->end_section(b);
+
+@@ -1833,7 +1867,7 @@ METHOD(listener_t, ike_update, bool,
+ b->add_kv(b, "remote-port", "%d", remote->get_port(remote));
+
+ b->begin_section(b, ike_sa->get_name(ike_sa));
+- list_ike(this, b, ike_sa, now);
++ list_ike(this, b, ike_sa, now, TRUE);
+ b->end_section(b);
+
+ this->dispatcher->raise_event(this->dispatcher,
+@@ -1863,7 +1897,7 @@ METHOD(listener_t, child_updown, bool,
+ }
+
+ b->begin_section(b, ike_sa->get_name(ike_sa));
+- list_ike(this, b, ike_sa, now);
++ list_ike(this, b, ike_sa, now, up);
+ b->begin_section(b, "child-sas");
+
+ snprintf(buf, sizeof(buf), "%s-%u", child_sa->get_name(child_sa),
+@@ -1898,7 +1932,7 @@ METHOD(listener_t, child_rekey, bool,
+ b = vici_builder_create();
+
+ b->begin_section(b, ike_sa->get_name(ike_sa));
+- list_ike(this, b, ike_sa, now);
++ list_ike(this, b, ike_sa, now, TRUE);
+ b->begin_section(b, "child-sas");
+
+ b->begin_section(b, old->get_name(old));
+--
+2.38.1
+
diff --git a/scripts/package-build/strongswan/patches/0003-vici-add-support-for-individual-sa-state-changes.patch b/scripts/package-build/strongswan/patches/0003-vici-add-support-for-individual-sa-state-changes.patch
new file mode 100644
index 00000000..45aadc72
--- /dev/null
+++ b/scripts/package-build/strongswan/patches/0003-vici-add-support-for-individual-sa-state-changes.patch
@@ -0,0 +1,159 @@
+From df6b501ed29b838efde0f1cb1c906ab9befc7b45 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
+Date: Mon, 21 Sep 2015 13:42:11 +0300
+Subject: [PATCH 3/3] vici: add support for individual sa state changes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Useful for monitoring and tracking full SA.
+
+Signed-off-by: Timo Teräs <timo.teras@iki.fi>
+---
+ src/libcharon/plugins/vici/vici_query.c | 105 ++++++++++++++++++++++++
+ 1 file changed, 105 insertions(+)
+
+diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c
+index 19acc0789..e008885f7 100644
+--- a/src/libcharon/plugins/vici/vici_query.c
++++ b/src/libcharon/plugins/vici/vici_query.c
+@@ -1774,8 +1774,16 @@ static void manage_commands(private_vici_query_t *this, bool reg)
+ this->dispatcher->manage_event(this->dispatcher, "ike-updown", reg);
+ this->dispatcher->manage_event(this->dispatcher, "ike-rekey", reg);
+ this->dispatcher->manage_event(this->dispatcher, "ike-update", reg);
++ this->dispatcher->manage_event(this->dispatcher, "ike-state-established", reg);
++ this->dispatcher->manage_event(this->dispatcher, "ike-state-destroying", reg);
+ this->dispatcher->manage_event(this->dispatcher, "child-updown", reg);
+ this->dispatcher->manage_event(this->dispatcher, "child-rekey", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-installing", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-installed", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-updating", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-rekeying", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-rekeyed", reg);
++ this->dispatcher->manage_event(this->dispatcher, "child-state-destroying", reg);
+ manage_command(this, "list-sas", list_sas, reg);
+ manage_command(this, "list-policies", list_policies, reg);
+ manage_command(this, "list-conns", list_conns, reg);
+@@ -1876,6 +1884,45 @@ METHOD(listener_t, ike_update, bool,
+ return TRUE;
+ }
+
++METHOD(listener_t, ike_state_change, bool,
++ private_vici_query_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
++{
++ char *event;
++ vici_builder_t *b;
++ time_t now;
++
++ switch (state)
++ {
++ case IKE_ESTABLISHED:
++ event = "ike-state-established";
++ break;
++ case IKE_DESTROYING:
++ event = "ike-state-destroying";
++ break;
++ default:
++ return TRUE;
++ }
++
++ if (!this->dispatcher->has_event_listeners(this->dispatcher, event))
++ {
++ return TRUE;
++ }
++
++ now = time_monotonic(NULL);
++
++ b = vici_builder_create();
++ b->begin_section(b, ike_sa->get_name(ike_sa));
++ list_ike(this, b, ike_sa, now, state != IKE_DESTROYING);
++ b->begin_section(b, "child-sas");
++ b->end_section(b);
++ b->end_section(b);
++
++ this->dispatcher->raise_event(this->dispatcher,
++ event, 0, b->finalize(b));
++
++ return TRUE;
++}
++
+ METHOD(listener_t, child_updown, bool,
+ private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up)
+ {
+@@ -1955,6 +2002,62 @@ METHOD(listener_t, child_rekey, bool,
+ return TRUE;
+ }
+
++METHOD(listener_t, child_state_change, bool,
++ private_vici_query_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, child_sa_state_t state)
++{
++ char *event;
++ vici_builder_t *b;
++ time_t now;
++
++ switch (state)
++ {
++ case CHILD_INSTALLING:
++ event = "child-state-installing";
++ break;
++ case CHILD_INSTALLED:
++ event = "child-state-installed";
++ break;
++ case CHILD_UPDATING:
++ event = "child-state-updating";
++ break;
++ case CHILD_REKEYING:
++ event = "child-state-rekeying";
++ break;
++ case CHILD_REKEYED:
++ event = "child-state-rekeyed";
++ break;
++ case CHILD_DESTROYING:
++ event = "child-state-destroying";
++ break;
++ default:
++ return TRUE;
++ }
++
++ if (!this->dispatcher->has_event_listeners(this->dispatcher, event))
++ {
++ return TRUE;
++ }
++
++ now = time_monotonic(NULL);
++
++ b = vici_builder_create();
++ b->begin_section(b, ike_sa->get_name(ike_sa));
++ list_ike(this, b, ike_sa, now, state != CHILD_DESTROYING);
++ b->begin_section(b, "child-sas");
++
++ b->begin_section(b, child_sa->get_name(child_sa));
++ list_child(this, b, child_sa, now);
++ b->end_section(b);
++
++ b->end_section(b);
++ b->end_section(b);
++
++ this->dispatcher->raise_event(this->dispatcher,
++ event, 0, b->finalize(b));
++
++ return TRUE;
++}
++
+ METHOD(vici_query_t, destroy, void,
+ private_vici_query_t *this)
+ {
+@@ -1975,8 +2078,10 @@ vici_query_t *vici_query_create(vici_dispatcher_t *dispatcher)
+ .ike_updown = _ike_updown,
+ .ike_rekey = _ike_rekey,
+ .ike_update = _ike_update,
++ .ike_state_change = _ike_state_change,
+ .child_updown = _child_updown,
+ .child_rekey = _child_rekey,
++ .child_state_change = _child_state_change,
+ },
+ .destroy = _destroy,
+ },
+--
+2.38.1
+
diff --git a/scripts/package-build/strongswan/patches/0004-VyOS-disable-options-enabled-by-Debian-that-are-unus.patch b/scripts/package-build/strongswan/patches/0004-VyOS-disable-options-enabled-by-Debian-that-are-unus.patch
new file mode 100644
index 00000000..57a622e8
--- /dev/null
+++ b/scripts/package-build/strongswan/patches/0004-VyOS-disable-options-enabled-by-Debian-that-are-unus.patch
@@ -0,0 +1,115 @@
+From ee6c0b3ff6e3df5c7aef628621e19a813ff308ed Mon Sep 17 00:00:00 2001
+From: Christian Poessinger <christian@poessinger.com>
+Date: Tue, 27 Dec 2022 13:36:43 +0000
+Subject: [PATCH] VyOS: disable options enabled by Debian that are unused
+
+VyOS does not implement CLI options for all options exposed by Debian.
+
+The following options need to be disabled for the DMVPN patchset:
+ - mediation
+ - nm
+
+In addition we have no LED, LDAP and SQL configuration knows, thus we spare
+the plugins.
+---
+ debian/libcharon-extra-plugins.install | 3 ---
+ debian/libstrongswan-extra-plugins.install | 3 ---
+ debian/rules | 11 ++++++++++-
+ debian/strongswan-nm.install | 2 --
+ 4 files changed, 10 insertions(+), 9 deletions(-)
+
+diff --git a/debian/libcharon-extra-plugins.install b/debian/libcharon-extra-plugins.install
+index 94fbabd88..068708ecb 100644
+--- a/debian/libcharon-extra-plugins.install
++++ b/debian/libcharon-extra-plugins.install
+@@ -13,7 +13,6 @@ usr/lib/ipsec/plugins/libstrongswan-error-notify.so
+ usr/lib/ipsec/plugins/libstrongswan-forecast.so
+ usr/lib/ipsec/plugins/libstrongswan-ha.so
+ usr/lib/ipsec/plugins/libstrongswan-kernel-libipsec.so
+-usr/lib/ipsec/plugins/libstrongswan-led.so
+ usr/lib/ipsec/plugins/libstrongswan-lookip.so
+ #usr/lib/ipsec/plugins/libstrongswan-medsrv.so
+ #usr/lib/ipsec/plugins/libstrongswan-medcli.so
+@@ -36,7 +35,6 @@ usr/share/strongswan/templates/config/plugins/error-notify.conf
+ usr/share/strongswan/templates/config/plugins/forecast.conf
+ usr/share/strongswan/templates/config/plugins/ha.conf
+ usr/share/strongswan/templates/config/plugins/kernel-libipsec.conf
+-usr/share/strongswan/templates/config/plugins/led.conf
+ usr/share/strongswan/templates/config/plugins/lookip.conf
+ #usr/share/strongswan/templates/config/plugins/medsrv.conf
+ #usr/share/strongswan/templates/config/plugins/medcli.conf
+@@ -60,7 +58,6 @@ etc/strongswan.d/charon/error-notify.conf
+ etc/strongswan.d/charon/forecast.conf
+ etc/strongswan.d/charon/ha.conf
+ etc/strongswan.d/charon/kernel-libipsec.conf
+-etc/strongswan.d/charon/led.conf
+ etc/strongswan.d/charon/lookip.conf
+ #etc/strongswan.d/charon/medsrv.conf
+ #etc/strongswan.d/charon/medcli.conf
+diff --git a/debian/libstrongswan-extra-plugins.install b/debian/libstrongswan-extra-plugins.install
+index 2846e2155..00cd0a146 100644
+--- a/debian/libstrongswan-extra-plugins.install
++++ b/debian/libstrongswan-extra-plugins.install
+@@ -8,7 +8,6 @@ usr/lib/ipsec/plugins/libstrongswan-ctr.so
+ usr/lib/ipsec/plugins/libstrongswan-curl.so
+ usr/lib/ipsec/plugins/libstrongswan-curve25519.so
+ usr/lib/ipsec/plugins/libstrongswan-gcrypt.so
+-usr/lib/ipsec/plugins/libstrongswan-ldap.so
+ usr/lib/ipsec/plugins/libstrongswan-pkcs11.so
+ usr/lib/ipsec/plugins/libstrongswan-test-vectors.so
+ usr/lib/ipsec/plugins/libstrongswan-tpm.so
+@@ -20,7 +19,6 @@ usr/share/strongswan/templates/config/plugins/ctr.conf
+ usr/share/strongswan/templates/config/plugins/curl.conf
+ usr/share/strongswan/templates/config/plugins/curve25519.conf
+ usr/share/strongswan/templates/config/plugins/gcrypt.conf
+-usr/share/strongswan/templates/config/plugins/ldap.conf
+ usr/share/strongswan/templates/config/plugins/pkcs11.conf
+ usr/share/strongswan/templates/config/plugins/test-vectors.conf
+ usr/share/strongswan/templates/config/plugins/tpm.conf
+@@ -31,7 +29,6 @@ etc/strongswan.d/charon/ctr.conf
+ etc/strongswan.d/charon/curl.conf
+ etc/strongswan.d/charon/curve25519.conf
+ etc/strongswan.d/charon/gcrypt.conf
+-etc/strongswan.d/charon/ldap.conf
+ etc/strongswan.d/charon/pkcs11.conf
+ etc/strongswan.d/charon/test-vectors.conf
+ etc/strongswan.d/charon/tpm.conf
+diff --git a/debian/rules b/debian/rules
+index 2fed1f10f..fa0d21a0c 100755
+--- a/debian/rules
++++ b/debian/rules
+@@ -3,6 +3,15 @@ export DEB_LDFLAGS_MAINT_APPEND=-Wl,-O1
+ #export DEB_LDFLAGS_MAINT_APPEND=-Wl,--as-needed -Wl,-O1 -Wl,-z,defs
+ export DEB_BUILD_MAINT_OPTIONS=hardening=+all
+
++CONFIGUREARGS_VYOS := --disable-warnings \
++ --disable-ldap \
++ --disable-led \
++ --disable-nm \
++ --disable-mediation \
++ --disable-mysql \
++ --disable-sqlite \
++ --disable-sql
++
+ CONFIGUREARGS := --libdir=/usr/lib --libexecdir=/usr/lib \
+ --enable-addrblock \
+ --enable-agent \
+@@ -88,7 +97,7 @@ ifeq ($(DEB_HOST_ARCH_OS),kfreebsd)
+ deb_systemdsystemunitdir = $(shell pkg-config --variable=systemdsystemunitdir systemd | sed s,^/,,)
+
+ override_dh_auto_configure:
+- dh_auto_configure -- $(CONFIGUREARGS)
++ dh_auto_configure -- $(CONFIGUREARGS) $(CONFIGUREARGS_VYOS)
+
+ override_dh_auto_clean:
+ dh_auto_clean
+diff --git a/debian/strongswan-nm.install b/debian/strongswan-nm.install
+index b0c05d94f..e69de29bb 100644
+--- a/debian/strongswan-nm.install
++++ b/debian/strongswan-nm.install
+@@ -1,2 +0,0 @@
+-usr/lib/ipsec/charon-nm
+-usr/share/dbus-1/system.d/nm-strongswan-service.conf
+--
+2.30.2
+
diff --git a/scripts/package-build/telegraf/.gitignore b/scripts/package-build/telegraf/.gitignore
new file mode 100644
index 00000000..bf2fcf43
--- /dev/null
+++ b/scripts/package-build/telegraf/.gitignore
@@ -0,0 +1,6 @@
+telegraf/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
diff --git a/scripts/package-build/telegraf/README.md b/scripts/package-build/telegraf/README.md
new file mode 100644
index 00000000..63119c0c
--- /dev/null
+++ b/scripts/package-build/telegraf/README.md
@@ -0,0 +1,4 @@
+# build
+```
+python3 build.py
+```
diff --git a/scripts/package-build/telegraf/build.py b/scripts/package-build/telegraf/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/telegraf/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/telegraf/build.sh b/scripts/package-build/telegraf/build.sh
new file mode 100755
index 00000000..2ba511d0
--- /dev/null
+++ b/scripts/package-build/telegraf/build.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+CWD=$(pwd)
+set -e
+
+BUILD_ARCH=$(dpkg-architecture -qDEB_TARGET_ARCH)
+
+SRC=telegraf
+if [ ! -d ${SRC} ]; then
+ echo "Source directory does not exists, please 'git clone'"
+ exit 1
+fi
+
+PLUGIN_DIR=${CWD}/plugins
+
+echo "I: Selecting Input plugins"
+cp ${PLUGIN_DIR}/inputs/all/all.go ${SRC}/plugins/inputs/all/all.go
+
+echo "I: Selecting Output plugins"
+cp ${PLUGIN_DIR}/outputs/all/all.go ${SRC}/plugins/outputs/all/all.go
+
+echo "I: Build Debian ${BUILD_ARCH} package"
+cd ${SRC}
+export PATH=/opt/go/bin:$PATH
+
+# Generate default telegraf config
+go run ./cmd/telegraf config > etc/telegraf.conf
+LDFLAGS=-w make "${BUILD_ARCH}.deb"
diff --git a/scripts/package-build/telegraf/package.toml b/scripts/package-build/telegraf/package.toml
new file mode 100644
index 00000000..c255c43d
--- /dev/null
+++ b/scripts/package-build/telegraf/package.toml
@@ -0,0 +1,5 @@
+[[packages]]
+name = "telegraf"
+commit_id = "v1.28.3"
+scm_url = "https://github.com/influxdata/telegraf.git"
+build_cmd = "cd ..; ./build.sh; cp telegraf/build/dist/*.deb ."
diff --git a/scripts/package-build/telegraf/plugins/inputs/all/all.go b/scripts/package-build/telegraf/plugins/inputs/all/all.go
new file mode 100644
index 00000000..8265681b
--- /dev/null
+++ b/scripts/package-build/telegraf/plugins/inputs/all/all.go
@@ -0,0 +1,72 @@
+package all
+
+import (
+ //Blank imports for plugins to register themselves
+ _ "github.com/influxdata/telegraf/plugins/inputs/azure_storage_queue"
+ _ "github.com/influxdata/telegraf/plugins/inputs/bond"
+ _ "github.com/influxdata/telegraf/plugins/inputs/cgroup"
+ _ "github.com/influxdata/telegraf/plugins/inputs/chrony"
+ _ "github.com/influxdata/telegraf/plugins/inputs/conntrack"
+ _ "github.com/influxdata/telegraf/plugins/inputs/cpu"
+ _ "github.com/influxdata/telegraf/plugins/inputs/disk"
+ _ "github.com/influxdata/telegraf/plugins/inputs/diskio"
+ _ "github.com/influxdata/telegraf/plugins/inputs/disque"
+ _ "github.com/influxdata/telegraf/plugins/inputs/dmcache"
+ _ "github.com/influxdata/telegraf/plugins/inputs/dns_query"
+ _ "github.com/influxdata/telegraf/plugins/inputs/docker"
+ _ "github.com/influxdata/telegraf/plugins/inputs/docker_log"
+ _ "github.com/influxdata/telegraf/plugins/inputs/ethtool"
+ _ "github.com/influxdata/telegraf/plugins/inputs/exec"
+ _ "github.com/influxdata/telegraf/plugins/inputs/execd"
+ _ "github.com/influxdata/telegraf/plugins/inputs/file"
+ _ "github.com/influxdata/telegraf/plugins/inputs/filecount"
+ _ "github.com/influxdata/telegraf/plugins/inputs/filestat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/fireboard"
+ _ "github.com/influxdata/telegraf/plugins/inputs/hddtemp"
+ _ "github.com/influxdata/telegraf/plugins/inputs/hugepages"
+ _ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
+ _ "github.com/influxdata/telegraf/plugins/inputs/influxdb_listener"
+ _ "github.com/influxdata/telegraf/plugins/inputs/influxdb_v2_listener"
+ _ "github.com/influxdata/telegraf/plugins/inputs/intel_pmu"
+ _ "github.com/influxdata/telegraf/plugins/inputs/intel_powerstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/intel_rdt"
+ _ "github.com/influxdata/telegraf/plugins/inputs/internal"
+ _ "github.com/influxdata/telegraf/plugins/inputs/internet_speed"
+ _ "github.com/influxdata/telegraf/plugins/inputs/interrupts"
+ _ "github.com/influxdata/telegraf/plugins/inputs/ipmi_sensor"
+ _ "github.com/influxdata/telegraf/plugins/inputs/ipset"
+ _ "github.com/influxdata/telegraf/plugins/inputs/iptables"
+ _ "github.com/influxdata/telegraf/plugins/inputs/ipvs"
+ _ "github.com/influxdata/telegraf/plugins/inputs/kernel"
+ _ "github.com/influxdata/telegraf/plugins/inputs/kernel_vmstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/mdstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/mem"
+ _ "github.com/influxdata/telegraf/plugins/inputs/net"
+ _ "github.com/influxdata/telegraf/plugins/inputs/netstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/nstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/ping"
+ _ "github.com/influxdata/telegraf/plugins/inputs/powerdns_recursor"
+ _ "github.com/influxdata/telegraf/plugins/inputs/processes"
+ _ "github.com/influxdata/telegraf/plugins/inputs/procstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/sensors"
+ _ "github.com/influxdata/telegraf/plugins/inputs/sflow"
+ _ "github.com/influxdata/telegraf/plugins/inputs/slab"
+ _ "github.com/influxdata/telegraf/plugins/inputs/smart"
+ _ "github.com/influxdata/telegraf/plugins/inputs/snmp"
+ _ "github.com/influxdata/telegraf/plugins/inputs/snmp_legacy"
+ _ "github.com/influxdata/telegraf/plugins/inputs/snmp_trap"
+ _ "github.com/influxdata/telegraf/plugins/inputs/socket_listener"
+ _ "github.com/influxdata/telegraf/plugins/inputs/socketstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/syslog"
+ _ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
+ _ "github.com/influxdata/telegraf/plugins/inputs/system"
+ _ "github.com/influxdata/telegraf/plugins/inputs/systemd_units"
+ _ "github.com/influxdata/telegraf/plugins/inputs/tail"
+ _ "github.com/influxdata/telegraf/plugins/inputs/tcp_listener"
+ _ "github.com/influxdata/telegraf/plugins/inputs/temp"
+ _ "github.com/influxdata/telegraf/plugins/inputs/twemproxy"
+ _ "github.com/influxdata/telegraf/plugins/inputs/udp_listener"
+ _ "github.com/influxdata/telegraf/plugins/inputs/wireguard"
+ _ "github.com/influxdata/telegraf/plugins/inputs/wireless"
+ _ "github.com/influxdata/telegraf/plugins/inputs/x509_cert"
+)
diff --git a/scripts/package-build/telegraf/plugins/outputs/all/all.go b/scripts/package-build/telegraf/plugins/outputs/all/all.go
new file mode 100644
index 00000000..49f7e63d
--- /dev/null
+++ b/scripts/package-build/telegraf/plugins/outputs/all/all.go
@@ -0,0 +1,9 @@
+package all
+
+import (
+ //Blank imports for plugins to register themselves
+ _ "github.com/influxdata/telegraf/plugins/outputs/azure_data_explorer"
+ _ "github.com/influxdata/telegraf/plugins/outputs/http"
+ _ "github.com/influxdata/telegraf/plugins/outputs/influxdb_v2"
+ _ "github.com/influxdata/telegraf/plugins/outputs/prometheus_client"
+)
diff --git a/scripts/package-build/waagent/.gitignore b/scripts/package-build/waagent/.gitignore
new file mode 100644
index 00000000..80401271
--- /dev/null
+++ b/scripts/package-build/waagent/.gitignore
@@ -0,0 +1,8 @@
+waagent/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+*.tar.gz
+*.tar.xz
diff --git a/scripts/package-build/waagent/build.py b/scripts/package-build/waagent/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/waagent/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/waagent/package.toml b/scripts/package-build/waagent/package.toml
new file mode 100644
index 00000000..d7343a7a
--- /dev/null
+++ b/scripts/package-build/waagent/package.toml
@@ -0,0 +1,7 @@
+[[packages]]
+name = "waagent"
+commit_id = "debian/2.9.1.1-2"
+scm_url = "https://salsa.debian.org/cloud-team/waagent.git"
+
+[packages.dependencies]
+packages = ["dpkg-source-gitarchive"]
diff --git a/scripts/package-build/wide-dhcpv6/.gitignore b/scripts/package-build/wide-dhcpv6/.gitignore
new file mode 100644
index 00000000..990f3c6c
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/.gitignore
@@ -0,0 +1,7 @@
+wide-dhcpv6/
+*.buildinfo
+*.build
+*.changes
+*.deb
+*.dsc
+*.udeb
diff --git a/scripts/package-build/wide-dhcpv6/build.py b/scripts/package-build/wide-dhcpv6/build.py
new file mode 120000
index 00000000..3c76af73
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/build.py
@@ -0,0 +1 @@
+../build.py \ No newline at end of file
diff --git a/scripts/package-build/wide-dhcpv6/package.toml b/scripts/package-build/wide-dhcpv6/package.toml
new file mode 100644
index 00000000..77d6239b
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/package.toml
@@ -0,0 +1,4 @@
+[[packages]]
+name = "wide-dhcpv6"
+commit_id = "debian/20080615-23"
+scm_url = "https://salsa.debian.org/debian/wide-dhcpv6"
diff --git a/scripts/package-build/wide-dhcpv6/patches/0023-dhcpc6-support-per-interface-client-DUIDs.patch b/scripts/package-build/wide-dhcpv6/patches/0023-dhcpc6-support-per-interface-client-DUIDs.patch
new file mode 100644
index 00000000..c1e71f0c
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/patches/0023-dhcpc6-support-per-interface-client-DUIDs.patch
@@ -0,0 +1,230 @@
+From 1e4a9a7b61090043924f2aa9359dcbc9f5e11bfc Mon Sep 17 00:00:00 2001
+From: Brandon Stepler <brandon@stepler.net>
+Date: Mon, 25 Jan 2021 14:18:57 +0000
+Subject: [PATCH] dhcpc6: support per-interface client DUIDs
+
+---
+ cfparse.y | 13 +++++++++++--
+ cftoken.l | 10 ++++++++++
+ config.c | 27 +++++++++++++++++++++++++++
+ config.h | 3 ++-
+ dhcp6c.c | 11 ++++++++---
+ dhcp6c.conf.5 | 6 ++++++
+ 6 files changed, 64 insertions(+), 6 deletions(-)
+
+diff --git a/cfparse.y b/cfparse.y
+index 9e685f4..244987c 100644
+--- a/cfparse.y
++++ b/cfparse.y
+@@ -116,6 +116,7 @@ static void cleanup_cflist __P((struct cf_list *));
+ %token BCMCS_SERVERS BCMCS_NAME
+ %token INFO_ONLY
+ %token SCRIPT DELAYEDKEY
++%token CLIENT_ID CLIENT_ID_DUID
+ %token AUTHENTICATION PROTOCOL ALGORITHM DELAYED RECONFIG HMACMD5 MONOCOUNTER
+ %token AUTHNAME RDM KEY
+ %token KEYINFO REALM KEYID SECRET KEYNAME EXPIRE
+@@ -134,8 +135,8 @@ static void cleanup_cflist __P((struct cf_list *));
+ struct dhcp6_poolspec *pool;
+ }
+
+-%type <str> IFNAME HOSTNAME AUTHNAME KEYNAME DUID_ID STRING QSTRING IAID
+-%type <str> POOLNAME PROFILENAME
++%type <str> IFNAME HOSTNAME CLIENT_ID_DUID AUTHNAME KEYNAME DUID_ID
++%type <str> STRING QSTRING IAID POOLNAME PROFILENAME
+ %type <num> NUMBER duration authproto authalg authrdm
+ %type <list> declaration declarations dhcpoption ifparam ifparams
+ %type <list> address_list address_list_ent dhcpoption_list
+@@ -639,6 +640,14 @@ dhcpoption:
+ /* no value */
+ $$ = l;
+ }
++ | CLIENT_ID CLIENT_ID_DUID
++ {
++ struct cf_list *l;
++
++ MAKE_CFLIST(l, DHCPOPT_CLIENT_ID, NULL, NULL);
++ l->ptr = $2;
++ $$ = l;
++ }
+ | AUTHENTICATION AUTHNAME
+ {
+ struct cf_list *l;
+diff --git a/cftoken.l b/cftoken.l
+index e266ac2..d7edd1f 100644
+--- a/cftoken.l
++++ b/cftoken.l
+@@ -119,6 +119,7 @@ ecl \}
+ %s S_HOST
+ %s S_DUID
+ %s S_IA
++%s S_CID
+ %s S_AUTH
+ %s S_KEY
+ %s S_SECRET
+@@ -249,6 +250,15 @@ ecl \}
+ /* duration */
+ <S_CNF>infinity { DECHO; return (INFINITY); }
+
++ /* client-id option */
++<S_CNF>client-id { DECHO; BEGIN S_CID; return (CLIENT_ID); }
++<S_CID>{duid} {
++ DECHO;
++ yylval.str = strdup(yytext);
++ BEGIN S_CNF;
++ return (CLIENT_ID_DUID);
++}
++
+ /* authentication option */
+ <S_CNF>authentication { DECHO; BEGIN S_AUTH; return (AUTHENTICATION); }
+ <S_AUTH>{string} {
+diff --git a/config.c b/config.c
+index 70f6287..0cbe631 100644
+--- a/config.c
++++ b/config.c
+@@ -100,6 +100,7 @@ struct dhcp6_ifconf {
+ struct dhcp6_ifconf *next;
+
+ char *ifname;
++ struct duid duid;
+
+ /* configuration flags */
+ u_long send_flags;
+@@ -1366,6 +1367,7 @@ configure_commit()
+ /* commit interface configuration */
+ for (ifp = dhcp6_if; ifp; ifp = ifp->next) {
+ /* re-initialization */
++ duidfree(&ifp->duid);
+ ifp->send_flags = 0;
+ ifp->allow_flags = 0;
+ dhcp6_clear_list(&ifp->reqopt_list);
+@@ -1395,6 +1397,8 @@ configure_commit()
+ }
+
+ /* copy new configuration */
++ ifp->duid = ifc->duid;
++ ifc->duid.duid_id = NULL;
+ ifp->send_flags = ifc->send_flags;
+ ifp->allow_flags = ifc->allow_flags;
+ dhcp6_copy_list(&ifp->reqopt_list, &ifc->reqopt_list);
+@@ -1505,6 +1509,7 @@ clear_ifconf(iflist)
+ ifc_next = ifc->next;
+
+ free(ifc->ifname);
++ duidfree(&ifc->duid);
+ dhcp6_clear_list(&ifc->reqopt_list);
+
+ clear_iaconf(&ifc->iaconf_list);
+@@ -1635,6 +1640,28 @@ add_options(opcode, ifc, cfl0)
+ return (-1);
+ }
+ break;
++ case DHCPOPT_CLIENT_ID:
++ if (opcode != DHCPOPTCODE_SEND) {
++ debug_printf(LOG_ERR, FNAME,
++ "invalid operation (%d) "
++ "for option type (%d)",
++ opcode, cfl->type);
++ return (-1);
++ }
++ if (ifc->duid.duid_id != NULL) {
++ debug_printf(LOG_ERR, FNAME, "%s:%d "
++ "client-id is doubly specified on %s",
++ configfilename, cfl->line, ifc->ifname);
++ return (-1);
++ }
++ if ((configure_duid((char *)cfl->ptr,
++ &ifc->duid)) != 0) {
++ debug_printf(LOG_ERR, FNAME, "%s:%d "
++ "failed to configure DUID for %s",
++ configfilename, cfl->line, ifc->ifname);
++ return (-1);
++ }
++ break;
+ case DHCPOPT_AUTHINFO:
+ if (opcode != DHCPOPTCODE_SEND) {
+ debug_printf(LOG_ERR, FNAME,
+diff --git a/config.h b/config.h
+index 36a5aa3..cfcfdd5 100644
+--- a/config.h
++++ b/config.h
+@@ -69,6 +69,7 @@ struct dhcp6_if {
+ u_int32_t linkid; /* to send link-local packets */
+ /* multiple global address configuration is not supported now */
+ struct in6_addr addr; /* global address */
++ struct duid duid;
+
+ /* configuration parameters */
+ u_long send_flags;
+@@ -267,7 +268,7 @@ enum { DECL_SEND, DECL_ALLOW, DECL_INFO_ONLY, DECL_REQUEST, DECL_DUID,
+ DECL_ADDRESS,
+ DECL_RANGE, DECL_ADDRESSPOOL,
+ IFPARAM_SLA_ID, IFPARAM_SLA_LEN, IFPARAM_IFID, IFPARAM_IFID_RAND,
+- DHCPOPT_RAPID_COMMIT, DHCPOPT_AUTHINFO,
++ DHCPOPT_RAPID_COMMIT, DHCPOPT_CLIENT_ID, DHCPOPT_AUTHINFO,
+ DHCPOPT_DNS, DHCPOPT_DNSNAME,
+ DHCPOPT_IA_PD, DHCPOPT_IA_NA, DHCPOPT_NTP,
+ DHCPOPT_REFRESHTIME,
+diff --git a/dhcp6c.c b/dhcp6c.c
+index 849835e..875a147 100644
+--- a/dhcp6c.c
++++ b/dhcp6c.c
+@@ -433,6 +433,11 @@ client6_start(ifp)
+ }
+ dhcp6_reset_timer(ev);
+
++ if (!ifp->duid.duid_id && duidcpy(&ifp->duid, &client_duid)) {
++ debug_printf(LOG_ERR, FNAME, "failed to copy client DUID");
++ return (-1);
++ }
++
+ return (0);
+ }
+
+@@ -1249,7 +1254,7 @@ client6_send(ev)
+ }
+
+ /* client ID */
+- if (duidcpy(&optinfo.clientID, &client_duid)) {
++ if (duidcpy(&optinfo.clientID, &ifp->duid)) {
+ debug_printf(LOG_ERR, FNAME, "failed to copy client ID");
+ goto end;
+ }
+@@ -1533,7 +1538,7 @@ client6_recvadvert(ifp, dh6, len, optinfo)
+ debug_printf(LOG_INFO, FNAME, "no client ID option");
+ return (-1);
+ }
+- if (duidcmp(&optinfo->clientID, &client_duid)) {
++ if (duidcmp(&optinfo->clientID, &ifp->duid)) {
+ debug_printf(LOG_INFO, FNAME, "client DUID mismatch");
+ return (-1);
+ }
+@@ -1805,7 +1810,7 @@ client6_recvreply(ifp, dh6, len, optinfo)
+ debug_printf(LOG_INFO, FNAME, "no client ID option");
+ return (-1);
+ }
+- if (duidcmp(&optinfo->clientID, &client_duid)) {
++ if (duidcmp(&optinfo->clientID, &ifp->duid)) {
+ debug_printf(LOG_INFO, FNAME, "client DUID mismatch");
+ return (-1);
+ }
+diff --git a/dhcp6c.conf.5 b/dhcp6c.conf.5
+index 5693fb8..589510a 100644
+--- a/dhcp6c.conf.5
++++ b/dhcp6c.conf.5
+@@ -139,6 +139,12 @@ An
+ statement for
+ .Ar authname
+ must be provided.
++.It Ic client-id Ar ID
++means the client's DHCP unique identifier
++.Pq DUID .
++.Ar ID
++is a colon-separated hexadecimal sequence where each separated part
++must be composed of two hexadecimal values.
+ .El
+ .\"
+ .Sh Interface statement
+--
+2.20.1
+
diff --git a/scripts/package-build/wide-dhcpv6/patches/0024-bind-to-single-socket.patch b/scripts/package-build/wide-dhcpv6/patches/0024-bind-to-single-socket.patch
new file mode 100644
index 00000000..b5751325
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/patches/0024-bind-to-single-socket.patch
@@ -0,0 +1,17 @@
+diff --git a/dhcp6c.c b/dhcp6c.c
+index 1caaaa5..04ce9c5 100644
+--- a/dhcp6c.c
++++ b/dhcp6c.c
+@@ -217,6 +217,12 @@ main(argc, argv)
+ argv[0]);
+ exit(1);
+ }
++
++ if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, argv[0], strlen(argv[0])) != 0) {
++ debug_printf(LOG_ERR, FNAME, "failed to bind %s", argv[0]);
++ exit(1);
++ }
++
+ argv++;
+ }
+
diff --git a/scripts/package-build/wide-dhcpv6/patches/0025-option-to-prevent-ia-release.patch b/scripts/package-build/wide-dhcpv6/patches/0025-option-to-prevent-ia-release.patch
new file mode 100644
index 00000000..32c15814
--- /dev/null
+++ b/scripts/package-build/wide-dhcpv6/patches/0025-option-to-prevent-ia-release.patch
@@ -0,0 +1,155 @@
+From: 1vivy <1vivy@tutanota.com>
+Date: Sat, 22 Jul 2023 13:07:10 -0600
+Subject: wide-dhcpv6: T5387: Add a no release option '-n'.
+
+This prevents a release signal from being sent to the ISP causing a new PD or address to be allocated.
+
+Co-authored-by: MrLenin <909621+MrLenin@users.noreply.github.com>
+Co-authored-by: marjohn56 <martin@queens-park.com>
+--- wide-dhcpv6.orig/common.h
++++ wide-dhcpv6/common.h
+@@ -120,6 +120,7 @@ sysdep_sa_len (const struct sockaddr *sa
+ extern int foreground;
+ extern int debug_thresh;
+ extern char *device;
++extern int opt_norelease;
+
+ /* search option for dhcp6_find_listval() */
+ #define MATCHLIST_PREFIXLEN 0x1
+--- wide-dhcpv6.orig/dhcp6c.8
++++ wide-dhcpv6/dhcp6c.8
+@@ -88,6 +88,10 @@ is terminated. (suits for a use in shel
+ Since the configuration is internally generated, you cannot provide a configuration in this mode. If you want to have different actions for the stateless DHCPv6 information, you should write an appropriate configuration and invoke
+ .Nm
+ without this option.
++.It Fl n
++Prevent Release message from being sent to DHCPv6 server when
++.Nm
++stops. This is useful for preventing a new address from being configured by the DHCPv6 server when restarting the DHCPv6 client.
+ .It Fl p Ar pid-file
+ Use
+ .Ar pid-file
+@@ -109,18 +113,22 @@ or
+ .Fl i
+ option is specified.
+ .Pp
+-Upon receipt of the
+-.Dv SIGHUP
++Upon receipt of a
++.Dv SIGHUP ,
++.Dv SIGTERM ,
+ or
+-.Dv SIGTERM
+-signals,
+-.Nm
+-will remove all stateful resources from the system.
+-In the former case the daemon will then reinvoke itself,
+-while it will stop running in the latter case.
+-In either case,
++.Dv SIGUSR1
++signal,
+ .Nm
+-will send DHCPv6 Release messages to release resources assigned from servers.
++will remove all stateful resources from the system. After that,
++.Dv SIGHUP
++reinitializes the daemon, and
++.Dv SIGTERM
++stops the daemon. In both cases, DHCPv6 Release message will be sent to release resources assigned from servers.
++.Dv SIGUSR1
++stops the daemon as
++.Dv SIGTERM
++does though DHCPv6 Release message will not be sent.
+ .\"
+ .Sh FILES
+ .Bl -tag -width /etc/wide-dhcpv6/dhcp6c.conf -compact
+--- wide-dhcpv6.orig/dhcp6c.c
++++ wide-dhcpv6/dhcp6c.c
+@@ -84,6 +84,7 @@ static int exit_ok = 0;
+ static sig_atomic_t sig_flags = 0;
+ #define SIGF_TERM 0x1
+ #define SIGF_HUP 0x2
++#define SIGF_USR1 0x4
+
+ const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_CLIENT;
+
+@@ -108,6 +109,8 @@ static int ctldigestlen;
+
+ static int infreq_mode = 0;
+
++int opt_norelease;
++
+ static inline int get_val32 __P((char **, int *, u_int32_t *));
+ static inline int get_ifname __P((char **, int *, char *, int));
+
+@@ -170,7 +173,7 @@ main(argc, argv)
+ else
+ progname++;
+
+- while ((ch = getopt(argc, argv, "c:dDfik:p:P:")) != -1) {
++ while ((ch = getopt(argc, argv, "c:dDfik:np:P:")) != -1) {
+ switch (ch) {
+ case 'c':
+ conffile = optarg;
+@@ -190,6 +193,9 @@ main(argc, argv)
+ case 'k':
+ ctlkeyfile = optarg;
+ break;
++ case 'n':
++ opt_norelease = 1;
++ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+@@ -395,6 +401,11 @@ client6_init()
+ strerror(errno));
+ exit(1);
+ }
++ if (signal(SIGUSR1, client6_signal) == SIG_ERR) {
++ debug_printf(LOG_WARNING, FNAME, "failed to set signal: %s",
++ strerror(errno));
++ exit(1);
++ }
+ }
+
+ int
+@@ -525,6 +536,13 @@ process_signals()
+ free_resources(NULL);
+ client6_startall(1);
+ }
++ if ((sig_flags & SIGF_USR1)) {
++ debug_printf(LOG_INFO, FNAME, "exit without release");
++ exit_ok = 1;
++ opt_norelease = 1;
++ free_resources(NULL);
++ check_exit();
++ }
+
+ sig_flags = 0;
+ }
+@@ -1171,6 +1189,9 @@ client6_signal(sig)
+ case SIGHUP:
+ sig_flags |= SIGF_HUP;
+ break;
++ case SIGUSR1:
++ sig_flags |= SIGF_USR1;
++ break;
+ }
+ }
+
+--- wide-dhcpv6.orig/dhcp6c_ia.c
++++ wide-dhcpv6/dhcp6c_ia.c
+@@ -420,7 +420,13 @@ release_all_ia(ifp)
+ for (ia = TAILQ_FIRST(&iac->iadata); ia; ia = ia_next) {
+ ia_next = TAILQ_NEXT(ia, link);
+
+- (void)release_ia(ia);
++ if (opt_norelease == 0) {
++ debug_printf(LOG_INFO, FNAME, "Start address "
++ "release");
++ (void)release_ia(ia);
++ } else
++ debug_printf(LOG_INFO, FNAME, "Bypassing address "
++ "release because of -n flag");
+
+ /*
+ * The client MUST stop using all of the addresses