Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Junos devices #51

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pierky/arouteserver/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from .tpl_rendering import HTMLCommand, DumpTemplateContextCommand, \
BIRDCommand, OpenBGPDCommand, BuildCommand
BIRDCommand, OpenBGPDCommand, BuildCommand, JunosDCommand
from .check_new_release import CheckNewRelease
from .clients_from_peeringdb import ClientsFromPeeringDBCommand
from .clients_from_euroix import ClientsFromEuroIXCommand
Expand All @@ -30,6 +30,7 @@
BuildCommand,
BIRDCommand,
OpenBGPDCommand,
JunosDCommand,
HTMLCommand,
DumpTemplateContextCommand,
ClientsFromPeeringDBCommand,
Expand Down
2 changes: 1 addition & 1 deletion pierky/arouteserver/commands/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def collect_answers(self):
)
self.add_answer("daemon", self.ask.ask,
"Which BGP daemon will be used?",
options=["bird", "openbgpd"]
options=["bird", "openbgpd", "junos"]
)

if self.answers["daemon"] == "openbgpd":
Expand Down
111 changes: 111 additions & 0 deletions pierky/arouteserver/commands/junos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (C) 2017-2019 Pier Carlo Chiodi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import sys
import yaml

from .base import ARouteServerCommand

from ..config.base import convert_deprecated
from ..config.general import ConfigParserGeneral
from ..config.program import program_config

class ShowConfigCommand(ARouteServerCommand):

NEEDS_CONFIG = True

COMMAND_NAME = "junos"
COMMAND_HELP = ("Create configurations for junos")

@classmethod
def add_arguments(cls, parser):
super(ShowConfigCommand, cls).add_arguments(parser)

parser.add_argument(
"--junos",
help="General route server configuration file.",
metavar="FILE",
dest="cfg_general")

def run(self):
current_config_path = program_config.get("cfg_general")
self.show_config(current_config_path, sys.stdout)

@staticmethod
def show_config(current_config_path, output):

def wr_line(configured, level, line):
if configured is True:
status = "configured"
elif configured is False:
status = "default"
else:
status = ""

output.write("{status:<15} {indent}{line}\n".format(
status=status,
indent=" " * level,
line=line
))

with open(current_config_path, "r") as f:
current_config = yaml.safe_load(f)
convert_deprecated(current_config["cfg"])

distrib = ConfigParserGeneral()
distrib._load_from_yaml("cfg:\n"
" rs_as: 65534\n"
" router_id: 192.0.2.1\n")
distrib.parse()
del distrib["communities"]
del distrib["custom_communities"]

ordered_schema = ConfigParserGeneral.get_schema()
del ordered_schema["cfg"]["communities"]
del ordered_schema["cfg"]["custom_communities"]

def get_val_repr(val):
if isinstance(val, dict):
return ", ".join("{}: {}".format(k, v) for k, v in sorted(val.items()))
else:
return str(val).strip()

def iterate(ordered_schema, distrib, current, level=0):
for k in ordered_schema:
if current is distrib:
configured = False
else:
configured = k in current

iterate_over = current if configured else distrib

if isinstance(ordered_schema[k], dict):

wr_line(None, level, k + ":")
iterate(ordered_schema[k], distrib[k], iterate_over[k],
level + 1)

elif isinstance(ordered_schema[k], list) or \
isinstance(iterate_over[k], list):

wr_line(configured, level, k + ":")
for v in iterate_over[k]:
wr_line(configured, level, " - " + get_val_repr(v))

else:
v = get_val_repr(iterate_over[k])
wr_line(configured, level, "{}: {}".format(k, v))

iterate(ordered_schema, distrib.cfg, current_config)
10 changes: 10 additions & 0 deletions pierky/arouteserver/commands/tpl_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,16 @@ class OpenBGPDCommand(ConfigRenderingCommand):
def _get_template_sub_dir(self):
return "openbgpd"

class JunosDCommand(ConfigRenderingCommand):

COMMAND_NAME = "junos"
COMMAND_HELP = "Junos route server configuration for Juniper devices."

BUILDER_CLASS = OpenBGPDConfigBuilder

def _get_template_sub_dir(self):
return "junos"

class HTMLCommand(TemplateRenderingCommands):

