diff --git a/net/udpbroadcastrelay/Makefile b/net/udpbroadcastrelay/Makefile new file mode 100644 index 000000000..22783a489 --- /dev/null +++ b/net/udpbroadcastrelay/Makefile @@ -0,0 +1,8 @@ +PLUGIN_NAME= os-udpbroadcastrelay +PLUGIN_VERSION= 0.7 +PLUGIN_DEVEL= YES +PLUGIN_COMMENT= Control ubpbroadcastrelay processes +PLUGIN_DEPENDS= udpbroadcastrelay +PLUGIN_MAINTAINER= mjwasley@gmail.com + +.include "../../Mk/plugins.mk" diff --git a/net/udpbroadcastrelay/pkg-descr b/net/udpbroadcastrelay/pkg-descr new file mode 100644 index 000000000..1a1c37af0 --- /dev/null +++ b/net/udpbroadcastrelay/pkg-descr @@ -0,0 +1,29 @@ +udbproadcastrelay is a UDP multicast relayer. Its intended use is to +rebroadbcast udp packets on a specific port across interfaces, be those +interfaces physical or VLAN. + +It is used where devices such as Sonos or Sky are spread accross +different subnets and are not able to detect the servers or devices. + +Examples of different devices and the ports are as follows: + +Syncthing discovery +udp_vars="--id 1 --port 21027 --dev igb1 --dev igb2" + +mDNS / Broadcast DNS (Chromecast Discovery + Bonjour + More) +udp_vars="--id 1 --port 5353 --dev eth0 --dev eth1 --multicast 224.0.0.251 -s 1.1.1.1" + +(Chromecast requires broadcasts to originate from an address on its subnet) use +the rebroadbcast address option. + +SSDP (Sonos Roku Discovery + More) +udp_vars="--id 1 --port 1900 --dev eth0 --dev eth1 --multicast 239.255.255.250" + +Lifx Bulb Discovery +udp_vars=" --id 1 --port 56700 --dev eth0 --dev eth1" + +Broadlink IR Emitter Discovery +udp_vars=" --id 1 --port 80 --dev eth0 --dev eth1" + +Warcraft 3 Server Discovery +udp_vars=" --id 1 --port 6112 --dev eth0 --dev eth1" diff --git a/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc b/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc new file mode 100644 index 000000000..1a3b3aa82 --- /dev/null +++ b/net/udpbroadcastrelay/src/etc/inc/plugins.inc.d/udpbroadcastrelay.inc @@ -0,0 +1,83 @@ +udpbroadcastrelay->iterateItems() as $server) { + if ($server->enabled == '1') { + return true; + } + } + + return false; +} + +function udpbroadcastrelay_firewall($fw) +{ + if (!udpbroadcastrelay_enabled()) { + return; + } +} + +function udpbroadcastrelay_services() +{ + $services = array(); + + if (!udpbroadcastrelay_enabled()) { + return $services; + } + + $model = new \OPNsense\UDPBroadcastRelay\UDPBroadcastRelay(); + + foreach ($model->udpbroadcastrelay->iterateItems() as $server) { + + if ($server->enabled == '0'){ + continue; + } + + $services[] = array( + + 'description' => $server->description, + 'id' => $server->InstanceID, + 'pidfile' => "/var/run/udpbroadcastrelay_{$server->InstanceID}.pid", + 'configd' => array( + 'restart' => array('udpbroadcastrelay restart '.$server->InstanceID), + 'start' => array('udpbroadcastrelay start '.$server->InstanceID), + 'stop' => array('udpbroadcastrelay stop '.$server->InstanceID), + + ), + + 'name' => 'udpbroadcastrelay', + ); + + } + + return $services; +} diff --git a/net/udpbroadcastrelay/src/etc/rc.d/os-udpbroadcastrelay b/net/udpbroadcastrelay/src/etc/rc.d/os-udpbroadcastrelay new file mode 100644 index 000000000..d076d6901 --- /dev/null +++ b/net/udpbroadcastrelay/src/etc/rc.d/os-udpbroadcastrelay @@ -0,0 +1,170 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +# PROVIDE: osudpbroadcastrelay +# KEYWORD: shutdown + +. /etc/rc.subr + +name="osudpbroadcastrelay" +rcvar="osudpbroadcastrelay_enable" +command="/usr/local/sbin/udpbroadcastrelay" +extra_commands="reload" +reload_cmd="udpbroadcastrelay_reload" + + +load_rc_config $name + +if [ -n "$2" ]; then + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${2} + pidfile="/var/run/udpbroadcastrelay_$2.pid" +fi + + +udpbroadcastrelay_start () { + udpbroadcastrelay_status + if [ $? -eq 0 ]; then # already running + return 0 + fi + run_rc_command "start" + if [ $? -eq 0 ]; then + cmd_string=`basename ${procname:-${command}}` + cmd="ps ax -o pid= -o command= | grep $cmd_string | grep -e "$osudpbroadcastrelay_flags" | grep -v grep | awk '{ print $1 }" + ps_pid=$cmd + if [ -z "$ps_pid" ]; then + err 1 "Cannot get pid for $cmd_string $osudpbroadcastrelay_flags" + fi + return $? + fi + return 1 +} + +udpbroadcastrelay_stop () { + udpbroadcastrelay_status + if [ $? -eq 1 ]; then # already stopped + return 0 + fi + run_rc_command "stop" + if [ $? -ne 0 ]; then + err 1 "Cannot stop udpbroadcastrelay with pid from $pidfile" + fi + rm -f $pidfile + return $? +} + +udpbroadcastrelay_restart () { + udpbroadcastrelay_stop + if [ $? -ne 0 ]; then + return $? + fi + udpbroadcastrelay_start + return $? +} + +udpbroadcastrelay_status () { + if [ -z "$pidfile" ]; then + err 1 "Instance name unknown" + fi + run_rc_command "status" + return $? +} + +udpbroadcastrelay_reload () { + osudpbroadcastrelay_flags="" + pidfile="" + # get running instances + ps ax -o pid= -o command= | grep "udpbroadcastrelay" | grep -v grep | while read line; do + # get instance name + instance=`echo $line | awk '{printf "%s", $4 }' | sed 's/\./_/g'` + # get instance flags + instance_flags="${line#*udpbroadcastrelay}" + # check if it should run + echo + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${instance} + + if [ -n "$osudpbroadcastrelay_flags" -a "$osudpbroadcastrelay_flags" = "$instance_flags" ]; then + echo "running instance $instance match config" + continue + fi + echo "running instance $instance not configured" + osudpbroadcastrelay_flags=$instance_flags + pidfile="/var/run/udpbroadcastrelay_$instance.pid" + udpbroadcastrelay_stop + done + # start configured instances + + if [ -n "$osudpbroadcastrelay_instances" ]; then + + for i in $osudpbroadcastrelay_instances; do + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i} + pidfile="/var/run/udpbroadcastrelay_$i.pid" + run_rc_command "start" + done + fi + return 0 +} + +case $1 in + start) + if [ -z "$osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i}" -o -z "$pidfile" ]; then + if [ -n "$osudpbroadcastrelay_instances" ]; then + for i in $osudpbroadcastrelay_instances; do + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i} + pidfile="/var/run/udpbroadcastrelay_$i.pid" + udpbroadcastrelay_start + done + fi + else + udpbroadcastrelay_start + fi + exit $? + ;; + stop) + if [ -z "$osudpbroadcastrelay_flags" -o -z "$pidfile" ]; then + if [ -n "$osudpbroadcastrelay_instances" ]; then + for i in $osudpbroadcastrelay_instances; do + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i} + pidfile="/var/run/udpbroadcastrelay_$i.pid" + udpbroadcastrelay_stop + done + fi + else + udpbroadcastrelay_stop + fi + exit $? + ;; + restart) + if [ -z "$osudpbroadcastrelay_flags" -o -z "$pidfile" ]; then + if [ -n "$osudpbroadcastrelay_instances" ]; then + for i in $osudpbroadcastrelay_instances; do + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i} + pidfile="/var/run/udpbroadcastrelay_$i.pid" + udpbroadcastrelay_restart + done + fi + else + udpbroadcastrelay_restart + fi + exit $? + ;; + status) + if [ -z "$osudpbroadcastrelay_flags" -a -z "$pidfile" ]; then + if [ -n "$osudpbroadcastrelay_instances" ]; then + for i in $osudpbroadcastrelay_instances; do + eval osudpbroadcastrelay_flags=\$osudpbroadcastrelay_${i} + pidfile="/var/run/udpbroadcastrelay_$i.pid" + udpbroadcastrelay_status + done + fi + else + udpbroadcastrelay_status + fi + exit $? + ;; + reload) + udpbroadcastrelay_reload; + exit $? + ;; +esac diff --git a/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/ServiceController.php b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/ServiceController.php new file mode 100644 index 000000000..d14f101f4 --- /dev/null +++ b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/ServiceController.php @@ -0,0 +1,174 @@ + "failed", "function" => "status"); + if ($this->request->isPost()) { + $this->sessionClose(); + } + if ($uuid != null) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + $result['result'] = $this->callBackend('status', $node); + } + } + return $result; + } + + /** + * start a udpbroadcastrelay process + * @param $uuid item unique id + * @return array + */ + public function startAction($uuid) + { + $result = array("result" => "failed", "function" => "start"); + if ($this->request->isPost()) { + $this->sessionClose(); + } + if ($uuid != null) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + $result['result'] = $this->callBackend('start', $node); + } + } + return $result; + } + + /** + * stop a udpbroadcastrelay process + * @param $uuid item unique id + * @return array + */ + public function stopAction($uuid) + { + $result = array("result" => "failed", "function" => "stop"); + if ($this->request->isPost()) { + $this->sessionClose(); + } + if ($uuid != null) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + $result['result'] = $this->callBackend('stop', $node); + } + } + return $result; + } + + /** + * restart a udpbroadcastrelay process + * @param $uuid item unique id + * @return array + */ + public function restartAction($uuid) + { + if ($this->request->isPost()) { + $this->sessionClose(); + } + if ($uuid != null) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + $result['result'] = $this->callBackend('restart', $node); + } + } + return $result; + } + + /** + * recreate configuration file from template + * @return array + */ + public function configAction() + { + $result = array("result" => "failed", "function" => "config"); + if ($this->request->isPost()) { + $this->sessionClose(); + } + $result['result'] = $this->callBackend('template'); + return $result; + } + + /** + * reload configuration + * @return array + */ + public function reloadAction() + { + if ($this->request->isPost()) { + $this->sessionClose(); + } + $result = $this->configAction(); + if ($result['result'] == 'OK') { + $result['function'] = "reload"; + $result['result'] = $this->callBackend('reload'); + } + return $result; + } + + /** + * call backend + * @param action, node + * @return string + */ + protected function callBackend($action, &$node = null) + { + $backend = new Backend(); + if ($node != null) { + $instance = preg_replace("/\./", "_", $node->InstanceID->__toString()); + return trim($backend->configdpRun('udpbroadcastrelay', array($action, $instance))); + } + if ($action == 'template') { + return trim($backend->configdRun('template reload OPNsense/UDPBroadcastRelay')); + } + if ($action == 'reload') { + return trim($backend->configdRun('udpbroadcastrelay reload')); + + } + return "Wrong action defined"; + } +} diff --git a/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/SettingsController.php b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/SettingsController.php new file mode 100644 index 000000000..8f08921d3 --- /dev/null +++ b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/Api/SettingsController.php @@ -0,0 +1,353 @@ +getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + // return node + return array("udpbroadcastrelay" => $node->getNodes()); + } + } else { + // generate new node, but don't save to disc + $node = $mdlUDPBroadcastRelay->udpbroadcastrelay->Add(); + return array("udpbroadcastrelay" => $node->getNodes()); + } + return array(); + } + + /** + * update udpbroadcastrelay with given properties + * @param $uuid item unique id + * @return array + */ + public function setRelayAction($uuid) + { + $result = array("result" => "failed"); + if ($this->request->isPost() && $this->request->hasPost("udpbroadcastrelay")) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + // keep a list to detect duplicates later + $CurrentProxies = $mdlUDPBroadcastRelay->getNodes(); + if ($uuid != null) { + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + $Enabled = $node->enabled->__toString(); + $result = array("result" => "failed", "validations" => array()); + $relayInfo = $this->request->getPost("udpbroadcastrelay"); + + $node->setNodes($relayInfo); + $valMsgs = $mdlUDPBroadcastRelay->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, "udpbroadcastrelay", $msg->getField()); + $result["validations"][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // check for duplicates + + foreach ($CurrentProxies['udpbroadcastrelay'] as $CurrentUUID => &$CurrentRelay) { + if ( + $node->InstanceID->__toString() == $CurrentRelay['InstanceID'] && + $node->listenport->__toString() == $CurrentRelay['listenport'] && + $uuid != $CurrentUUID + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.InstanceID" => "Instance ID already in use.", + "udpbroadcastrelay.listenport" => "Listen port already in use." + ) + ); + } + if ( + $node->listenport->__toString() == $CurrentRelay['listenport'] && + $uuid != $CurrentUUID + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.listenport" => "Listen Port already in use." + ) + ); + } + if ( + $node->InstanceID->__toString() == $CurrentRelay['InstanceID'] && + $uuid != $CurrentUUID + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.InstanceID" => "Instance ID already In use." + ) + ); + } + $result = count(explode(',',$node->interfaces)); + if ( + $result < 2 && + $uuid != $CurrentUUID + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.interfaces" => "At least two interfaces must be selected." + ) + ); + } + } + + // save config if validated correctly + $mdlUDPBroadcastRelay->serializeToConfig(); + Config::getInstance()->save(); + // reload config + $svcUDPBroadcastRelay = new ServiceController(); + $result = $svcUDPBroadcastRelay->reloadAction(); + } + } + } + } + return $result; + } + + /** + * add new udpbroadcastrelay and set with attributes from post + * @return array + */ + public function addRelayAction() + { + $result = array("result" => "failed"); + if ($this->request->isPost() && $this->request->hasPost("udpbroadcastrelay")) { + $result = array("result" => "failed", "validations" => array()); + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + // keep a list to detect duplicates later + $CurrentProxies = $mdlUDPBroadcastRelay->getNodes(); + $node = $mdlUDPBroadcastRelay->udpbroadcastrelay->Add(); + $node->setNodes($this->request->getPost("udpbroadcastrelay")); + + $valMsgs = $mdlUDPBroadcastRelay->performValidation(); + + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, "udpbroadcastrelay", $msg->getField()); + $result["validations"][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + foreach ($CurrentProxies['udpbroadcastrelay'] as &$CurrentRelay) { + if ( + $node->InstanceID->__toString() == $CurrentRelay['InstanceID'] && + $node->listenport->__toString() == $CurrentRelay['listenport'] + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.InstanceID" => "Instance ID already in use.", + "udpbroadcastrelay.listenport" => "Listen port already in use." + ) + ); + } + if ( + $node->listenport->__toString() == $CurrentRelay['listenport'] + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.listenport" => "Listen Port already in use." + ) + ); + } + if ( + $node->InstanceID->__toString() == $CurrentRelay['InstanceID'] + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.InstanceID" => "Instance ID already In use." + ) + ); + } + + $result = count(explode(',',$node->interfaces)); + if ( + $result < 2 + ) { + return array( + "result" => "failed", + "validations" => array( + "udpbroadcastrelay.interfaces" => "At least two interfaces must be selected." + ) + ); + } + } + + // save config if validated correctly + $mdlUDPBroadcastRelay->serializeToConfig(); + Config::getInstance()->save(); + // reload config + $svcUDPBroadcastRelay = new ServiceController(); + $result = $svcUDPBroadcastRelay->reloadAction(); + } + } + return $result; + } + + /** + * delete udpbroadcastrelay by uuid + * @param $uuid item unique id + * @return array status + */ + public function delRelayAction($uuid) + { + + $result = array("result" => "failed"); + if ($this->request->isPost()) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + if ($uuid != null) { + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + if ($mdlUDPBroadcastRelay->udpbroadcastrelay->del($uuid) == true) { + // if item is removed, serialize to config and save + $mdlUDPBroadcastRelay->serializeToConfig(); + Config::getInstance()->save(); + // reload config + $svcUDPBroadcastRelay = new ServiceController(); + $result = $svcUDPBroadcastRelay->reloadAction(); + } + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + + /** + * toggle udpbroadcastrelay by uuid (enable/disable) + * @param $uuid item unique id + * @return array status + */ + public function toggleRelayAction($uuid) + { + + $result = array("result" => "failed"); + if ($this->request->isPost()) { + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + if ($uuid != null) { + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == "1") { + $node->enabled = "0"; + } else { + $node->enabled = "1"; + } + // if item has toggled, serialize to config and save + $mdlUDPBroadcastRelay->serializeToConfig(); + Config::getInstance()->save(); + // reload config + $svcUDPBroadcastRelay = new ServiceController(); + $result = $svcUDPBroadcastRelay->reloadAction(); + } + } + } + return $result; + } + + /** + * + * search udpbroadcastrelay + * @return array + */ + public function searchRelayAction() + { + $this->sessionClose(); + $fields = array( + "enabled", + "interfaces", + "multicastaddress", + "sourceaddress", + "listenport", + "InstanceID", + "RevertTTL", + "description" + ); + $mdlUDPBroadcastRelay = new UDPBroadcastRelay(); + + $grid = new UIModelGrid($mdlUDPBroadcastRelay->udpbroadcastrelay); + $response = $grid->fetchBindRequest( + $this->request, + $fields, + "InstanceID" + ); + $svcUDPBroadcastRelay = new ServiceController(); + foreach ($response['rows'] as &$row) { + $result = $svcUDPBroadcastRelay->statusAction($row['uuid']); + if ($result['result'] == 'OK') { + $row['status'] = 0; + continue; + } + $node = $mdlUDPBroadcastRelay->getNodeByReference('udpbroadcastrelay.' . $row['uuid']); + if ($node != null) { + if ($node->enabled->__toString() == "1") { + $row['status'] = 2; + } else { + $row['status'] = 5; + } + } + } + + return $response; + } +} diff --git a/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/IndexController.php b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/IndexController.php new file mode 100644 index 000000000..6301a81bc --- /dev/null +++ b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/IndexController.php @@ -0,0 +1,47 @@ +view->formDialogEdit = $this->getForm("dialogEdit"); + $this->view->pick('OPNsense/UDPBroadcastRelay/index'); + } +} diff --git a/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/forms/dialogEdit.xml b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/forms/dialogEdit.xml new file mode 100644 index 000000000..f39ae5111 --- /dev/null +++ b/net/udpbroadcastrelay/src/opnsense/mvc/app/controllers/OPNsense/UDPBroadcastRelay/forms/dialogEdit.xml @@ -0,0 +1,52 @@ +
diff --git a/net/udpbroadcastrelay/src/opnsense/mvc/app/models/OPNsense/UDPBroadcastRelay/ACL/ACL.xml b/net/udpbroadcastrelay/src/opnsense/mvc/app/models/OPNsense/UDPBroadcastRelay/ACL/ACL.xml new file mode 100644 index 000000000..051ea7fe9 --- /dev/null +++ b/net/udpbroadcastrelay/src/opnsense/mvc/app/models/OPNsense/UDPBroadcastRelay/ACL/ACL.xml @@ -0,0 +1,9 @@ +| {{ lang._('Enabled') }} | +{{ lang._('Interfaces') }} | +{{ lang._('Multicast Addresses') }} | +{{ lang._('Source Address') }} | +{{ lang._('Listen Port') }} | +{{ lang._('ID') }} | +{{ lang._('Description') }} | +{{ lang._('ID') }} | +{{ lang._('Use ID as TTL') }} | +{{ lang._('Commands') }} | +
|---|---|---|---|---|---|---|---|---|---|
| + | + + + | +