#!/bin/sh /etc/rc.common START=99 STOP=01 USE_PROCD=1 PROG=/usr/sbin/uspc CONFIGURATION=uspc RESET_FILE="/tmp/usp/uspc_param_reset.txt" SQL_DB_FILE="/tmp/usp/uspc.db" BASEPATH="" INSTANCE_COUNT=0 . /usr/share/libubox/jshn.sh . /lib/functions.sh global_init() { BASEPATH="" INSTANCE_COUNT=0 } log() { echo "$*"|logger -t uspc.init -p debug } db_set_reset_file() { local param value param="${1}" shift value="$*" if [ -n "${param}" ] && [ -n "${value}" ]; then echo "${param} \"${value}\"">>${RESET_FILE} else echo >>${RESET_FILE} fi } db_set() { db_set_reset_file "$@" } # if db present then check if it matches with existing instances # fallback to max instance present + 1 # In case of no db get the count get_base_path() { local refpath value path count refpath="${1}" value="${2}" path="" count=0 if [ -z "${path}" ]; then INSTANCE_COUNT=$(( INSTANCE_COUNT + 1 )) path="${refpath}${INSTANCE_COUNT}" fi BASEPATH="${path}" } get_refrence_path() { local dmref value path dmref="${1}" value="${2}" path="" path=$(grep "${dmref}\d.Alias " ${RESET_FILE}|grep -w "${value}") path=${path%.*} echo "${path}" } convert_to_hex() { local val="" local optval="${1}" OPTIND=1 while getopts ":" opt "-$optval" do temp=$(printf "%02X" "'${OPTARG:-:}") val="${val}:${temp}" done echo "${val}" } check_for_suboptions() { new_opt="" # Check if option 25 and 29 present inside enterprise id 3561 data=$(echo "${1}" | sed 's/://g') len=$(printf "${data}"|wc -c) rem_len="${len}" while [ $rem_len -gt 8 ]; do subopt_present=0 ent_id="${data:0:8}" ent_id=$(printf "%d\n" "0x$ent_id") if [ $ent_id -ne 3561 ]; then len_val=${data:8:2} data_len=$(printf "%d\n" "0x$len_val") # add 4 byte for ent_id and 1 byte for len data_len=$(( data_len * 2 + 10 )) opt=${data:0:"${data_len}"} if [ -n "${new_opt}" ]; then new_opt="${new_opt}:$(echo ${opt} | sed 's/../&:/g;s/:$//')" else new_opt="$(echo ${opt} | sed 's/../&:/g;s/:$//')" fi # move ahead data to next enterprise id data=${data:"${data_len}":"${rem_len}"} rem_len=$(( rem_len - data_len )) continue fi # read the length of enterprise data len_val=${data:8:2} data_len=$(printf "%d\n" "0x$len_val") # add 4 byte for ent_id and 1 byte for len data_len=$(( data_len * 2 + 10 )) len_val=${data:8:2} opt_len=$(printf "%d\n" "0x$len_val") if [ $opt_len -eq 0 ]; then echo "${new_opt}" return 0 fi # populate the option data of enterprise id sub_data_len=$(( opt_len * 2)) # starting 10 means ahead of length field sub_data=${data:10:"${sub_data_len}"} # parsing of suboption of option 125 while [ $sub_data_len -gt 0 ]; do # get the suboption id sub_opt_id=${sub_data:0:2} sub_opt_id=$(printf "%d\n" "0x$sub_opt_id") case "${sub_opt_id}" in "25") subopt_present=1 ;; "26") subopt_present=1 ;; "27") subopt_present=1 ;; "28") subopt_present=1 ;; "29") subopt_present=1 ;; esac if [ $subopt_present -eq 1 ]; then break; fi # get the length of suboption sub_opt_len=${sub_data:2:2} sub_opt_len=$(printf "%d\n" "0x$sub_opt_len") sub_opt_len=$(( sub_opt_len * 2 )) # add 2 bytes for sub_opt id and sub_opt len field sub_opt_end=$(( sub_opt_len + 4 )) # update the remaining sub option hex string length sub_data_len=$((sub_data_len - sub_opt_end)) # fetch next sub option hex string sub_data=${sub_data:${sub_opt_end}:${sub_data_len}} done if [ $subopt_present -eq 1 ]; then # move ahead data to next enterprise id rem_len=$(( rem_len - $data_len )) data=${data:"${data_len}":"${rem_len}"} else opt=${data:0:"${data_len}"} if [ -n "${new_opt}" ]; then new_opt="${new_opt}:$(echo ${opt} | sed 's/../&:/g;s/:$//')" else new_opt="$(echo ${opt} | sed 's/../&:/g;s/:$//')" fi # move ahead data to next enterprise id rem_len=$(( rem_len - $data_len )) data=${data:"${data_len}":"${rem_len}"} fi done echo "${new_opt}" } configure_dnsmasq_op125() { intf="${1}" endpoint="" proto="" address="" port="" topic="" prov_code="obusp-client" interval="5" multiplier="2000" config_load ${CONFIGURATION} config_get endpoint controller EndpointID config_get proto controller Protocol if [ -z "${endpoint}" ] || [ -z "${proto}" ]; then return 0 fi if [ "${proto}" = "MQTT" ]; then config_get port mqtt BrokerPort "1883" config_get topic controller ResponseTopicConfigured proto="mqtt://" else return 0 fi address=$(ifstatus "${intf}" | jsonfilter -q -e '@["ipv4-address"][0].address') if [ -z "${address}" ] || [ -z "${topic}" ]; then return 0 fi subop_present=0 opt125="125," base_opt="" service="$(uci -q get dhcp."${intf}".dhcpv4)" if [ "${service}" = "server" ]; then opt_list="$(uci -q get dhcp."${intf}".dhcp_option)" for sopt in $opt_list; do if [[ "$sopt" == "$opt125"* ]]; then base_opt=$(check_for_suboptions "${sopt:4}") uci -q del_list dhcp."${intf}".dhcp_option="$sopt" uci -q commit dhcp break fi done else return 0 fi if [ -z "${base_opt}" ]; then opt125="125,00:00:0D:E9" else opt125="125,${base_opt}:00:00:0D:E9" fi url="${proto}${address}:${port}${topic}" url_len=$(echo -n "${url}" | wc -m) prov_code_len=$(echo -n "${prov_code}" | wc -m) endpoint_len=$(echo -n "${endpoint}" | wc -m) interval_len=$(echo -n "${interval}" | wc -m) multiplier_len=$(echo -n "${multiplier}" | wc -m) ([ ${url_len} -gt 255 ] || [ ${prov_code_len} -gt 255 ] || [ ${endpoint_len} -gt 255 ]) && return 0 ([ ${interval_len} -gt 255 ] || [ ${multiplier_len} -gt 255 ]) && return 0 opt125_len=$((url_len + prov_code_len + endpoint_len + interval_len + multiplier_len)) opt125_len=$((opt125_len + 10)) [ $opt125_len -gt 255 ] && return 0 hex_opt125_len=$(printf "%02X" "${opt125_len}") opt125="${opt125}:${hex_opt125_len}" hex_url=$(convert_to_hex "${url}") if [ -z "${hex_url}" ]; then return 0 fi hex_url_len=$(printf "%02X" "${url_len}") opt125="${opt125}:19:${hex_url_len}${hex_url}" hex_prov_code=$(convert_to_hex "${prov_code}") if [ -z "${hex_prov_code}" ]; then return 0 fi hex_prov_len=$(printf "%02X" "${prov_code_len}") opt125="${opt125}:1A:${hex_prov_len}${hex_prov_code}" hex_interval=$(convert_to_hex "${interval}") if [ -z "${hex_interval}" ]; then return 0 fi hex_interval_len=$(printf "%02X" "${interval_len}") opt125="${opt125}:1B:${hex_interval_len}${hex_interval}" hex_multiplier=$(convert_to_hex "${multiplier}") if [ -z "${hex_multiplier}" ]; then return 0 fi hex_multiplier_len=$(printf "%02X" "${multiplier_len}") opt125="${opt125}:1C:${hex_multiplier_len}${hex_multiplier}" hex_endpoint=$(convert_to_hex "${endpoint}") if [ -z "${hex_endpoint}" ]; then return 0 fi hex_endpoint_len=$(printf "%02X" "${endpoint_len}") opt125="${opt125}:1D:${hex_endpoint_len}${hex_endpoint}" uci -q add_list dhcp."${intf}".dhcp_option="$opt125" ubus call uci commit '{"config":"dhcp"}' } boot() { local enabled local interface config_load ${CONFIGURATION} config_get_bool enabled global enabled 0 config_get interface global interface "lan" if [ "${enabled}" -eq 0 ]; then return 0; fi # Check if device is an extender nothing to do lan_proto="$(uci -q get network.lan.proto)" if [ "${lan_proto}" == "dhcp" ]; then # extender so return return 0; fi configure_dnsmasq_op125 "${interface}" start } validate_global_section() { uci_validate_section ${CONFIGURATION} uspc "${1}" \ 'enabled:bool:1' \ 'debug:bool:0' \ 'prototrace:bool:0' \ 'log_level:uinteger' \ 'log_dest:string' \ 'db_file:string' } validate_mqtt_client_section() { uci_validate_section ${CONFIGURATION} mqtt "${1}" \ 'Enable:bool:1' \ 'BrokerAddress:string' \ 'BrokerPort:port:1883' \ 'Username:string' \ 'Password:string' \ 'ProtocolVersion:or("3.1", "3.1.1","5.0"):5.0' \ 'TransportProtocol:or("TCP/IP","TLS"):TCP/IP' \ 'ClientID:string' } validate_controller_section() { uci_validate_section ${CONFIGURATION} mtp "${1}" \ 'EndpointID:string' \ 'Protocol:or("MQTT", "WebSocket")' \ 'ResponseTopicConfigured:string' \ 'Destination:string' \ 'Port:port' \ 'Path:string' \ 'mqtt:string' \ 'stomp:string' \ 'Reference:string' \ 'EnableEncryption:bool:0' } configure_controller() { local EndpointID Protocol ResponseTopicConfigured local Destination Path Port EnableEncryption Reference local stomp mqtt dm_ref sec sec="${1}" validate_controller_section "${1}" || { log "Validation of mtp section failed" return 1; } if [ -z "${EndpointID}" ]; then log "EndpointID not defined for controller" return 1; fi db_set Device.LocalAgent.EndpointID "${EndpointID}" sec="${sec/mtp_/cpe-}" get_base_path "Device.LocalAgent.MTP." "${sec}" if [ -z "${BASEPATH}" ]; then log "Failed to get path [$BASEPATH]" return 1; fi if [ -z "${Protocol}" ]; then log "Protocol not defined for the mtp[${1}] section" return 1; fi dm_ref="" if [ -z "${Reference}" ]; then if [ "${Protocol}" = "STOMP" ]; then stomp="${stomp/stomp_/cpe-}" dm_ref=$(get_refrence_path "Device.STOMP.Connection." "${stomp}") elif [ "${Protocol}" = "MQTT" ]; then mqtt="${mqtt/mqtt_/cpe-}" dm_ref=$(get_refrence_path "Device.MQTT.Client." "${mqtt}") fi else dm_ref="${Reference}" fi db_set "${BASEPATH}.Alias" "${sec}" db_set "${BASEPATH}.Enable" "1" db_set "${BASEPATH}.Protocol" "${Protocol}" if [ "${Protocol}" = "MQTT" ]; then db_set "${BASEPATH}.MQTT.Reference" "${dm_ref}" db_set "${BASEPATH}.MQTT.ResponseTopicConfigured" "${ResponseTopicConfigured}" elif [ "${Protocol}" = "STOMP" ]; then db_set "${BASEPATH}.STOMP.Reference" "${dm_ref}" db_set "${BASEPATH}.STOMP.Destination" "${Destination}" elif [ "${Protocol}" = "CoAP" ]; then db_set "${BASEPATH}.CoAP.Path" "${Path}" db_set "${BASEPATH}.CoAP.Port" "${Port}" elif [ "${Protocol}" = "WebSocket" ]; then db_set "${BASEPATH}.WebSocket.Path" "${Path}" db_set "${BASEPATH}.WebSocket.Port" "${Port}" db_set "${BASEPATH}.WebSocket.EnableEncryption" "${EnableEncryption}" fi db_set } validate_agent_section() { uci_validate_section ${CONFIGURATION} agent "${1}" \ 'name:string' \ 'EndpointID:string' \ 'Topic:string' } configure_agent() { local EndpointID Topic name local stomp mqtt dm_ref sec sec="${1}" validate_agent_section "${1}" || { log "Validation of agent section failed" return 1; } if [ -z "${EndpointID}" ]; then log "EndpointID not defined for agent" return 1; fi if [ -z "${Topic}" ]; then log "Topic not defined for agent" return 1; fi sec="${sec/mtp_/cpe-}" get_base_path "Device.LocalAgent.Controller." "${sec}" if [ -z "${BASEPATH}" ]; then log "Failed to get path [$BASEPATH]" return 1; fi db_set "${BASEPATH}.Alias" "${sec}" db_set "${BASEPATH}.Enable" "1" db_set "${BASEPATH}.EndpointID" "${EndpointID}" db_set "${BASEPATH}.MTP.1.Enable" "1" db_set "${BASEPATH}.MTP.1.Protocol" "MQTT" db_set "${BASEPATH}.MTP.1.MQTT.Reference" "Device.MQTT.Client.1" db_set "${BASEPATH}.MTP.1.MQTT.Topic" "${Topic}" db_set } configure_mqtt_client() { local BrokerAddress BrokerPort Enable Username Password ProtocolVersion local TransportProtocol ClientID local sec sec="${1}" validate_mqtt_client_section "${1}" || { log "Validation of mqtt section failed" return 1; } sec="${sec/mqtt_/cpe-}" get_base_path "Device.MQTT.Client." "${sec}" if [ -z "${BASEPATH}" ]; then log "Failed to get path [$BASEPATH]" return 1; fi db_set "${BASEPATH}.Alias" "${sec}" db_set "${BASEPATH}.Enable" "${Enable}" db_set "${BASEPATH}.BrokerAddress" "${BrokerAddress}" db_set "${BASEPATH}.BrokerPort" "${BrokerPort}" db_set "${BASEPATH}.Username" "${Username}" db_set "${BASEPATH}.Password" "${Password}" db_set "${BASEPATH}.ProtocolVersion" "${ProtocolVersion}" db_set "${BASEPATH}.TransportProtocol" "${TransportProtocol}" db_set "${BASEPATH}.ClientID" "${ClientID}" db_set } configure_uspc() { local enabled trust_cert ifname interface debug prototrace log_level db_file log_dest local client_cert validate_global_section "global" if [ "${debug}" -ne "0" ]; then # Forward stdout of the command to logd procd_set_param stdout 1 # Same for stderr procd_set_param stderr 1 fi procd_append_param command -u if [ "${prototrace}" -eq 1 ]; then procd_append_param command -p fi if [ -n "${log_level}" ]; then procd_append_param command -v "${log_level}" fi if [ -n "${log_dest}" ]; then procd_append_param command -l "${log_dest}" fi if [ -f "${RESET_FILE}" ]; then procd_append_param command -r ${RESET_FILE} fi } # Create factory reset file db_init() { # Load configuration config_load $CONFIGURATION config_get SQL_DB_FILE global db_file "/tmp/usp/uspc.db" # Remove DB and generate from uci [ -f "${SQL_DB_FILE}" ] && rm -f "${SQL_DB_FILE}" # Remove reset file if present [ -f "${RESET_FILE}" ] && rm -f ${RESET_FILE} config_load $CONFIGURATION global_init config_foreach configure_mqtt_client mqtt global_init config_foreach configure_controller controller global_init config_foreach configure_agent agent return 0; } register_service() { procd_open_instance ${CONFIGURATION} procd_set_param command ${PROG} configure_uspc procd_set_param respawn \ "${respawn_threshold:-5}" \ "${respawn_timeout:-10}" "${respawn_retry:-3}" procd_close_instance } start_service() { local enabled mode config_load ${CONFIGURATION} config_get_bool enabled global enabled 0 if [ "${enabled}" -eq 0 ]; then return 0; fi # Check if device is an extender then do not start the service lan_proto="$(uci -q get network.lan.proto)" if [ "${lan_proto}" == "dhcp" ]; then # extender so return return 0; fi mkdir -p /tmp/usp/ db_init register_service } stop_service() { ${PROG} -c stop >/dev/null 2>&1 } reload_service() { stop start } service_triggers() { procd_add_reload_trigger "uspc" }