COMMAND_NAME = "html"
Expand Down
28 changes: 28 additions & 0 deletions templates/junos/community.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{%- set prepend_once = cfg.communities.prepend_once_to_peer.std.split(":")[0] %}
{%- set prepend_twice = cfg.communities.prepend_twice_to_peer.std.split(":")[0] %}
{%- set prepend_thrice = cfg.communities.prepend_thrice_to_peer.std.split(":")[0] %}
community GRACEFUL_SHUTDOWN members 65535:0;
community WELL-KNOWN members [ no-advertise no-export ];
community NO-ADVERTISE 65535:65281;
community NO-EXPORT 65535:65282;

community REDISTRIBUTE-ALL members [{{ cfg.rs_as }}:{{ cfg.rs_as }} target:{{ cfg.rs_as }}:{{ cfg.rs_as }} {{ cfg.rs_as }}:1:0 ];
community NO-REDISTRIBUTE members [0:{{ cfg.rs_as }} target:0:{{ cfg.rs_as }} {{ cfg.rs_as }}:0:0 ];
community PREPEND-ONCE-ALL members [{{ prepend_once }}:0 target:{{ prepend_once }}:0 {{ cfg.rs_as }}:101:0 ];
community PREPEND-TWICE-ALL members [{{ prepend_twice }}:0 target:{{ prepend_twice }}:0 {{ cfg.rs_as }}:102:0 ];
community PREPEND-TRICE-ALL members [{{ prepend_thrice }}:0 target:{{ prepend_thrice }}:0 {{ cfg.rs_as }}:103:0 ];

{% for client in clients %}
community BLOCK-TO-{{ client.id }} members [0:{{ client.asn }} target:0:{{ client.asn }} 6695:0:{{ client.asn }}];
community PERMIT-TO-{{ client.id }} members [{{ cfg.rs_as }}:{{ client.asn }} target:{{ cfg.rs_as }}:{{ client.asn }} {{ cfg.rs_as }}:1:{{ client.asn }}];
community PREPEND-ONCE-{{ client.id }} members [{{ prepend_once }}:{{ client.asn }} target:{{ prepend_once }}:{{ client.asn }} {{ cfg.rs_as }}:101:{{ client.asn }}];
community PREPEND-TWICE-{{ client.id }} members [{{ prepend_twice }}:{{ client.asn }} target:{{ prepend_twice }}:{{ client.asn }} {{ cfg.rs_as }}:102:{{ client.asn }}];
community PREPEND-TRICE-{{ client.id }} members [{{ prepend_thrice }}:{{ client.asn }} target:{{ prepend_thrice }}:{{ client.asn }} {{ cfg.rs_as }}:103:{{ client.asn }}];
community NO-ADVERTISE-{{ client.id }} members {{ cfg.rs_as }}:902:{{ client.asn }};
community NO-EXPORT-{{ client.id }} members {{ cfg.rs_as }}:901:{{ client.asn }};

{% endfor %}

community origin-validation-state-invalid members 0x4300:0.0.0.0:2;
community origin-validation-state-unknown members 0x4300:0.0.0.0:1;
community origin-validation-state-valid members 0x4300:0.0.0.0:0;
4 changes: 4 additions & 0 deletions templates/junos/header.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
routing-options {
autonomous-system {{ cfg.rs_as }}
router-id {{ cfg.router_id }}
}
8 changes: 8 additions & 0 deletions templates/junos/main.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# built by ARouteServer
{{ "header"|include_local_file -}}

{% include "header.j2" %}

{% include "policy-options.j2" %}

{% include "routing-instance.j2" %}
36 changes: 36 additions & 0 deletions templates/junos/policy-options.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
policy-options {

{% include "community.j2" %}

prefix-list BOGONS_v4 {
{% for entry in bogons %}
{{ entry.prefix }}{{ "/{}".format(entry.length) if entry.length > 0 else ""}};
{% endfor %}
}
prefix-list TOO-MANY-HOPS {
apply-path ".{50,}";
}
prefix-list EBGP_PEERS {
apply-path "routing-instances <*> protocols bgp group <*> neighbor <*>";
}
as-path-group BOGON-ASNs {
/* RFC7607 */
as-path zero ".* 0 .*";
/* RFC 4893 AS_TRANS */
as-path as_trans ".* 23456 .*";
/* RFC 5398 and documentation/example ASNs */
as-path examples1 ".* [64496-64511] .*";
as-path examples2 ".* [65536-65551] .*";
/* RFC 6996 Private ASNs*/
as-path reserved1 ".* [64512-65534] .*";
as-path reserved2 ".* [4200000000-4294967294] .*";
/* RFC 6996 Last 16 and 32 bit ASNs */
as-path last16 ".* 65535 .*";
as-path last32 ".* 4294967295 .*";
/* RFC IANA reserved ASNs*/
as-path iana-reserved ".* [65552-131071] .*";
}

{% include "policy-statement.j2" %}

}
109 changes: 109 additions & 0 deletions templates/junos/policy-statement.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
## Common policy statements begins ##

