mirror of
https://github.com/opnsense/plugins.git
synced 2026-06-14 20:13:19 -04:00
net/quagga: version 1.1.0 (BGP support and assorted fixes)
This commit is contained in:
parent
87e559d5a9
commit
e3f2425e80
16 changed files with 1056 additions and 9 deletions
|
|
@ -1,7 +1,7 @@
|
|||
PLUGIN_NAME= quagga
|
||||
PLUGIN_VERSION= 1.0.0
|
||||
PLUGIN_VERSION= 1.1.0
|
||||
PLUGIN_COMMENT= Quagga Routing Suite
|
||||
PLUGIN_DEPENDS= quagga
|
||||
PLUGIN_DEPENDS= quagga ruby
|
||||
PLUGIN_MAINTAINER= franz.fabian.94@gmail.com
|
||||
|
||||
.include "../../Mk/plugins.mk"
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ function quagga_services()
|
|||
'stop' => array('quagga stop'),
|
||||
),
|
||||
'name' => 'quagga',
|
||||
'pidfile' => '/var/run/quagga/zebra.pid'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
namespace OPNsense\Quagga\Api;
|
||||
|
||||
use \OPNsense\Base\ApiControllerBase;
|
||||
use \OPNsense\Quagga\BGP;
|
||||
use \OPNsense\Core\Config;
|
||||
use \OPNsense\Base\ApiMutableModelControllerBase;
|
||||
use \OPNsense\Base\UIModelGrid;
|
||||
|
||||
/**
|
||||
* Copyright (C) 2015 - 2017 Deciso B.V.
|
||||
* Copyright (C) 2017 Fabian Franz
|
||||
* Copyright (C) 2017 Michael Muenz
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
class BgpController extends ApiMutableModelControllerBase
|
||||
{
|
||||
static protected $internalModelName = 'BGP';
|
||||
static protected $internalModelClass = '\OPNsense\Quagga\BGP';
|
||||
public function getAction()
|
||||
{
|
||||
// define list of configurable settings
|
||||
$result = array();
|
||||
if ($this->request->isGet()) {
|
||||
$mdlBGP = new BGP();
|
||||
$result['bgp'] = $mdlBGP->getNodes();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
public function setAction()
|
||||
{
|
||||
$result = array("result"=>"failed");
|
||||
if ($this->request->isPost()) {
|
||||
// load model and update with provided data
|
||||
$mdlBGP = new BGP();
|
||||
$mdlBGP->setNodes($this->request->getPost("bgp"));
|
||||
// perform validation
|
||||
$valMsgs = $mdlBGP->performValidation();
|
||||
foreach ($valMsgs as $field => $msg) {
|
||||
if (!array_key_exists("validations", $result)) {
|
||||
$result["validations"] = array();
|
||||
}
|
||||
$result["validations"]["bgp.".$msg->getField()] = $msg->getMessage();
|
||||
}
|
||||
// serialize model to config and save
|
||||
if ($valMsgs->count() == 0) {
|
||||
$mdlBGP->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
$result["result"] = "saved";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function searchNeighborAction()
|
||||
{
|
||||
$this->sessionClose();
|
||||
$mdlBGP = $this->getModel();
|
||||
$grid = new UIModelGrid($mdlBGP->neighbors->neighbor);
|
||||
return $grid->fetchBindRequest(
|
||||
$this->request,
|
||||
array("enabled", "address", "remoteas", "updatesource", "nexthopself", "defaultoriginate" )
|
||||
);
|
||||
}
|
||||
|
||||
public function getNeighborAction($uuid = null)
|
||||
{
|
||||
$mdlBGP = $this->getModel();
|
||||
if ($uuid != null) {
|
||||
$node = $mdlBGP->getNodeByReference('neighbors.neighbor.' . $uuid);
|
||||
if ($node != null) {
|
||||
// return node
|
||||
return array("neighbor" => $node->getNodes());
|
||||
}
|
||||
} else {
|
||||
$node = $mdlBGP->neighbors->neighbor->add();
|
||||
return array("neighbor" => $node->getNodes());
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function addNeighborAction()
|
||||
{
|
||||
$result = array("result" => "failed");
|
||||
if ($this->request->isPost() && $this->request->hasPost("neighbor")) {
|
||||
$result = array("result" => "failed", "validations" => array());
|
||||
$mdlBGP = $this->getModel();
|
||||
$node = $mdlBGP->neighbors->neighbor->Add();
|
||||
$node->setNodes($this->request->getPost("neighbor"));
|
||||
$valMsgs = $mdlBGP->performValidation();
|
||||
foreach ($valMsgs as $field => $msg) {
|
||||
$fieldnm = str_replace($node->__reference, "neighbor", $msg->getField());
|
||||
$result["validations"][$fieldnm] = $msg->getMessage();
|
||||
}
|
||||
if (count($result['validations']) == 0) {
|
||||
// save config if validated correctly
|
||||
$mdlBGP->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
$result["result"] = "saved";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function delNeighborAction($uuid)
|
||||
{
|
||||
$result = array("result" => "failed");
|
||||
if ($this->request->isPost()) {
|
||||
$mdlBGP = $this->getModel();
|
||||
if ($uuid != null) {
|
||||
if ($mdlBGP->neighbors->neighbor->del($uuid)) {
|
||||
$mdlBGP->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
$result['result'] = 'deleted';
|
||||
} else {
|
||||
$result['result'] = 'not found';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setNeighborAction($uuid)
|
||||
{
|
||||
if ($this->request->isPost() && $this->request->hasPost("neighbor")) {
|
||||
$mdlNeighbor = $this->getModel();
|
||||
if ($uuid != null) {
|
||||
$node = $mdlNeighbor->getNodeByReference('neighbors.neighbor.' . $uuid);
|
||||
if ($node != null) {
|
||||
$result = array("result" => "failed", "validations" => array());
|
||||
$neighborInfo = $this->request->getPost("neighbor");
|
||||
$node->setNodes($neighborInfo);
|
||||
$valMsgs = $mdlNeighbor->performValidation();
|
||||
foreach ($valMsgs as $field => $msg) {
|
||||
$fieldnm = str_replace($node->__reference, "neighbor", $msg->getField());
|
||||
$result["validations"][$fieldnm] = $msg->getMessage();
|
||||
}
|
||||
if (count($result['validations']) == 0) {
|
||||
// save config if validated correctly
|
||||
$mdlNeighbor->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
$result = array("result" => "saved");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array("result" => "failed");
|
||||
}
|
||||
|
||||
public function toggle_handler($uuid, $elements, $element)
|
||||
{
|
||||
$result = array("result" => "failed");
|
||||
if ($this->request->isPost()) {
|
||||
$mdlNeighbor = $this->getModel();
|
||||
if ($uuid != null) {
|
||||
$node = $mdlNeighbor->getNodeByReference($elements . '.'. $element .'.' . $uuid);
|
||||
if ($node != null) {
|
||||
if ($node->enabled->__toString() == "1") {
|
||||
$result['result'] = "Disabled";
|
||||
$node->enabled = "0";
|
||||
} else {
|
||||
$result['result'] = "Enabled";
|
||||
$node->enabled = "1";
|
||||
}
|
||||
// if item has toggled, serialize to config and save
|
||||
$mdlNeighbor->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function toggleNeighborAction($uuid)
|
||||
{
|
||||
return $this->toggle_handler($uuid, 'neighbors', 'neighbor');
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +150,7 @@ class OspfsettingsController extends ApiMutableModelControllerBase
|
|||
// save config if validated correctly
|
||||
$mdlOSPF->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
unset($result['validations']);
|
||||
$result["result"] = "saved";
|
||||
}
|
||||
}
|
||||
|
|
@ -174,6 +175,7 @@ class OspfsettingsController extends ApiMutableModelControllerBase
|
|||
// save config if validated correctly
|
||||
$mdlOSPF->serializeToConfig();
|
||||
Config::getInstance()->save();
|
||||
unset($result['validations']);
|
||||
$result["result"] = "saved";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,35 @@
|
|||
<?php
|
||||
namespace OPNsense\Quagga;
|
||||
|
||||
/*
|
||||
Copyright (C) 2017 Fabian Franz
|
||||
Copyright (C) 2017 Michael Muenz
|
||||
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.
|
||||
*/
|
||||
class BgpController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->title = gettext("BGP-Settings");
|
||||
$this->view->generalForm = $this->getForm("bgp");
|
||||
$this->view->title = gettext("BGP Settings");
|
||||
$this->view->bgpForm = $this->getForm("bgp");
|
||||
$this->view->formDialogEditBGPNeighbor = $this->getForm("dialogEditBGPNeighbor");
|
||||
$this->view->pick('OPNsense/Quagga/bgp');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,30 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>routing.bgp.general.Enabled</id>
|
||||
<id>bgp.enabled</id>
|
||||
<label>enable</label>
|
||||
<type>checkbox</type>
|
||||
<help>This will activate the bgp service.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>bgp.asnumber</id>
|
||||
<label>BGP AS Number</label>
|
||||
<type>text</type>
|
||||
<hint>Your AS Number here</hint>
|
||||
</field>
|
||||
<field>
|
||||
<id>bgp.networks</id>
|
||||
<label>Network</label>
|
||||
<style>tokenize</style>
|
||||
<type>select_multiple</type>
|
||||
<allownew>true</allownew>
|
||||
<help>Select the network to advertise, you have to set a Null route via System -> Routes</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>bgp.redistribute</id>
|
||||
<label>Route Redistribution</label>
|
||||
<type>select_multiple</type>
|
||||
<style>tokenize</style>
|
||||
<help><![CDATA[Select other routing sources, which should be redistributed to the other nodes.]]></help>
|
||||
<hint>Type or select a route source.</hint>
|
||||
</field>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>neighbor.enabled</id>
|
||||
<label>Enabled</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.address</id>
|
||||
<label>Peer-IP</label>
|
||||
<type>text</type>
|
||||
<help>Specify the IP of your neighbor.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.remoteas</id>
|
||||
<label>Remote AS</label>
|
||||
<type>text</type>
|
||||
<help>Neighbor AS.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.updatesource</id>
|
||||
<label>Update-Source Interface</label>
|
||||
<type>select_multiple</type>
|
||||
<help>Physical name of the interface facing the peer</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.nexthopself</id>
|
||||
<label>Next-Hop-Self</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>neighbor.defaultoriginate</id>
|
||||
<label>Send Defaultroute</label>
|
||||
<type>checkbox</type>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
namespace OPNsense\Quagga;
|
||||
|
||||
use OPNsense\Base\BaseModel;
|
||||
|
||||
/*
|
||||
Copyright (C) 2017 Fabian Franz
|
||||
Copyright (C) 2017 Michael Muenz
|
||||
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.
|
||||
*/
|
||||
class BGP extends BaseModel
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<model>
|
||||
<mount>//OPNsense/quagga/bgp</mount>
|
||||
<description>BGP Routing configuration</description>
|
||||
<items>
|
||||
<enabled type="BooleanField">
|
||||
<default>0</default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<asnumber type="TextField">
|
||||
<default></default>
|
||||
<Required>Y</Required>
|
||||
</asnumber>
|
||||
<networks type="CSVListField">
|
||||
<default></default>
|
||||
<Required>N</Required>
|
||||
<mask>/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2},)*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2})$/</mask>
|
||||
</networks>
|
||||
<redistribute type="OptionField">
|
||||
<Required>N</Required>
|
||||
<multiple>Y</multiple>
|
||||
<default></default>
|
||||
<OptionValues>
|
||||
<babel>Babel routing protocol (Babel)</babel>
|
||||
<ospf>Open Shortest Path First (OSPF)</ospf>
|
||||
<connected>Connected routes (directly attached subnet or host)</connected>
|
||||
<isis>Intermediate System to Intermediate System (IS-IS)</isis>
|
||||
<kernel>Kernel routes (not installed via the zebra RIB)</kernel>
|
||||
<pim>Protocol Independent Multicast (PIM)</pim>
|
||||
<rip>Routing Information Protocol (RIP)</rip>
|
||||
<static>Statically configured routes</static>
|
||||
</OptionValues>
|
||||
</redistribute>
|
||||
<neighbors>
|
||||
<neighbor type="ArrayField">
|
||||
<enabled type="BooleanField">
|
||||
<default>1</default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<address type="TextField">
|
||||
<default></default>
|
||||
<Required>Y</Required>
|
||||
<mask>/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/</mask>
|
||||
</address>
|
||||
<remoteas type="TextField">
|
||||
<default></default>
|
||||
<Required>Y</Required>
|
||||
</remoteas>
|
||||
<updatesource type="InterfaceField">
|
||||
<default></default>
|
||||
<Required>N</Required>
|
||||
<multiple>N</multiple>
|
||||
<filters>
|
||||
<enable>/^(?!0).*$/</enable>
|
||||
</filters>
|
||||
</updatesource>
|
||||
<nexthopself type="BooleanField">
|
||||
<default>0</default>
|
||||
<Required>N</Required>
|
||||
</nexthopself>
|
||||
<defaultoriginate type="BooleanField">
|
||||
<default>0</default>
|
||||
<Required>N</Required>
|
||||
</defaultoriginate>
|
||||
</neighbor>
|
||||
</neighbors>
|
||||
</items>
|
||||
</model>
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
<General VisibleName="General" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/general/index" order="1"/>
|
||||
<RIP VisibleName="RIP" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/rip/index" order="10" />
|
||||
<OSPF VisibleName="OSPF" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/ospf/index" order="20" />
|
||||
<!--<ISIS VisibleName="IS-IS" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/isis/index" order="30" />
|
||||
<BGP VisibleName="BGPv4" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/bgp/index" order="40" />-->
|
||||
<!--<ISIS VisibleName="IS-IS" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/isis/index" order="30" />-->
|
||||
<BGP VisibleName="BGPv4" cssClass="fa fa-bolt fa-fw" url="/ui/quagga/bgp/index" order="40" />
|
||||
|
||||
</Routing>
|
||||
</menu>
|
||||
|
|
|
|||
|
|
@ -1 +1,112 @@
|
|||
{{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_ospf_settings'])}}
|
||||
{#
|
||||
|
||||
OPNsense® is Copyright © 2014 – 2017 by Deciso B.V.
|
||||
Copyright (C) 2017 Fabian Franz
|
||||
Copyright (C) 2017 Michael Muenz
|
||||
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.
|
||||
|
||||
#}
|
||||
<!-- Navigation bar -->
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#general">{{ lang._('General') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#neighbors">{{ lang._('Neighbors') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box tab-content">
|
||||
<div id="general" class="tab-pane fade in active">
|
||||
<div class="content-box" style="padding-bottom: 1.5em;">
|
||||
{{ partial("layout_partials/base_form",['fields':bgpForm,'id':'frm_bgp_settings'])}}
|
||||
|
||||
<hr />
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-primary" id="saveAct" type="button"><b>{{ lang._('Save') }}</b></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="neighbors" class="tab-pane fade in">
|
||||
<table id="grid-neighbors" class="table table-responsive" data-editDialog="DialogEditBGPNeighbor">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="enabled" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
|
||||
<th data-column-id="address" data-type="string" data-visible="true">{{ lang._('Neighbor Address') }}</th>
|
||||
<th data-column-id="remoteas" data-type="string" data-visible="true">{{ lang._('Remote AS') }}</th>
|
||||
<th data-column-id="updatesource" data-type="string" data-visible="true">{{ lang._('Update Source Address') }}</th>
|
||||
<th data-column-id="nexthopself" data-type="string" data-formatter="rowtoggle">{{ lang._('Next Hop Self') }}</th>
|
||||
<th data-column-id="defaultoriginate" data-type="string" data-formatter="rowtoggle">{{ lang._('Default Originate') }}</th>
|
||||
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
|
||||
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5"></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>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var data_get_map = {'frm_bgp_settings':"/api/quagga/bgp/get"};
|
||||
mapDataToFormUI(data_get_map).done(function(data){
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
});
|
||||
ajaxCall(url="/api/quagga/service/status", sendData={}, callback=function(data,status) {
|
||||
updateServiceStatusUI(data['status']);
|
||||
});
|
||||
|
||||
// link save button to API set action
|
||||
$("#saveAct").click(function(){
|
||||
saveFormToEndpoint(url="/api/quagga/bgp/set",formid='frm_bgp_settings',callback_ok=function(){
|
||||
ajaxCall(url="/api/quagga/service/reconfigure", sendData={}, callback=function(data,status) {
|
||||
ajaxCall(url="/api/quagga/service/status", sendData={}, callback=function(data,status) {
|
||||
updateServiceStatusUI(data['status']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
$("#grid-neighbors").UIBootgrid(
|
||||
{ 'search':'/api/quagga/bgp/searchNeighbor',
|
||||
'get':'/api/quagga/bgp/getNeighbor/',
|
||||
'set':'/api/quagga/bgp/setNeighbor/',
|
||||
'add':'/api/quagga/bgp/addNeighbor/',
|
||||
'del':'/api/quagga/bgp/delNeighbor/',
|
||||
'toggle':'/api/quagga/bgp/toggleNeighbor/',
|
||||
'options':{selection:false, multiSelect:false}
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
{{ partial("layout_partials/base_dialog",['fields':formDialogEditBGPNeighbor,'id':'DialogEditBGPNeighbor','label':lang._('Edit Neighbor')])}}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
480
net/quagga/src/opnsense/scripts/quagga/quagga.rb
Executable file
480
net/quagga/src/opnsense/scripts/quagga/quagga.rb
Executable file
|
|
@ -0,0 +1,480 @@
|
|||
#!/usr/local/bin/ruby
|
||||
=begin
|
||||
Copyright 2017 Fabian Franz
|
||||
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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
=end
|
||||
require 'json'
|
||||
require 'shellwords'
|
||||
require 'pp'
|
||||
class VTYSH
|
||||
def initialize(path = '/usr/local/bin/vtysh')
|
||||
@path = path
|
||||
end
|
||||
|
||||
def execute(param)
|
||||
o = `#{@path} -c #{param.shellescape}`
|
||||
raise "error" if o.length <= 2
|
||||
raise "command error - command: #{param}" if o.include? "% Unknown command"
|
||||
o
|
||||
end
|
||||
|
||||
#def execute(param)
|
||||
# fn = param.sub("show","sh").gsub(" ","_")
|
||||
# File.read(fn)
|
||||
#end
|
||||
end
|
||||
|
||||
class QuaggaTableReader
|
||||
attr_accessor :headers
|
||||
def initialize(headers = [])
|
||||
@headers = headers
|
||||
end
|
||||
def read_headline(line, start_without_header = false, start_without_header_name = 'status')
|
||||
# get begin of header (number of the first char of the string)
|
||||
header = line
|
||||
header_offset = {}
|
||||
header_offset[0] = start_without_header_name if start_without_header
|
||||
@headers.map do |x|
|
||||
header_offset[header.index(x)] = x.strip
|
||||
end
|
||||
|
||||
|
||||
# make ranges: this will make a range of the first char of the sting until
|
||||
# the the char befor the next heading begins
|
||||
ranges = []
|
||||
0.upto (header_offset.keys.length - 2) do |i|
|
||||
ranges << ((header_offset.keys[i])...(header_offset.keys[i + 1]))
|
||||
end
|
||||
# the last one has no next heading - this will go to the end of the line
|
||||
ranges.push ((header_offset.keys.last)..-1) # path
|
||||
@header_offset = header_offset
|
||||
@ranges = ranges
|
||||
nil
|
||||
end
|
||||
|
||||
def read_entry(line, expand_fields = {})
|
||||
raise "heading missing" unless @ranges
|
||||
tmp = {}
|
||||
return tmp unless line&.strip.length > 2
|
||||
|
||||
@ranges.each do |r|
|
||||
# the string starts here
|
||||
b = r.begin
|
||||
# get the heading starting where the string starts
|
||||
n = @header_offset[b]
|
||||
# get the data or return an empty string
|
||||
tmp[n] = line[r]&.strip || ""
|
||||
end
|
||||
# replace characters by the meaning
|
||||
expand_fields.keys.each do |key|
|
||||
tmp[key] = tmp[key].split("").map {|x| {dn: expand_fields[key][x], abb: x} } if tmp[key]
|
||||
end
|
||||
tmp
|
||||
end
|
||||
end
|
||||
|
||||
class General
|
||||
def initialize(vtysh)
|
||||
@vtysh = vtysh
|
||||
end
|
||||
def routes
|
||||
lines = @vtysh.execute("show ip route").lines
|
||||
|
||||
# headers
|
||||
meanings = {}
|
||||
while (line = lines.shift.strip) != ''
|
||||
line = line.gsub('Codes: ','')
|
||||
line.split(",").each do |meaning|
|
||||
short, long = meaning.strip.split(" - ")
|
||||
meanings[short] = long
|
||||
end
|
||||
end
|
||||
|
||||
# you don't have to understand this regex ;)
|
||||
entry_regex = /(\S+?)\s+?(\S+?)(?: \[(\d+)\/(\d+)\])? (?:via (\S+?)|is ([^,]+?)), ([^,\n]+)(?:, (\S+))?/
|
||||
entries = []
|
||||
while (line = lines.shift&.strip)
|
||||
if line.length > 10
|
||||
code, network, ad, metric, via, direct, interface, time = line.scan(entry_regex).first
|
||||
code = code.split('').map {|c| {short: c, long: meanings[c]}}
|
||||
entries << {code: code, network: (network || direct), ad: ad, metric: metric, interface: interface, time: time }
|
||||
end
|
||||
end
|
||||
entries
|
||||
end
|
||||
end
|
||||
|
||||
class OSPF
|
||||
def initialize(vtysh)
|
||||
@vtysh = vtysh
|
||||
end
|
||||
def neighbors
|
||||
qta = QuaggaTableReader.new(["Neighbor ID", "Pri State", "Dead Time", "Address", "Interface", "RXmtL", "RqstL", "DBsmL"])
|
||||
lines = @vtysh.execute("show ip ospf neighbor").lines
|
||||
lines.shift # empty line
|
||||
data = []
|
||||
qta.read_headline(lines.shift)
|
||||
while (line = lines.shift) && (line.length > 2)
|
||||
data << qta.read_entry(line)
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
def interface
|
||||
lines = @vtysh.execute("show ip ospf interface").lines
|
||||
interfaces = {}
|
||||
current_if = ''
|
||||
while line = lines.shift
|
||||
next if line.strip.length <= 1
|
||||
if line[0] != ' ' # we are in a heading
|
||||
current_if = line.split(" ").first
|
||||
interfaces[current_if] = {}
|
||||
current_if = interfaces[current_if]
|
||||
current_if[:enabled] = true
|
||||
lines.shift
|
||||
else
|
||||
line.strip!
|
||||
case line
|
||||
when 'OSPF not enabled on this interface'
|
||||
current_if[:enabled] = false
|
||||
when /Internet Address ([^,]+?), Broadcast ([^,]+?), Area (.*)/
|
||||
current_if[:address] = $1
|
||||
current_if[:broadcast] = $2
|
||||
current_if[:area] = $3
|
||||
when /MTU mismatch detection:(.*)/
|
||||
current_if[:mtu_mismatch_detection] = ($1 == 'enabled')
|
||||
when /Router ID ([^,]+?), Network Type ([^,]+?), Cost: (\d+)/
|
||||
current_if[:router_id] = $1
|
||||
current_if[:network_type] = $2
|
||||
current_if[:cost] = $3.to_i
|
||||
when /Transmit Delay is (\d+) sec, State ([^,]+?), Priority (\d+)/
|
||||
current_if[:transmit_delay] = $1.to_i
|
||||
current_if[:state] = $2
|
||||
current_if[:priority] = $3.to_i
|
||||
when "No designated router on this network"
|
||||
current_if[:designated_router] = nil
|
||||
when /Designated Router \(ID\) ([^,]+?), Interface Address (.*)/
|
||||
current_if[:designated_router] = $1
|
||||
current_if[:designated_router_interface_address] = $2
|
||||
when "No backup designated router on this network"
|
||||
current_if[:backup_designated_router] = nil
|
||||
when /Timer intervals configured, Hello (\d+)s, Dead (\d+)s, Wait (\d+)s, Retransmit (\d+)/
|
||||
current_if[:intervals] = {hello: $1.to_i, dead: $2.to_i, wait: $3.to_i, retransmit: $4.to_i}
|
||||
when /Multicast group memberships: (.*)/
|
||||
current_if[:multicast_group_memberships] = $1.split(" ")
|
||||
when /Hello due in ([\d\.]+|inactive)s?/
|
||||
current_if[:hello_due_in] = $1 == 'inactive' ? $1 : $1.to_f
|
||||
when /Neighbor Count is (\d+), Adjacent neighbor count is (\d+)/
|
||||
current_if[:neighbor_count] = $1.to_i
|
||||
current_if[:adjacent_neighbor_count] = $2.to_i
|
||||
else
|
||||
# make sure there is an array to write in
|
||||
current_if[:unparsed] ||= []
|
||||
current_if[:unparsed] << line
|
||||
end
|
||||
end
|
||||
end
|
||||
interfaces
|
||||
end
|
||||
|
||||
def database
|
||||
lines = @vtysh.execute("show ip ospf database").lines
|
||||
db = {}
|
||||
heading = ''
|
||||
router = ''
|
||||
router_link_states_area = ''
|
||||
mode = :none
|
||||
qta = nil
|
||||
while line = lines.shift
|
||||
next if line == ''
|
||||
if line[0] == ' ' # heading
|
||||
heading = line.strip
|
||||
case heading
|
||||
when /OSPF Router with ID \(([\.\d]+)\)/
|
||||
router = $1
|
||||
db[router] ||= {}
|
||||
mode = :router
|
||||
when /Router Link States \(Area ([\.\d]+)\)/
|
||||
router_link_states_area = $1
|
||||
db[router]['link_state_area'] ||= {}
|
||||
db[router]['link_state_area'][$1] ||= []
|
||||
mode = :link_state
|
||||
qta = nil
|
||||
when 'AS External Link States'
|
||||
mode = :states
|
||||
db[router]['external_states'] ||= []
|
||||
qta = nil
|
||||
else
|
||||
$stderr.puts "unknown heading"
|
||||
end
|
||||
else
|
||||
if qta == nil
|
||||
case mode
|
||||
when :link_state
|
||||
qta = QuaggaTableReader.new(["Link ID", "ADV Router", "Age", "Seq#", "CkSum", "Link count"])
|
||||
when :states
|
||||
qta = QuaggaTableReader.new(["Link ID", "ADV Router", "Age", "Seq#", "CkSum", "Route\n"])
|
||||
else
|
||||
next
|
||||
end
|
||||
headline = lines.shift
|
||||
qta.read_headline(headline,true)
|
||||
else
|
||||
entry = qta.read_entry(line)
|
||||
case mode
|
||||
when :link_state
|
||||
db[router]['link_state_area'][router_link_states_area] << entry
|
||||
when :states
|
||||
db[router]['external_states'] << entry
|
||||
end
|
||||
end
|
||||
end
|
||||
# table
|
||||
end
|
||||
db
|
||||
end
|
||||
|
||||
def route
|
||||
lines = @vtysh.execute("show ip ospf route").lines
|
||||
heading = ''
|
||||
route = {}
|
||||
last_line = []
|
||||
while line = lines.shift
|
||||
if line[0] == "=" #heading
|
||||
heading = line.scan(/=* ([^=]*) =*/).first.first
|
||||
route[heading] = []
|
||||
else # data
|
||||
case line.strip
|
||||
when /N\s+([\d\.\/]+)\s+\[(\d+)\]\s+area:\s(.*)/
|
||||
last_line = {network: $1, cost: $2.to_i, area: $3, type: 'N'}
|
||||
route[heading] << last_line
|
||||
when /N (E(?:\d+) (?:\S+))\s+\[([\d\/]+)\] tag: (\d+)/
|
||||
last_line = {network: $1, cost: $2, tag: $3.to_i, type: 'N'}
|
||||
route[heading] << last_line
|
||||
when /(?:(directly attached) to|via ([^,]+),) (.*)/
|
||||
last_line[:via] = $1 || $2
|
||||
last_line[:via_interface] = $3
|
||||
when /R\s+(\S+)\s+\[(\d+)\] area: ([^,]+)(, ASBR)/
|
||||
last_line = {ip: $1, cost: $2.to_i, area: $3, asbr: (", ASBR" == $4), type: 'R'}
|
||||
route[heading] << last_line
|
||||
else
|
||||
#puts line
|
||||
end
|
||||
end
|
||||
end
|
||||
route
|
||||
end
|
||||
|
||||
def overview
|
||||
lines = @vtysh.execute("show ip ospf").lines
|
||||
overview = {rfc2328_conform: false, asbr: false}
|
||||
while line = lines.shift&.strip
|
||||
case line
|
||||
when /OSPF Routing Process, Router ID: ([\d\.]+)/
|
||||
overview[:router_id] = $1
|
||||
when "This implementation conforms to RFC2328"
|
||||
overview[:rfc2328_conform] = true
|
||||
when /OpaqueCapability flag is (\S+)/
|
||||
overview[:opaque_capability] = ($1 == 'enabled')
|
||||
when /Initial SPF scheduling delay (\d+) millisec\(s\)/
|
||||
overview[:initial_spf_scheduling_delay] = $1.to_i
|
||||
when /(Min|Max)imum hold time between consecutive SPFs (\d+) millisec\(s\)/
|
||||
overview[:hold_time] ||= {}
|
||||
overview[:hold_time][$1.downcase] = $2.to_i
|
||||
when "This router is an ASBR (injecting external routing information)"
|
||||
overview[:asbr] = true
|
||||
when /Number of external LSA (\d+). Checksum Sum ([x\d]+)/
|
||||
overview[:external_lsa] = {count: $1.to_i, checksum: $2}
|
||||
when /Number of opaque AS LSA (\d+). Checksum Sum ([x\d]+)/
|
||||
overview[:opaque_as_lsa] = {count: $1.to_i, checksum: $2}
|
||||
when /Refresh timer (\d+) secs/
|
||||
overview[:refresh_timer] = $1.to_i
|
||||
when /Number of areas attached to this router: (\d+)/
|
||||
overview[:areas_attached_count] = $1.to_i
|
||||
when /Hold time multiplier is currently (\d+)/
|
||||
overview[:current_hold_time_multipier] = $1.to_i
|
||||
when /RFC1583Compatibility flag is (\S+)/
|
||||
overview[:rfc1583_compatibility] = ($1 == 'enabled')
|
||||
when /SPF timer is (.*)/
|
||||
overview[:spf_timer] = $1
|
||||
when ""
|
||||
break
|
||||
else
|
||||
# debug
|
||||
#puts line
|
||||
end
|
||||
end
|
||||
# general overview has ended - now the area overviews come
|
||||
overview[:areas] = {}
|
||||
current_area = {}
|
||||
while line = lines.shift&.strip
|
||||
case line
|
||||
when /Area ID: (.*)/
|
||||
current_area = {}
|
||||
overview[:areas][$1] = current_area
|
||||
when /Number of interfaces in this area: Total: (\d+), Active: (\d+)/
|
||||
current_area[:interfaces] = {total: $1.to_i,active: $2.to_i}
|
||||
when /Number of (router|network|summary) LSA (\d+). Checksum Sum ([\da-fx]+)/
|
||||
current_area[:lsa] ||= {}
|
||||
current_area[:lsa][$1] = {count: $2.to_i, checksum: $3}
|
||||
when /Number of LSA (\d+)/
|
||||
current_area[:lsa] ||= {}
|
||||
current_area[:lsa][:count] = $1.to_i
|
||||
when /Number of (opaque (?:area|link)|NSSA|ASBR summary) LSA (\d+). Checksum Sum ([\da-fx]+)/
|
||||
current_area[:lsa] ||= {}
|
||||
current_area[:lsa][$1] = {count: $2.to_i, checksum: $3}
|
||||
when /Number of fully adjacent neighbors in this area: (\d+)/
|
||||
current_area[:fully_adjacent_neighbour_count] = $1.to_i
|
||||
when /SPF algorithm executed (\d) times/
|
||||
current_area[:spf_exec_count] = $1.to_i
|
||||
when "Area has no authentication"
|
||||
current_area[:auth] = "none"
|
||||
else
|
||||
#puts line
|
||||
end
|
||||
end
|
||||
overview
|
||||
end
|
||||
end
|
||||
|
||||
class BGP
|
||||
def initialize(sh)
|
||||
@vtysh = sh
|
||||
end
|
||||
|
||||
def overview
|
||||
output = @vtysh.execute('show ip bgp')
|
||||
return {} if output.include? "No BGP process is configured"
|
||||
return {} unless output.include? 'version' # we get an empty output if quagga is not running
|
||||
output = output.split("\n")
|
||||
bgp = {}
|
||||
|
||||
x,y = output.shift.scan(/.*?version is (\d+).*?ID is ([0-9\.]+).*/).first
|
||||
bgp['table_version'] = x
|
||||
bgp['local_router_id'] = y
|
||||
|
||||
# find out, what the status abbreviations mean
|
||||
status_codes = {}
|
||||
line = output.shift
|
||||
line.split(":").last.strip.split(",").each do |x|
|
||||
k,v = x.strip.split(" ")
|
||||
status_codes[k] = v
|
||||
end
|
||||
while line.end_with? ","
|
||||
line = output.shift
|
||||
line.strip.split(",").each do |x|
|
||||
k,v = x.strip.split(" ")
|
||||
status_codes[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
# same like before but for the origin codes
|
||||
origin_codes = {}
|
||||
output.shift.split(":").last.strip.split(",").each do |x|
|
||||
k,v = x.strip.split(" - ")
|
||||
origin_codes[k] = v
|
||||
end
|
||||
|
||||
# drop empty line
|
||||
output.shift
|
||||
# read entries
|
||||
bgp['output'] = []
|
||||
qta = QuaggaTableReader.new(["Network", "Next Hop", "Metric", "LocPrf", "Weight", "Path"])
|
||||
qta.read_headline(output.shift,true)
|
||||
while line = output.shift&.strip
|
||||
break if line == ''
|
||||
data = qta.read_entry(line)
|
||||
data['status'] = data['status'].split("").map {|x| {dn: status_codes[x], abb: x} }
|
||||
data['Path'] = data['Path'].split("").map {|x| {dn: origin_codes[x], abb: x} }
|
||||
bgp['output'] << data
|
||||
end
|
||||
bgp
|
||||
end
|
||||
end
|
||||
|
||||
require 'optparse'
|
||||
options = {}
|
||||
supported_sections = %w{general ospf}
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: #{__FILE__} -s section [section specific params]"
|
||||
opts.on("-d", "--ospf-database") do |od|
|
||||
options[:ospf_database] = od
|
||||
end
|
||||
opts.on("-r", "--ospf-route", 'print OSPF routing table') do |od|
|
||||
options[:ospf_route] = od
|
||||
end
|
||||
opts.on("-i", "--ospf-interface", 'print OSPF interface information') do |od|
|
||||
options[:ospf_interface] = od
|
||||
end
|
||||
opts.on("-n", "--ospf-neighbor", 'Print OSPF neighbors') do |od|
|
||||
options[:ospf_neighbors] = od
|
||||
end
|
||||
opts.on("-o", "--ospf-overview", "Print OSPF summary") do |od|
|
||||
options[:ospf_overview] = od
|
||||
end
|
||||
opts.on("-R", "--general-routes", "Print Routing Table") do |od|
|
||||
options[:general_routes] = od
|
||||
end
|
||||
opts.on("-B", "--bgp-overview", "Print an overview of BGP") do |od|
|
||||
options[:bgp_overview] = od
|
||||
end
|
||||
opts.on("-H", "--human-readable", "Print the output human readable (not json)") do |od|
|
||||
options[:human_readable] = od
|
||||
end
|
||||
opts.on("-h", "--help", "Prints this help") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
end.parse!
|
||||
# use the lib
|
||||
sh = VTYSH.new
|
||||
ospf = OSPF.new sh
|
||||
bgp = BGP.new sh
|
||||
general = General.new sh
|
||||
|
||||
result = {}
|
||||
options.keys.each do |k|
|
||||
# if it is true
|
||||
if options[k]
|
||||
begin
|
||||
if k.to_s.include? 'ospf'
|
||||
cmd = k.to_s.split('_').last
|
||||
result[k] = ospf.send(cmd)
|
||||
elsif k.to_s.include? 'general'
|
||||
cmd = k.to_s.split('_').last
|
||||
result[k] = general.send(cmd)
|
||||
elsif k.to_s.include? 'bgp'
|
||||
cmd = k.to_s.split('_').last
|
||||
result[k] = bgp.send(cmd)
|
||||
end
|
||||
rescue # do nothing on an error
|
||||
result[k] = "error"
|
||||
#puts $!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ospf.database, general.routes, ospf.interface, ospf.neighbors, ospf.route, ospf.overview
|
||||
if options[:human_readable]
|
||||
pp result
|
||||
else
|
||||
print result.to_json
|
||||
end
|
||||
|
|
@ -27,3 +27,39 @@ command:/usr/local/etc/rc.d/quagga status;exit 0
|
|||
parameters:
|
||||
type:script_output
|
||||
message:request quagga
|
||||
|
||||
[ospf-database]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospf-database
|
||||
parameters:
|
||||
type:script_output
|
||||
message: Shows the OSPF database
|
||||
|
||||
[ospf-route]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospf-route
|
||||
parameters:
|
||||
type:script_output
|
||||
message: print OSPF routing table
|
||||
|
||||
[ospf-interface]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospf-interface
|
||||
parameters:
|
||||
type:script_output
|
||||
message: print OSPF interface information
|
||||
|
||||
[ospf-neighbor]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospf-neighbor
|
||||
parameters:
|
||||
type:script_output
|
||||
message: Print OSPF neighbors
|
||||
|
||||
[ospf-overview]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospf-overview
|
||||
parameters:
|
||||
type:script_output
|
||||
message: Print OSPF summary
|
||||
|
||||
[general-routes]
|
||||
command:/usr/local/opnsense/scripts/quagga/quagga.rb --general-routes
|
||||
parameters:
|
||||
type:script_output
|
||||
message: Print Routing Table
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
bgpd.conf:/usr/local/etc/quagga/bgpd.conf
|
||||
ospfd.conf:/usr/local/etc/quagga/ospfd.conf
|
||||
ripd.conf:/usr/local/etc/quagga/ripd.conf
|
||||
quagga:/etc/rc.conf.d/quagga
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
{% if helpers.exists('OPNsense.quagga.bgp.enabled') and OPNsense.quagga.bgp.enabled == '1' %}
|
||||
{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
|
||||
!
|
||||
! Zebra configuration saved from vty
|
||||
! 2017/03/03 20:21:04
|
||||
!
|
||||
!
|
||||
!
|
||||
!
|
||||
{% if helpers.exists('OPNsense.quagga.bgp.asnumber') and OPNsense.quagga.bgp.asnumber != '' %}
|
||||
router bgp {{ OPNsense.quagga.bgp.asnumber }}
|
||||
{% if helpers.exists('OPNsense.quagga.bgp.networks') %}
|
||||
{% for network in OPNsense.quagga.bgp.networks.split(',') %}
|
||||
network {{ network }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if helpers.exists('OPNsense.quagga.bgp.neighbors.neighbor') %}
|
||||
{% for neighbor in helpers.toList('OPNsense.quagga.bgp.neighbors.neighbor') %}
|
||||
{% if neighbor.enabled == '1' %}
|
||||
neighbor {{ neighbor.address }} remote-as {{ neighbor.remoteas }}
|
||||
{% if 'updatesource' in neighbor and neighbor.updatesource != '' %}
|
||||
neighbor {{ neighbor.address }} update-source {{ physical_interface(neighbor.updatesource) }}
|
||||
{% endif %}
|
||||
{% if 'nexthopself' in neighbor and neighbor.nexthopself == '1' %}
|
||||
neighbor {{ neighbor.address }} next-hop-self
|
||||
{% endif %}
|
||||
{% if 'defaultoriginate' in neighbor and neighbor.defaultoriginate == '1' %}
|
||||
neighbor {{ neighbor.address }} default-originate
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
!
|
||||
line vty
|
||||
!
|
||||
{% endif %}
|
||||
Loading…
Reference in a new issue