diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index e65ebf013..a221d3396 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - target: [ 'cig_wf186w', 'cig_wf188n-ca', 'cig_wf188n-ca-ath12', 'cig_wf188n-us', 'cig_wf196-us', 'cig_wf196-ca', 'cig_wf196-ca-ath12', 'cig_wf610d', 'cig_wf660a', 'cig_wf808', 'cybertan_eww622-a1', 'cybertan_eww631-a1', 'cybertan_eww631-b1', 'edgecore_eap101', 'edgecore_eap101-ath12', 'edgecore_eap102', 'edgecore_eap102-ath12', 'edgecore_eap104', 'edgecore_eap104-ath12', 'liteon_wpx8324', 'edgecore_ecs4100-12ph', 'edgecore_ecw5211', 'edgecore_ecw5410', 'edgecore_oap100', 'edgecore_oap101-6e', 'edgecore_oap101e', 'hfcl_ion4','hfcl_ion4xi_wp', 'hfcl_ion4xe', 'hfcl_ion4xi', 'hfcl_ion4x', 'hfcl_ion4x_2', 'hfcl_ion4xi_w', 'hfcl_ion4xi_HMR', 'hfcl_ion4x_w', 'indio_um-305ac', 'indio_um-305ax', 'indio_um-325ac', 'indio_um-510ac-v3', 'indio_um-550ac', 'indio_um-310ax-v1', 'indio_um-510axp-v1', 'indio_um-510axm-v1', 'udaya_a5-id2', 'wallys_dr40x9', 'wallys_dr6018', 'wallys_dr6018_v4', 'x64_vm', 'yuncore_ax840', 'yuncore_fap640', 'yuncore_fap650', 'yuncore_fap655' ] + target: [ 'edgecore_oap100', 'udaya_a5-id2' ] steps: - uses: actions/checkout@v3 diff --git a/feeds/tip/certificates/files/etc/init.d/certificates b/feeds/tip/certificates/files/etc/init.d/certificates deleted file mode 100755 index c810644d1..000000000 --- a/feeds/tip/certificates/files/etc/init.d/certificates +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=80 - -copy_certificates() { - [ -f /certificates/dev-id ] || return - - cp /certificates/*.pem /etc/ucentral/ - cp /certificates/dev-id /etc/ucentral/ - chown root.network /etc/ucentral/*.pem - chmod 0440 root.network /etc/ucentral/*.pem - chmod 0400 /etc/ucentral/dev-id - [ -f /certificates/restrictions.json ] && cp /certificates/restrictions.json /etc/ucentral/ - [ -f /certificates/sign_pubkey.pem ] && cp /certificates/sign_pubkey.pem /etc/ucentral/ - exit 0 -} - -boot() { - [ -f /etc/ucentral/dev-id ] && return - . /lib/functions.sh - mkdir -p /certificates /etc/ucentral/ - local mtd=$(find_mtd_index certificates) - - if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then - mount -t squashfs /dev/mtdblock$mtd /certificates - else - [ -n "$mtd" -a -f /sys/class/mtd/mtd$mtd/oobsize ] && ubiattach -p /dev/mtd$mtd - if [ -n "$(ubinfo -a | grep certificates)" ]; then - [ -e /dev/ubi0 ] && mount -t ubifs ubi0:certificates /certificates - [ -e /dev/ubi1 ] && mount -t ubifs ubi1:certificates /certificates - fi - fi - - case "$(board_name)" in - cig,wf660a) - mmc_dev=$(echo $(find_mmc_part "0:ETHPHYFW") | sed 's/^.\{5\}//') - [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates - ;; - esac - - copy_certificates - - # if we get here no valid certificates were found - - local PART_NAME - - case "$(board_name)" in - actiontec,web7200) - if grep -q bootselect=0 /proc/cmdline; then - PART_NAME=firmware2 - else - PART_NAME=firmware1 - fi - ;; - edgecore,ecw5211|\ - edgecore,eap101|\ - edgecore,eap102) - if grep -q rootfs1 /proc/cmdline; then - PART_NAME=rootfs2 - else - PART_NAME=rootfs1 - fi - ;; - hfcl,ion4xi|\ - hfcl,ion4xi_w|\ - hfcl,ion4x_w|\ - hfcl,ion4xi_HMR|\ - hfcl,ion4x|\ - hfcl,ion4x_2|\ - hfcl,ion4xi_wp|\ - hfcl,ion4xe) - if grep -q rootfs_1 /proc/cmdline; then - PART_NAME=rootfs - else - PART_NAME=rootfs_1 - fi - ;; - cig,wf186w|\ - cig,wf186h|\ - yuncore,ax840|\ - yuncore,fap655) - PART_NAME=rootfs_1 - ;; - *) - return 1 - ;; - esac - - local MTD=$(find_mtd_index $PART_NAME) - - [ -z "$MTD" ] && return 1 - - ubiattach -m $MTD -d 3 - [ -e /dev/ubi3 ] && mount -t ubifs ubi3:certificates /certificates - copy_certificates -} diff --git a/feeds/tip/certificates/files/etc/init.d/early_boot b/feeds/tip/certificates/files/etc/init.d/early_boot new file mode 100755 index 000000000..e4f1d9ca5 --- /dev/null +++ b/feeds/tip/certificates/files/etc/init.d/early_boot @@ -0,0 +1,36 @@ +#!/bin/sh /etc/rc.common + +START=09 + +copy_certificates() { + [ -f /certificates/key.pem ] || return + + cp /certificates/cert.pem /certificates/key.pem /certificates/operational.* /etc/ucentral/ + chown root.network /etc/ucentral/*.pem /etc/ucentral/*.ca + chmod 0440 root.network /etc/ucentral/*.pem /etc/ucentral/*.ca + [ -f /certificates/gateway.json ] && cp /certificates/gateway.json /etc/ucentral/gateway.flash + [ -f /certificates/restrictions.json ] && cp /certificates/restrictions.json /etc/ucentral/ + [ -f /certificates/sign_pubkey.pem ] && cp /certificates/sign_pubkey.pem /etc/ucentral/ + country=`cat /certificates/ucentral.defaults | jsonfilter -e '@.country'` + [ -z "$country" -a -f /etc/uci-defaults/30_uboot-envtools ] && { + (. /etc/uci-defaults/30_uboot-envtools) + fw_printenv + country=$(fw_printenv -n country) + } + [ -z "$country" ] && country=US + echo "options cfg80211 ieee80211_regdom="$country > /etc/modules.conf + echo -n $country > /etc/ucentral/country + sync + exit 0 +} + +boot() { + case "$(board_name)" in + sonicfi,rap6*) + touch /tmp/squashfs + ;; + esac + [ -f /etc/ucentral/key.pem ] && return + /usr/bin/mount_certs + copy_certificates +} diff --git a/feeds/tip/certificates/files/etc/uci-defaults/99-tip-certificates.sh b/feeds/tip/certificates/files/etc/uci-defaults/99-tip-certificates.sh index 445b67cf3..9d974d5db 100755 --- a/feeds/tip/certificates/files/etc/uci-defaults/99-tip-certificates.sh +++ b/feeds/tip/certificates/files/etc/uci-defaults/99-tip-certificates.sh @@ -2,5 +2,6 @@ uci add system certificates uci set system.@certificates[-1].key=/etc/ucentral/key.pem -uci set system.@certificates[-1].cert=/etc/ucentral/cert.pem -uci set system.@certificates[-1].ca=/etc/ucentral/cas.pem +uci set system.@certificates[-1].cert=/etc/ucentral/operational.pem +uci set system.@certificates[-1].ca=/etc/ucentral/operational.ca +uci commit diff --git a/feeds/tip/certificates/files/usr/bin/mount_certs b/feeds/tip/certificates/files/usr/bin/mount_certs index 022d3c055..f074f23a4 100755 --- a/feeds/tip/certificates/files/usr/bin/mount_certs +++ b/feeds/tip/certificates/files/usr/bin/mount_certs @@ -1,15 +1,158 @@ #!/bin/sh -. /lib/functions.sh -mkdir -p /certificates -mtd=$(find_mtd_index certificates) +check_certificates() { + [ -f /certificates/cert.pem -a -f /certificates/key.pem ] && exit 0 +} -if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then - mount -t squashfs /dev/mtdblock$mtd /certificates -else - [ -n "$mtd" -a -f /sys/class/mtd/mtd$mtd/oobsize ] && ubiattach -p /dev/mtd$mtd - if [ -n "$(ubinfo -a | grep certificates)" ]; then - [ -e /dev/ubi0 ] && mount -t ubifs ubi0:certificates /certificates - [ -e /dev/ubi1 ] && mount -t ubifs ubi1:certificates /certificates +check_certificates + +tar_part_lookup() { + part="$(fw_printenv -n cert_part)" + if [ "$part" -eq 0 ]; then + echo "$2" + part=1 + else + echo "$1" + part=0 fi -fi + fw_setenv cert_part $part +} + +. /lib/functions.sh +mkdir -p /certificates /etc/ucentral/ +case "$(board_name)" in +cig,wf660a) + mmc_dev=$(echo $(find_mmc_part "0:ETHPHYFW") | sed 's/^.\{5\}//') + [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates + ;; +cig,wf672) + mmc_dev=$(echo $(find_mmc_part "cert") | sed 's/^.\{5\}//') + [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates + ;; +sonicfi,rap7*) + if [ "$(board_name)" = "sonicfi,rap7110c-341x" ]; then + mmc_dev=$(echo $(find_mmc_part "certificates") | sed 's/^.\{5\}//') + [ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates + else + mtd=$(find_mtd_index certificates) + [ -n "$mtd" ] && mount -t ext4 /dev/mtdblock$mtd /certificates + fi + ;; +sonicfi,rap6*) + mtd=$(find_mtd_index certificates) + if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then + mount -t squashfs /dev/mtdblock$mtd /mnt + cp /mnt/* /certificates + umount /mnt + fi + mtd=$(find_mtd_index devinfo) + [ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates + ;; +udaya,a5-id2|\ +yuncore,ax820) + mtd=$(find_mtd_index certificates) + if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then + mount -t squashfs /dev/mtdblock$mtd /mnt + cp /mnt/* /certificates + umount /mnt + fi + part=$(tar_part_lookup "insta1" "insta2") + if [ -n "insta" ]; then + mtd=$(find_mtd_index $part) + [ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates + fi + ;; +*) + mtd=$(find_mtd_index certificates) + + if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then + mount -t squashfs /dev/mtdblock$mtd /certificates + else + [ -n "$mtd" -a -f /sys/class/mtd/mtd$mtd/oobsize ] && ubiattach -p /dev/mtd$mtd + if [ -n "$(ubinfo -a | grep certificates)" ]; then + [ -e /dev/ubi0 ] && mount -t ubifs ubi0:certificates /certificates + [ -e /dev/ubi1 ] && mount -t ubifs ubi1:certificates /certificates + fi + fi +esac + +check_certificates + +# if we get here no valid certificates were found + +PART_NAME= + +case "$(board_name)" in +actiontec,web7200) + if grep -q bootselect=0 /proc/cmdline; then + PART_NAME=firmware2 + else + PART_NAME=firmware1 + fi + ;; +edgecore,ecw5211|\ +edgecore,eap101|\ +edgecore,eap102|\ +edgecore,eap104|\ +edgecore,eap105|\ +edgecore,eap111|\ +edgecore,eap112|\ +edgecore,oap101|\ +edgecore,oap101e|\ +edgecore,oap101-6e|\ +edgecore,oap101e-6e|\ +edgecore,oap103) + if grep -q rootfs1 /proc/cmdline; then + PART_NAME=rootfs2 + else + PART_NAME=rootfs1 + fi + ;; +hfcl,ion4xi|\ +hfcl,ion4xi_w|\ +hfcl,ion4x_w|\ +hfcl,ion4xi_HMR|\ +hfcl,ion4x|\ +hfcl,ion4x_2|\ +hfcl,ion4xi_wp|\ +hfcl,ion4xe) + if grep -q rootfs_1 /proc/cmdline; then + PART_NAME=rootfs + else + PART_NAME=rootfs_1 + fi + ;; +cig,wf186w|\ +cig,wf189|\ +cig,wf189w|\ +cig,wf189h|\ +cig,wf186h|\ +cig,wf196|\ +cig,wf188n|\ +emplus,wap380c|\ +emplus,wap385c|\ +emplus,wap386v2|\ +emplus,wap581|\ +yuncore,ax840|\ +yuncore,fap655) + PART_NAME=rootfs_1 + ;; +senao,iap2300m|\ +senao,iap4300m|\ +emplus,wap588m|\ +senao,jeap6500) + PART_NAME=ubi + ;; +*) + return 1 + ;; +esac + +MTD=$(find_mtd_index $PART_NAME) + +[ -z "$MTD" ] && return 1 + +ubiattach -m $MTD -d 3 +[ -e /dev/ubi3 ] && mount -t ubifs ubi3:certificates /certificates + +check_certificates diff --git a/feeds/tip/certificates/files/usr/bin/store_certs b/feeds/tip/certificates/files/usr/bin/store_certs new file mode 100755 index 000000000..d3d7ba4ce --- /dev/null +++ b/feeds/tip/certificates/files/usr/bin/store_certs @@ -0,0 +1,36 @@ +#!/bin/sh + +tar_part_lookup() { + part="$(fw_printenv -n cert_part)" + if [ "$part" -eq 0 ]; then + echo "$2" + part=1 + else + echo "$1" + part=0 + fi + fw_setenv cert_part $part +} + +. /lib/functions.sh +case "$(board_name)" in +udaya,a5-id2|\ +yuncore,ax820) + cd /certificates + tar cf /tmp/certs.tar . + part=$(tar_part_lookup "insta1" "insta2") + mtd=$(find_mtd_index $part) + dd if=/tmp/certs.tar of=/dev/mtdblock$mtd + ;; +sonicfi,rap6*) + if [ "$(fw_printenv -n store_certs_disabled)" != "1" ]; then + cd /certificates + tar cf /tmp/certs.tar . + mtd=$(find_mtd_index devinfo) + block_size=$(cat /sys/class/mtd/mtd$mtd/size) + dd if=/tmp/certs.tar of=/tmp/certs_pad.tar bs=$block_size conv=sync + mtd write /tmp/certs_pad.tar /dev/mtd$mtd + rm -f /tmp/certs.tar /tmp/certs_pad.tar + fi + ;; +esac diff --git a/feeds/tip/cloud_discovery/Makefile b/feeds/tip/cloud_discovery/Makefile new file mode 100644 index 000000000..adb8487d1 --- /dev/null +++ b/feeds/tip/cloud_discovery/Makefile @@ -0,0 +1,24 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=cloud_discovery +PKG_RELEASE:=1 + +PKG_LICENSE:=BSD-3-Clause +PKG_MAINTAINER:=John Crispin + +include $(INCLUDE_DIR)/package.mk + +define Package/cloud_discovery + SECTION:=ucentral + CATEGORY:=uCentral + TITLE:=TIP cloud_discovery + DEPENDS:=+certificates +ucode-mod-log +endef + +Build/Compile= + +define Package/cloud_discovery/install + $(CP) ./files/* $(1) +endef + +$(eval $(call BuildPackage,cloud_discovery)) diff --git a/feeds/tip/cloud_discovery/files/etc/hotplug.d/ntp/30-cloud-discover b/feeds/tip/cloud_discovery/files/etc/hotplug.d/ntp/30-cloud-discover new file mode 100644 index 000000000..64d1b3572 --- /dev/null +++ b/feeds/tip/cloud_discovery/files/etc/hotplug.d/ntp/30-cloud-discover @@ -0,0 +1 @@ +touch /tmp/ntp.set diff --git a/feeds/tip/cloud_discovery/files/etc/init.d/cloud_discover b/feeds/tip/cloud_discovery/files/etc/init.d/cloud_discover new file mode 100755 index 000000000..e9828b22f --- /dev/null +++ b/feeds/tip/cloud_discovery/files/etc/init.d/cloud_discover @@ -0,0 +1,42 @@ +#!/bin/sh /etc/rc.common + +START=98 +USE_PROCD=1 +PROG=/usr/bin/cloud_discovery + +service_triggers() { + procd_add_reload_trigger ucentral +} + +reload_service() { + ubus call cloud reload +} + +start_service() { + [ -f /etc/ucentral/capabilities.json ] || { + mkdir -p /etc/ucentral/ + /usr/share/ucentral/capabilities.uc + } + + local valid=$(cat /etc/ucentral/gateway.json | jsonfilter -e '@["valid"]') + [ "$valid" == "true" ] || + /usr/share/ucentral/ucentral.uc /etc/ucentral/ucentral.cfg.0000000001 > /dev/null + + est_client check + [ $? -eq 1 ] && { + logger ERROR + logger ERROR + logger ERROR + logger The certificate used has a CN that does not match the serial of the device + echo The certificate used has a CN that does not match the serial of the device + logger ERROR + logger ERROR + logger ERROR + return + } + + procd_open_instance + procd_set_param command "$PROG" + procd_set_param respawn + procd_close_instance +} diff --git a/feeds/tip/cloud_discovery/files/etc/udhcpc.user.d/cloud_discovery b/feeds/tip/cloud_discovery/files/etc/udhcpc.user.d/cloud_discovery new file mode 100755 index 000000000..ae6173759 --- /dev/null +++ b/feeds/tip/cloud_discovery/files/etc/udhcpc.user.d/cloud_discovery @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/share/ucentral/cloud_discovery.uc $1 diff --git a/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery b/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery new file mode 100755 index 000000000..8c9a4f0ea --- /dev/null +++ b/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery @@ -0,0 +1,445 @@ +#!/usr/bin/ucode + +'use strict'; + +import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log'; +import * as libubus from 'ubus'; +import * as uloop from 'uloop'; +import * as libuci from 'uci'; +import * as math from 'math'; +import * as fs from 'fs'; + +const DISCOVER = 0; +const VALIDATING = 1; +const ONLINE = 2; +const OFFLINE = 3; +const ORPHAN = 4; + +const DISCOVER_DHCP = "DHCP"; +const DISCOVER_FLASH = "FLASH"; +const DISCOVER_LOOKUP = "OpenLAN"; + +let ubus = libubus.connect(); +let uci = libuci.cursor(); +let state = DISCOVER; +let discovery_method = ""; +let discovery_block_list = []; +let validate_time; +let offline_time; +let orphan_time; +let interval; +let timeouts = { + 'offline': 4 * 60 * 60, + 'validate': 120, + 'orphan': 2 * 60 * 60, + interval: 10000, + expiry_interval: 60 * 60 * 1000, + expiry_threshold: 1 * 365 * 24 * 60 * 60, +}; + +ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "cloud_discover"); + +ulog(LOG_INFO, 'Start\n'); + +uloop.init(); + +let cds_server = 'discovery.open-lan.org'; + +function detect_certificate_type() { + let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`); + let issuer = pipe.read("all"); + pipe.close(); + + if (match(issuer, /OpenLAN Demo Birth CA/)) { + ulog(LOG_INFO, 'Certificate type is "Demo" \n'); + cds_server = 'discovery-qa.open-lan.org'; + timeouts.expiry_threshold = 3 * 24 * 60 * 60; + } else if (match(issuer, /OpenLAN Birth Issuing CA/)) { + ulog(LOG_INFO, 'Certificate type is "Production"\n'); + } else { + ulog(LOG_INFO, 'Certificate type is "TIP"\n'); + } +} + +function readjsonfile(path) { + let file = fs.readfile(path); + if (file) + file = json(file); + return file; +} + +function timeouts_load(){ + let data = uci.get_all('ucentral', 'timeouts'); + + for (let key in [ 'offline', 'validate', 'orphan' ]) + if (data && data[key]) + timeouts[key] = +data[key]; + let time_skew = timeouts.offline / 50 * (math.rand() % 50); + timeouts.offline_skew = timeouts.offline + time_skew; + ulog(LOG_INFO, 'Randomizing offline time from %d->%d \n', timeouts.offline, timeouts.offline_skew); + + time_skew = timeouts.orphan / 50 * (math.rand() % 50); + timeouts.orphan_skew = timeouts.orphan + time_skew; + ulog(LOG_INFO, 'Randomizing orphan time from %d->%d \n', timeouts.orphan, timeouts.orphan_skew); +} + +function client_start() { + ulog(LOG_INFO, '(re)starting client\n'); + system('/etc/init.d/ucentral restart'); +} + +function dhcp_restart() { + ulog(LOG_INFO, 'restarting dhcp\n'); + system('killall -USR1 udhcpc'); +} + +function ntp_restart() { + ulog(LOG_INFO, 'restarting ntp\n'); + system('/etc/init.d/sysntpd restart'); +} + +function gateway_load() { + return readjsonfile('/etc/ucentral/gateway.json'); +} + +function discovery_state_write() { + if (length(discovery_method) == 0) + return; + + let discovery_state = { + "type": discovery_method, + "updated": time() + }; + fs.writefile('/etc/ucentral/discovery.state.json', discovery_state); +} + +function gateway_write(data) { + let gateway = gateway_load(); + gateway ??= {}; + let new = {}; + let changed = false; + for (let key in [ 'server', 'port', 'valid', 'hostname_validate' ]) { + if (exists(data, key)) + new[key] = data[key]; + else if (exists(gateway, key)) + new[key] = gateway[key]; + if (new[key] != gateway[key]) + changed = true; + } + if (changed) { + fs.writefile('/etc/ucentral/gateway.json', new); + system('sync'); + } + return changed; +} + +function gateway_available() { + let gateway = gateway_load(); + if (!gateway || !gateway.server || !gateway.port) + return false; + return true; +} + +function set_state(set) { + if (state == set) + return; + let prev = state; + state = set; + + switch(state) { + case DISCOVER: + ulog(LOG_INFO, 'Setting cloud to undiscovered\n'); + fs.unlink('/tmp/cloud.json'); + fs.unlink('/etc/ucentral/gateway.json'); + gateway_write({ valid: false }); + dhcp_restart(); + break; + + case VALIDATING: + ulog(LOG_INFO, 'Wait for validation\n'); + validate_time = time(); + state = VALIDATING; + push(discovery_block_list, discovery_method); + break; + + case ONLINE: + ulog(LOG_INFO, 'Connected to cloud\n'); + if (prev == VALIDATING) { + ulog(LOG_INFO, 'Setting cloud controller to validated\n'); + gateway_write({ valid: true }); + discovery_state_write(); + discovery_block_list = []; + } + break; + + case OFFLINE: + ulog(LOG_INFO, 'Lost connection to cloud\n'); + offline_time = time(); + break; + + case ORPHAN: + ulog(LOG_INFO, 'Device is an orphan\n'); + orphan_time = time(); + break; + } +} + +function discover_dhcp() { + let dhcp = readjsonfile('/tmp/cloud.json'); + if (dhcp?.dhcp_server && dhcp?.dhcp_port) { + if (gateway_write({ server: dhcp.dhcp_server, port:dhcp.dhcp_port, valid: false, hostname_validate: dhcp.no_validation ? 0 : 1 })) { + ulog(LOG_INFO, `Discovered cloud via DHCP ${dhcp.dhcp_server}:${dhcp.dhcp_port}\n`); + client_start(); + set_state(VALIDATING); + } + return true; + } + return !dhcp?.lease; +} + +function redirector_lookup() { + const path = '/tmp/ucentral.redirector'; + ulog(LOG_INFO, 'Contact redirector service\n'); + let serial = uci.get('ucentral', 'config', 'serial'); + + fs.unlink(path); + system(`curl -k --cert /etc/ucentral/operational.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/operational.ca https://${cds_server}/v1/devices/${serial} --output /tmp/ucentral.redirector`); + if (!fs.stat(path)) + return; + let redir = readjsonfile(path); + if (redir?.controller_endpoint) { + let controller_endpoint = split(redir.controller_endpoint, ':'); + if (gateway_write({ server: controller_endpoint[0], port: controller_endpoint[1] || 15002, valid: false, hostname_validate: 1 })) { + ulog(LOG_INFO, `Discovered cloud via lookup service ${controller_endpoint[0]}:${controller_endpoint[1] || 15002}\n`); + client_start(); + set_state(VALIDATING); + } + } else { + ulog(LOG_INFO, 'Failed to discover cloud endpoint\n'); + } +} + +function discover_flash() { + if (!fs.stat('/etc/ucentral/gateway.flash')) + return 1; + ulog(LOG_INFO, 'Using pre-populated cloud information\n'); + fs.writefile('/etc/ucentral/gateway.json', fs.readfile('/etc/ucentral/gateway.flash')); + client_start(); + set_state(VALIDATING); + return 0; +} + +function time_is_valid() { + let valid = !!fs.stat('/tmp/ntp.set'); + if (!valid) + ntp_restart(); + ulog(LOG_INFO, `Time is ${valid ? '': 'not '}valid\n`); + return valid; +} + +function is_discover_method_blacked() { + if (discovery_method in discovery_block_list) + return true; + + return false; +} + +function interval_handler() { + printf(`State ${state}\n`); + switch(state) { + case DISCOVER: + if (timeouts.interval < 60000) + timeouts.interval += 10000; + break; + default: + timeouts.interval = 10000; + break; + } + + printf('setting interval to %d\n', timeouts.interval); + interval.set(timeouts.interval); + + switch(state) { + case ORPHAN: + if (time() - orphan_time <= timeouts.orphan_skew) + break; + orphan_time = time(); + + /* fall through */ + + case DISCOVER: + ulog(LOG_INFO, 'Starting discover\n'); + + if (!time_is_valid()) + return; + + if (system('/usr/bin/est_client enroll')) + return; + + discovery_method = DISCOVER_DHCP; + if (!is_discover_method_blacked() && discover_dhcp()) + return; + + discovery_method = DISCOVER_FLASH; + if (!is_discover_method_blacked() && !discover_flash()) + return; + + discovery_method = DISCOVER_LOOKUP; + redirector_lookup(); + + discovery_block_list = []; + break; + + case VALIDATING: + if (time() - validate_time <= timeouts.validate) + break; + ulog(LOG_INFO, 'validation failed, restarting discovery process\n'); + set_state(DISCOVER); + break; + + case OFFLINE: + if (time() - offline_time <= timeouts.offline_skew) + break; + ulog(LOG_INFO, 'offline for too long, setting device as orphaned\n'); + set_state(ORPHAN); + break; + } +} + +function trigger_reenroll() { + ulog(LOG_INFO, 'triggering reenroll\n'); + + if (system('/usr/bin/est_client reenroll')) { + ulog(LOG_INFO, 'reenroll failed\n'); + return; + } + + ulog(LOG_INFO, 'reenroll succeeded\n'); + ulog(LOG_INFO, 'stopping client\n'); + + system('/etc/init.d/ucentral stop'); + set_state(DISCOVER); +} + +function expiry_handler() { + let stat = fs.stat('/etc/ucentral/operational.ca'); + if (!stat) + return; + + let ret = system(`openssl x509 -checkend ${timeouts.expiry_threshold} -noout -in /certificates/operational.pem`); + if (!ret) { + ulog(LOG_INFO, 'checked certificate expiry - all ok\n'); + return; + } + + ulog(LOG_INFO, 'certificate will expire soon\n'); + trigger_reenroll(); +} + +let ubus_methods = { + discover: { + call: function(req) { + set_state(DISCOVER); + return 0; + }, + args: { + } + }, + renew: { + call: function(req) { + if (state != ONLINE) + return; + + ulog(LOG_INFO, 'Validate cloud due to DHCP renew event\n'); + + let gateway = gateway_load(); + let cloud = readjsonfile('/tmp/cloud.json'); + if (!cloud?.dhcp_server || !cloud?.dhcp_port) + return 0; + + if (cloud.dhcp_server != gateway?.server || cloud.dhcp_port != gateway?.port) + set_state(DISCOVER); + else + ulog(LOG_INFO, 'Cloud has not changed\n'); + }, + args: { + } + }, + online: { + call: function(req) { + set_state(ONLINE); + return 0; + }, + args: { + } + }, + offline: { + call: function(req) { + if (state == ONLINE) + set_state(OFFLINE); + return 0; + }, + args: { + } + }, + reload: { + call: function(req) { + timeouts_load(); + return 0; + }, + args: { + + } + }, + status: { + call: function(req) { + const names = [ 'discover', 'validate', 'online', 'offline', 'orphan' ]; + let ret = { state: names[state] }; + switch(state){ + case OFFLINE: + ret.since = time() - offline_time; + break; + case ORPHAN: + ret.since = time() - orphan_time; + break; + case VALIDATING: + ret.since = time() - validate_time;; + break; + } + return ret; + }, + args: {}, + }, + reenroll: { + call: function(req) { + trigger_reenroll(); + return 0; + }, + args: {}, + }, +}; + +detect_certificate_type(); + +if (gateway_available()) { + let status = ubus.call('ucentral', 'status'); + ulog(LOG_INFO, 'cloud is known\n'); + if (status?.connected) { + state = ONLINE; + } else { + client_start(); + set_state(VALIDATING); + } +} else { + dhcp_restart(); +} + +timeouts_load(); + +interval = uloop.interval(timeouts.interval, interval_handler); +uloop.interval(timeouts.expiry_interval, expiry_handler); + +ubus.publish('cloud', ubus_methods); + +uloop.run(); +uloop.done(); diff --git a/feeds/tip/cloud_discovery/files/usr/bin/est_client b/feeds/tip/cloud_discovery/files/usr/bin/est_client new file mode 100755 index 000000000..b22c06340 --- /dev/null +++ b/feeds/tip/cloud_discovery/files/usr/bin/est_client @@ -0,0 +1,228 @@ +#!/usr/bin/ucode + +'use strict'; + +import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log'; +import * as fs from 'fs'; +import * as libuci from 'uci'; + +let store_operational_pem = false; +let store_operational_ca = false; +let est_server = 'est.certificates.open-lan.org'; +let cert_prefix = 'operational'; + +function set_est_server() { + let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`); + let issuer = pipe.read("all"); + pipe.close(); + + if (match(issuer, /OpenLAN Demo Birth CA/)) { + ulog(LOG_INFO, 'Certificate type is "Demo" \n'); + est_server = 'qaest.certificates.open-lan.org:8001'; + } else if (match(issuer, /OpenLAN Birth Issuing CA/)) { + ulog(LOG_INFO, 'Certificate type is "Production"\n'); + } else { + ulog(LOG_INFO, 'Certificate type is "TIP"\n'); + } +} + +if (getenv('EST_SERVER')) + est_server = getenv('EST_SERVER'); + +if (getenv('CERT_PREFIX')) + cert_prefix = getenv('CERT_PREFIX'); + +ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "est_client"); + +function generate_csr(cert) { + if (!fs.stat('/tmp/csr.nohdr.p10')) { + let pipe = fs.popen(`openssl x509 -in ${cert} -noout -subject`); + let subject = pipe.read("all"); + pipe.close(); + subject = rtrim(subject); + subject = replace(subject, 'subject=', '/'); + subject = replace(subject, ' = ', '='); + subject = replace(subject, ', ', '/'); + + let ret = system(`openssl req -subj "${subject}" -new -key /etc/ucentral/key.pem -out /tmp/csr.p10`); + if (ret) { + ulog(LOG_INFO, 'Failed to generate CSR\n'); + return 1; + } + + let input = fs.open('/tmp/csr.p10', 'r'); + let output = fs.open('/tmp/csr.nohdr.p10', 'w'); + let line; + while (line = input.read('line')) { + if (substr(line, 0, 4) == '----') + continue; + output.write(line); + } + input.close(); + output.close(); + ulog(LOG_INFO, 'Generated CSR\n'); + } + return 0; +} + +function store_operational_cert(path, target) { + system('mount_certs'); + system(`cp ${path} /certificates/${target}`); + + ulog(LOG_INFO, `Persistently stored ${target}\n`); +} + +function p7_too_pem(src, dst) { + let input = fs.readfile(src); + let output = fs.open('/tmp/convert.p7', 'w'); + output.write('-----BEGIN PKCS #7 SIGNED DATA-----\n'); + output.write(`${input}\n-----END PKCS #7 SIGNED DATA-----`); + output.close(); + + let ret = system(`openssl pkcs7 -outform PEM -print_certs -in /tmp/convert.p7 -out ${dst}`); + if (ret) { + ulog(LOG_INFO, 'Failed to convert P7 to PEM\n'); + return 1; + } + + ulog(LOG_INFO, 'Converted P7 to PEM\n'); + + return 0; +} + +function call_est_server(path, cert, target) { + if (generate_csr(cert)) + return 1; + + set_est_server(); + + let ret = system('curl -m 10 -X POST https://' + est_server + '/.well-known/est/' + path + ' -d @/tmp/csr.nohdr.p10 -H "Content-Type: application/pkcs10" --cert ' + cert + ' --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/operational.nohdr.p7'); + if (ret) { + ulog(LOG_INFO, 'Failed to request operational certificate\n'); + return 1; + } + ulog(LOG_INFO, 'EST succeeded\n'); + + return p7_too_pem('/tmp/operational.nohdr.p7', target); +} + + +function simpleenroll() { + if (fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) { + ulog(LOG_INFO, 'Operational certificate is present\n'); + return 0; + } + + if (call_est_server('simpleenroll', '/etc/ucentral/cert.pem', '/etc/ucentral/' + cert_prefix + '.pem')) + return 1; + + ulog(LOG_INFO, 'Operational cert acquired\n'); + store_operational_pem = true; + return 0; +} + +function simplereenroll() { + if (!fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) { + ulog(LOG_INFO, 'Operational certificate was not found\n'); + return 0; + } + + if (call_est_server('simplereenroll', '/etc/ucentral/' + cert_prefix + '.pem', '/tmp/' + cert_prefix + '.pem')) + return 1; + + ulog(LOG_INFO, 'Operational cert updated\n'); + store_operational_cert('/tmp/' + cert_prefix + '.pem', cert_prefix + '.pem'); + system('cp /tmp/' + cert_prefix + '.pem /etc/ucentral/'); + system('store_certs'); + + return 0; +} + +function load_operational_ca() { + if (fs.stat('/etc/ucentral/' + cert_prefix + '.ca')) { + ulog(LOG_INFO, 'Operational CA is present\n'); + return 0; + } + + set_est_server(); + + let ret = system('curl -m 10 -X GET https://' + est_server + '/.well-known/est/cacerts --cert /etc/ucentral/' + cert_prefix + '.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/' + cert_prefix + '.ca.nohdr.p7'); + if (!ret) + ret = p7_too_pem('/tmp/' + cert_prefix + '.ca.nohdr.p7', '/etc/ucentral/' + cert_prefix + '.ca'); + if (ret) { + ulog(LOG_INFO, 'Failed to load CA\n'); + return 1; + } + system('cat /etc/ucentral/openlan.pem >> /etc/ucentral/' + cert_prefix + '.ca'); + ulog(LOG_INFO, 'Acquired CA\n'); + store_operational_ca = true; + return 0; +} + +function fwtool() { + if (!fs.stat('/etc/ucentral/cert.pem')) + return 0; + + let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`); + let issuer = pipe.read("all"); + pipe.close(); + + if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth/))) + return 0; + + ulog(LOG_INFO, 'The issuer is insta\n'); + + let metadata = fs.readfile('/tmp/sysupgrade.meta'); + if (metadata) + metadata = json(metadata); + if (!metadata) + return 0; + + if (!metadata.est_supported) { + ulog(LOG_INFO, 'The image does not support EST\n'); + return 1; + } + ulog(LOG_INFO, 'The image supports EST\n'); + + return 0; +} + +function check_cert() { + if (!fs.stat('/etc/ucentral/cert.pem')) + return 0; + let pipe = fs.popen("openssl x509 -in /etc/ucentral/cert.pem -noout -subject -nameopt multiline | grep commonName | awk '{ print $3 }'"); + let cn = pipe.read("all"); + pipe.close(); + if (!cn) + return 0; + cn = lc(trim(cn)); + let uci = libuci.cursor(); + let serial = uci.get('ucentral', 'config', 'serial'); + return cn != serial; +} + +switch(ARGV[0]) { +case 'enroll': + let ret = simpleenroll(); + if (!ret) + ret = load_operational_ca(); + if (store_operational_pem) + store_operational_cert('/etc/ucentral/' + cert_prefix + '.pem', cert_prefix + '.pem'); + if (store_operational_ca) + store_operational_cert('/etc/ucentral/' + cert_prefix + '.ca', cert_prefix + '.ca'); + if (store_operational_pem || store_operational_ca) + system('store_certs'); + + exit(ret); + +case 'reenroll': + if (simplereenroll()) + exit(1); + exit(0); + +case 'fwtool': + exit(fwtool()); + +case 'check': + exit(check_cert()); +} diff --git a/feeds/tip/cloud_discovery/files/usr/share/ucentral/cloud_discovery.uc b/feeds/tip/cloud_discovery/files/usr/share/ucentral/cloud_discovery.uc new file mode 100755 index 000000000..79f79da98 --- /dev/null +++ b/feeds/tip/cloud_discovery/files/usr/share/ucentral/cloud_discovery.uc @@ -0,0 +1,45 @@ +#!/usr/bin/ucode + +import * as libubus from 'ubus'; +import * as fs from 'fs'; + +let cmd = ARGV[0]; +let ifname = getenv("interface"); +let opt138 = fs.readfile('/tmp/dhcp-option-138'); +let opt224 = fs.readfile('/tmp/dhcp-option-224'); + +if (cmd != 'bound' && cmd != 'renew') + exit(0); + +/*let file = fs.readfile('/etc/ucentral/gateway.json'); +if (file) + file = json(file); +file ??= {}; +if (file.server && file.port && file.valid) + exit(0); +*/ + +let cloud = { + lease: true, +}; +if (opt138) { + let dhcp = opt138; + dhcp = split(dhcp, ':'); + cloud.dhcp_server = dhcp[0]; + cloud.dhcp_port = dhcp[1] ?? 15002; + cloud.no_validation = true; +} +if (opt224) { + let dhcp = opt224; + dhcp = split(dhcp, ':'); + cloud.dhcp_server = dhcp[0]; + cloud.dhcp_port = dhcp[1] ?? 15002; +} +fs.writefile('/tmp/cloud.json', cloud); + +if ((opt138 || opt224) && cmd == 'renew') { + let ubus = libubus.connect(); + ubus.call('cloud', 'renew'); +} + +exit(0); diff --git a/feeds/tip/firstcontact/Makefile b/feeds/tip/firstcontact/Makefile deleted file mode 100644 index 3717c3f9e..000000000 --- a/feeds/tip/firstcontact/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=firstcontact -PKG_RELEASE:=1 - -PKG_LICENSE:=BSD-3-Clause -PKG_MAINTAINER:=John Crispin - -include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/cmake.mk - -define Package/firstcontact - SECTION:=ucentral - CATEGORY:=uCentral - TITLE:=TIP DigiCert firstcontact - DEPENDS:=+libubox +libcurl +libopenssl +certificates -endef - -define Package/firstcontact/install - $(INSTALL_DIR) $(1)/usr/sbin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/firstcontact $(1)/usr/sbin/ - $(CP) ./files/* $(1) -endef - -$(eval $(call BuildPackage,firstcontact)) diff --git a/feeds/tip/firstcontact/files/etc/init.d/firstcontact b/feeds/tip/firstcontact/files/etc/init.d/firstcontact deleted file mode 100755 index f55cbaff0..000000000 --- a/feeds/tip/firstcontact/files/etc/init.d/firstcontact +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=99 - -USE_PROCD=1 -PROG=/usr/bin/ucode - -start_service() { - [ -f /etc/ucentral/capabilities.json ] || { - mkdir -p /etc/ucentral/ - /usr/share/ucentral/capabilities.uc - } - - /usr/share/ucentral/ucentral.uc /etc/ucentral/ucentral.cfg.0000000001 > /dev/null - - procd_open_instance - procd_set_param command "$PROG" -l uci -l fs /usr/share/ucentral/firstcontact.uc - procd_set_param respawn 1 10 0 - procd_close_instance -} diff --git a/feeds/tip/firstcontact/files/etc/uci-defaults/zzz-firstcontact b/feeds/tip/firstcontact/files/etc/uci-defaults/zzz-firstcontact deleted file mode 100755 index 1bc0e6ddb..000000000 --- a/feeds/tip/firstcontact/files/etc/uci-defaults/zzz-firstcontact +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -[ -f "/etc/ucentral/redirector.json" ] && /etc/init.d/firstcontact disable -[ -f "/etc/ucentral/redirector.json" ] || /etc/init.d/ucentral disable diff --git a/feeds/tip/firstcontact/files/usr/share/ucentral/firstcontact.uc b/feeds/tip/firstcontact/files/usr/share/ucentral/firstcontact.uc deleted file mode 100644 index 7db685583..000000000 --- a/feeds/tip/firstcontact/files/usr/share/ucentral/firstcontact.uc +++ /dev/null @@ -1,70 +0,0 @@ -let devid; -let fd = fs.open("/etc/ucentral/dev-id", "r"); -if (!fd) { - warn("firstcontact: failed to find device id"); - exit(1); -} -devid = fd.read("all"); -fd.close(); - -let config = {}; - -function store_config(path) { - let cursor = uci.cursor(path); - let redir = split(config.Redirector, ":"); - - cursor.load("ucentral"); - cursor.set("ucentral", "config", "server", redir[0]); - cursor.set("ucentral", "config", "port", redir[1] || 15002); - cursor.commit(); -} - -ret = system(sprintf('/usr/sbin/firstcontact -i %s', devid)); -if (ret) { - warn("firstcontact failed to contact redirector, check DHCP option\n"); - let fd = fs.open("/tmp/capwap/dhcp_opt.txt", "r"); - if (!fd) { - warn("No redirector found\n"); - exit(1); - } else { - config.Redirector = fd.read("all"); - fd.close(); - } -} else { - let redirector = { }; - let fd = fs.open("/etc/ucentral/redirector.json", "r"); - if (fd) { - let data = fd.read("all"); - fd.close(); - - try { - redirector = json(data); - } - catch (e) { - warn("firstcontact: Unable to parse JSON data in %s: %s", path, e); - - exit(1); - } - } - - for (let r in redirector.fields) - if (r.name && r.value) - config[r.name] = r.value; - if (!config.Redirector) { - warn("Reply is missing Redirector field\n"); - exit(1); - } - -} - -store_config(); -store_config("/etc/config-shadow/"); - -warn("firstcontact: managed to look up redirector\n"); - -system("/etc/init.d/ucentral enable"); -system("/etc/init.d/firstcontact disable"); - -system("reload_config"); -system("/etc/init.d/ucentral start"); -system("/etc/init.d/firstcontact stop"); diff --git a/feeds/tip/firstcontact/src/CMakeLists.txt b/feeds/tip/firstcontact/src/CMakeLists.txt deleted file mode 100644 index f72c7d89e..000000000 --- a/feeds/tip/firstcontact/src/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 2.6) - -PROJECT(firstcontact C) -INCLUDE(GNUInstallDirs) -ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) - -SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") - -ADD_EXECUTABLE(firstcontact firstcontact.c) -TARGET_LINK_LIBRARIES(firstcontact curl crypto ssl ubox) -INSTALL(TARGETS firstcontact - RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} -) diff --git a/feeds/tip/tip-defaults/files/etc/ucentral/insta.pem b/feeds/tip/tip-defaults/files/etc/ucentral/insta.pem new file mode 100644 index 000000000..1ccf7e427 --- /dev/null +++ b/feeds/tip/tip-defaults/files/etc/ucentral/insta.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgICDnowDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAgMR4wHAYDVQQDExVPcGVuTEFOIERlbW8gQmlydGggQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVWIyySul6Fv4wl1O+DQpaLRa0p+Az5L/jcqTpdVf6w+8tlmeIY9C28uDQoDjewrIkvf3lcfK86nshs02s9ehqZUnEP8+GvKM19x3JbWxeTvWwFirjHir4x897iQ606bAMbrHHtntI9ZyBZyXDGeElGJxJQNX+0d50SFq609cB3yxpBPJ67ag+4Oq0uHgROHjEQMrfwLwlAune0c1fjQDrN14PDNjMZHvvhc/pkAHxR1PP6LOFNV5NuQ58tC5N7R2EqqFbIJ8VZgcagrGRYuAuFFTaV+D7RIt9xGTuWlCyxHI7VkRBJ1mRoEr4GOrP9QFjBD8NzNK+/wnR/fZwhpEnRsgHiI33wKHBDg+l3r8tvRzuB5X6Gl/SfuAeaoCuDHMncTjQg1zGhyEwjQhUe4RY3w+yHAjeeOE6c5spOMDDdaBibkzLmSjXztuLeAdzsUcD3fvGeOvh9vG14TKEmF8puNkqEcc0W8NyUWKFdr9umdJEMbaRSSsMGtp8bDj3Ddh4PhEJrIFeo89+HwXhU6sk+wzE9BULTohahsfwOV/08t1cZ3Q04Oj1KI+4YWu8BJns5gX35rQ8GIbkXQwfvFMwqmbg+ij2o9HWdkSL4bcqW/83Ho+31ce210rVGPK9cav0CjA2Eexgxi45cbgnfoade74Qa5zXboJEBmp7rbo4swIDAQABo4GuMIGrMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBS5xC3inqLQl+vxzn9PsjNzlZ5hYDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vZGVtby5jZXJ0aWZpY2F0ZS5maS9jcmwvT3BlbkxBTkRlbW9Sb290Q0EuY3JsMA0GCSqGSIb3DQEBCwUAA4ICAQA9DJEjsDLqtSFkF0XTWfzbebXA+X8++Qmiukrw0s2LRx798ce0mVITRAFDLf78BeUYF0B+PQ8hgq4fWyFsXRgZVrITd1BszT3LJ2r/6xWQJVpVHzLqKgIlW/PY/uTUz+xqR0Ev6hYrmrjfya0K0XEZZqxkmrTrcECaA3RCFkWQl9ZUlb9BClmdhayO8x1XpJplIYAMKVuoPL9IUQH6HUPFnzlPNQHIK9gcFACtgPVWCJg3IAvSLa41KpRxTDwGFvlrNKtkBlGRYhFGCHWXXZn8fdQHW9vykkkfPOaPR/AVyuRzfAT6wbtVWSy38BurSdqSCuNQPQBfF2vMeUGwNbD/7B4tYrWVtnIbgxRPKvX0o3mZkKry6BJf2m/AKWA16W+i4ojnPRORLTTq9cEZ0WL6NRHCgMrbWaCs/+ADTErqK6cv7GhoOVEiqugvnz93dit2IXg4zdDJ4hF9ZSlicwgZKVvMqnNQ1iiXezIQBehgYcWwXRIfdrRPe88HgkySuDZ2lkKYdc+oTc6e7upRh4Kh2ZSipcRb6ehPan533jnQJyU8A9vFAJiQfZZ4lD3tcsqlsDnlu5YEDYSjcfnkyOH/Mlx5VVTWYGvqNNVKRDw2slSxKwVCobkcF/2dAxP9DqOaGaCnMeOaR7kMaBm5d1fwb+bCl9usQAELjZBv2vAH8g== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFIDCCAwigAwIBAgICDnkwDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAfMR0wGwYDVQQDDBRPcGVuTEFOIERlbW8gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMjExylKdJWoJu9mOHPJ6yZFXKe1lE467G65acpS2FKIWnPVFjNCmATMpkMOIFzEFwyFdbQjzOidtiL+73zlE52lOJpXCfOcxDFqDYDJJ8//J1/gQWsBaKpSvgLiHU/0awkQg+yJYZpj8YZa4NkFe+zTjQScSfOsqPPb3rZ7DOQ2BKAhjVShKmVbtNil0iO0zm8vE8DNkktTNMREp2pzb8MbCAgfOkwlrby6T+rV3TvmjThGdFUb5lWDFxWtlF8W0SUII9qj7p5TdGpryeLsO0nZTBtS4HxZNdvmKOHfgcRHmSZIJigB2NzKLNrXF9JBW0WnUSwZJZAG2C1RTx6lADILPueuusyfR/hZ3koKi4PHnSiTwQghzia9K9QjNHq5z9R9ZoCnhBg1VyU4LKmp862L0sIp2vgnOYunEIi9aCYBaDwo+0FuVjZuXyDIatwVuA7TN5IWPHA6XLdOt1mmkeYy1Ldr4XHjdondhtOyeei1UFXmyyLm2+kmRYfTm91TqYmNzRgbRV2NHO50AmsnBknX4Rv3gishGe0+dV5yFcUwZud0z2rSCkuoai5tKrPT+6Y6NqkT9u9HFifIBXnLwEzVUqHRtW6SuWj2DClVQIXIUZtFnhY4GuTuf6DlzgnXO58oDVCZmCW4ULIpbqGeRsvBHR8Sw5JXP/1+TMUYhE8TAgMBAAGjZjBkMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBQ8yIPHskyN8XOANketH/B2p4QWQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAgEAkHZ5KR8IOrdfMFy+iOvauvZxfQ84LL6TpB2FQKDjneJUdd7c29UJJFNW/0mp4Gc6jKZab6J8Dx/pNnbH0RqFjGjeRGtJ4Sk0G7gf9zw1S7qut5WJDcisM9l/wXC+zy/KSKKPQmbt0grWOtU7+NNPh1YU76hIrInq/u2sVZyKH8SXQ957fbJk6BX6JTKyNEn05AB6rNSrbOWo8sy2MlcJ7bBsrWYI1t6GcWFh4b36bLu7/dKJWpyFNXXIkKJsgMEDpEQae56+fSSDo0KRNtYB82fNZDIQlGK81rGJWNzAahM+3GD1tgk/3ZVugfaJhcBpoHHKNOGqZAvtirLAIDocno7AzqoeIz974Rh2Olsl2/arApYPyyfi8PMYuFe/d4h+Wie8n+jh5n48lZ2Ve4PK+j+QHD6tTZS4f0bGnPL1puMxzQloltuQWgLDeVfEgrc3snLvjOg8aDzWm/es85lP8XcyW54U4t3JmrNUC2C7v+Uafx7cL7eDeunhs+BRhtGV+IUmjub2IrpqZp3zZqn+LVRdYJIy/qHhjS5+ImckXkFojOmeWhfmEmYSuNP8Oa6cGuXp829qnbxLh9Qzi3TfXV883KLse4kL5Zl7gBA/4hz2hVMyGJ8fY+VvzbaTuOXyvKJ+rGZCTcRSeotBLnIevVMiL7SqOEwN0j4Mfbznfq8= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFFTCCAv2gAwIBAgICAxIwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPT3BlbkxBTiBSb290IENBMCAXDTI1MDUxNDA4NDcxMFoYDzIwNTUwNTE0MDg0NzEwWjAaMRgwFgYDVQQDDA9PcGVuTEFOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGibJ04A55kSURTBSKgcBmLnND2I5wws1taKqqU9aaRhB7NtvMHwh2voH9b1brUiulZaZwTN/9kzd4AnXeKQ+0u5tV7Ofk0fzF2MK47n17TS30Yenqc4NuQEKdpKK/pM3VvOEppR/bqtgyLtDmbDnmFOx+zTj/+smTgouwA+Iier0P4s5OohYxn/bjOqwQbHbU79VpGBIWv6/kt55AhH7zvsqqKHkrzTxnsRBv3SBIufrjJr9PIhZBLDrqr56P6KgAi0eoutNt2ToiJbE0WfjU7GI1RSiSN5bGj1zXhjNVzQWs1H9QzRf3c9pl3+haHQZ7FZ1UqiTRewmbNrQ6I9k81au3SttUlb87MyAuDSzatkiq7CjQ8VE1J6te6ZBt2zWpUhHsR/Lg7g3eOw5dL4oZJdK5GgGu/MUajLUXifIqM13Mvg0VTzDhN69VLXLSL0gPcicsQCwJuAza1IC/VqmBGx19fAkyJhOurCXWOgisi0g1+xzPKRphUNwMPUf8vBVOM/Vc6xDIvwVGE3+eWXyhixneFlSpAI03nWWjpwWXihTBoxbfRXO3Y/ilJqrgFN+U4PJcCPA+Wo7ThH0mgX6bOTPcgXMUzT3v3FF6Bx5/PNV3kYrw2yLzribUiS6AGvVGnW4hX2Z6OQvA/aHME8KF+6y6m4pC7FkUjVaRlzWu/wIDAQABo2MwYTAfBgNVHSMEGDAWgBSUaFuoOPk4QLByZP47kj4p1IbCJjAdBgNVHQ4EFgQUlGhbqDj5OECwcmT+O5I+KdSGwiYwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAB+/RUC2X6eVoPsFNMkaXO5Iib/ub0JoWhODQm8j2Mr5dpGXESSpXjfDcqDOLuJbWWoflXBLdr8BsVCBqOA9YgCX0H8Br7dUWmCScixxLW0he592/424EvdwifxcKHZLjv9CKV5Txhqnm2djc5RY/nTH5MYVrIh/If2TNO5ydDP6+vgy9GQ4en04VK7rz+PW17O8l7k9/lOmYptZmHgSDAPj/cT3PlG+McqaI5rMSHeEHlzH+PvgWjtSeEhF4FwFBXroDl4/yb4l2JB8bqAZ3vsOXSkigFcZh5MXPe+zuSSW+G8iLr4xoi0CFsP2DaHEyxgqP4B1FtE9nFPo6cvWbwqTVT7QSzqfH+jPJuQvpFXeRF5UFegNZTFT5/uFFPamihakFslEYxeJey1y+OJdLcP6ef87ruSt8amsq56OAETYpnW4JFowlEh0C+QwLGHGGY6WrOgHY/90hJmPgXBdBVg/IoOhzbvk5A+LqZDvxV2/rLNfClw8Kr3g5e8obcB6dWgMCy2z+us0H79ucnmhzQKsjpxM9T1ncHovAQfiD3jVqfHULY53avh0wIAjosoTGbe8dyx80quHe+16qWan7C9idXeAYYJXbZt5hs6hLw4I8M1LsjTg6vwsqiaHZpsmDyyQLdFjNJldG7aosfS9F+BIpuwijF+1dashL0CPsbIJ +-----END CERTIFICATE----- diff --git a/feeds/tip/tip-defaults/files/etc/ucentral/openlan.pem b/feeds/tip/tip-defaults/files/etc/ucentral/openlan.pem new file mode 100644 index 000000000..f89c90223 --- /dev/null +++ b/feeds/tip/tip-defaults/files/etc/ucentral/openlan.pem @@ -0,0 +1,75 @@ +-----BEGIN CERTIFICATE----- +MIIEcTCCA1mgAwIBAgIUJFhIMlIJHJ7hW4gEzZuLBUaWjNcwDQYJKoZIhvcNAQEL +BQAwbDELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj +dCwgSW5jLjEMMAoGA1UECxMDVElQMSkwJwYDVQQDEyBUZWxlY29tIEluZnJhIFBy +b2plY3QgSXNzdWluZyBDQTAeFw0yMTA0MjUyMDMzNTRaFw0yNjA0MTMyMjM4NDZa +MCMxITAfBgNVBAMTGGNhY2VydHMub25lLmRpZ2ljZXJ0LmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJwKRHdkdEQkp32bNi9TdgN4FNRG0nRppguQ +mdCysJHA6/SuyAXNwKSbENysjFrcBkfYTlALjvIMqSu4d26ix6Mv4HnVxLjDzapV +TZhOhfxIbRQa3HNieNup2vMi8jJvgwLcK/4CwhBJsbEMkB5lbyL8UnCBxzW9GGbM +IvurvDFkUDUpUmiFg47nTpjub79KME6NqK38DxKzlUHvJge1TKFM73kZ3YkfWExQ +yRQPRiU5KxMi/Wkr30FOf/rMTx4XNacOgyTJvzcStGwrlr0iGr8eLC1/XVXoOQz3 +0lyOeUzTB+HPU1Z2JrbPW5PnGxcQ0f7v/3qkWV1B2wuvFcQk+D0CAwEAAaOCAVIw +ggFOMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFIj2Mhdk10e46DeI+aEZKSSK8Hj+ +MB8GA1UdIwQYMBaAFLMbVLjgR6s98ziA5Dzl/QBhbdHoMA4GA1UdDwEB/wQEAwIE +8DAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjCBhgYIKwYBBQUHAQEEejB4MCgGCCsG +AQUFBzABhhxodHRwOi8vb2NzcC5vbmUuZGlnaWNlcnQuY29tMEwGCCsGAQUFBzAC +hkBodHRwOi8vY2FjZXJ0cy5vbmUuZGlnaWNlcnQuY29tL1RlbGVjb21JbmZyYVBy +b2plY3RJc3N1aW5nQ0EuY3J0ME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9jcmwu +b25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQcm9qZWN0SXNzdWluZ0NBLmNy +bDANBgkqhkiG9w0BAQsFAAOCAQEADlFwshNPkeI2Gl6ooIauZL9d+6k+RWa5RTle +JWziYL23XVEBT11+dvp4IB9HwVw5dByl3XAfTd1r4qyncwgXQpc6j2X8e45E8izI +z2S1zhLMe1bA2lOiZz/sdpbonvxIHdiISyQI7q3mWQsvNkpkbjivjxLAJTcGPmOS +gc/95YL+2xqPV45XAnPcl5qkLThtmb57Xst1sLWiSS2fUId6HMVuCgZa5su+aAl9 +iMXv9YfHcvyfwXBaOtoBlItyMGl60uy0E/Fr5uEhEWi53EIqhty6KQckQBB7wdjQ +eiXNI5Ox5cf+TFdesuKPaoEn3WNpFL9PCA3S5nGegJlZQ4N9Eg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL +BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj +dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy +b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx +CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu +Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0 +IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq +qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R +yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU +4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO +ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4 +UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3 +YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny +98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr +BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw +AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ +cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u +ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ +KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX +IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe +XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8 +IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v +DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ +EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL +BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj +dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy +b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx +CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu +Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0 +IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u +AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm +KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO +aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO +t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6 +Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX +720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG +lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM +dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF +PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj +19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG +L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA +5IOM7ItsRmen6u3qu+JXros54e4juQ== +-----END CERTIFICATE----- diff --git a/feeds/ucentral/ieee8021x/Makefile b/feeds/ucentral/ieee8021x/Makefile deleted file mode 100644 index c945d3cdd..000000000 --- a/feeds/ucentral/ieee8021x/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=ieee8021x -PKG_RELEASE:=1 - -PKG_LICENSE:=GPL-2.0 -PKG_MAINTAINER:=John Crispin - -PKG_SOURCE_URL=https://github.com/blogic/ieee8021x.git -PKG_MIRROR_HASH:=ec3311751a17d37e8ba17d69226392a2bc9ccbcfc3dd01371b8a25148dd41c06 -PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2023-08-21 -PKG_SOURCE_VERSION:=560fe3003fa2a5837b351dba78a4d6652142ce90 - -include $(INCLUDE_DIR)/package.mk -include $(INCLUDE_DIR)/cmake.mk - -define Package/ieee8021x - SECTION:=net - CATEGORY:=Network - TITLE:=Wired 802.1x - DEPENDS:=+libubox +libubus +libuci -endef - -define Package/ieee8021x/install - $(INSTALL_DIR) $(1)/usr/sbin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/ieee8021x $(1)/usr/sbin/ - $(CP) ./files/* $(1) -endef - -$(eval $(call BuildPackage,ieee8021x)) diff --git a/feeds/ucentral/ieee8021x/files/etc/config/ieee8021x b/feeds/ucentral/ieee8021x/files/etc/config/ieee8021x deleted file mode 100644 index 73d2213cd..000000000 --- a/feeds/ucentral/ieee8021x/files/etc/config/ieee8021x +++ /dev/null @@ -1,6 +0,0 @@ -#config network -# option network 'lan' -# list ports 'lan1' -# list ports 'lan2' -# list ports 'lan3' -# list ports 'lan4' diff --git a/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x b/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x deleted file mode 100755 index 222952d6c..000000000 --- a/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=80 -USE_PROCD=1 -PROG=/usr/sbin/ieee8021x - -reload_service() { - restart -} - -service_triggers() { - procd_add_reload_trigger ieee8021x -} - -start_service() { - procd_open_instance - procd_set_param command "$PROG" - procd_set_param respawn - procd_close_instance -} diff --git a/feeds/ucentral/ucentral-client/files/etc/init.d/ucentral b/feeds/ucentral/ucentral-client/files/etc/init.d/ucentral index 93e0da093..be69e1b59 100755 --- a/feeds/ucentral/ucentral-client/files/etc/init.d/ucentral +++ b/feeds/ucentral/ucentral-client/files/etc/init.d/ucentral @@ -27,8 +27,6 @@ start_service() { cp /etc/config-shadow/ucentral /etc/config/ config_load 'ucentral' config_get serial 'config' 'serial' - config_get server 'config' 'server' - config_get port 'config' 'port' config_get debug 'config' 'debug' 0 config_get insecure 'config' 'insecure' 0 @@ -37,6 +35,10 @@ start_service() { [ "${selfsigned}" == "true" ] && insecure=1 fi + server=$(cat /etc/ucentral/gateway.json | jsonfilter -e '@["server"]') + port=$(cat /etc/ucentral/gateway.json | jsonfilter -e '@["port"]') + [ -n "$server" -a -n "$port" ] || return 0 + boot_cause=$(cat /tmp/pstore | jsonfilter -e '@["pstore"][-1]'.boot_cause) [ -z $boot_cause ] && boot_cause=coldboot procd_open_instance diff --git a/feeds/ucentral/ucentral-client/patches/0001-use-new-operational-cert-and-ca.patch b/feeds/ucentral/ucentral-client/patches/0001-use-new-operational-cert-and-ca.patch new file mode 100644 index 000000000..cb20e245d --- /dev/null +++ b/feeds/ucentral/ucentral-client/patches/0001-use-new-operational-cert-and-ca.patch @@ -0,0 +1,30 @@ +From 4d01f3ee74cd08cb894f372c8cc185d299f977a7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 13 Jun 2025 06:28:23 +0200 +Subject: [PATCH] use new operational cert and ca + +Signed-off-by: John Crispin +--- + main.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/main.c b/main.c +index 19a61e8..0adc07e 100644 +--- a/main.c ++++ b/main.c +@@ -414,10 +414,10 @@ int main(int argc, char **argv) + memset(&info, 0, sizeof info); + info.port = CONTEXT_PORT_NO_LISTEN; + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; +- info.client_ssl_cert_filepath = UCENTRAL_CONFIG"cert.pem"; ++ info.client_ssl_cert_filepath = UCENTRAL_CONFIG"operational.pem"; + if (!stat(UCENTRAL_CONFIG"key.pem", &st)) + info.client_ssl_private_key_filepath = UCENTRAL_CONFIG"key.pem"; +- info.ssl_ca_filepath = UCENTRAL_CONFIG"cas.pem"; ++ info.ssl_ca_filepath = UCENTRAL_CONFIG"operational.ca"; + info.protocols = protocols; + info.fd_limit_per_thread = 1 + 1 + 1; + info.connect_timeout_secs = 30; +-- +2.34.1 + diff --git a/feeds/ucentral/ucentral-client/patches/0003-send-on-offline-events-to-cloud-discovery-service.patch b/feeds/ucentral/ucentral-client/patches/0003-send-on-offline-events-to-cloud-discovery-service.patch new file mode 100644 index 000000000..0c13b665c --- /dev/null +++ b/feeds/ucentral/ucentral-client/patches/0003-send-on-offline-events-to-cloud-discovery-service.patch @@ -0,0 +1,28 @@ +From 8c45f965c30d1cf11e3a5a625a5e2baf3178697f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 12 Nov 2024 10:35:52 +0100 +Subject: [PATCH] send on/offline events to cloud discovery service + +Signed-off-by: John Crispin +--- + ubus.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/main.c ++++ b/main.c +@@ -111,6 +111,7 @@ sul_connect_attempt(struct lws_sorted_us + { + struct per_vhost_data__minimal *vhd; + ++ system("ubus call cloud offline"); + vhd = lws_container_of(sul, struct per_vhost_data__minimal, sul); + + vhd->i.context = vhd->context; +@@ -215,6 +216,7 @@ callback_broker(struct lws *wsi, enum lw + return 0; + + case LWS_CALLBACK_CLIENT_ESTABLISHED: ++ system("ubus call cloud online"); + ULOG_INFO("connection established\n"); + if (!client.selfsigned) { + if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO, &ci, 0)) diff --git a/feeds/ucentral/ucentral-tools/Makefile b/feeds/ucentral/ucentral-tools/Makefile index 179c58f44..461311947 100644 --- a/feeds/ucentral/ucentral-tools/Makefile +++ b/feeds/ucentral/ucentral-tools/Makefile @@ -3,12 +3,6 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ucentral-tools PKG_RELEASE:=1 -PKG_SOURCE_URL=https://github.com/blogic/ucentral-tools.git -PKG_MIRROR_HASH:=9ae6a0cd431595871c233550427c4043c2ba7ddb3c5d87e46ab74a03b2b5a947 -PKG_SOURCE_PROTO:=git -PKG_SOURCE_DATE:=2021-01-28 -PKG_SOURCE_VERSION:=b013fc636e48d407870a46aaa68a09ed74de8d6f - PKG_MAINTAINER:=John Crispin PKG_LICENSE:=BSD-3-Clause diff --git a/feeds/ucentral/ucentral-tools/src/CMakeLists.txt b/feeds/ucentral/ucentral-tools/src/CMakeLists.txt new file mode 100644 index 000000000..e15bf82da --- /dev/null +++ b/feeds/ucentral/ucentral-tools/src/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(openwifi-tools C) +INCLUDE(GNUInstallDirs) +ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -Wmissing-declarations) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +ADD_EXECUTABLE(firstcontact firstcontact.c) +TARGET_LINK_LIBRARIES(firstcontact curl crypto ssl ubox) +INSTALL(TARGETS firstcontact + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) + +ADD_EXECUTABLE(dhcpdiscover dhcpdiscover.c) +INSTALL(TARGETS dhcpdiscover + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) + +ADD_EXECUTABLE(dnsprobe dnsprobe.c) +TARGET_LINK_LIBRARIES(dnsprobe ubox resolv) +INSTALL(TARGETS dnsprobe + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) + +ADD_EXECUTABLE(radiusprobe radiusprobe.c) +TARGET_LINK_LIBRARIES(radiusprobe radcli) +INSTALL(TARGETS radiusprobe + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) + +ADD_EXECUTABLE(ip-collide ip-collide.c) +TARGET_LINK_LIBRARIES(ip-collide ubox) +INSTALL(TARGETS ip-collide + RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} +) diff --git a/feeds/ucentral/ucentral-tools/src/dhcpdiscover.c b/feeds/ucentral/ucentral-tools/src/dhcpdiscover.c new file mode 100644 index 000000000..2e8c0ebfd --- /dev/null +++ b/feeds/ucentral/ucentral-tools/src/dhcpdiscover.c @@ -0,0 +1,1345 @@ +/****************************************************************************** + * + * Original name: CHECK_DHCP.C + * Current name: DHCPDISCOVER.C + * + * License: GPL + * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) + * Copyright (c) 2006-2013 OpenWRT.org + * + * License Information: + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +const char* COPYRIGHT = " \ +*\n \ +* License: GPL\n \ +* Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n \ +* Copyright (c) 2006-2013 OpenWRT.org \n \ +* ====================================================== \n \ +* Mike Gore 25 Aug 2005 \n \ +* Modified for standalone operation \n \ +* ====================================================== \n \ +* Pau Escrich Jun 2013 \n \ +* Added -b option and ported to OpenWRT \n \ +* ====================================================== \n \ +*\n \ +** License Information:\n \ +*\n \ +* This program is free software; you can redistribute it and/or modify\n \ +* it under the terms of the GNU General Public License as published by\n \ +* the Free Software Foundation; either version 2 of the License, or\n \ +* (at your option) any later version.\n \ +*\n \ +* This program is distributed in the hope that it will be useful,\n \ +* but WITHOUT ANY WARRANTY; without even the implied warranty of\n \ +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n \ +* GNU General Public License for more details.\n \ +*\n \ +* You should have received a copy of the GNU General Public License\n \ +* along with this program; if not, write to the Free Software\n \ +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n \ +*\n \ +* $Id: dhcpdiscover.c,v 2$\n \ +*"; + +const char* progname = "dhcpdiscover"; +const char* revision = "$Revision: 2$"; +const char* copyright = "2006-2013"; +const char* email = "p4u@dabax.net"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) + +#include +#include + +#elif defined(__bsd__) + +#include +#include +#include + +#elif defined(__sun__) || defined(__solaris__) || defined(__hpux__) + +#define INSAP 22 +#define OUTSAP 24 + +#include +#include +#include +#include +#include + +#define bcopy(source, destination, length) memcpy(destination, source, length) + +#define AREA_SZ 5000 /* buffer length in bytes */ +static u_long ctl_area[AREA_SZ]; +static u_long dat_area[AREA_SZ]; +static struct strbuf ctl = { AREA_SZ, 0, (char*)ctl_area }; +static struct strbuf dat = { AREA_SZ, 0, (char*)dat_area }; + +#define GOT_CTRL 1 +#define GOT_DATA 2 +#define GOT_BOTH 3 +#define GOT_INTR 4 +#define GOT_ERR 128 + +#define u_int8_t uint8_t +#define u_int16_t uint16_t +#define u_int32_t uint32_t + +static int get_msg(int); +static int check_ctrl(int); +static int put_ctrl(int, int, int); +static int put_both(int, int, int, int); +static int dl_open(const char*, int, int*); +static int dl_bind(int, int, u_char*); +long mac_addr_dlpi(const char*, int, u_char*); + +#endif + +#define HAVE_GETOPT_H + +#define usage printf + +/**** Common definitions ****/ + +#define STATE_OK 0 +#define STATE_WARNING 1 +#define STATE_CRITICAL 2 +#define STATE_UNKNOWN -1 + +#define OK 0 +#define ERROR -1 + +#define FALSE 0 +#define TRUE 1 + +/**** DHCP definitions ****/ + +#define MAX_DHCP_CHADDR_LENGTH 16 +#define MAX_DHCP_SNAME_LENGTH 64 +#define MAX_DHCP_FILE_LENGTH 128 +#define MAX_DHCP_OPTIONS_LENGTH 312 + +typedef struct dhcp_packet_struct { + u_int8_t op; /* packet type */ + u_int8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ + u_int8_t hlen; /* length of hardware address (of this machine) */ + u_int8_t hops; /* hops */ + u_int32_t xid; /* random transaction id number - chosen by this machine */ + u_int16_t secs; /* seconds used in timing */ + u_int16_t flags; /* flags */ + struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ + struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ + struct in_addr siaddr; /* IP address of DHCP server */ + struct in_addr giaddr; /* IP address of DHCP relay */ + unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ + char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ + char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless + booting?) */ + char options[MAX_DHCP_OPTIONS_LENGTH]; /* options */ +} dhcp_packet; + +typedef struct dhcp_offer_struct { + struct in_addr server_address; /* address of DHCP server that sent this offer */ + struct in_addr offered_address; /* the IP address that was offered to us */ + u_int32_t lease_time; /* lease time in seconds */ + u_int32_t renewal_time; /* renewal time in seconds */ + u_int32_t rebinding_time; /* rebinding time in seconds */ + struct dhcp_offer_struct* next; +} dhcp_offer; + +typedef struct requested_server_struct { + struct in_addr server_address; + struct requested_server_struct* next; +} requested_server; + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNACK 6 +#define DHCPRELEASE 7 + +#define DHCP_OPTION_MESSAGE_TYPE 53 +#define DHCP_OPTION_HOST_NAME 12 +#define DHCP_OPTION_BROADCAST_ADDRESS 28 +#define DHCP_OPTION_REQUESTED_ADDRESS 50 +#define DHCP_OPTION_LEASE_TIME 51 +#define DHCP_OPTION_RENEWAL_TIME 58 +#define DHCP_OPTION_REBINDING_TIME 59 + +#define DHCP_INFINITE_TIME 0xFFFFFFFF + +#define DHCP_BROADCAST_FLAG 32768 + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ +#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ + +unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = ""; +unsigned int my_client_mac[MAX_DHCP_CHADDR_LENGTH]; +int mymac = 0; + +struct in_addr banned_ip; +int banned = 0; +char baddr[16]; + +char network_interface_name[16] = "eth0"; + +u_int32_t packet_xid = 0; + +u_int32_t dhcp_lease_time = 0; +u_int32_t dhcp_renewal_time = 0; +u_int32_t dhcp_rebinding_time = 0; + +int dhcpoffer_timeout = 2; + +dhcp_offer* dhcp_offer_list = NULL; +requested_server* requested_server_list = NULL; + +int valid_responses = 0; /* number of valid DHCPOFFERs we received */ +int requested_servers = 0; +int requested_responses = 0; + +int request_specific_address = FALSE; +int received_requested_address = FALSE; +int verbose = 0; +int prometheus = 0; +struct in_addr requested_address; + +static void print_revision(const char* progname, const char* revision) { + fprintf(stderr, "Program: %s %s\n", progname, revision); +} + +int process_arguments(int, char**); +int call_getopt(int, char**); +int validate_arguments(void); +void print_usage(void); +void print_help(void); + +int get_hardware_address(int, char*); + +int send_dhcp_discover(int); +int get_dhcp_offer(int); + +int get_results(void); + +int add_dhcp_offer(struct in_addr, dhcp_packet*); +int free_dhcp_offer_list(void); +int free_requested_server_list(void); + +int create_dhcp_socket(void); +int close_dhcp_socket(int); +int send_dhcp_packet(void*, int, int, struct sockaddr_in*); +int receive_dhcp_packet(void*, int, int, int, struct sockaddr_in*); + +int main(int argc, char** argv) +{ + int dhcp_socket; + int result = STATE_UNKNOWN; + + setlocale(LC_ALL, ""); + // bindtextdomain (PACKAGE, LOCALEDIR); + // textdomain (PACKAGE); + + if (process_arguments(argc, argv) != OK) { + printf("Could not parse arguments"); + } + + // Set banned addr in string format in global var baddr + if (banned) { + inet_ntop(AF_INET, &(banned_ip), baddr, INET_ADDRSTRLEN); + if (verbose) + fprintf(stderr, "Banned addr:%s\n", baddr); + } + + /* create socket for DHCP communications */ + dhcp_socket = create_dhcp_socket(); + + /* get hardware address of client machine */ + get_hardware_address(dhcp_socket, network_interface_name); + + /* send DHCPDISCOVER packet */ + send_dhcp_discover(dhcp_socket); + + /* wait for a DHCPOFFER packet */ + get_dhcp_offer(dhcp_socket); + + /* close socket we created */ + close_dhcp_socket(dhcp_socket); + + /* determine state/plugin output to return */ + if (!prometheus) { + result = get_results(); + } + + /* free allocated memory */ + free_dhcp_offer_list(); + free_requested_server_list(); + + return result; +} + +/* determines hardware address on client machine */ +int get_hardware_address(int sock, char* interface_name) +{ + int i; + +#if defined(__linux__) + struct ifreq ifr; + + strncpy((char*)&ifr.ifr_name, interface_name, sizeof(ifr.ifr_name)); + + // Added code to try to set local MAC address just to be through + // If this fails the test will still work since + // we do encode the MAC as part of the DHCP frame - tests show it works + if (mymac) { + int i; + for (i = 0; i < MAX_DHCP_CHADDR_LENGTH; ++i) + client_hardware_address[i] = my_client_mac[i]; + memcpy(&ifr.ifr_hwaddr.sa_data, &client_hardware_address[0], 6); + if (ioctl(sock, SIOCSIFHWADDR, &ifr) < 0) { + fprintf(stderr, "Could not set hardware address of interface '%s'\n", interface_name); + // perror("Error"); + // exit(STATE_UNKNOWN); + } + } else { + /* try and grab hardware address of requested interface */ + if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { + fprintf(stderr, "Could not get hardware address of interface '%s'\n", interface_name); + exit(STATE_UNKNOWN); + } + memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); + } + +#elif defined(__bsd__) + /* King 2004 see ACKNOWLEDGEMENTS */ + + int mib[6], len; + char* buf; + unsigned char* ptr; + struct if_msghdr* ifm; + struct sockaddr_dl* sdl; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_LINK; + mib[4] = NET_RT_IFLIST; + + if ((mib[5] = if_nametoindex(interface_name)) == 0) { + fprintf(stderr, "if_nametoindex error - %s.\n", strerror(errno); + exit(STATE_UNKNOWN); + } + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + fprintf(stderr, "Couldn't get hardware address from %s. sysctl 1 error - %s.\n", interface_name, strerror(errno); + exit(STATE_UNKNOWN); + } + + if ((buf = malloc(len)) == NULL) { + fprintf(stderr, "Couldn't get hardware address from interface %s. malloc error - %s.\n", interface_name, strerror(errno); + exit(4); + } + + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + fprintf(stderr, "Couldn't get hardware address from %s. sysctl 2 error - %s.\n", interface_name, strerror(errno); + exit(STATE_UNKNOWN); + } + + ifm = (struct if_msghdr*)buf; + sdl = (struct sockaddr_dl*)(ifm + 1); + ptr = (unsigned char*)LLADDR(sdl); + memcpy(&client_hardware_address[0], ptr, 6); + /* King 2004 */ + +#elif defined(__sun__) || defined(__solaris__) + + /* Kompf 2000-2003 see ACKNOWLEDGEMENTS */ + long stat; + char dev[20] = "/dev/"; + char* p; + int unit; + + for (p = interface_name; *p && isalpha(*p); p++) + /* no-op */; + if (p != '\0') { + unit = atoi(p); + *p = '\0'; + strncat(dev, interface_name, 6); + } else { + fprintf(stderr, "can't find unit number in interface_name (%s) - expecting " + "TypeNumber eg lnc0.\n", interface_name); + exit(STATE_UNKNOWN); + } + stat = mac_addr_dlpi(dev, unit, client_hardware_address); + if (stat != 0) { + fprintf(stderr, "can't read MAC address from DLPI streams interface for " + "device %s unit %d.\n", dev, unit); + exit(STATE_UNKNOWN); + } + +#elif defined(__hpux__) + + long stat; + char dev[20] = "/dev/dlpi"; + int unit = 0; + + stat = mac_addr_dlpi(dev, unit, client_hardware_address); + if (stat != 0) { + fprintf(stderr, "can't read MAC address from DLPI streams interface for " + "device %s unit %d.\n", dev, unit); + exit(STATE_UNKNOWN); + } + /* Kompf 2000-2003 */ + +#else + fprintf(stderr, "can't get MAC address for this architecture.\n"); + exit(STATE_UNKNOWN); +#endif + + if (verbose) { + fprintf(stderr, "Hardware address: "); + for (i = 0; i < 6; ++i) + fprintf(stderr, "%2.2x", client_hardware_address[i]); + fprintf(stderr, "\n"); + } + + return OK; +} + +/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ +int send_dhcp_discover(int sock) +{ + dhcp_packet discover_packet; + struct sockaddr_in sockaddr_broadcast; + + /* clear the packet data structure */ + bzero(&discover_packet, sizeof(discover_packet)); + + /* boot request flag (backward compatible with BOOTP servers) */ + discover_packet.op = BOOTREQUEST; + + /* hardware address type */ + discover_packet.htype = ETHERNET_HARDWARE_ADDRESS; + + /* length of our hardware address */ + discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; + + discover_packet.hops = 0; + + /* transaction id is supposed to be random */ + srand(time(NULL)); + packet_xid = random(); + discover_packet.xid = htonl(packet_xid); + + /**** WHAT THE HECK IS UP WITH THIS?!? IF I DON'T MAKE THIS CALL, ONLY ONE + * SERVER RESPONSE IS PROCESSED!!!! ****/ + /* downright bizzarre... */ + ntohl(discover_packet.xid); + + /*discover_packet.secs=htons(65535);*/ + discover_packet.secs = 0xFF; + + /* tell server it should broadcast its response */ + discover_packet.flags = htons(DHCP_BROADCAST_FLAG); + + /* our hardware address */ + memcpy(discover_packet.chaddr, client_hardware_address, ETHERNET_HARDWARE_ADDRESS_LENGTH); + + /* first four bytes of options field is magic cookie (as per RFC 2132) */ + discover_packet.options[0] = '\x63'; + discover_packet.options[1] = '\x82'; + discover_packet.options[2] = '\x53'; + discover_packet.options[3] = '\x63'; + + /* DHCP message type is embedded in options field */ + discover_packet.options[4] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ + discover_packet.options[5] = '\x01'; /* DHCP message option length in bytes */ + discover_packet.options[6] = DHCPDISCOVER; + + /* the IP address we're requesting */ + if (request_specific_address == TRUE) { + discover_packet.options[7] = DHCP_OPTION_REQUESTED_ADDRESS; + discover_packet.options[8] = '\x04'; + memcpy(&discover_packet.options[9], &requested_address, sizeof(requested_address)); + } + + /* send the DHCPDISCOVER packet to broadcast address */ + sockaddr_broadcast.sin_family = AF_INET; + sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); + sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST; + bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); + + if (verbose) { + fprintf(stderr, "DHCPDISCOVER to %s port %d\n", + inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port)); + fprintf(stderr, "DHCPDISCOVER XID: %lu (0x%X)\n", + (unsigned long)ntohl(discover_packet.xid), + ntohl(discover_packet.xid)); + fprintf(stderr, "DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); + fprintf(stderr, "DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); + fprintf(stderr, "DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); + fprintf(stderr, "DHCDISCOVER giaddr: %s\n", inet_ntoa(discover_packet.giaddr)); + } + + /* send the DHCPDISCOVER packet out */ + send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); + + if (verbose) + fprintf(stderr, "\n\n"); + + return OK; +} + +/* waits for a DHCPOFFER message from one or more DHCP servers */ +int get_dhcp_offer(int sock) +{ + dhcp_packet offer_packet; + struct sockaddr_in source; + int result = OK; + int responses = 0; + int x; + time_t start_time; + time_t current_time; + + time(&start_time); + + /* receive as many responses as we can */ + for (responses = 0, valid_responses = 0;;) { + time(¤t_time); + if ((current_time - start_time) >= dhcpoffer_timeout) + break; + + if (verbose) + fprintf(stderr, "\n\n"); + + bzero(&source, sizeof(source)); + bzero(&offer_packet, sizeof(offer_packet)); + + result = OK; + result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, + dhcpoffer_timeout, &source); + + // checks if the source address is the banned one + char saddr[16]; + inet_ntop(AF_INET, &(source.sin_addr), saddr, INET_ADDRSTRLEN); + + if (banned && strcmp(saddr, baddr) == 0) { + fprintf(stderr, "DHCP offer comming from the banned addr %s, ignoring it\n", + baddr); + result = 1; + } + + if (result != OK) { + if (verbose) + fprintf(stderr, "Result=ERROR\n"); + continue; + } else { + if (verbose) + fprintf(stderr, "Result=OK\n"); + responses++; + } + + if (verbose) { + fprintf(stderr, "DHCPOFFER from IP address %s\n", inet_ntoa(source.sin_addr)); + fprintf(stderr, "DHCPOFFER XID: %lu (0x%X)\n", + (unsigned long)ntohl(offer_packet.xid), ntohl(offer_packet.xid)); + } + + /* check packet xid to see if its the same as the one we used in the + * discover packet */ + if (ntohl(offer_packet.xid) != packet_xid) { + if (verbose) + fprintf(stderr, "DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - " + "ignoring packet\n", + (unsigned long)ntohl(offer_packet.xid), + (unsigned long)packet_xid); + continue; + } + + /* check hardware address */ + result = OK; + if (verbose) + fprintf(stderr, "DHCPOFFER chaddr: "); + + for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { + if (verbose) + fprintf(stderr, "%02X", (unsigned char)offer_packet.chaddr[x]); + + if (offer_packet.chaddr[x] != client_hardware_address[x]) + result = ERROR; + } + + if (verbose) + fprintf(stderr, "\n"); + + if (result == ERROR) { + if (verbose) + fprintf(stderr, "DHCPOFFER hardware address did not match our own - ignoring " + "packet\n"); + continue; + } + + if (verbose) { + fprintf(stderr, "DHCPOFFER ciaddr: %s\n", inet_ntoa(offer_packet.ciaddr)); + fprintf(stderr, "DHCPOFFER yiaddr: %s\n", inet_ntoa(offer_packet.yiaddr)); + fprintf(stderr, "DHCPOFFER siaddr: %s\n", inet_ntoa(offer_packet.siaddr)); + fprintf(stderr, "DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); + } + + add_dhcp_offer(source.sin_addr, &offer_packet); + + valid_responses++; + } + + if (verbose) { + fprintf(stderr, "Total responses seen on the wire: %d\n", responses); + fprintf(stderr, "Valid responses for this machine: %d\n", valid_responses); + } + + return OK; +} + +/* sends a DHCP packet */ +int send_dhcp_packet(void* buffer, int buffer_size, int sock, struct sockaddr_in* dest) +{ + int result; + + result = sendto(sock, (char*)buffer, buffer_size, 0, (struct sockaddr*)dest, sizeof(*dest)); + + if (verbose) + fprintf(stderr, "send_dhcp_packet result: %d\n", result); + + if (result < 0) + return ERROR; + + return OK; +} + +/* receives a DHCP packet */ +int receive_dhcp_packet(void* buffer, int buffer_size, int sock, int timeout, struct sockaddr_in* address) +{ + struct timeval tv; + fd_set readfds; + int recv_result; + socklen_t address_size; + struct sockaddr_in source_address; + + /* wait for data to arrive (up time timeout) */ + tv.tv_sec = timeout; + tv.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + select(sock + 1, &readfds, NULL, NULL, &tv); + + /* make sure some data has arrived */ + if (!FD_ISSET(sock, &readfds)) { + if (verbose) + fprintf(stderr, "No (more) data received\n"); + return ERROR; + } + else { + /* why do we need to peek first? i don't know, its a hack. without it, the + source address of the first packet received was not being interpreted + correctly. sigh... */ + bzero(&source_address, sizeof(source_address)); + address_size = sizeof(source_address); + recv_result = recvfrom(sock, (char*)buffer, buffer_size, MSG_PEEK, (struct sockaddr*)&source_address, &address_size); + + if (verbose) + fprintf(stderr, "recv_result_1: %d\n", recv_result); + + recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, + (struct sockaddr *)&source_address, &address_size); + if (verbose) + fprintf(stderr, "recv_result_2: %d\n", recv_result); + + if (recv_result == -1) { + if (verbose) { + fprintf(stderr, "recvfrom() failed, "); + fprintf(stderr, "errno: (%d) -> %s\n", errno, strerror(errno)); + } + return ERROR; + } else { + if (verbose) { + fprintf(stderr, "receive_dhcp_packet() result: %d\n", recv_result); + fprintf(stderr, "receive_dhcp_packet() source: %s\n", + inet_ntoa(source_address.sin_addr)); + } + + memcpy(address, &source_address, sizeof(source_address)); + return OK; + } + } + + return OK; +} + +/* creates a socket for DHCP communication */ +int create_dhcp_socket(void) +{ + struct sockaddr_in myname; + struct ifreq interface; + int sock; + int flag = 1; + + /* Set up the address we're going to bind to. */ + bzero(&myname, sizeof(myname)); + myname.sin_family = AF_INET; + myname.sin_port = htons(DHCP_CLIENT_PORT); + myname.sin_addr.s_addr = INADDR_ANY; /* listen on any address */ + bzero(&myname.sin_zero, sizeof(myname.sin_zero)); + + /* create a socket for DHCP communications */ + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) { + fprintf(stderr, "Could not create socket!\n"); + exit(STATE_UNKNOWN); + } + + if (verbose) + fprintf(stderr, "DHCP socket: %d\n", sock); + + /* set the reuse address flag so we don't get errors when restarting */ + flag = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { + fprintf(stderr, "Could not set reuse address option on DHCP socket!\n"); + exit(STATE_UNKNOWN); + } + + /* set the broadcast option - we need this to listen to DHCP broadcast + * messages */ + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&flag, sizeof flag) < 0) { + fprintf(stderr, "Could not set broadcast option on DHCP socket!\n"); + exit(STATE_UNKNOWN); + } + + /* bind socket to interface */ +#if defined(__linux__) + strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ); + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, + sizeof(interface)) < 0) { + fprintf(stderr, "Could not bind socket to interface %s. Check your " + "privileges...\n", + network_interface_name); + perror("foo"); + exit(STATE_UNKNOWN); + } +#else + strncpy(interface.ifr_name, network_interface_name, IFNAMSIZ); +#endif + + /* bind the socket */ + if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { + fprintf(stderr, "Could not bind to DHCP socket (port %d)! Check your " + "privileges...\n", DHCP_CLIENT_PORT); + exit(STATE_UNKNOWN); + } + + return sock; +} + +/* closes DHCP socket */ +int close_dhcp_socket(int sock) +{ + close(sock); + return OK; +} + +/* adds a requested server address to list in memory */ +static int add_requested_server(struct in_addr server_address) +{ + requested_server* new_server; + + new_server = (requested_server*)malloc(sizeof(requested_server)); + if (new_server == NULL) + return ERROR; + + new_server->server_address = server_address; + + new_server->next = requested_server_list; + requested_server_list = new_server; + + requested_servers++; + + if (verbose) + fprintf(stderr, "Requested server address: %s\n", + inet_ntoa(new_server->server_address)); + + return OK; +} + +/* adds a DHCP OFFER to list in memory */ +int add_dhcp_offer(struct in_addr source, dhcp_packet* offer_packet) +{ + dhcp_offer* new_offer; + int x; + int y; + unsigned option_type; + unsigned option_length; + + if (offer_packet == NULL) + return ERROR; + + /* process all DHCP options present in the packet */ + for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH;) { + + /* end of options (0 is really just a pad, but bail out anyway) */ + if ((int)offer_packet->options[x] == -1 || (int)offer_packet->options[x] == 0) + break; + + /* get option type */ + option_type = offer_packet->options[x++]; + + /* get option length */ + option_length = offer_packet->options[x++]; + + if (verbose) + fprintf(stderr, "Option: %d (0x%02X)\n", option_type, option_length); + + /* get option data */ + if (option_type == DHCP_OPTION_LEASE_TIME) { + memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); + dhcp_lease_time = ntohl(dhcp_lease_time); + } + if (option_type == DHCP_OPTION_RENEWAL_TIME) { + memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); + dhcp_renewal_time = ntohl(dhcp_renewal_time); + } + if (option_type == DHCP_OPTION_REBINDING_TIME) { + memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); + dhcp_rebinding_time = ntohl(dhcp_rebinding_time); + } + + /* skip option data we're ignoring */ + else + for (y = 0; y < option_length; y++, x++); + } + + if (verbose) { + if (dhcp_lease_time == DHCP_INFINITE_TIME) + fprintf(stderr, "Lease Time: Infinite\n"); + else + fprintf(stderr, "Lease Time: %lu seconds\n", (unsigned long)dhcp_lease_time); + if (dhcp_renewal_time == DHCP_INFINITE_TIME) + fprintf(stderr, "Renewal Time: Infinite\n"); + else + fprintf(stderr, "Renewal Time: %lu seconds\n", (unsigned long)dhcp_renewal_time); + if (dhcp_rebinding_time == DHCP_INFINITE_TIME) + fprintf(stderr, "Rebinding Time: Infinite\n"); + fprintf(stderr, "Rebinding Time: %lu seconds\n", (unsigned long)dhcp_rebinding_time); + } + + new_offer = (dhcp_offer*)malloc(sizeof(dhcp_offer)); + + if (new_offer == NULL) + return ERROR; + + new_offer->server_address = source; + new_offer->offered_address = offer_packet->yiaddr; + new_offer->lease_time = dhcp_lease_time; + new_offer->renewal_time = dhcp_renewal_time; + new_offer->rebinding_time = dhcp_rebinding_time; + + if (verbose) { + fprintf(stderr, "Added offer from server @ %s", + inet_ntoa(new_offer->server_address)); + fprintf(stderr, " of IP address %s\n", inet_ntoa(new_offer->offered_address)); + } + + if (prometheus) { + fprintf(stderr, "dhcpdiscover_offer{ "); + fprintf(stderr, "server=\"%s\",", inet_ntoa(new_offer->server_address)); + fprintf(stderr, "address=\"%s\",", inet_ntoa(new_offer->offered_address)); + fprintf(stderr, "dev=\"%s\" } 1\n", network_interface_name); + } + + /* add new offer to head of list */ + new_offer->next = dhcp_offer_list; + dhcp_offer_list = new_offer; + + return OK; +} + +/* frees memory allocated to DHCP OFFER list */ +int free_dhcp_offer_list(void) +{ + dhcp_offer* this_offer; + dhcp_offer* next_offer; + + for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) { + next_offer = this_offer->next; + free(this_offer); + } + + return OK; +} + +/* frees memory allocated to requested server list */ +int free_requested_server_list(void) +{ + requested_server* this_server; + requested_server* next_server; + + for (this_server = requested_server_list; this_server != NULL; this_server = next_server) { + next_server = this_server->next; + free(this_server); + } + + return OK; +} + +/* gets state and plugin output to return */ +int get_results(void) +{ + dhcp_offer* temp_offer; + requested_server* temp_server; + int result; + u_int32_t max_lease_time = 0; + + received_requested_address = FALSE; + + /* checks responses from requested servers */ + requested_responses = 0; + if (requested_servers > 0) { + + for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { + + for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + + /* get max lease time we were offered */ + if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) + max_lease_time = temp_offer->lease_time; + + /* see if we got the address we requested */ + if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) + received_requested_address = TRUE; + + /* see if the servers we wanted a response from talked to us or not */ + if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { + if (verbose) { + fprintf(stderr, "DHCP Server Match: Offerer=%s", + inet_ntoa(temp_offer->server_address)); + fprintf(stderr, " Requested=%s\n", inet_ntoa(temp_server->server_address)); + } + requested_responses++; + } + } + } + } + + /* else check and see if we got our requested address from any server */ + else { + for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + + /* get max lease time we were offered */ + if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) + max_lease_time = temp_offer->lease_time; + + /* see if we got the address we requested */ + if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) + received_requested_address = TRUE; + } + } + + result = STATE_OK; + if (valid_responses == 0) + result = STATE_CRITICAL; + else if (requested_servers > 0 && requested_responses == 0) + result = STATE_CRITICAL; + else if (requested_responses < requested_servers) + result = STATE_WARNING; + else if (request_specific_address == TRUE && received_requested_address == FALSE) + result = STATE_WARNING; + + fprintf(stderr, "DHCP %s: ", (result == STATE_OK) ? "ok" : "problem"); + + /* we didn't receive any DHCPOFFERs */ + if (dhcp_offer_list == NULL) { + fprintf(stderr, "No DHCPOFFERs were received.\n"); + return result; + } + + fprintf(stderr, "Received %d DHCPOFFER(s)", valid_responses); + + if (requested_servers > 0) + fprintf(stderr, ", %s%d of %d requested servers responded", + ((requested_responses < requested_servers) && requested_responses > 0) + ? "only " : "", + requested_responses, requested_servers); + + if (request_specific_address == TRUE) + fprintf(stderr, ", requested address (%s) was %soffered", + inet_ntoa(requested_address), + (received_requested_address == TRUE) ? "" : "not "); + + fprintf(stderr, ", max lease time = "); + if (max_lease_time == DHCP_INFINITE_TIME) + fprintf(stderr, "Infinity"); + else + fprintf(stderr, "%lu sec", (unsigned long)max_lease_time); + + fprintf(stderr, ".\n"); + + return result; +} + +/* process command-line arguments */ +int process_arguments(int argc, char** argv) +{ + int c; + + if (argc < 1) + return ERROR; + + c = 0; + while ((c += (call_getopt(argc - c, &argv[c]))) < argc) { + /* + if(is_option(argv[c])) + continue; + */ + } + + return validate_arguments(); +} + +int call_getopt(int argc, char** argv) +{ + int c = 0; + int i = 0; + struct in_addr ipaddress; + +#ifdef HAVE_GETOPT_H + int option_index = 0; + int ret; + static struct option long_options[] = { { "serverip", required_argument, 0, 's' }, { "requestedip", required_argument, 0, 'r' }, { "timeout", required_argument, 0, 't' }, { "interface", required_argument, 0, 'i' }, { "mac", required_argument, 0, 'm' }, { "bannedip", required_argument, 0, 'b' }, { "verbose", no_argument, 0, 'v' }, { "prometheus", no_argument, 0, 'p' }, { "version", no_argument, 0, 'V' }, { "help", no_argument, 0, 'h' }, { 0, 0, 0, 0 } }; +#endif + + while (1) { +#ifdef HAVE_GETOPT_H + c = getopt_long(argc, argv, "+hVvpt:s:r:t:i:m:b:", long_options, &option_index); +#else + c = getopt(argc, argv, "+?hVvpt:s:r:t:i:m:b:"); +#endif + + i++; + + if (c == -1 || c == EOF || c == 1) + break; + + switch (c) { + case 'w': + case 'r': + case 't': + case 'i': + i++; + break; + default: + break; + } + + switch (c) { + + case 'm': /* Our MAC address */ + { + ret = sscanf(optarg, "%x:%x:%x:%x:%x:%x", my_client_mac + 0, my_client_mac + 1, my_client_mac + 2, my_client_mac + 3, my_client_mac + 4, my_client_mac + 5); + if (ret != 6) { + usage("Invalid MAC address\n"); + break; + } + for (i = 0; i < 6; ++i) + client_hardware_address[i] = my_client_mac[i]; + } + mymac = 1; + break; + case 'b': /* Banned IP */ + if (inet_aton(optarg, &ipaddress)) { + banned_ip = ipaddress; + banned = 1; + } else + fprintf(stderr, "Banned IP not specified, ignoring -b"); + break; + + case 's': /* DHCP server address */ + if (inet_aton(optarg, &ipaddress)) + add_requested_server(ipaddress); + /* + else + usage("Invalid server IP address\n"); + */ + break; + + case 'r': /* address we are requested from DHCP servers */ + if (inet_aton(optarg, &ipaddress)) { + requested_address = ipaddress; + request_specific_address = TRUE; + } + /* + else + usage("Invalid requested IP address\n"); + */ + break; + + case 't': /* timeout */ + /* + if(is_intnonneg(optarg)) + */ + if (atoi(optarg) > 0) + dhcpoffer_timeout = atoi(optarg); + /* + else + usage("Time interval must be a nonnegative integer\n"); + */ + break; + + case 'i': /* interface name */ + strncpy(network_interface_name, optarg, + sizeof(network_interface_name) - 1); + network_interface_name[sizeof(network_interface_name) - 1] = '\x0'; + break; + + case 'V': /* version */ + print_revision(progname, revision); + exit(STATE_OK); + + case 'h': /* help */ + print_help(); + exit(STATE_OK); + + case 'v': /* verbose */ + verbose = 1; + break; + + case 'p': /* prometheus */ + prometheus = 1; + break; + + default: /* help */ + fprintf(stderr, "Unknown argument: %s", optarg); + break; + } + } + + return i; +} + +int validate_arguments(void) { return OK; } + +#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) + +/* Kompf 2000-2003 see ACKNOWLEDGEMENTS */ + +/* get a message from a stream; return type of message */ +static int get_msg(int fd) +{ + int flags = 0; + int res, ret; + ctl_area[0] = 0; + dat_area[0] = 0; + ret = 0; + res = getmsg(fd, &ctl, &dat, &flags); + + if (res < 0) { + if (errno == EINTR) { + return (GOT_INTR); + } else { + fprintf(stderr, "%s\n", "get_msg FAILED."); + return (GOT_ERR); + } + } + if (ctl.len > 0) { + ret |= GOT_CTRL; + } + if (dat.len > 0) { + ret |= GOT_DATA; + } + return (ret); +} + +/* verify that dl_primitive in ctl_area = prim */ +static int check_ctrl(int prim) +{ + dl_error_ack_t* err_ack = (dl_error_ack_t*)ctl_area; + if (err_ack->dl_primitive != prim) { + fprintf(stderr, "DLPI stream API failed to get MAC in check_ctrl: %s.\n", strerror(errno); + exit(STATE_UNKNOWN); + } + return 0; +} + +/* put a control message on a stream */ +static int put_ctrl(int fd, int len, int pri) +{ + ctl.len = len; + if (putmsg(fd, &ctl, 0, pri) < 0) { + fprintf(stderr, "DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n", strerror(errno); + exit(STATE_UNKNOWN); + } + return 0; +} + +/* put a control + data message on a stream */ +static int put_both(int fd, int clen, int dlen, int pri) +{ + ctl.len = clen; + dat.len = dlen; + if (putmsg(fd, &ctl, &dat, pri) < 0) { + fprintf(stderr, "DLPI stream API failed to get MAC in put_both/putmsg().\n", strerror(errno); + exit(STATE_UNKNOWN); + } + return 0; +} + +/* open file descriptor and attach */ +static int dl_open(const char* dev, int unit, int* fd) +{ + dl_attach_req_t* attach_req = (dl_attach_req_t*)ctl_area; + if ((*fd = open(dev, O_RDWR)) == -1) { + fprintf(stderr, "DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n", dev, strerror(errno); + exit(STATE_UNKNOWN); + } + attach_req->dl_primitive = DL_ATTACH_REQ; + attach_req->dl_ppa = unit; + put_ctrl(*fd, sizeof(dl_attach_req_t), 0); + get_msg(*fd); + return check_ctrl(DL_OK_ACK); +} + +/* send DL_BIND_REQ */ +static int dl_bind(int fd, int sap, u_char* addr) +{ + dl_bind_req_t* bind_req = (dl_bind_req_t*)ctl_area; + dl_bind_ack_t* bind_ack = (dl_bind_ack_t*)ctl_area; + bind_req->dl_primitive = DL_BIND_REQ; + bind_req->dl_sap = sap; + bind_req->dl_max_conind = 1; + bind_req->dl_service_mode = DL_CLDLS; + bind_req->dl_conn_mgmt = 0; + bind_req->dl_xidtest_flg = 0; + put_ctrl(fd, sizeof(dl_bind_req_t), 0); + get_msg(fd); + if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { + fprintf(stderr, "DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n", strerror(errno); + exit(STATE_UNKNOWN); + } + bcopy((u_char*)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); + return 0; +} + +/*********************************************************************** + * interface: + * function mac_addr_dlpi - get the mac address of the interface with + * type dev (eg lnc, hme) and unit (0, 1 ..) + * + * parameter: addr: an array of six bytes, has to be allocated by the caller + * + * return: 0 if OK, -1 if the address could not be determined + * + * + ***********************************************************************/ + +long mac_addr_dlpi(const char* dev, int unit, u_char* addr) +{ + int fd; + u_char mac_addr[25]; + + if (GOT_ERR != dl_open(dev, unit, &fd)) { + if (GOT_ERR != dl_bind(fd, INSAP, mac_addr)) { + bcopy(mac_addr, addr, 6); + return 0; + } + } + close(fd); + return -1; +} + +/* Kompf 2000-2003 */ + +#endif + +/* print usage help */ +void print_help(void) +{ + + print_revision(progname, revision); + + fprintf(stderr, COPYRIGHT, copyright, email); + + fprintf(stderr, "\n\nThis program checks the existence and more details of a DHCP " + "server\n\n"); + + print_usage(); + + fprintf(stderr, "\ + -s, --serverip=IPADDRESS\n\ + IP address of DHCP server that we must hear from\n\ + -r, --requestedip=IPADDRESS\n\ + IP address that should be offered by at least one DHCP server\n\ + -m, --mac=MACADDRESS\n\ + Client MAC address to use for sending packets\n\ + -b, --bannedip=IPADDRESS\n\ + Server IP address to ignore\n\ + -t, --timeout=INTEGER\n\ + Seconds to wait for DHCPOFFER before timeout occurs\n\ + -i, --interface=STRING\n\ + Interface to to use for listening (i.e. eth0)\n\ + -v, --verbose\n\ + Print extra information (command-line use only)\n\ + -p, --prometheus\n\ + Print extra information in prometheus format\n\ + -h, --help\n\ + Print detailed help screen\n\ + -V, --version\n\ + Print version information\n\n\ +Example: sudo ./dhcpdiscover -i eth0 -b 192.168.1.1\n\ +"); +} + +void print_usage(void) +{ + fprintf(stderr, "\ +Usage: %s [-s serverip] [-r requestedip] [-m clientmac ] [-b bannedip] [-t timeout] [-i interface]\n\ + [-v] [-p]", progname); +} diff --git a/feeds/ucentral/ucentral-tools/src/dnsprobe.c b/feeds/ucentral/ucentral-tools/src/dnsprobe.c new file mode 100644 index 000000000..3b06ecb2a --- /dev/null +++ b/feeds/ucentral/ucentral-tools/src/dnsprobe.c @@ -0,0 +1,690 @@ +/* + * nslookup_lede - musl compatible replacement for busybox nslookup + * + * Copyright (C) 2017 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +//config:config NSLOOKUP_OPENWRT +//config: bool "nslookup_openwrt" +//config: depends on !NSLOOKUP +//config: default y +//config: help +//config: nslookup is a tool to query Internet name servers (LEDE flavor). +//config: +//config:config FEATURE_NSLOOKUP_OPENWRT_LONG_OPTIONS +//config: bool "Enable long options" +//config: default y +//config: depends on NSLOOKUP_OPENWRT && LONG_OPTS +//config: help +//config: Support long options for the nslookup applet. + +//applet:IF_NSLOOKUP_OPENWRT(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_NSLOOKUP_OPENWRT) += nslookup_lede.o + +//usage:#define nslookup_lede_trivial_usage +//usage: "[HOST] [SERVER]" +//usage:#define nslookup_lede_full_usage "\n\n" +//usage: "Query the nameserver for the IP address of the given HOST\n" +//usage: "optionally using a specified DNS server" +//usage: +//usage:#define nslookup_lede_example_usage +//usage: "$ nslookup localhost\n" +//usage: "Server: default\n" +//usage: "Address: default\n" +//usage: "\n" +//usage: "Name: debian\n" +//usage: "Address: 127.0.0.1\n" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ENABLE_FEATURE_IPV6 1 + +typedef struct len_and_sockaddr { + socklen_t len; + union { + struct sockaddr sa; + struct sockaddr_in sin; +#if ENABLE_FEATURE_IPV6 + struct sockaddr_in6 sin6; +#endif + } u; +} len_and_sockaddr; + +struct ns { + const char *name; + len_and_sockaddr addr; + int failures; + int replies; +}; + +struct query { + const char *name; + size_t qlen, rlen; + unsigned char query[512], reply[512]; + unsigned long latency; + int rcode, n_ns; +}; + +static const char *rcodes[] = { + "NOERROR", + "FORMERR", + "SERVFAIL", + "NXDOMAIN", + "NOTIMP", + "REFUSED", + "YXDOMAIN", + "YXRRSET", + "NXRRSET", + "NOTAUTH", + "NOTZONE", + "RESERVED11", + "RESERVED12", + "RESERVED13", + "RESERVED14", + "RESERVED15", + "BADVERS" +}; + +static unsigned int default_port = 53; +static unsigned int default_retry = 1; +static unsigned int default_timeout = 2; + + +static int parse_reply(const unsigned char *msg, size_t len, int *bb_style_counter) +{ + ns_msg handle; + ns_rr rr; + int i, n, rdlen; + const char *format = NULL; + char astr[INET6_ADDRSTRLEN], dname[MAXDNAME]; + const unsigned char *cp; + + if (ns_initparse(msg, len, &handle) != 0) { + //fprintf(stderr, "Unable to parse reply: %s\n", strerror(errno)); + return -1; + } + + for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) { + if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) { + //fprintf(stderr, "Unable to parse resource record: %s\n", strerror(errno)); + return -1; + } + + rdlen = ns_rr_rdlen(rr); + + switch (ns_rr_type(rr)) + { + case ns_t_a: + if (rdlen != 4) { + //fprintf(stderr, "Unexpected A record length\n"); + return -1; + } + inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr)); + printf("Name:\t%s\nAddress: %s\n", ns_rr_name(rr), astr); + break; + +#if ENABLE_FEATURE_IPV6 + case ns_t_aaaa: + if (rdlen != 16) { + //fprintf(stderr, "Unexpected AAAA record length\n"); + return -1; + } + inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr)); + printf("%s\thas AAAA address %s\n", ns_rr_name(rr), astr); + break; +#endif + + case ns_t_ns: + if (!format) + format = "%s\tnameserver = %s\n"; + /* fall through */ + + case ns_t_cname: + if (!format) + format = "%s\tcanonical name = %s\n"; + /* fall through */ + + case ns_t_ptr: + if (!format) + format = "%s\tname = %s\n"; + if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), + ns_rr_rdata(rr), dname, sizeof(dname)) < 0) { + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); + return -1; + } + printf(format, ns_rr_name(rr), dname); + break; + + case ns_t_mx: + if (rdlen < 2) { + fprintf(stderr, "MX record too short\n"); + return -1; + } + n = ns_get16(ns_rr_rdata(rr)); + if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), + ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) { + //fprintf(stderr, "Cannot uncompress MX domain: %s\n", strerror(errno)); + return -1; + } + printf("%s\tmail exchanger = %d %s\n", ns_rr_name(rr), n, dname); + break; + + case ns_t_txt: + if (rdlen < 1) { + //fprintf(stderr, "TXT record too short\n"); + return -1; + } + n = *(unsigned char *)ns_rr_rdata(rr); + if (n > 0) { + memset(dname, 0, sizeof(dname)); + memcpy(dname, ns_rr_rdata(rr) + 1, n); + printf("%s\ttext = \"%s\"\n", ns_rr_name(rr), dname); + } + break; + + case ns_t_soa: + if (rdlen < 20) { + //fprintf(stderr, "SOA record too short\n"); + return -1; + } + + printf("%s\n", ns_rr_name(rr)); + + cp = ns_rr_rdata(rr); + n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), + cp, dname, sizeof(dname)); + + if (n < 0) { + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); + return -1; + } + + printf("\torigin = %s\n", dname); + cp += n; + + n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), + cp, dname, sizeof(dname)); + + if (n < 0) { + //fprintf(stderr, "Unable to uncompress domain: %s\n", strerror(errno)); + return -1; + } + + printf("\tmail addr = %s\n", dname); + cp += n; + + printf("\tserial = %lu\n", ns_get32(cp)); + cp += 4; + + printf("\trefresh = %lu\n", ns_get32(cp)); + cp += 4; + + printf("\tretry = %lu\n", ns_get32(cp)); + cp += 4; + + printf("\texpire = %lu\n", ns_get32(cp)); + cp += 4; + + printf("\tminimum = %lu\n", ns_get32(cp)); + break; + + default: + break; + } + } + + return i; +} + +static int parse_nsaddr(const char *addrstr, len_and_sockaddr *lsa) +{ + char *eptr, *hash, ifname[IFNAMSIZ]; + unsigned int port = default_port; + unsigned int scope = 0; + + hash = strchr(addrstr, '#'); + + if (hash) { + *hash++ = '\0'; + port = strtoul(hash, &eptr, 10); + + if (eptr == hash || *eptr != '\0' || port > 65535) { + errno = EINVAL; + return -1; + } + } + + hash = strchr(addrstr, '%'); + + if (hash) { + for (eptr = ++hash; *eptr != '\0' && *eptr != '#'; eptr++) { + if ((eptr - hash) >= IFNAMSIZ) { + errno = ENODEV; + return -1; + } + + ifname[eptr - hash] = *eptr; + } + + ifname[eptr - hash] = '\0'; + scope = if_nametoindex(ifname); + + if (scope == 0) { + errno = ENODEV; + return -1; + } + } + +#if ENABLE_FEATURE_IPV6 + if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) { + lsa->u.sin6.sin6_family = AF_INET6; + lsa->u.sin6.sin6_port = htons(port); + lsa->u.sin6.sin6_scope_id = scope; + lsa->len = sizeof(lsa->u.sin6); + return 0; + } +#endif + + if (!scope && inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) { + lsa->u.sin.sin_family = AF_INET; + lsa->u.sin.sin_port = htons(port); + lsa->len = sizeof(lsa->u.sin); + return 0; + } + + errno = EINVAL; + return -1; +} + +static unsigned long mtime(void) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (unsigned long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +#if ENABLE_FEATURE_IPV6 +static void to_v4_mapped(len_and_sockaddr *a) +{ + if (a->u.sa.sa_family != AF_INET) + return; + + memcpy(a->u.sin6.sin6_addr.s6_addr + 12, + &a->u.sin.sin_addr, 4); + + memcpy(a->u.sin6.sin6_addr.s6_addr, + "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); + + a->u.sin6.sin6_family = AF_INET6; + a->u.sin6.sin6_flowinfo = 0; + a->u.sin6.sin6_scope_id = 0; + a->len = sizeof(a->u.sin6); +} +#endif + + +/* + * Function logic borrowed & modified from musl libc, res_msend.c + */ + +static int send_queries(struct ns *ns, int n_ns, struct query *queries, int n_queries) +{ + int fd; + int timeout = default_timeout * 1000, retry_interval, servfail_retry = 0; + len_and_sockaddr from = { }; +#if ENABLE_FEATURE_IPV6 + int one = 1; +#endif + int recvlen = 0; + int n_replies = 0; + struct pollfd pfd; + unsigned long t0, t1, t2; + int nn, qn, next_query = 0; + + from.u.sa.sa_family = AF_INET; + from.len = sizeof(from.u.sin); + +#if ENABLE_FEATURE_IPV6 + for (nn = 0; nn < n_ns; nn++) { + if (ns[nn].addr.u.sa.sa_family == AF_INET6) { + from.u.sa.sa_family = AF_INET6; + from.len = sizeof(from.u.sin6); + break; + } + } +#endif + + /* Get local address and open/bind a socket */ + fd = socket(from.u.sa.sa_family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + +#if ENABLE_FEATURE_IPV6 + /* Handle case where system lacks IPv6 support */ + if (fd < 0 && from.u.sa.sa_family == AF_INET6 && errno == EAFNOSUPPORT) { + fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + from.u.sa.sa_family = AF_INET; + } +#endif + + if (fd < 0) + return -1; + + if (bind(fd, &from.u.sa, from.len) < 0) { + close(fd); + return -1; + } + +#if ENABLE_FEATURE_IPV6 + /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ + if (from.u.sa.sa_family == AF_INET6) { + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + + for (nn = 0; nn < n_ns; nn++) + to_v4_mapped(&ns[nn].addr); + } +#endif + + pfd.fd = fd; + pfd.events = POLLIN; + retry_interval = timeout / default_retry; + t0 = t2 = mtime(); + t1 = t2 - retry_interval; + + for (; t2 - t0 < timeout; t2 = mtime()) { + if (t2 - t1 >= retry_interval) { + for (qn = 0; qn < n_queries; qn++) { + if (queries[qn].rlen) + continue; + + for (nn = 0; nn < n_ns; nn++) { + sendto(fd, queries[qn].query, queries[qn].qlen, + MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); + } + } + + t1 = t2; + servfail_retry = 2 * n_queries; + } + + /* Wait for a response, or until time to retry */ + if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) + continue; + + while (1) { + recvlen = recvfrom(fd, queries[next_query].reply, + sizeof(queries[next_query].reply), 0, + &from.u.sa, &from.len); + + /* read error */ + if (recvlen < 0) + break; + + /* Ignore non-identifiable packets */ + if (recvlen < 4) + continue; + + /* Ignore replies from addresses we didn't send to */ + for (nn = 0; nn < n_ns; nn++) + if (memcmp(&from.u.sa, &ns[nn].addr.u.sa, from.len) == 0) + break; + + if (nn >= n_ns) + continue; + + /* Find which query this answer goes with, if any */ + for (qn = next_query; qn < n_queries; qn++) + if (!memcmp(queries[next_query].reply, queries[qn].query, 2)) + break; + + if (qn >= n_queries || queries[qn].rlen) + continue; + + queries[qn].rcode = queries[next_query].reply[3] & 15; + queries[qn].latency = mtime() - t0; + queries[qn].n_ns = nn; + + ns[nn].replies++; + + /* Only accept positive or negative responses; + * retry immediately on server failure, and ignore + * all other codes such as refusal. */ + switch (queries[qn].rcode) { + case 0: + case 3: + break; + + case 2: + if (servfail_retry && servfail_retry--) { + ns[nn].failures++; + sendto(fd, queries[qn].query, queries[qn].qlen, + MSG_NOSIGNAL, &ns[nn].addr.u.sa, ns[nn].addr.len); + } + /* fall through */ + + default: + continue; + } + + /* Store answer */ + n_replies++; + + queries[qn].rlen = recvlen; + + if (qn == next_query) { + while (next_query < n_queries) { + if (!queries[next_query].rlen) + break; + + next_query++; + } + } + else { + memcpy(queries[qn].reply, queries[next_query].reply, recvlen); + } + + if (next_query >= n_queries) + return n_replies; + } + } + + return n_replies; +} + +static struct ns *add_ns(struct ns **ns, int *n_ns, const char *addr) +{ + char portstr[sizeof("65535")], *p; + len_and_sockaddr a = { }; + struct ns *tmp; + struct addrinfo *ai, *aip, hints = { + .ai_flags = AI_NUMERICSERV, + .ai_socktype = SOCK_DGRAM + }; + + if (parse_nsaddr(addr, &a)) { + /* Maybe we got a domain name, attempt to resolve it using the standard + * resolver routines */ + + p = strchr(addr, '#'); + snprintf(portstr, sizeof(portstr), "%hu", + (unsigned short)(p ? strtoul(p, NULL, 10) : default_port)); + + if (!getaddrinfo(addr, portstr, &hints, &ai)) { + for (aip = ai; aip; aip = aip->ai_next) { + if (aip->ai_addr->sa_family != AF_INET && + aip->ai_addr->sa_family != AF_INET6) + continue; + +#if ! ENABLE_FEATURE_IPV6 + if (aip->ai_addr->sa_family != AF_INET) + continue; +#endif + + tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); + + if (!tmp) + return NULL; + + *ns = tmp; + + (*ns)[*n_ns].name = addr; + (*ns)[*n_ns].replies = 0; + (*ns)[*n_ns].failures = 0; + (*ns)[*n_ns].addr.len = aip->ai_addrlen; + + memcpy(&(*ns)[*n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen); + + (*n_ns)++; + } + + freeaddrinfo(ai); + + return &(*ns)[*n_ns]; + } + + return NULL; + } + + tmp = realloc(*ns, sizeof(**ns) * (*n_ns + 1)); + + if (!tmp) + return NULL; + + *ns = tmp; + + (*ns)[*n_ns].addr = a; + (*ns)[*n_ns].name = addr; + (*ns)[*n_ns].replies = 0; + (*ns)[*n_ns].failures = 0; + + return &(*ns)[(*n_ns)++]; +} + +static struct query *add_query(struct query **queries, int *n_queries, + int type, const char *dname) +{ + struct query *tmp; + ssize_t qlen; + + tmp = realloc(*queries, sizeof(**queries) * (*n_queries + 1)); + + if (!tmp) + return NULL; + + memset(&tmp[*n_queries], 0, sizeof(*tmp)); + + qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL, + tmp[*n_queries].query, sizeof(tmp[*n_queries].query)); + + tmp[*n_queries].qlen = qlen; + tmp[*n_queries].name = dname; + *queries = tmp; + + return &tmp[(*n_queries)++]; +} + +int main(int argc, char **argv) +{ + int rc = 1; + struct ns *ns = NULL; + struct query *queries = NULL; + int n_ns = 0, n_queries = 0; + int c = 0; + + char *url = "telecominfraproject.com"; + char *server = "127.0.0.1"; + int v6 = 0; + + while (1) { + int option = getopt(argc, argv, "u:s:i:6"); + + if (option == -1) + break; + + switch (option) { + case '6': + v6 = 1; + break; + case 'u': + url = optarg; + break; + case 's': + server = optarg; + break; + default: + case 'h': + printf("Usage: dnsprobe OPTIONS\n" + " -6 - use ipv6\n" + " -u \n" + " -s \n"); + return -1; + } + } + + ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "dnsprobe"); + + ULOG_INFO("attempting to probe dns - %s %s %s\n", + url, server, v6 ? "ipv6" : ""); + + + add_query(&queries, &n_queries, v6 ? T_AAAA : T_A, url); + + add_ns(&ns, &n_ns, server); + + rc = send_queries(&ns[0], 1, queries, n_queries); + if (rc <= 0) { + fprintf(stderr, "Failed to send queries: %s\n", strerror(errno)); + rc = -1; + goto out; + } + + if (queries[0].rcode != 0) { + printf("** server can't find %s: %s\n", queries[0].name, + rcodes[queries[0].rcode]); + goto out; + } + + if (queries[0].rlen) { + c = parse_reply(queries[0].reply, queries[0].rlen, NULL); + } + + if (c == 0) + printf("*** Can't find %s: No answer\n", queries[0].name); + else if (c < 0) + printf("*** Can't find %s: Parse error\n", queries[0].name); + else + rc = 0; + +out: + if (n_ns) + free(ns); + + if (n_queries) + free(queries); + + return rc; +} diff --git a/feeds/tip/firstcontact/src/firstcontact.c b/feeds/ucentral/ucentral-tools/src/firstcontact.c similarity index 84% rename from feeds/tip/firstcontact/src/firstcontact.c rename to feeds/ucentral/ucentral-tools/src/firstcontact.c index 80dd9b7b5..da06988e5 100644 --- a/feeds/tip/firstcontact/src/firstcontact.c +++ b/feeds/ucentral/ucentral-tools/src/firstcontact.c @@ -1,15 +1,14 @@ #define _GNU_SOURCE #include -#include #include #include #include -static const char *file_cert = "/etc/ucentral/cert.pem"; -static const char *file_key = "/etc/ucentral/key.pem"; -static const char *file_json = "/etc/ucentral/redirector.json"; +static const char *file_cert = "/etc/open-wifi/client.pem"; +static const char *file_key = "/etc/open-wifi/client_dec.key"; +static const char *file_json = "/etc/open-wifi/redirector.json"; static const char *file_dbg = "/tmp/firstcontact.hdr"; int main(int argc, char **argv) @@ -21,8 +20,6 @@ int main(int argc, char **argv) char *devid = NULL; char *url; - alarm(15); - while (1) { int option = getopt(argc, argv, "k:c:o:hi:"); @@ -75,7 +72,7 @@ int main(int argc, char **argv) return -1; } - if (asprintf(&url, "https://clientauth.one.digicert.com/iot/api/v2/device/%s", devid) < 0) { + if (asprintf(&url, "https://clientauth.demo.one.digicert.com/iot/api/v2/device/%s", devid) < 0) { ULOG_ERR("failed to assemble url\n"); return -1; } @@ -88,7 +85,6 @@ int main(int argc, char **argv) curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_SSLKEY, file_key); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); res = curl_easy_perform(curl); if (res != CURLE_OK) diff --git a/feeds/ucentral/ucentral-tools/src/ip-collide.c b/feeds/ucentral/ucentral-tools/src/ip-collide.c new file mode 100644 index 000000000..cf81fc8c1 --- /dev/null +++ b/feeds/ucentral/ucentral-tools/src/ip-collide.c @@ -0,0 +1,86 @@ +#include +#include + +#include +#include + +#include +#include +#include + +struct route { + struct list_head list; + char devname[64]; + uint32_t domain; + uint32_t mask; +}; + +static struct list_head routes = LIST_HEAD_INIT(routes); + +static int parse_routes(void) +{ + FILE *fp = fopen("/proc/net/route", "r"); + int flgs, ref, use, metric, mtu, win, ir; + struct route *route; + unsigned long g; + int r; + + r = fscanf(fp, "%*[^\n]\n"); + if (r < 0) { + fprintf(stderr, "failed to parse routes\n"); + return -1; + } + while (1) { + route = malloc(sizeof(*route)); + if (!route) + break; + memset(route, 0, sizeof(*route)); + r = fscanf(fp, "%63s%x%lx%X%d%d%d%x%d%d%d\n", + route->devname, &route->domain, &g, &flgs, &ref, &use, &metric, &route->mask, + &mtu, &win, &ir); + if (r != 11 && (r < 0) && feof(fp)) + break; + list_add(&route->list, &routes); + printf("1 %s %x %x\n", route->devname, ntohl(route->domain), ntohl(route->mask)); + } + + fclose(fp); + + return 0; +} + +static int find_collisions(void) +{ + struct route *route; + + list_for_each_entry(route, &routes, list) { + struct route *compare; + + if (!route->domain || !route->mask) + continue; + list_for_each_entry(compare, &routes, list) { + if (!compare->domain || !compare->mask) + continue; + if (compare == route) + continue; + if (((route->domain & route->mask) == (compare->domain & route->mask)) || + ((route->domain & compare->mask) == (compare->domain & compare->mask))) { + ULOG_ERR("collision detected\n"); + return 1; + } + } + } + ULOG_INFO("no collision detected\n"); + return 0; +} + +int main(int argc, char **argv) +{ + ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "ip-collide"); + + parse_routes(); + if (!list_empty(&routes)) + return find_collisions(); + + return 0; +} diff --git a/feeds/ucentral/ucentral-tools/src/radiusprobe.c b/feeds/ucentral/ucentral-tools/src/radiusprobe.c new file mode 100644 index 000000000..a86fc7435 --- /dev/null +++ b/feeds/ucentral/ucentral-tools/src/radiusprobe.c @@ -0,0 +1,47 @@ +#include +#include +#include + +int +main(int argc, char **argv) +{ + int result; + char username[128]; + char passwd[AUTH_PASS_LEN + 1]; + VALUE_PAIR *send, *received; + uint32_t service; + rc_handle *rh; + + /* Not needed if you already used openlog() */ + rc_openlog("radiusprobe"); + + if ((rh = rc_read_config("/tmp/radius.conf")) == NULL) + return ERROR_RC; + + strcpy(username, "healthcheck"); + strcpy(passwd, "uCentral"); + + send = NULL; + + if (rc_avpair_add(rh, &send, PW_USER_NAME, username, -1, 0) == NULL) + return ERROR_RC; + + if (rc_avpair_add(rh, &send, PW_USER_PASSWORD, passwd, -1, 0) == NULL) + return ERROR_RC; + + service = PW_AUTHENTICATE_ONLY; + if (rc_avpair_add(rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) + return ERROR_RC; + + result = rc_auth(rh, 0, send, &received, NULL); + + if (result == OK_RC || result == REJECT_RC) { + fprintf(stderr, "RADIUS server OK\n"); + result = 0; + } else { + fprintf(stderr, "RADIUS server failure\n"); + result = -1; + } + + return result; +} diff --git a/feeds/ucentral/ucode/Makefile b/feeds/ucentral/ucode/Makefile index 1cffcb1b6..493ef845a 100644 --- a/feeds/ucentral/ucode/Makefile +++ b/feeds/ucentral/ucode/Makefile @@ -12,21 +12,34 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/jow-/ucode.git -PKG_SOURCE_DATE:=2023-06-06 -PKG_SOURCE_VERSION:=c7d84aae09691a99ae3db427c0b2463732ef84f4 -PKG_MIRROR_HASH:=38826ae70d886d1d7ada3fc6591ac807169aa28107f60f7f2e617520083525fb +PKG_MIRROR_HASH:=7a4d35d14ede7d853b2095ee239a86aab0b17da2b8cfac814dfa58fabec6374b +PKG_SOURCE_DATE:=2024-04-15 +PKG_SOURCE_VERSION:=cfe137be068a7ba1895d3c9bcb7b38d21e5a95dd PKG_MAINTAINER:=Jo-Philipp Wich PKG_LICENSE:=ISC -PKG_ABI_VERSION:=20220812 +PKG_ABI_VERSION:=20230711 include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/host-build.mk include $(INCLUDE_DIR)/cmake.mk -CMAKE_OPTIONS += -DSOVERSION=$(PKG_ABI_VERSION) -DLINUX=ON +CMAKE_OPTIONS += \ + -DSOVERSION=$(PKG_ABI_VERSION) + +CMAKE_HOST_OPTIONS += \ + -DCMAKE_SKIP_RPATH=FALSE \ + -DCMAKE_INSTALL_RPATH="${STAGING_DIR_HOSTPKG}/lib" + +ifeq ($(HOST_OS),Darwin) + CMAKE_HOST_OPTIONS += \ + -DCMAKE_MACOSX_RPATH=1 +else + CMAKE_HOST_OPTIONS += \ + -DUSE_RPATH="${STAGING_DIR_HOSTPKG}/lib" +endif + CMAKE_HOST_OPTIONS += \ - -DSOVERSION=$(PKG_ABI_VERSION) \ -DFS_SUPPORT=ON \ -DMATH_SUPPORT=ON \ -DNL80211_SUPPORT=OFF \ @@ -35,8 +48,17 @@ CMAKE_HOST_OPTIONS += \ -DSTRUCT_SUPPORT=ON \ -DUBUS_SUPPORT=OFF \ -DUCI_SUPPORT=OFF \ - -DULOOP_SUPPORT=OFF + -DULOOP_SUPPORT=OFF \ + -DDEBUG_SUPPORT=ON \ + -DLOG_SUPPORT=OFF +ifeq ($(CONFIG_TARGET_ipq95xx)$(CONFIG_TARGET_ipq53xx),y) +TARGET_CFLAGS += -DQCA_WIFI_7 +endif + +ifeq ($(CONFIG_TARGET_ipq807x)$(CONFIG_TARGET_ipq60xx)$(CONFIG_TARGET_ipq50xx)$(CONFIG_TARGET_ipq40xx),y) +TARGET_CFLAGS += -DQCA_v4_4 +endif define Package/ucode/default SUBMENU:=ucode @@ -66,116 +88,41 @@ define Package/libucode DEPENDS:=+libjson-c endef -define Package/libucode/description - The libucode package provides the shared runtime library for the ucode interpreter. -endef - - -define Package/ucode-mod-fs - $(Package/ucode/default) - TITLE+= (filesystem module) - DEPENDS:=ucode -endef - -define Package/ucode-mod-fs/description - The filesystem plugin module allows interaction with the local file system. -endef - - -define Package/ucode-mod-math - $(Package/ucode/default) - TITLE+= (math module) - DEPENDS:=ucode -endef - -define Package/ucode-mod-math/description - The math plugin provides access to various procedures. -endef - - -define Package/ucode-mod-nl80211 - $(Package/ucode/default) - TITLE+= (nl80211 module) - DEPENDS:=ucode +libnl-tiny +libubox +kmod-mac80211 -endef - -define Package/ucode-mod-nl80211/description - The nl80211 plugin provides access to the Linux wireless 802.11 netlink API. -endef - - -define Package/ucode-mod-resolv - $(Package/ucode/default) - TITLE+= (resolv module) - DEPENDS:=ucode -endef - -define Package/ucode-mod-resolv/description - The resolv plugin implements simple DNS resolving. -endef - - -define Package/ucode-mod-rtnl - $(Package/ucode/default) - TITLE+= (rtnl module) - DEPENDS:=ucode +libnl-tiny +libubox -endef - -define Package/ucode-mod-rtnl/description - The rtnl plugin provides access to the Linux routing netlink API. -endef - - -define Package/ucode-mod-struct - $(Package/ucode/default) - TITLE+= (struct module) - DEPENDS:=ucode -endef - -define Package/ucode-mod-struct/description - The struct plugin implements Python 3 compatible struct.pack/unpack functionality. -endef - - -define Package/ucode-mod-ubus - $(Package/ucode/default) - TITLE+= (ubus module) - DEPENDS:=ucode +libubus +libblobmsg-json -endef - -define Package/ucode-mod-ubus/description - The ubus module allows ucode template scripts to enumerate and invoke ubus - procedures. -endef - - -define Package/ucode-mod-uci - $(Package/ucode/default) - TITLE+= (uci module) - DEPENDS:=ucode +libuci -endef - -define Package/ucode-mod-uci/description - The uci module allows templates to read and modify uci configuration. -endef - - -define Package/ucode-mod-uloop - $(Package/ucode/default) - TITLE+= (uloop module) - DEPENDS:=ucode +libubox -endef - -define Package/ucode-mod-uloop/description - The uloop module allows ucode scripts to interact with OpenWrt uloop event - loop implementation. -endef - define Build/Prepare $(Build/Prepare/Default) $(CP) $(STAGING_DIR)/usr/include/mac80211/uapi/linux/nl80211.h $(PKG_BUILD_DIR)/nl80211_copy.h endef +define Package/libucode/description + The libucode package provides the shared runtime library for the ucode interpreter. +endef + +# 1: name +# 2: cmake symbol +# 3: depends +# 4: description +define UcodeModule + UCODE_MODULES += ucode-mod-$(strip $(1)) + CMAKE_OPTIONS += -D$(strip $(2))=$(if $(CONFIG_PACKAGE_ucode-mod-$(strip $(1))),ON,OFF) + PKG_CONFIG_DEPENDS += CONFIG_PACKAGE_ucode-mod-$(strip $(1)) + + define Package/ucode-mod-$(strip $(1)) + $(Package/ucode/default) + TITLE+= ($(strip $(1)) module) + DEPENDS:=ucode $(3) + endef + + define Package/ucode-mod-$(strip $(1))/description + $(strip $(4)) + endef + + define Package/ucode-mod-$(strip $(1))/install + $(INSTALL_DIR) $$(1)/usr/lib/ucode + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/$(strip $(1)).so $$(1)/usr/lib/ucode/ + endef +endef + + define Build/InstallDev $(INSTALL_DIR) $(1)/usr/lib $(1)/usr/include/ucode $(CP) $(PKG_INSTALL_DIR)/usr/include/ucode/*.h $(1)/usr/include/ucode/ @@ -193,60 +140,55 @@ define Package/libucode/install $(CP) $(PKG_INSTALL_DIR)/usr/lib/libucode.so.* $(1)/usr/lib/ endef -define Package/ucode-mod-fs/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/fs.so $(1)/usr/lib/ucode/ -endef -define Package/ucode-mod-math/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/math.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + debug, DEBUG_SUPPORT, +libubox +libucode, \ + The debug plugin module provides runtime debugging and introspection facilities.)) -define Package/ucode-mod-nl80211/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/nl80211.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + fs, FS_SUPPORT, , \ + The filesystem plugin module allows interaction with the local file system.)) -define Package/ucode-mod-resolv/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/resolv.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + log, LOG_SUPPORT, +libubox, \ + The log plugin module provides access to the syslog and libubox ulog APIs.)) -define Package/ucode-mod-rtnl/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/rtnl.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + math, MATH_SUPPORT, , \ + The math plugin provides access to various procedures.)) -define Package/ucode-mod-struct/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/struct.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + nl80211, NL80211_SUPPORT, +libnl-tiny +libubox +kmod-mac80211, \ + The nl80211 plugin provides access to the Linux wireless 802.11 netlink API.)) -define Package/ucode-mod-ubus/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/ubus.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + resolv, RESOLV_SUPPORT, , \ + The resolv plugin implements simple DNS resolving.)) -define Package/ucode-mod-uci/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/uci.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + rtnl, RTNL_SUPPORT, +libnl-tiny +libubox, \ + The rtnl plugin provides access to the Linux routing netlink API.)) -define Package/ucode-mod-uloop/install - $(INSTALL_DIR) $(1)/usr/lib/ucode - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/ucode/uloop.so $(1)/usr/lib/ucode/ -endef +$(eval $(call UcodeModule, \ + struct, STRUCT_SUPPORT, , \ + The struct plugin implements Python 3 compatible struct.pack/unpack functionality.)) + +$(eval $(call UcodeModule, \ + ubus, UBUS_SUPPORT, +libubus +libblobmsg-json, \ + The ubus module allows ucode template scripts to enumerate and invoke ubus procedures.)) + +$(eval $(call UcodeModule, \ + uci, UCI_SUPPORT, +libuci, \ + The uci module allows templates to read and modify uci configuration.)) + +$(eval $(call UcodeModule, \ + uloop, ULOOP_SUPPORT, +libubox, \ + The uloop module allows ucode scripts to interact with OpenWrt uloop event loop implementation.)) $(eval $(call BuildPackage,libucode)) $(eval $(call BuildPackage,ucode)) -$(eval $(call BuildPackage,ucode-mod-fs)) -$(eval $(call BuildPackage,ucode-mod-math)) -$(eval $(call BuildPackage,ucode-mod-nl80211)) -$(eval $(call BuildPackage,ucode-mod-resolv)) -$(eval $(call BuildPackage,ucode-mod-rtnl)) -$(eval $(call BuildPackage,ucode-mod-struct)) -$(eval $(call BuildPackage,ucode-mod-ubus)) -$(eval $(call BuildPackage,ucode-mod-uci)) -$(eval $(call BuildPackage,ucode-mod-uloop)) + +$(foreach mod,$(UCODE_MODULES), \ + $(eval $(call BuildPackage,$(mod)))) + $(eval $(call HostBuild)) diff --git a/feeds/ucentral/ucode/patches/000-nl80211_copy.patch b/feeds/ucentral/ucode/patches/000-nl80211_copy.patch index ff20e3e7e..fb268cb96 100644 --- a/feeds/ucentral/ucode/patches/000-nl80211_copy.patch +++ b/feeds/ucentral/ucode/patches/000-nl80211_copy.patch @@ -1,10 +1,11 @@ --- a/lib/nl80211.c +++ b/lib/nl80211.c -@@ -38,7 +38,7 @@ limitations under the License. +@@ -37,7 +37,8 @@ limitations under the License. #include #include -#include ++#define BIT(_n) (1UL << (_n)) +#include "../nl80211_copy.h" #include #include diff --git a/feeds/ucentral/ucode/patches/0001-fixes.patch b/feeds/ucentral/ucode/patches/0001-fixes.patch index 8ef37ecf1..1f60bdd65 100644 --- a/feeds/ucentral/ucode/patches/0001-fixes.patch +++ b/feeds/ucentral/ucode/patches/0001-fixes.patch @@ -10,91 +10,55 @@ Subject: [PATCH] fixes --- a/lib/nl80211.c +++ b/lib/nl80211.c -@@ -56,6 +56,8 @@ limitations under the License. +@@ -57,6 +57,33 @@ limitations under the License. #define NL80211_CMDS_BITMAP_SIZE DIV_ROUND_UP(NL80211_CMD_MAX + 1, 32) -+#define NL80211_ATTR_NOT_IMPLEMENTED 0x10000 ++#ifdef QCA_v4_4 ++#define NL80211_STA_INFO_CONNECTED_TO_AS NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_FREQUENCY_ATTR_1MHZ NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_FREQUENCY_ATTR_2MHZ NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_FREQUENCY_ATTR_4MHZ NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_FREQUENCY_ATTR_8MHZ NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_FREQUENCY_ATTR_16MHZ NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_MESHCONF_NOLEARN NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_MESHCONF_CONNECTED_TO_AS NL80211_ATTR_NOT_IMPLEMENTED ++#endif ++ ++#ifndef QCA_WIFI_7 ++#define NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_WIPHY_RADIO_FREQ_ATTR_START NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_WIPHY_RADIO_FREQ_ATTR_END NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_WIPHY_RADIO_ATTR_INDEX NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_ATTR_WIPHY_RADIOS NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_ATTR_VIF_RADIO_MASK NL80211_ATTR_NOT_IMPLEMENTED ++#endif ++ + static struct { int code; char *msg; -@@ -277,6 +279,14 @@ static const uc_nl_nested_spec_t nl80211 - } - }; +@@ -561,7 +588,7 @@ static const uc_nl_nested_spec_t nl80211 -+#ifndef NL80211_MESHCONF_NOLEARN -+#define NL80211_MESHCONF_NOLEARN NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_MESHCONF_CONNECTED_TO_AS -+#define NL80211_MESHCONF_CONNECTED_TO_AS NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ - static const uc_nl_nested_spec_t nl80211_mesh_params_nla = { - .headsize = 0, - .nattrs = 29, -@@ -368,6 +378,14 @@ static const uc_nl_nested_spec_t nl80211 - } - }; - -+#ifndef NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK -+#define NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR -+#define NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ - static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_req_data_ftm_nla = { - .headsize = 0, - .nattrs = 13, -@@ -517,6 +535,26 @@ static const uc_nl_nested_spec_t nl80211 - } - }; - -+#ifndef NL80211_FREQUENCY_ATTR_1MHZ -+#define NL80211_FREQUENCY_ATTR_1MHZ NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_FREQUENCY_ATTR_2MHZ -+#define NL80211_FREQUENCY_ATTR_2MHZ NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_FREQUENCY_ATTR_4MHZ -+#define NL80211_FREQUENCY_ATTR_4MHZ NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_FREQUENCY_ATTR_8MHZ -+#define NL80211_FREQUENCY_ATTR_8MHZ NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ -+#ifndef NL80211_FREQUENCY_ATTR_16MHZ -+#define NL80211_FREQUENCY_ATTR_16MHZ NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ - static const uc_nl_nested_spec_t nl80211_wiphy_bands_freqs_nla = { - .headsize = 0, - .nattrs = 25, -@@ -558,6 +596,10 @@ static const uc_nl_nested_spec_t nl80211 - } - }; - -+#ifndef NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS -+#define NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ static const uc_nl_nested_spec_t nl80211_wiphy_bands_iftype_data_nla = { .headsize = 0, - .nattrs = 7, -@@ -728,6 +770,10 @@ static const uc_nl_nested_spec_t nl80211 +- .nattrs = 7, ++ .nattrs = 9, + .attrs = { + { NL80211_BAND_IFTYPE_ATTR_IFTYPES, "iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, + { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, "he_cap_mac", DT_U8, DF_ARRAY, NULL }, +@@ -570,6 +597,8 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, "he_cap_ppe", DT_U8, DF_ARRAY, NULL }, + { NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, "he_6ghz_capa", DT_U16, 0, NULL }, + { NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, "vendor_elems", DT_STRING, DF_BINARY, NULL }, ++ { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, "eht_cap_mac", DT_U16, DF_ARRAY, NULL }, ++ { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, "eht_cap_phy", DT_U16, DF_ARRAY, NULL }, } }; -+#ifndef NL80211_STA_INFO_CONNECTED_TO_AS -+#define NL80211_STA_INFO_CONNECTED_TO_AS NL80211_ATTR_NOT_IMPLEMENTED -+#endif -+ - static const uc_nl_nested_spec_t nl80211_sta_info_nla = { - .headsize = 0, - .nattrs = 40, diff --git a/feeds/ucentral/ucode/patches/0001-nl80211-cover-extended-feature-and-EHT-rate-info-att.patch b/feeds/ucentral/ucode/patches/0001-nl80211-cover-extended-feature-and-EHT-rate-info-att.patch new file mode 100644 index 000000000..60f1368f6 --- /dev/null +++ b/feeds/ucentral/ucode/patches/0001-nl80211-cover-extended-feature-and-EHT-rate-info-att.patch @@ -0,0 +1,67 @@ +From 1423ad7b8411a2cb727bfd4e4f3511469abb3214 Mon Sep 17 00:00:00 2001 +From: Jo-Philipp Wich +Date: Wed, 23 Oct 2024 14:31:49 +0200 +Subject: [PATCH] nl80211: cover extended feature and EHT rate info attributes + +These new attributes are required when dealing with WiFi 7 radios. + +Reported-by: John Crispin +Signed-off-by: Jo-Philipp Wich +--- + lib/nl80211.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +--- a/lib/nl80211.c ++++ b/lib/nl80211.c +@@ -81,6 +81,10 @@ limitations under the License. + #define NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION NL80211_ATTR_NOT_IMPLEMENTED + #define NL80211_ATTR_WIPHY_RADIOS NL80211_ATTR_NOT_IMPLEMENTED + #define NL80211_ATTR_VIF_RADIO_MASK NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_RATE_INFO_EHT_NSS NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_RATE_INFO_EHT_MCS NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_RATE_INFO_EHT_GI NL80211_ATTR_NOT_IMPLEMENTED ++#define NL80211_RATE_INFO_320_MHZ_WIDTH NL80211_ATTR_NOT_IMPLEMENTED + #endif + + +@@ -695,7 +699,7 @@ static const uc_nl_nested_spec_t nl80211 + + static const uc_nl_nested_spec_t nl80211_sta_info_bitrate_nla = { + .headsize = 0, +- .nattrs = 18, ++ .nattrs = 22, + .attrs = { + { NL80211_RATE_INFO_BITRATE, "bitrate", DT_U16, 0, NULL }, + { NL80211_RATE_INFO_BITRATE32, "bitrate32", DT_U32, 0, NULL }, +@@ -709,10 +713,14 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_RATE_INFO_HE_GI, "he_gi", DT_U8, 0, NULL }, + { NL80211_RATE_INFO_HE_DCM, "he_dcm", DT_U8, 0, NULL }, + { NL80211_RATE_INFO_HE_RU_ALLOC, "he_ru_alloc", DT_U8, 0, NULL }, ++ { NL80211_RATE_INFO_EHT_MCS, "eht_mcs", DT_U8, 0, NULL }, ++ { NL80211_RATE_INFO_EHT_NSS, "eht_nss", DT_U8, 0, NULL }, ++ { NL80211_RATE_INFO_EHT_GI, "eht_gi", DT_U8, 0, NULL }, + { NL80211_RATE_INFO_40_MHZ_WIDTH, "width_40", DT_FLAG, 0, NULL }, + { NL80211_RATE_INFO_80_MHZ_WIDTH, "width_80", DT_FLAG, 0, NULL }, + { NL80211_RATE_INFO_80P80_MHZ_WIDTH, "width_80p80", DT_FLAG, 0, NULL }, + { NL80211_RATE_INFO_160_MHZ_WIDTH, "width_160", DT_FLAG, 0, NULL }, ++ { NL80211_RATE_INFO_320_MHZ_WIDTH, "width_320", DT_FLAG, 0, NULL }, + { NL80211_RATE_INFO_10_MHZ_WIDTH, "width_10", DT_FLAG, 0, NULL }, + { NL80211_RATE_INFO_5_MHZ_WIDTH, "width_5", DT_FLAG, 0, NULL }, + } +@@ -837,7 +845,7 @@ static const uc_nl_nested_spec_t nl80211 + + static const uc_nl_nested_spec_t nl80211_msg = { + .headsize = 0, +- .nattrs = 127, ++ .nattrs = 128, + .attrs = { + { NL80211_ATTR_4ADDR, "4addr", DT_U8, 0, NULL }, + { NL80211_ATTR_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL }, +@@ -864,6 +872,7 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_ATTR_DFS_REGION, "dfs_region", DT_U8, 0, NULL }, + { NL80211_ATTR_DTIM_PERIOD, "dtim_period", DT_U32, 0, NULL }, + { NL80211_ATTR_DURATION, "duration", DT_U32, 0, NULL }, ++ { NL80211_ATTR_EXT_FEATURES, "extended_features", DT_U8, DF_ARRAY, NULL }, + { NL80211_ATTR_FEATURE_FLAGS, "feature_flags", DT_U32, 0, NULL }, + { NL80211_ATTR_FRAME, "frame", DT_STRING, DF_BINARY, NULL }, + { NL80211_ATTR_FRAME_MATCH, "frame_match", DT_STRING, DF_BINARY, NULL }, diff --git a/feeds/ucentral/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch b/feeds/ucentral/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch new file mode 100644 index 000000000..a1659be3c --- /dev/null +++ b/feeds/ucentral/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch @@ -0,0 +1,26 @@ +From: Felix Fietkau +Date: Wed, 1 May 2024 18:40:19 +0200 +Subject: [PATCH] ubus: fix uc_ubus_have_uloop for eloop+uloop combination + +When uloop has been integrated with eloop (in hostapd/wpa_supplicant), +uloop_cancelling will return false, since uloop_run is not being called. +This leads to ubus.defer() calling uloop_run in a context where it can +prevent the other event loop from running. + +Fix this by detecting event loop integration via uloop_fd_set_cb being set + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/ubus.c ++++ b/lib/ubus.c +@@ -665,6 +665,9 @@ uc_ubus_have_uloop(void) + bool prev = uloop_cancelled; + bool active; + ++ if (uloop_fd_set_cb) ++ return true; ++ + uloop_cancelled = true; + active = uloop_cancelling(); + uloop_cancelled = prev; diff --git a/feeds/ucentral/ucode/patches/110-nl80211-move-access-to-tb-array-out-of-uc_nl_convert.patch b/feeds/ucentral/ucode/patches/110-nl80211-move-access-to-tb-array-out-of-uc_nl_convert.patch new file mode 100644 index 000000000..7e9d07e01 --- /dev/null +++ b/feeds/ucentral/ucode/patches/110-nl80211-move-access-to-tb-array-out-of-uc_nl_convert.patch @@ -0,0 +1,341 @@ +From: Felix Fietkau +Date: Sat, 29 Jun 2024 12:03:21 +0200 +Subject: [PATCH] nl80211: move access to tb array out of uc_nl_convert_attr + and below + +Only one place needs access to another attribute from the tb array +(HE MCS rates). In order to make that access possible, add a flag to indicate +a second attribute dependency via auxdata. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/nl80211.c ++++ b/lib/nl80211.c +@@ -197,6 +197,7 @@ enum { + DF_OFFSET1 = (1 << 4), + DF_ARRAY = (1 << 5), + DF_BINARY = (1 << 6), ++ DF_RELATED = (1 << 7), + }; + + typedef struct uc_nl_attr_spec { +@@ -215,6 +216,7 @@ typedef struct uc_nl_nested_spec { + + #define SIZE(type) (void *)(uintptr_t)sizeof(struct type) + #define MEMBER(type, field) (void *)(uintptr_t)offsetof(struct type, field) ++#define ATTRID(id) (void *)(uintptr_t)(id) + + static const uc_nl_nested_spec_t nl80211_cqm_nla = { + .headsize = 0, +@@ -593,7 +595,7 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_BAND_IFTYPE_ATTR_IFTYPES, "iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, + { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, "he_cap_mac", DT_U8, DF_ARRAY, NULL }, + { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, "he_cap_phy", DT_U8, DF_ARRAY, NULL }, +- { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, "he_cap_mcs_set", DT_HE_MCS, 0, NULL }, ++ { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, "he_cap_mcs_set", DT_HE_MCS, DF_RELATED, ATTRID(NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY) }, + { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, "he_cap_ppe", DT_U8, DF_ARRAY, NULL }, + { NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, "he_6ghz_capa", DT_U16, 0, NULL }, + { NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, "vendor_elems", DT_STRING, DF_BINARY, NULL }, +@@ -1065,12 +1067,12 @@ static bool + uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx); + + static uc_value_t * +-uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm); ++uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm); + + static bool + uc_nl_convert_attrs(struct nl_msg *msg, void *buf, size_t buflen, size_t headsize, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj) + { +- struct nlattr **tb, *nla, *nla_nest; ++ struct nlattr **tb, *nla, *nla2, *nla_nest; + size_t i, type, maxattr = 0; + uc_value_t *v, *arr; + int rem; +@@ -1106,9 +1108,7 @@ uc_nl_convert_attrs(struct nl_msg *msg, + attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata) + continue; + +- tb[attrs[i].attr] = nla; +- +- v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm); ++ v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm); + + if (!v) + continue; +@@ -1128,7 +1128,12 @@ uc_nl_convert_attrs(struct nl_msg *msg, + v = arr; + } + else { +- v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb, vm); ++ if (attrs[i].flags & DF_RELATED) ++ nla2 = tb[(uintptr_t)attrs[i].auxdata]; ++ else ++ nla2 = NULL; ++ ++ v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb[attrs[i].attr], nla2, vm); + + if (!v) + continue; +@@ -1218,7 +1223,7 @@ uc_nl_parse_rta_nested(const uc_nl_attr_ + } + + static uc_value_t * +-uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) + { + const uc_nl_nested_spec_t *nest = spec->auxdata; + uc_value_t *nested_obj; +@@ -1227,13 +1232,13 @@ uc_nl_convert_rta_nested(const uc_nl_att + if (!nest) + return NULL; + +- if (!nla_check_len(tb[spec->attr], nest->headsize)) ++ if (!nla_check_len(attr, nest->headsize)) + return NULL; + + nested_obj = ucv_object_new(vm); + + rv = uc_nl_convert_attrs(msg, +- nla_data(tb[spec->attr]), nla_len(tb[spec->attr]), nest->headsize, ++ nla_data(attr), nla_len(attr), nest->headsize, + nest->attrs, nest->nattrs, + vm, nested_obj); + +@@ -1247,17 +1252,17 @@ uc_nl_convert_rta_nested(const uc_nl_att + } + + static uc_value_t * +-uc_nl_convert_rta_ht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_ht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) + { + uc_value_t *mcs_obj, *mcs_idx; + uint16_t max_rate = 0; + uint8_t *mcs; + size_t i; + +- if (!nla_check_len(tb[spec->attr], 16)) ++ if (!nla_check_len(attr, 16)) + return NULL; + +- mcs = nla_data(tb[spec->attr]); ++ mcs = nla_data(attr); + mcs_obj = ucv_object_new(vm); + + max_rate = (mcs[10] | ((mcs[11] & 0x3) << 8)); +@@ -1282,16 +1287,16 @@ uc_nl_convert_rta_ht_mcs(const uc_nl_att + } + + static uc_value_t * +-uc_nl_convert_rta_ht_cap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_ht_cap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) + { + uc_value_t *cap_obj, *mcs_obj, *rx_mask; + struct ieee80211_ht_cap *cap; + size_t i; + +- if (!nla_check_len(tb[spec->attr], sizeof(*cap))) ++ if (!nla_check_len(attr, sizeof(*cap))) + return NULL; + +- cap = nla_data(tb[spec->attr]); ++ cap = nla_data(attr); + cap_obj = ucv_object_new(vm); + + ucv_object_add(cap_obj, "cap_info", ucv_uint64_new(le16toh(cap->cap_info))); +@@ -1316,17 +1321,17 @@ uc_nl_convert_rta_ht_cap(const uc_nl_att + } + + static uc_value_t * +-uc_nl_convert_rta_vht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_vht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) + { + uc_value_t *mcs_obj, *mcs_set, *mcs_entry, *mcs_idx; + size_t i, j, max_idx; + uint16_t u16; + uint8_t *mcs; + +- if (!nla_check_len(tb[spec->attr], 8)) ++ if (!nla_check_len(attr, 8)) + return NULL; + +- mcs = nla_data(tb[spec->attr]); ++ mcs = nla_data(attr); + mcs_obj = ucv_object_new(vm); + + u16 = mcs[0] | (mcs[1] << 8); +@@ -1387,7 +1392,7 @@ uc_nl_convert_rta_vht_mcs(const uc_nl_at + } + + static uc_value_t * +-uc_nl_convert_rta_he_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_he_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, struct nlattr *phy_attr, uc_vm_t *vm) + { + uint8_t bw_support_mask[] = { (1 << 1) | (1 << 2), (1 << 3), (1 << 4) }; + uc_value_t *mcs_set, *mcs_bw, *mcs_dir, *mcs_entry, *mcs_idx; +@@ -1395,13 +1400,13 @@ uc_nl_convert_rta_he_mcs(const uc_nl_att + uint16_t u16, phy_cap_0 = 0; + size_t i, j, k, l, max_idx; + +- if (!nla_check_len(tb[spec->attr], sizeof(mcs))) ++ if (!nla_check_len(attr, sizeof(mcs))) + return NULL; + +- if (nla_check_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY], sizeof(phy_cap_0))) +- phy_cap_0 = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]); ++ if (nla_check_len(phy_attr, sizeof(phy_cap_0))) ++ phy_cap_0 = nla_get_u16(phy_attr); + +- memcpy(mcs, nla_data(tb[spec->attr]), sizeof(mcs)); ++ memcpy(mcs, nla_data(attr), sizeof(mcs)); + + mcs_set = ucv_array_new_length(vm, 3); + +@@ -1458,14 +1463,14 @@ uc_nl_convert_rta_he_mcs(const uc_nl_att + } + + static uc_value_t * +-uc_nl_convert_rta_ie(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_rta_ie(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm) + { + uc_value_t *ie_arr, *ie_obj; + uint8_t *ie; + size_t len; + +- len = nla_len(tb[spec->attr]); +- ie = nla_data(tb[spec->attr]); ++ len = nla_len(attr); ++ ie = nla_data(attr); + + if (len < 2) + return NULL; +@@ -1734,7 +1739,7 @@ uc_nl_convert_numval(const uc_nl_attr_sp + } + + static uc_value_t * +-uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr **tb, uc_vm_t *vm) ++uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm) + { + union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 }; + char buf[sizeof("FF:FF:FF:FF:FF:FF")]; +@@ -1751,17 +1756,17 @@ uc_nl_convert_attr(const uc_nl_attr_spec + case DT_U64: + if (spec->flags & DF_ARRAY) { + assert(spec->attr != 0); +- assert((nla_len(tb[spec->attr]) % dt_sizes[spec->type]) == 0); ++ assert((nla_len(attr) % dt_sizes[spec->type]) == 0); + +- v = ucv_array_new_length(vm, nla_len(tb[spec->attr]) / dt_sizes[spec->type]); ++ v = ucv_array_new_length(vm, nla_len(attr) / dt_sizes[spec->type]); + +- for (i = 0; i < nla_len(tb[spec->attr]); i += dt_sizes[spec->type]) +- ucv_array_push(v, uc_nl_convert_numval(spec, nla_data(tb[spec->attr]) + i)); ++ for (i = 0; i < nla_len(attr); i += dt_sizes[spec->type]) ++ ucv_array_push(v, uc_nl_convert_numval(spec, nla_data(attr) + i)); + + return v; + } +- else if (nla_check_len(tb[spec->attr], dt_sizes[spec->type])) { +- return uc_nl_convert_numval(spec, nla_data(tb[spec->attr])); ++ else if (nla_check_len(attr, dt_sizes[spec->type])) { ++ return uc_nl_convert_numval(spec, nla_data(attr)); + } + + return NULL; +@@ -1769,15 +1774,15 @@ uc_nl_convert_attr(const uc_nl_attr_spec + case DT_BOOL: + if (spec->attr == 0) + t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata); +- else if (nla_check_len(tb[spec->attr], sizeof(t.u8))) +- t.u8 = nla_get_u8(tb[spec->attr]); ++ else if (nla_check_len(attr, sizeof(t.u8))) ++ t.u8 = nla_get_u8(attr); + + return ucv_boolean_new(t.u8 != 0); + + case DT_FLAG: + if (spec->attr == 0) + t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata); +- else if (tb[spec->attr] != NULL) ++ else if (attr != NULL) + t.u8 = 1; + + return ucv_boolean_new(t.u8 != 0); +@@ -1785,21 +1790,21 @@ uc_nl_convert_attr(const uc_nl_attr_spec + case DT_STRING: + assert(spec->attr != 0); + +- if (!nla_check_len(tb[spec->attr], 1)) ++ if (!nla_check_len(attr, 1)) + return NULL; + +- t.sz = nla_len(tb[spec->attr]); ++ t.sz = nla_len(attr); + + if (!(spec->flags & DF_BINARY)) + t.sz -= 1; + +- return ucv_string_new_length(nla_data(tb[spec->attr]), t.sz); ++ return ucv_string_new_length(nla_data(attr), t.sz); + + case DT_NETDEV: + if (spec->attr == 0) + t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata); +- else if (nla_check_len(tb[spec->attr], sizeof(t.u32))) +- t.u32 = nla_get_u32(tb[spec->attr]); ++ else if (nla_check_len(attr, sizeof(t.u32))) ++ t.u32 = nla_get_u32(attr); + + if (if_indextoname(t.u32, buf)) + return ucv_string_new(buf); +@@ -1809,10 +1814,10 @@ uc_nl_convert_attr(const uc_nl_attr_spec + case DT_LLADDR: + assert(spec->attr != 0); + +- if (!nla_check_len(tb[spec->attr], sizeof(*ea))) ++ if (!nla_check_len(attr, sizeof(*ea))) + return NULL; + +- ea = nla_data(tb[spec->attr]); ++ ea = nla_data(attr); + + snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", + ea->ether_addr_octet[0], ea->ether_addr_octet[1], +@@ -1824,29 +1829,29 @@ uc_nl_convert_attr(const uc_nl_attr_spec + case DT_INADDR: + assert(spec->attr != 0); + +- if (!nla_check_len(tb[spec->attr], sizeof(struct in_addr)) || +- !inet_ntop(AF_INET, nla_data(tb[spec->attr]), buf, sizeof(buf))) ++ if (!nla_check_len(attr, sizeof(struct in_addr)) || ++ !inet_ntop(AF_INET, nla_data(attr), buf, sizeof(buf))) + return NULL; + + return ucv_string_new(buf); + + case DT_NESTED: +- return uc_nl_convert_rta_nested(spec, msg, tb, vm); ++ return uc_nl_convert_rta_nested(spec, msg, attr, vm); + + case DT_HT_MCS: +- return uc_nl_convert_rta_ht_mcs(spec, msg, tb, vm); ++ return uc_nl_convert_rta_ht_mcs(spec, msg, attr, vm); + + case DT_HT_CAP: +- return uc_nl_convert_rta_ht_cap(spec, msg, tb, vm); ++ return uc_nl_convert_rta_ht_cap(spec, msg, attr, vm); + + case DT_VHT_MCS: +- return uc_nl_convert_rta_vht_mcs(spec, msg, tb, vm); ++ return uc_nl_convert_rta_vht_mcs(spec, msg, attr, vm); + + case DT_HE_MCS: +- return uc_nl_convert_rta_he_mcs(spec, msg, tb, vm); ++ return uc_nl_convert_rta_he_mcs(spec, msg, attr, attr2, vm); + + case DT_IE: +- return uc_nl_convert_rta_ie(spec, msg, tb, vm); ++ return uc_nl_convert_rta_ie(spec, msg, attr, vm); + + default: + assert(0); diff --git a/feeds/ucentral/ucode/patches/111-nl80211-add-support-for-multi-attribute-arrays.patch b/feeds/ucentral/ucode/patches/111-nl80211-add-support-for-multi-attribute-arrays.patch new file mode 100644 index 000000000..517d88285 --- /dev/null +++ b/feeds/ucentral/ucode/patches/111-nl80211-add-support-for-multi-attribute-arrays.patch @@ -0,0 +1,94 @@ +From: Felix Fietkau +Date: Sat, 29 Jun 2024 12:05:29 +0200 +Subject: [PATCH] nl80211: add support for multi-attribute arrays + +For newly added attributes, the kernel prefers to no longer add a nesting +container attribute. Instead, an attribute with the element type is simply +added multiple times within the outer container. +Add support for this array style, which will be used in the pending wiphy +multi radio support. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/nl80211.c ++++ b/lib/nl80211.c +@@ -198,6 +198,7 @@ enum { + DF_ARRAY = (1 << 5), + DF_BINARY = (1 << 6), + DF_RELATED = (1 << 7), ++ DF_REPEATED = (1 << 8), + }; + + typedef struct uc_nl_attr_spec { +@@ -1043,26 +1044,6 @@ uc_nl_get_struct_member_u32(char *base, + return u32; + } + +-static void +-uc_nl_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len) +-{ +- struct nlattr *nla; +- int rem; +- +- memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); +- +- nla_for_each_attr(nla, head, len, rem) { +- int type = nla_type(nla); +- +- if (type <= maxtype) +- tb[type] = nla; +- } +- +- if (rem > 0) +- fprintf(stderr, "netlink: %d bytes leftover after parsing attributes.\n", rem); +-} +- +- + static bool + uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx); + +@@ -1086,12 +1067,10 @@ uc_nl_convert_attrs(struct nl_msg *msg, + if (!tb) + return false; + +- uc_nl_nla_parse(tb, maxattr, buf + headsize, buflen - headsize); +- + nla_for_each_attr(nla, buf + headsize, buflen - headsize, rem) { + type = nla_type(nla); + +- if (type <= maxattr) ++ if (type <= maxattr && !tb[type]) + tb[type] = nla; + } + +@@ -1099,7 +1078,28 @@ uc_nl_convert_attrs(struct nl_msg *msg, + if (attrs[i].attr != 0 && !tb[attrs[i].attr]) + continue; + +- if (attrs[i].flags & DF_MULTIPLE) { ++ if (attrs[i].flags & DF_REPEATED) { ++ arr = ucv_array_new(vm); ++ ++ nla = tb[attrs[i].attr]; ++ rem = buflen - ((void *)nla - buf); ++ for (; nla_ok(nla, rem); nla = nla_next(nla, &rem)) { ++ if (nla_type(nla) != (int)attrs[i].attr) ++ break; ++ v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm); ++ if (!v) ++ continue; ++ ++ ucv_array_push(arr, v); ++ } ++ if (!ucv_array_length(arr)) { ++ ucv_put(arr); ++ continue; ++ } ++ ++ v = arr; ++ } ++ else if (attrs[i].flags & DF_MULTIPLE) { + arr = ucv_array_new(vm); + nla_nest = tb[attrs[i].attr]; + diff --git a/feeds/ucentral/ucode/patches/112-nl80211-add-wiphy-multi-radio-support.patch b/feeds/ucentral/ucode/patches/112-nl80211-add-wiphy-multi-radio-support.patch new file mode 100644 index 000000000..588e9a5e7 --- /dev/null +++ b/feeds/ucentral/ucode/patches/112-nl80211-add-wiphy-multi-radio-support.patch @@ -0,0 +1,50 @@ +From: Felix Fietkau +Date: Tue, 9 Jul 2024 17:53:30 +0200 +Subject: [PATCH] nl80211: add wiphy multi-radio support + +Support new attributes that describe multiple radios belonging to a single +wiphy. + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/nl80211.c ++++ b/lib/nl80211.c +@@ -842,9 +842,28 @@ static const uc_nl_nested_spec_t nl80211 + } + }; + ++static const uc_nl_nested_spec_t nl80211_radio_freq_range_nla = { ++ .headsize = 0, ++ .nattrs = 2, ++ .attrs = { ++ { NL80211_WIPHY_RADIO_FREQ_ATTR_START, "start", DT_U32, 0, NULL }, ++ { NL80211_WIPHY_RADIO_FREQ_ATTR_END, "end", DT_U32, 0, NULL }, ++ } ++}; ++ ++static const uc_nl_nested_spec_t nl80211_wiphy_radio_nla = { ++ .headsize = 0, ++ .nattrs = 3, ++ .attrs = { ++ { NL80211_WIPHY_RADIO_ATTR_INDEX, "index", DT_U32, 0, NULL }, ++ { NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, "freq_ranges", DT_NESTED, DF_REPEATED, &nl80211_radio_freq_range_nla }, ++ { NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, "interface_combinations", DT_NESTED, DF_REPEATED, &nl80211_ifcomb_nla }, ++ } ++}; ++ + static const uc_nl_nested_spec_t nl80211_msg = { + .headsize = 0, +- .nattrs = 128, ++ .nattrs = 129, + .attrs = { + { NL80211_ATTR_4ADDR, "4addr", DT_U8, 0, NULL }, + { NL80211_ATTR_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL }, +@@ -974,6 +993,7 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_ATTR_SOFTWARE_IFTYPES, "software_iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla }, + { NL80211_ATTR_MAX_AP_ASSOC_STA, "max_ap_assoc", DT_U16, 0, NULL }, + { NL80211_ATTR_SURVEY_INFO, "survey_info", DT_NESTED, 0, &nl80211_survey_info_nla }, ++ { NL80211_ATTR_WIPHY_RADIOS, "radios", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_radio_nla }, + } + }; + diff --git a/feeds/ucentral/ucode/patches/113-nl80211-add-new-attributes-for-multi-radio-support.patch b/feeds/ucentral/ucode/patches/113-nl80211-add-new-attributes-for-multi-radio-support.patch new file mode 100644 index 000000000..15e504751 --- /dev/null +++ b/feeds/ucentral/ucode/patches/113-nl80211-add-new-attributes-for-multi-radio-support.patch @@ -0,0 +1,30 @@ +From: Felix Fietkau +Date: Wed, 23 Oct 2024 18:50:10 +0200 +Subject: [PATCH] nl80211: add new attributes for multi-radio support + +- vif radio mask: used to assign vifs to specific radios +- monitor skip_tx flag: do not pass locally transmitted packets on the monitor interface +- radio antenna mask: radio specific part of the phy antenna mask + +Signed-off-by: Felix Fietkau +--- + +--- a/lib/nl80211.c ++++ b/lib/nl80211.c +@@ -863,7 +863,7 @@ static const uc_nl_nested_spec_t nl80211 + + static const uc_nl_nested_spec_t nl80211_msg = { + .headsize = 0, +- .nattrs = 129, ++ .nattrs = 130, + .attrs = { + { NL80211_ATTR_4ADDR, "4addr", DT_U8, 0, NULL }, + { NL80211_ATTR_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL }, +@@ -994,6 +994,7 @@ static const uc_nl_nested_spec_t nl80211 + { NL80211_ATTR_MAX_AP_ASSOC_STA, "max_ap_assoc", DT_U16, 0, NULL }, + { NL80211_ATTR_SURVEY_INFO, "survey_info", DT_NESTED, 0, &nl80211_survey_info_nla }, + { NL80211_ATTR_WIPHY_RADIOS, "radios", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_radio_nla }, ++ { NL80211_ATTR_VIF_RADIO_MASK, "vif_radio_mask", DT_U32, 0, NULL }, + } + }; + diff --git a/feeds/ucentral/ucode/patches/200-fix-libnl-linking.patch b/feeds/ucentral/ucode/patches/200-fix-libnl-linking.patch new file mode 100644 index 000000000..789c78475 --- /dev/null +++ b/feeds/ucentral/ucode/patches/200-fix-libnl-linking.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: OpenWrt Build System +Date: Sun, 24 Nov 2024 00:00:00 +0000 +Subject: [PATCH] cmake: explicitly link nl80211 and rtnl modules against libnl-tiny + +The nl80211 and rtnl modules use symbols from libnl-tiny but were not +being linked against it when the CMake find_library() call failed to +populate the ${libnl_tiny} variable. This resulted in undefined symbol +errors at runtime when loading the modules. + +Fix this by explicitly specifying the nl-tiny library name in +target_link_libraries() calls instead of relying on the CMake variable. + +Fixes runtime error: + Unable to dlopen file '/usr/lib/ucode/nl80211.so': Error relocating + /usr/lib/ucode/nl80211.so: nl_socket_free: symbol not found + +Signed-off-by: OpenWrt Build System +--- + CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -189,7 +189,7 @@ if(RTNL_SUPPORT) + add_library(rtnl_lib MODULE lib/rtnl.c) + set_target_properties(rtnl_lib PROPERTIES OUTPUT_NAME rtnl PREFIX "") + target_link_options(rtnl_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) +- target_link_libraries(rtnl_lib ${libnl_tiny} ${libubox}) ++ target_link_libraries(rtnl_lib nl-tiny ubox) + endif() + + if(NL80211_SUPPORT) +@@ -199,6 +199,6 @@ if(NL80211_SUPPORT) + add_library(nl80211_lib MODULE lib/nl80211.c) + set_target_properties(nl80211_lib PROPERTIES OUTPUT_NAME nl80211 PREFIX "") + target_link_options(nl80211_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) +- target_link_libraries(nl80211_lib ${libnl_tiny} ${libubox}) ++ target_link_libraries(nl80211_lib nl-tiny ubox) + endif() + diff --git a/patches/rest/0900-uboot-envtools-add-udaya-board.patch b/patches/rest/0900-uboot-envtools-add-udaya-board.patch new file mode 100644 index 000000000..dbc595b3e --- /dev/null +++ b/patches/rest/0900-uboot-envtools-add-udaya-board.patch @@ -0,0 +1,25 @@ +From d5a42e3d2420609a2c745b47e52bbc173e202271 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Dec 2025 08:01:28 +0100 +Subject: [PATCH 900/903] uboot-envtools: add udaya board + +Signed-off-by: John Crispin +--- + package/boot/uboot-envtools/files/ipq40xx | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/package/boot/uboot-envtools/files/ipq40xx b/package/boot/uboot-envtools/files/ipq40xx +index d112d9eb40..704e58e462 100644 +--- a/package/boot/uboot-envtools/files/ipq40xx ++++ b/package/boot/uboot-envtools/files/ipq40xx +@@ -41,6 +41,7 @@ glinet,gl-b1300 |\ + luma,wrtq-329acn |\ + openmesh,a42 |\ + openmesh,a62 |\ ++udaya,a5-id2|\ + plasmacloud,pa1200 |\ + plasmacloud,pa2200) + ubootenv_add_uci_config "/dev/mtd5" "0x0" "0x10000" "0x10000" +-- +2.34.1 + diff --git a/patches/rest/0901-libnl-tiny-update-to-version-used-in-v4.2.patch b/patches/rest/0901-libnl-tiny-update-to-version-used-in-v4.2.patch new file mode 100644 index 000000000..94f951f45 --- /dev/null +++ b/patches/rest/0901-libnl-tiny-update-to-version-used-in-v4.2.patch @@ -0,0 +1,55 @@ +From 7650ca560c9673935e6080be35c497d5e908f1de Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Dec 2025 08:01:48 +0100 +Subject: [PATCH 901/903] libnl-tiny: update to version used in v4.2 + +Signed-off-by: John Crispin +--- + package/libs/libnl-tiny/Makefile | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/package/libs/libnl-tiny/Makefile b/package/libs/libnl-tiny/Makefile +index 48df6a4c8c..4bdd7c1c39 100644 +--- a/package/libs/libnl-tiny/Makefile ++++ b/package/libs/libnl-tiny/Makefile +@@ -12,9 +12,9 @@ PKG_RELEASE:=1 + + PKG_SOURCE_PROTO:=git + PKG_SOURCE_URL=$(PROJECT_GIT)/project/libnl-tiny.git +-PKG_SOURCE_DATE:=2022-05-17 +-PKG_SOURCE_VERSION:=b5b2ba09c4f1c8b3c21580aea7223edc2f5e92be +-PKG_MIRROR_HASH:=b957d56aa8c2e7b55184111be69eb8dea734f1feba19e670a91f302459a48a78 ++PKG_SOURCE_DATE:=2023-07-27 ++PKG_SOURCE_VERSION:=bc92a280186f9becc53c0f17e4e43cfbdeec7e7b ++PKG_MIRROR_HASH:=57c5ac75fdb4413e98e525bee7de419fc6cce5f23389581dafd9ffe22321224d + CMAKE_INSTALL:=1 + + PKG_LICENSE:=LGPL-2.1 +@@ -27,7 +27,7 @@ define Package/libnl-tiny + SECTION:=libs + CATEGORY:=Libraries + TITLE:=netlink socket library +- ABI_VERSION:=$(PKG_SOURCE_DATE) ++ ABI_VERSION:=1 + endef + + define Package/libnl-tiny/description +@@ -37,13 +37,13 @@ endef + define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/lib/pkgconfig $(1)/usr/include/libnl-tiny + $(CP) $(PKG_INSTALL_DIR)/usr/include/libnl-tiny/* $(1)/usr/include/libnl-tiny +- $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libnl-tiny.so $(1)/usr/lib/ ++ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libnl-tiny.so* $(1)/usr/lib/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/libnl-tiny.pc $(1)/usr/lib/pkgconfig + endef + + define Package/libnl-tiny/install + $(INSTALL_DIR) $(1)/usr/lib +- $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libnl-tiny.so $(1)/usr/lib/ ++ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libnl-tiny.so.* $(1)/usr/lib/ + endef + + $(eval $(call BuildPackage,libnl-tiny)) +-- +2.34.1 + diff --git a/patches/rest/0902-libubox-update-to-version-used-in-v4.2.patch b/patches/rest/0902-libubox-update-to-version-used-in-v4.2.patch new file mode 100644 index 000000000..4a5993816 --- /dev/null +++ b/patches/rest/0902-libubox-update-to-version-used-in-v4.2.patch @@ -0,0 +1,56 @@ +From ba7a1aa768715d9a533f9c402e30ee7baea9f35b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Dec 2025 08:02:16 +0100 +Subject: [PATCH 902/903] libubox: update to version used in v4.2 + +Signed-off-by: John Crispin +--- + package/libs/libubox/Makefile | 8 ++++---- + .../libs/libubox/patches/001-CLOCK_BOOTTIME.patch | 13 +++++++++++++ + 2 files changed, 17 insertions(+), 4 deletions(-) + create mode 100644 package/libs/libubox/patches/001-CLOCK_BOOTTIME.patch + +diff --git a/package/libs/libubox/Makefile b/package/libs/libubox/Makefile +index ad9016edb8..6bba69ed27 100644 +--- a/package/libs/libubox/Makefile ++++ b/package/libs/libubox/Makefile +@@ -1,13 +1,13 @@ + include $(TOPDIR)/rules.mk + + PKG_NAME:=libubox +-PKG_RELEASE=2 ++PKG_RELEASE=1 + + PKG_SOURCE_PROTO:=git + PKG_SOURCE_URL=$(PROJECT_GIT)/project/libubox.git +-PKG_MIRROR_HASH:=f9f9c631e8167597ce721c64a0023dae9e0f1b2cc37ae846a49984d535a3c480 +-PKG_SOURCE_DATE:=2023-07-05 +-PKG_SOURCE_VERSION:=75a3b870cace1171faf57bd55e5a9a2f1564f757 ++PKG_MIRROR_HASH:=242e33eca235124c7e005d25fbc8f8bf08a324335343e60278d4535c91157ba4 ++PKG_SOURCE_DATE:=2024-03-29 ++PKG_SOURCE_VERSION:=eb9bcb64185ac155c02cc1a604692c4b00368324 + PKG_ABI_VERSION:=$(call abi_version_str,$(PKG_SOURCE_DATE)) + CMAKE_INSTALL:=1 + +diff --git a/package/libs/libubox/patches/001-CLOCK_BOOTTIME.patch b/package/libs/libubox/patches/001-CLOCK_BOOTTIME.patch +new file mode 100644 +index 0000000000..5e78d5ade9 +--- /dev/null ++++ b/package/libs/libubox/patches/001-CLOCK_BOOTTIME.patch +@@ -0,0 +1,13 @@ ++Index: libubox-2023-05-23-82fa6480/uloop.c ++=================================================================== ++--- libubox-2023-05-23-82fa6480.orig/uloop.c +++++ libubox-2023-05-23-82fa6480/uloop.c ++@@ -293,7 +293,7 @@ static void uloop_gettime(struct timeval ++ { ++ struct timespec ts; ++ ++- clock_gettime(CLOCK_MONOTONIC, &ts); +++ clock_gettime(CLOCK_BOOTTIME, &ts); ++ tv->tv_sec = ts.tv_sec; ++ tv->tv_usec = ts.tv_nsec / 1000; ++ } +-- +2.34.1 + diff --git a/patches/rest/0903-ipq40xx-add-insta-partitions.patch b/patches/rest/0903-ipq40xx-add-insta-partitions.patch new file mode 100644 index 000000000..2171db470 --- /dev/null +++ b/patches/rest/0903-ipq40xx-add-insta-partitions.patch @@ -0,0 +1,42 @@ +From 85a7d0292d962a3db6549871e5402e40939ddb1c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Dec 2025 08:02:43 +0100 +Subject: [PATCH 903/903] ipq40xx: add insta partitions + +Signed-off-by: John Crispin +--- + .../arch/arm/boot/dts/qcom-ipq4018-udaya-a5-id2.dts | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-udaya-a5-id2.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-udaya-a5-id2.dts +index ee2d4ac374..421113607e 100644 +--- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-udaya-a5-id2.dts ++++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4018-udaya-a5-id2.dts +@@ -90,7 +90,6 @@ + APPSBLENV@e0000 { + label = "APPSBLENV"; + reg = <0xe0000 0x10000>; +- read-only; + }; + APPSBL@f0000 { + label = "APPSBL"; +@@ -104,7 +103,15 @@ + }; + firmware@180000 { + label = "firmware"; +- reg = <0x180000 0x1e70000>; ++ reg = <0x180000 0x1e50000>; ++ }; ++ insta1@1ff0000 { ++ label = "insta1"; ++ reg = <0x1fd0000 0x10000>; ++ }; ++ insta2@1ff0000 { ++ label = "insta2"; ++ reg = <0x1fe0000 0x10000>; + }; + certificates@1ff0000 { + label = "certificates"; +-- +2.34.1 + diff --git a/profiles/ucentral-ap.yml b/profiles/ucentral-ap.yml index 322743726..54f28638a 100644 --- a/profiles/ucentral-ap.yml +++ b/profiles/ucentral-ap.yml @@ -20,7 +20,7 @@ packages: - curl - dnsmasq-full - dynamic-vlan - - firstcontact + - cloud_discovery - gre - ethtool - ieee8021x