policy-statement ALLOW-GRACEFUL-SHUTDOWN {
term 1 {
from {
protocol bgp;
community GRACEFUL_SHUTDOWN;
}
then {
local-preference 0;
next policy;
}
}
}
policy-statement BGP-IMPORT-REJECTv4 {
term bogon-asns {
from as-path-group BOGON-ASNs;
then reject;
}
term bogon-ipv4 {
from {
prefix-list BOGONS_v4;
}
then reject;
}
term reject_too_small_prefixes_v4 {
from {
route-filter 0.0.0.0/0 prefix-length-range /25-/32;
}
then reject;
}
term no-long-paths {
from as-path TOO-MANY-HOPS;
then reject;
}
term reject-rpki-invalid
from {
protocol bgp;
validation-database invalid;
}
then {
validation-state invalid;
reject;
}
}
}
policy-statement NO-REDISTRIBUTE {
term 0 {
from community NO-REDISTRIBUTE;
then reject;
}
term 1 {
from community WELL-KNOWN;
then reject;
}
}
policy-statement IMPORT-ALL-RIBS {
term to-all-ribs {
from community REDISTRIBUTE-ALL;
then accept;
}
}
policy-statement NO-ADVERTISE-NO-EXPORT {
term set-no-advertise {
from community NO-ADVERTISE;
then set community add no-advertise;
}
term set-no-export {
from community NO-EXPORT;
then set community add no-export;
}
}
## Common policy statements ends ##

{% for client in clients %}
policy-statement IMPORT-{{ client.id }}-RIB {
term block-this-rib {
from community BLOCK-TO-{{ client.id }};
then reject;
}
term permit-this-rib {
from community PERMIT-TO-{{ client.id }};
then accept;
}
term no-advertise-this-rib {
from community NO-ADVERTISE-{{ client.id }};
then community add no-advertise;
}
term no-export-this-rib {
from community NO-EXPORT-{{ client.id }};
then community add no-export;
}
}

policy-statement EXPORT-ASPATH-PADDING-{{ client.id }} {
term prepend-once-all {
from community [ PREPEND-ONCE-ALL PREPEND-ONCE-{{ client.id }} ];
then as-path-prepend {{ client.asn }};
}
term prepend-twice-all {
from community [ PREPEND-TWICE-ALL PREPEND-TWICE-{{ client.id }} ];
then as-path-prepend "{{ client.asn }} {{ client.asn }}";
}
term prepend-trice-all {
from community [ PREPEND-TRICE-ALL PREPEND-TRICE-{{ client.id }}];
then as-path-prepend "{{ client.asn }} {{ client.asn }} {{ client.asn }}";
}
}
{% endfor %}
23 changes: 23 additions & 0 deletions templates/junos/routing-instance.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% for client in clients %}
routing-instances {
CLIENT1_{{ client.id }} {
instance-type no-forwarding;
routing-options {
router-id {{ cfg.router_id }};
instance-import [ IPv4-ONLY IMPORT-{{ client.id }}-RIB IMPORT-ALL-RIBS ];
}
protocols {
bgp {
peer-as {{ client.asn }};
group CLIENT-{{ client.id }} {
export EXPORT-ASPATH-PADDING-{{ client.id }}
neighbor {{ client.ip }} {
authentication-key Sup3rS3cr3t-{{ client.asn }};
forwarding-context master;
}
}
}
}
}
}
{% endfor %}
3 changes: 3 additions & 0 deletions tests/cli
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ build_cmd "openbgpd" --target-version 6.1 | must_not_contain "Compatibility issu
SUB_TEST="$LINENO"
build_cmd "openbgpd" --ignore-issues "blackhole_filtering_rewrite_ipv6_nh" | must_not_contain "'do_not_announce_to_peer'"

SUB_TEST="$LINENO"
build_cmd "junos" | must_not_contain "invalid choice"

# ---------------------------------------------
reset
TITLE="OpenBGPD large comms"
Expand Down