net/udpbroadcastrelay plugin - Initial commit (#1677)

This commit is contained in:
Martin Wasley
2020-04-15 14:26:11 +02:00
committed by GitHub
parent c5f72d30bf
commit 55cffd5baf
16 changed files with 1214 additions and 0 deletions
+8
View File
@@ -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"
+29
View File
@@ -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"
@@ -0,0 +1,83 @@
<?php
/*
* Copyright (C) 2020 Martin Wasley
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
function udpbroadcastrelay_enabled()
{
$model = new \OPNsense\UDPBroadcastRelay\UDPBroadcastRelay();
foreach ($model->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;
}
@@ -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
@@ -0,0 +1,174 @@
<?php
/*
* Copyright (C) 2020 Martin Wasley
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\UDPBroadcastRelay\Api;
use \OPNsense\Base\ApiMutableModelControllerBase;
use \OPNsense\UDPBroadcastRelay\UDPBroadcastRelay;
use \OPNsense\Core\Backend;
/**
* Class ServiceController Handles settings related API actions for the UDPBroadcastRelay
* @package OPNsense\UDPBroadcastRelay
*/
class ServiceController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'udpbroadcastrelay';
protected static $internalModelClass = '\OPNsense\\UDPBroadcastRelay\\UDPBroadcastRelay';
protected static $internalModelUseSafeDelete = true;
public function statusAction($uuid)
{
$result = array("result" => "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";
}
}
@@ -0,0 +1,353 @@
<?php
/*
* Copyright (C) 2020 Martin Wasley
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\UDPBroadcastRelay\Api;
use \OPNsense\Base\ApiMutableModelControllerBase;
use \OPNsense\Core\Config;
use \OPNsense\UDPBroadcastRelay\UDPBroadcastRelay;
use \OPNsense\Base\UIModelGrid;
/**
* Class SettingsController Handles settings related API actions for the UDPBroadcastRelay
* @package OPNsense\UDPBroadcastRelay
*/
class SettingsController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'udpbroadcastrelay';
protected static $internalModelClass = '\OPNsense\UDPBroadcastRelay\UDPBroadcastRelay;';
protected static $internalModelUseSafeDelete = true;
/**
* Class SettingsController
* @package OPNsense\UDPBroadcastRelay
*/
/**
* retrieve udpbroadcastrelay settings or return defaults
* @param $uuid item unique id
* @return array
*/
public function getRelayAction($uuid = null)
{
$mdlUDPBroadcastRelay = new UDPBroadcastRelay();
if ($uuid != null) {
$node = $mdlUDPBroadcastRelay->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;
}
}
@@ -0,0 +1,47 @@
<?php
/*
* Copyright (C) 2020 Martin Wasley
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\UDPBroadcastRelay;
/**
* Class IndexController
* @package OPNsense\UDPBroadcastRelay
*/
class IndexController extends \OPNsense\Base\IndexController
{
/**
* udpbroadcastrelay index page
* @throws \Exception
*/
public function indexAction()
{
// include dialog form definitions
$this->view->formDialogEdit = $this->getForm("dialogEdit");
$this->view->pick('OPNsense/UDPBroadcastRelay/index');
}
}
@@ -0,0 +1,52 @@
<form>
<field>
<id>udpbroadcastrelay.enabled</id>
<label>enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable or disable this udp relay.]]></help>
</field>
<field>
<id>udpbroadcastrelay.listenport</id>
<label>Relay Port</label>
<type>text</type>
<help><![CDATA[Port where the relay will listen for packets to relay. e.g. Sonos is Port 1900.]]></help>
</field>
<field>
<id>udpbroadcastrelay.interfaces</id>
<label>Relay Interfaces</label>
<type>select_multiple</type>
<help>At least two interfaces must be selected.</help>
</field>
<field>
<id>udpbroadcastrelay.multicastaddress</id>
<label>Broadcast Address</label>
<type>select_multiple</type>
<allownew>true</allownew>
<style>tokenize</style>
<help><![CDATA[Multicast addresses to use on this port when relaying multicast packets. e.g Sonos is 239.255.255.250, you may enter multiple addresses.]]></help>
</field>
<field>
<id>udpbroadcastrelay.sourceaddress</id>
<label>Source Address</label>
<type>text</type>
<help><![CDATA[The relay will use this as the source address for packets leaving the interface. The special values of "1.1.1.1" and "1.1.1.2" mean for the relayed packet to use the outgoing interface's address as the source IP address. "1.1.1.1" also means use the original packet's destination port as the new source port for the relayed packet. "1.1.1.2" means keep the source port unaltered from the original. Chromecast users should use 1.1.1.1.]]></help>
</field>
<field>
<id>udpbroadcastrelay.InstanceID</id>
<label>Instance ID</label>
<type>text</type>
<help><![CDATA[Each Relay instance requires a unique ID. Valid values are 1 - 63]]></help>
</field>
<field>
<id>udpbroadcastrelay.RevertTTL</id>
<label>Use TTL for ID</label>
<type>checkbox</type>
<help><![CDATA[Use the ID to set the TTL Value of the packet (Original method) rather than DSCP, default is not ticked]]></help>
</field>
<field>
<id>udpbroadcastrelay.description</id>
<label>Description</label>
<type>text</type>
<help><![CDATA[Brief description of this udp relay]]></help>
</field>
</form>
@@ -0,0 +1,9 @@
<acl>
<page-services-udpbroadcastrelay>
<name>Services: UDP Broadcast Relay</name>
<patterns>
<pattern>ui/udpbroadcastrelay/*</pattern>
<pattern>api/udpbroadcastrelay/*</pattern>
</patterns>
</page-services-udpbroadcastrelay>
</acl>
@@ -0,0 +1,5 @@
<menu>
<Services>
<UDPBroadcastRelay VisibleName="UDP Broadcast Relay" cssClass="fa fa-bolt fa-fw" url="/ui/udpbroadcastrelay"/>
</Services>
</menu>
@@ -0,0 +1,39 @@
<?php
/*
* Copyright (C) 2020 Martin Wasley
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\UDPBroadcastRelay;
use OPNsense\Base\BaseModel;
/**
* Class UDPBroadcastRelay
* @package OPNsense\UDPBroadcastRelay
*/
class UDPBroadcastRelay extends BaseModel
{
}
@@ -0,0 +1,60 @@
<model>
<mount>//OPNsense/udpbroadcastrelays</mount>
<version>1.0.0</version>
<description>UDP Broadcast Relay settings</description>
<items>
<udpbroadcastrelay type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<interfaces type="InterfaceField">
<default>lan</default>
<Required>Y</Required>
<multiple>Y</multiple>
</interfaces>
<multicastaddress type="CSVListField">
<Required>N</Required>
<default></default>
<multiple>Y</multiple>
<mask>/^([\/0-9.,])*/u</mask>
<ValidationMessage>Broadcast address must be a valid IPv4 address</ValidationMessage>
</multicastaddress>
<sourceaddress type="TextField">
<Required>N</Required>
<default></default>
<mask>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/</mask>
<ValidationMessage>Source address must be a valid IPv4 address</ValidationMessage>
</sourceaddress>
<listenport type="IntegerField">
<default></default>
<Required>Y</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<ValidationMessage>Listen port needs to be an integer value between 1 and 65535</ValidationMessage>
</listenport>
<sourceaddress type="TextField">
<Required>N</Required>
<default></default>
<mask>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/</mask>
<ValidationMessage>Source address must be a valid IPv4 address</ValidationMessage>
</sourceaddress>
<InstanceID type="IntegerField">
<default>0</default>
<Required>Y</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>63</MaximumValue>
<ValidationMessage>InstanceID needs to be an integer value between 1 and 63</ValidationMessage>
</InstanceID>
<RevertTTL type="BooleanField">
<default>0</default>
<Required>Y</Required>
</RevertTTL>
<description type="TextField">
<Required>N</Required>
<mask>/^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u</mask>
<ValidationMessage>Enter a description.</ValidationMessage>
</description>
</udpbroadcastrelay>
</items>
</model>
@@ -0,0 +1,118 @@
{#
OPNsense® is Copyright © 2014 2017 by Deciso B.V.
Copyright (C) 2020 Martin Wasley
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
#}
<script>
$( document ).ready(function() {
/**
* inline open dialog, go back to previous page on exit
*/
function openDialog(uuid) {
var editDlg = "DialogEdit";
var setUrl = "/api/udpbroadcastrelay/settings/setRelay/";
var getUrl = "/api/udpbroadcastrelay/settings/getRelay/";
var urlMap = {};
urlMap['frm_' + editDlg] = getUrl + uuid;
mapDataToFormUI(urlMap).done(function () {
// update selectors
$('.selectpicker').selectpicker('refresh');
// clear validation errors (if any)
clearFormValidation('frm_' + editDlg);
// show
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
$('#'+editDlg).on('hidden.bs.modal', function () {
// go back to previous page on exit
parent.history.back();
});
});
}
/*************************************************************************************************************
* link grid actions
*************************************************************************************************************/
$("#grid-proxies").UIBootgrid(
{ 'search':'/api/udpbroadcastrelay/settings/searchRelay',
'get':'/api/udpbroadcastrelay/settings/getRelay/',
'set':'/api/udpbroadcastrelay/settings/setRelay/',
'add':'/api/udpbroadcastrelay/settings/addRelay/',
'del':'/api/udpbroadcastrelay/settings/delRelay/',
'toggle':'/api/udpbroadcastrelay/settings/toggleRelay/',
'options':{selection:false, multiSelect:false}
}
);
{% if (selected_uuid|default("") != "") %}
openDialog(uuid='{{selected_uuid}}');
{% endif %}
/*************************************************************************************************************
* Commands
*************************************************************************************************************/
});
</script>
<!-- <ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#grid-proxies">{{ lang._('UDPBroadcastRelays') }}</a></li>
</ul> -->
<div class="tab-content content-box tab-content">
<div id="udpbroadcastrelays" class="tab-pane fade in active">
<!-- tab page "udpbroadcastrelay items" -->
<table id="grid-proxies" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogEdit">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="interfaces" data-width="10em" data-type="string" data-visible="true">{{ lang._('Interfaces') }}</th>
<th data-column-id="multicastaddress" data-width="15em" data-type="string" data-visible="true">{{ lang._('Multicast Addresses') }}</th>
<th data-column-id="sourceaddress" data-width="11em" data-type="string" data-visible="true">{{ lang._('Source Address') }}</th>
<th data-column-id="listenport" data-width="8em" data-type="string" data-visible="true">{{ lang._('Listen Port') }}</th>
<th data-column-id="InstanceID" data-width="6em" data-type="string" data-visible="true">{{ lang._('ID') }}</th>
<th data-column-id="description" data-width="15em" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="RevertTTL" data-width="10em" data-type="string" data-visible="true" data-formatter="boolean">{{ lang._('Use ID as TTL') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<!-- <button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button> -->
</td>
</tr>
</tfoot>
</table>
</div>
</div>
{# include dialog #}
{{ partial("layout_partials/base_dialog",['fields':formDialogEdit,'id':'DialogEdit','label':lang._('Edit Relay')])}}
@@ -0,0 +1,29 @@
[start]
command:/usr/local/etc/rc.d/os-udpbroadcastrelay start
parameters:%s
type:script
message:starting udpbroadcastrelay instance
[stop]
command:/usr/local/etc/rc.d/os-udpbroadcastrelay stop
parameters:%s
type:script
message:stopping udpbroadcastrelay instance
[status]
command:/usr/local/etc/rc.d/os-udpbroadcastrelay status
parameters:%s
type:script
message:get udpbroadcastrelay instance status
[restart]
command:/usr/local/etc/rc.d/os-udpbroadcastrelay restart
parameters:%s
type:script
message:restarting udpbroadcastrelay instance
[reload]
command:/usr/local/etc/rc.d/os-udpbroadcastrelay reload
parameters:%s
type:script
message:reload udpbroadcastrelay
@@ -0,0 +1 @@
rc.conf.d:/etc/rc.conf.d/osudpbroadcastrelay
@@ -0,0 +1,37 @@
{% if helpers.exists('OPNsense.udpbroadcastrelays.udpbroadcastrelay') %}
{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
osudpbroadcastrelay_enable="YES"
{% set Instances=[] %}
{% for osudpbroadcastrelay in helpers.toList('OPNsense.udpbroadcastrelays.udpbroadcastrelay') %}
{% if osudpbroadcastrelay.enabled|default('0') == '1' %}
{% set Parameters=[] %}
{% if osudpbroadcastrelay.InstanceID %}
{% do Parameters.append("--id " ~ osudpbroadcastrelay.InstanceID) %}
{% endif %}
{% set osifnames = osudpbroadcastrelay.interfaces.split(',') %}
{% set interface_list=[] %}
{% for i in osifnames %}
{% do interface_list.append(physical_interface(i)) %}
{% do Parameters.append("--dev " ~ physical_interface(i)) %}
{% endfor %}
{% do Parameters.append("--port " ~ osudpbroadcastrelay.listenport) %}
{% if osudpbroadcastrelay.multicastaddress %}
{% set osmcastaddresses = osudpbroadcastrelay.multicastaddress.split(',') %}
{% for mcastaddress in osmcastaddresses %}
{% do Parameters.append("--multicast " ~ mcastaddress) %}
{% endfor %}
{% endif %}
{% if osudpbroadcastrelay.sourceaddress %}
{% do Parameters.append("-s " ~ osudpbroadcastrelay.sourceaddress) %}
{% endif %}
{% if osudpbroadcastrelay.RevertTTL|default('0') == '1' %}
{% do Parameters.append("-t ") %}
{% endif %}
{% do Parameters.append("-f") %}
{% set Instance=osudpbroadcastrelay.InstanceID %}
osudpbroadcastrelay_{{Instance}}="{% for Parameter in Parameters %} {{Parameter}}{% endfor %}"
{% do Instances.append(Instance) %}
{% endif %}
{% endfor %}
osudpbroadcastrelay_instances="{% for Instance in Instances %} {{Instance}}{% endfor %}"
{% endif %}