Add support for reading routing operational data for IPv4

In the CLI:
admin@infix-00-00-00:/> show routes
PREFIX                        DESTINATION                   PROTOCOL  METRIC
1.1.1.0/24                    e0                            kernel
192.168.100.0/24              1.1.1.1                       static    20
192.168.110.2/32              blackhole                     static    20
192.168.120.2/32              blackhole                     static    20
192.168.130.2/32              unreachable                   static    20
This commit is contained in:
Mattias Walström
2023-11-23 10:30:26 +01:00
parent 6cfcf3ca10
commit 37f7b5ba92
9 changed files with 372 additions and 97 deletions
+68 -15
View File
@@ -14,6 +14,12 @@ class Pad:
state = 12
data = 41
class PadRoute:
prefix = 30
protocol = 10
next_hop = 30
metric = 10
class Decore():
@staticmethod
def decorate(sgr, txt, restore="0"):
@@ -31,27 +37,53 @@ class Decore():
def green(txt):
return Decore.decorate("32", txt, "39")
def get_json_data(default, indata, *args):
data = indata
for arg in args:
if arg in data:
data = data.get(arg)
else:
return default
return data
class Route:
def __init__(self,data):
self.data = data
self.prefix = data.get('ietf-ipv4-unicast-routing:destination-prefix', '')
self.protocol = data.get('source-protocol','')[14:]
self.metric = data.get('route-preference','')
interface = get_json_data(None, self.data, 'next-hop', 'outgoing-interface')
address = get_json_data(None, self.data, 'next-hop', 'ietf-ipv4-unicast-routing:next-hop-address')
special = get_json_data(None, self.data, 'next-hop', 'special-next-hop')
if interface:
self.next_hop = interface
elif address:
self.next_hop = address
elif special:
self.next_hop = special
else:
self.next_hop = "unspecified"
def print(self):
row = f"{self.prefix:<{PadRoute.prefix}}"
row += f"{self.next_hop:<{PadRoute.next_hop}}"
row += f"{self.metric:<{PadRoute.metric}}"
row += f"{self.protocol:<{PadRoute.protocol}}"
print(row)
class Iface:
def get_json_data(self, default, *args):
data = self.data
for arg in args:
if arg in data:
data = data.get(arg)
else:
return default
return data
def __init__(self, data):
self.data = data
self.name = data.get('name', '')
self.type = data.get('type', '')
self.index = data.get('if-index', '')
self.oper_status = data.get('oper-status', '')
self.autoneg = self.get_json_data('unknown', 'ieee802-ethernet-interface:ethernet',
self.autoneg = get_json_data('unknown', self.data, 'ieee802-ethernet-interface:ethernet',
'auto-negotiation', 'enable')
self.duplex = self.get_json_data('', 'ieee802-ethernet-interface:ethernet', 'duplex')
self.speed = self.get_json_data('', 'ieee802-ethernet-interface:ethernet', 'speed')
self.duplex = get_json_data('', self.data,'ieee802-ethernet-interface:ethernet','duplex')
self.speed = get_json_data('', self.data, 'ieee802-ethernet-interface:ethernet', 'speed')
self.phys_address = data.get('phys-address', '')
if data.get('statistics'):
@@ -231,8 +263,8 @@ class Iface:
print(f"{'in-octets':<{20}}: {self.in_octets}")
print(f"{'out-octets':<{20}}: {self.out_octets}")
frame = self.get_json_data([], 'ieee802-ethernet-interface:ethernet',
'statistics', 'frame')
frame = get_json_data([], self.data,'ieee802-ethernet-interface:ethernet',
'statistics', 'frame')
if frame:
print(f"")
for key, val in frame.items():
@@ -297,6 +329,25 @@ def ietf_interfaces(json, name):
sys.exit(1)
pr_interface_list(json)
def ietf_routing(json, protocol="ipv4"):
if not json.get("ietf-routing:routing"):
print(f"Error, top level \"ietf-routing:routing\" missing")
sys.exit(1)
routes = get_json_data({}, json, 'ietf-routing:routing','ribs', 'rib')[0]
routes = get_json_data(None, routes, "routes", "route")
hdr = (f"{'PREFIX':<{PadRoute.prefix}}"
f"{'NEXT-HOP':<{PadRoute.next_hop}}"
f"{'METRIC':<{PadRoute.metric}}"
f"{'PROTOCOL':<{PadRoute.protocol}}")
print(Decore.invert(hdr))
if routes:
for r in routes:
route = Route(r)
route.print()
def main():
try:
json_data = json.load(sys.stdin)
@@ -309,6 +360,8 @@ def main():
if args.module == "ietf-interfaces":
sys.exit(ietf_interfaces(json_data, args.name))
if args.module == "ietf-routing":
sys.exit(ietf_routing(json_data, args.name))
else:
print(f"Error, unknown module {args.module}")
sys.exit(1)
+74 -18
View File
@@ -116,6 +116,40 @@ def iface_is_dsa(iface_in):
return False
return True
def add_ipv4_route(routes):
cmd = ['ip', '-4', '-s', '-d', '-j', 'route']
data = run_json_cmd(cmd)
out={}
out["route"] = []
for d in data:
new = {}
next_hop = {}
if(d['dst'] == "default"):
d['dst'] = "0.0.0.0/0"
if(d['dst'].find('/') == -1):
d['dst'] = d['dst']+"/32"
new['ietf-ipv4-unicast-routing:destination-prefix'] = d['dst']
new['source-protocol'] = "infix-routing:"+d['protocol']
if d.get("metric"):
new['route-preference'] = d['metric']
if d['type'] == "blackhole":
next_hop['special-next-hop'] = "blackhole"
if d['type'] == "unreachable":
next_hop['special-next-hop'] = "unreachable"
if d['type'] == "unicast":
if(d.get("gateway")):
next_hop['ietf-ipv4-unicast-routing:next-hop-address'] = d['gateway']
elif(d.get("dev")):
next_hop['outgoing-interface'] = d['dev']
new['next-hop'] = next_hop
out['route'].append(new)
insert(routes, 'routes', out)
def add_ip_link(ifname, iface_out):
cmd = ['ip', '-s', '-d', '-j', 'link', 'show', 'dev', ifname]
@@ -125,7 +159,6 @@ def add_ip_link(ifname, iface_out):
sys,exit(1)
iface_in = data[0]
iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0]
if 'ifname' in iface_in:
iface_out['name'] = iface_in['ifname']
@@ -323,25 +356,48 @@ def add_ethtool_std(ifname, iface_out):
insert(iface_out, "ieee802-ethernet-interface:ethernet", "speed", str(num))
if __name__ == "__main__":
yang_data = {
"ietf-interfaces:interfaces": {
"interface": [{}]
}
}
# For now, we handle each interface separately, as this is how it's
# currently implemented in sysrepo. I.e sysrepo will subscribe to
# each individual interface and query it for YANG data.
if len(sys.argv) != 2:
print(f"usage: yanger IFNAME", file=sys.stderr)
if len(sys.argv) < 2:
print(f"usage: yanger <model> [params]", file=sys.stderr)
sys.exit(1)
ifname = sys.argv[1]
iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0]
model = sys.argv[1]
if(model == 'ietf-interfaces'):
# For now, we handle each interface separately, as this is how it's
# currently implemented in sysrepo. I.e sysrepo will subscribe to
# each individual interface and query it for YANG data.
add_ip_link(ifname, iface_out)
add_ip_addr(ifname, iface_out)
add_ethtool_groups(ifname, iface_out)
add_ethtool_std(ifname, iface_out)
if len(sys.argv) != 3:
print(f"usage: yanger ietf-interfaces IFNAME", file=sys.stderr)
sys.exit(1)
yang_data = {
"ietf-interfaces:interfaces": {
"interface": [{}]
}
}
ifname = sys.argv[2]
iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0]
add_ip_link(ifname, iface_out)
add_ip_addr(ifname, iface_out)
add_ethtool_groups(ifname, iface_out)
add_ethtool_std(ifname, iface_out)
elif(model == 'ietf-routing'):
yang_data = {
"ietf-routing:routing": {
"ribs": {
"rib": [{
"name": "ipv4",
"address-family": "ipv4",
}]
}
}
}
ipv4routes = yang_data['ietf-routing:routing']['ribs']['rib'][0]
add_ipv4_route(ipv4routes);
else:
print(f"Unsupported model {model}", file=sys.stderr)
sys.exit(1)
print(json.dumps(yang_data, indent=2))
+7
View File
@@ -17,3 +17,10 @@ run:
run-menuconfig: $(BUILD_DIR)/buildroot-config/mconf
CONFIG_="CONFIG_" BR2_CONFIG="$(BINARIES_DIR)/.config" \
$(BUILD_DIR)/buildroot-config/mconf $(BINARIES_DIR)/Config.in
define FRR_POST_BUILD_HOOK
mkdir -p $(TARGET_DIR)/etc/iproute2/
cp -r $(@D)/tools/etc/iproute2/rt_protos.d/ $(TARGET_DIR)/etc/iproute2/
endef
FRR_POST_BUILD_HOOKS += FRR_POST_BUILD_HOOK
+1 -1
View File
@@ -711,6 +711,6 @@ UseTab: Always
# Infix overrides
#
ColumnLimit: 0
ForEachMacros: ['LYX_LIST_FOR_EACH', 'TAILQ_FOREACH', 'LY_LIST_FOR']
ForEachMacros: ['LYX_LIST_FOR_EACH', 'TAILQ_FOREACH', 'LY_LIST_FOR', 'json_array_foreach']
UseTab: AlignWithSpaces
...
@@ -43,12 +43,39 @@ module infix-routing {
}
}
identity infix-source-protocol {
description "Infix naming of source protocols";
}
identity kernel {
description "Kernel added route";
base infix-source-protocol;
}
identity static {
description "Static added route";
base infix-source-protocol;
}
identity ospf {
description "OSPF added route";
base infix-source-protocol;
}
identity dhcp {
description "DHCP added route";
base infix-source-protocol;
}
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:name" {
description "Limitation: Only one routing instance per protocol.";
deviate replace {
type infix-control-plane-name;
}
}
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:source-protocol" {
description "Use type names from iproute2";
deviate replace {
type identityref {
base infix-source-protocol;
}
}
}
/* Static routes */
deviation "/ietf-r:routing/ietf-r:control-plane-protocols/ietf-r:control-plane-protocol/ietf-r:static-routes/v4ur:ipv4/v4ur:route/v4ur:next-hop/v4ur:next-hop-options/v4ur:next-hop-list" {
deviate not-supported;
@@ -63,6 +90,9 @@ module infix-routing {
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:active-route" {
deviate not-supported;
}
deviation "/ietf-r:routing/ietf-r:ribs/ietf-r:rib/ietf-r:routes/ietf-r:route/ietf-r:next-hop/ietf-r:next-hop-options/ietf-r:next-hop-list" {
deviate not-supported;
}
/* OSPF */
/*
+6
View File
@@ -230,6 +230,12 @@
</SWITCH>
</COMMAND>
<COMMAND name="routes" help="Show routing table">
<ACTION sym="script">
sysrepocfg -f json -X -d operational -m ietf-routing | \
/lib/infix/cli-pretty "ietf-routing"
</ACTION>
</COMMAND>
<!-- https://www.cisco.com/c/en/us/td/docs/wireless/access_point/mob_exp/83/cmd-ref/me_cr_book/me_ports_and_interfaces_cli.html -->
<COMMAND name="interfaces" help="Show interface info">
<SWITCH name="optional" min="0" max="1">
+124 -55
View File
@@ -26,11 +26,12 @@
#include "shared.h"
#define SOCK_RMEM_SIZE 1000000 /* Arbitrary chosen, default = 212992 */
#define SOCK_RMEM_SIZE 1000000 /* Arbitrary chosen, default = 212992 */
#define NL_BUF_SIZE 4096 /* Arbitrary chosen */
#define XPATH_MAX PATH_MAX
#define XPATH_IFACE_BASE "/ietf-interfaces:interfaces"
#define XPATH_ROUTING_BASE "/ietf-routing:routing"
TAILQ_HEAD(sub_head, sub);
@@ -38,11 +39,12 @@ TAILQ_HEAD(sub_head, sub);
* types, not only interfaces.
*/
struct sub {
char ifname[IFNAMSIZ];
char name[IFNAMSIZ+3];
struct ev_io watcher;
sr_subscription_ctx_t *sr_sub;
TAILQ_ENTRY(sub) entries;
TAILQ_ENTRY(sub)
entries;
};
struct netlink {
@@ -59,11 +61,11 @@ struct statd {
static void set_sock_rcvbuf(int sd, int size)
{
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
perror("setsockopt");
return;
}
DEBUG("Socket receive buffer size increased to: %d bytes", size);
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) {
perror("setsockopt");
return;
}
DEBUG("Socket receive buffer size increased to: %d bytes", size);
}
static int nl_sock_init(void)
@@ -90,12 +92,12 @@ static int nl_sock_init(void)
return sock;
}
static struct sub *sub_find_iface(struct sub_head *subs, const char *ifname)
static struct sub *sub_find(struct sub_head *subs, const char *name)
{
struct sub *sub;
TAILQ_FOREACH(sub, subs, entries) {
if (strcmp(sub->ifname, ifname) == 0)
if (strcmp(sub->name, name) == 0)
return sub;
}
@@ -120,17 +122,26 @@ static json_t *json_get_ip_link(void)
}
static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent,
char *ifname)
char *model, const char *arg)
{
char *args[] = {
char *yanger_args[4] = {
"/lib/infix/yanger",
ifname,
model,
NULL,
NULL
};
FILE *stream;
int err;
int fd;
if (!model) {
ERROR("Missing yang model to use");
return SR_ERR_SYS;
}
if (!strcmp(model, "ietf-interfaces"))
yanger_args[2] = (char *)arg;
fd = memfd_create("my_temp_file", 0);
if (fd == -1) {
ERROR("Error, unable to create memfd");
@@ -145,7 +156,7 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent
return SR_ERR_SYS;
}
err = fsystemv(args, NULL, stream, NULL);
err = fsystemv(yanger_args, NULL, stream, NULL);
if (err) {
ERROR("Error, running yanger");
fclose(stream);
@@ -162,7 +173,7 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent
return SR_ERR_SYS;
}
err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY , 0, parent);
err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY, 0, parent);
if (err)
ERROR("Error, parsing yanger data (%d)", err);
@@ -179,13 +190,14 @@ static int sr_ifaces_cb(sr_session_ctx_t *session, uint32_t, const char *path,
struct sub *sub = priv;
const struct ly_ctx *ctx;
sr_conn_ctx_t *con;
char *ifname;
int err;
DEBUG("Incoming query for xpath: %s", path);
con = sr_session_get_connection(session);
if (!con) {
ERROR("Error, getting connection");
ERROR("Error, getting sr connection");
return SR_ERR_INTERNAL;
}
@@ -195,7 +207,39 @@ static int sr_ifaces_cb(sr_session_ctx_t *session, uint32_t, const char *path,
return SR_ERR_INTERNAL;
}
err = ly_add_yanger_data(ctx, parent, sub->ifname);
ifname = &sub->name[3];
err = ly_add_yanger_data(ctx, parent, "ietf-interfaces", ifname);
if (err)
ERROR("Error adding yanger data");
sr_release_context(con);
return err;
}
static int sr_routes_cb(sr_session_ctx_t *session, uint32_t, const char *path,
const char *, const char *, uint32_t,
struct lyd_node **parent, __attribute__((unused)) void *priv)
{
const struct ly_ctx *ctx;
sr_conn_ctx_t *con;
sr_error_t err;
DEBUG("Incoming query for xpath: %s", path);
con = sr_session_get_connection(session);
if (!con) {
ERROR("Error, getting sr connection");
return SR_ERR_INTERNAL;
}
ctx = sr_acquire_context(con);
if (!ctx) {
ERROR("Error, acquiring context");
return SR_ERR_INTERNAL;
}
err = ly_add_yanger_data(ctx, parent, "ietf-routing", NULL);
if (err)
ERROR("Error adding yanger data");
@@ -221,48 +265,36 @@ static void sr_event_cb(struct ev_loop *, struct ev_io *w, int)
sr_subscription_process_events(sub->sr_sub, NULL, NULL);
}
static int sub_to_iface(struct statd *statd, const char *ifname)
static int subscribe(struct statd *statd, char *model, char *xpath, const char *name,
int (*cb)(sr_session_ctx_t *session, uint32_t, const char *, const char *,
const char *, uint32_t, struct lyd_node **parent, void *priv))
{
char path[XPATH_MAX] = {};
struct sub *sub;
int sr_ev_pipe;
int err;
sr_error_t err;
/**
* Skip internal interfaces (such as dsa0)
*
* NOTE: this is a good solution but it might be to slow if a lot of
* new interfaces pops up at the same time. We don't want to delay
* processing the netlink messages to much.
*/
if (ip_link_check_group(ifname, "internal") == 1)
return SR_ERR_OK;
sub = sub_find_iface(&statd->subs, ifname);
sub = sub_find(&statd->subs, name);
if (sub) {
DEBUG("Interface %s already subscribed", ifname);
DEBUG("%s already subscribed", name);
return SR_ERR_OK;
}
sub = malloc(sizeof(struct sub));
if (!sub)
return SR_ERR_INTERNAL;
memset(sub, 0, sizeof(struct sub));
snprintf(sub->ifname, sizeof(sub->ifname), "%s", ifname);
snprintf(path, sizeof(path), "%s/interface[name='%s']",
XPATH_IFACE_BASE, ifname);
if (strlen(name) > sizeof(sub->name)) {
ERROR("Subscriber name is to long");
return SR_ERR_INTERNAL;
}
snprintf(sub->name, sizeof(sub->name), "%s", name);
DEBUG("Subscribe to events for \"%s\"", path);
err = sr_oper_get_subscribe(statd->sr_ses, "ietf-interfaces",
path, sr_ifaces_cb, sub,
DEBUG("Subscribe to events for \"%s\"", xpath);
err = sr_oper_get_subscribe(statd->sr_ses, model,xpath, cb, sub,
SR_SUBSCR_DEFAULT | SR_SUBSCR_NO_THREAD | SR_SUBSCR_DONE_ONLY,
&sub->sr_sub);
if (err) {
ERROR("Error, subscribing to path \"%s\": %s", path, sr_strerror(err));
ERROR("Error, subscribing to path \"%s\": %s", xpath, sr_strerror(err));
free(sub);
return SR_ERR_INTERNAL;
return err;
}
err = sr_get_event_pipe(sub->sr_sub, &sr_ev_pipe);
@@ -270,7 +302,7 @@ static int sub_to_iface(struct statd *statd, const char *ifname)
ERROR("Error, getting sysrepo event pipe: %s", sr_strerror(err));
sr_unsubscribe(sub->sr_sub);
free(sub);
return SR_ERR_INTERNAL;
return err;
}
TAILQ_INSERT_TAIL(&statd->subs, sub, entries);
@@ -282,31 +314,63 @@ static int sub_to_iface(struct statd *statd, const char *ifname)
return SR_ERR_OK;
}
static void unsub_to_ifaces(struct statd *statd)
static int sub_to_routes(struct statd *statd)
{
return subscribe(statd, "ietf-routing", XPATH_ROUTING_BASE, "routes", sr_routes_cb);
}
static int sub_to_iface(struct statd *statd, const char *ifname)
{
char path[XPATH_MAX] = {};
char name[IFNAMSIZ+3];
snprintf(name,sizeof(name),"if-%s",ifname);
/**
* Skip internal interfaces (such as dsa0)
*
* NOTE: this is a good solution but it might be to slow if a lot of
* new interfaces pops up at the same time. We don't want to delay
* processing the netlink messages to much.
*/
if (ip_link_check_group(ifname, "internal") == 1)
return SR_ERR_OK;
snprintf(path, sizeof(path), "%s/interface[name='%s']", XPATH_IFACE_BASE, ifname);
return subscribe(statd, "ietf-interfaces", path, name, sr_ifaces_cb);
}
static void unsub_to_all(struct statd *statd)
{
struct sub *sub;
while (!TAILQ_EMPTY(&statd->subs)) {
sub = TAILQ_FIRST(&statd->subs);
DEBUG("Unsubscribe from \"%s\" (all)", sub->ifname);
DEBUG("Unsubscribe from \"%s\" (all)", sub->name);
sub_delete(statd->ev_loop, &statd->subs, sub);
}
}
static int unsub_to_iface(struct statd *statd, char *ifname)
static int unsub_to_name(struct statd *statd, char *name)
{
struct sub *sub;
sub = sub_find_iface(&statd->subs, ifname);
sub = sub_find(&statd->subs, name);
if (!sub) {
ERROR("Error, can't find interface to delete (%s)", ifname);
ERROR("Error, can't find indentity to delete (%s)", name);
return SR_ERR_INTERNAL;
}
DEBUG("Unsubscribe from \"%s\"", sub->ifname);
DEBUG("Unsubscribe from \"%s\"", sub->name);
sub_delete(statd->ev_loop, &statd->subs, sub);
return SR_ERR_OK;
}
static int unsub_to_iface(struct statd *statd, char *ifname)
{
char name[IFNAMSIZ+3];
snprintf(name,sizeof(name),"if-%s",ifname);
return unsub_to_name(statd, name);
}
static int nl_process_msg(struct nlmsghdr *nlh, struct statd *statd)
{
@@ -337,7 +401,7 @@ static int nl_process_msg(struct nlmsghdr *nlh, struct statd *statd)
static void nl_event_cb(struct ev_loop *, struct ev_io *w, int)
{
struct statd *statd = (struct statd *) w->data;
struct statd *statd = (struct statd *)w->data;
char buf[NL_BUF_SIZE];
struct nlmsghdr *nlh;
int err;
@@ -379,13 +443,11 @@ static int sub_to_ifaces(struct statd *statd)
json_array_foreach(j_root, i, j_iface) {
json_t *j_ifname;
int err;
j_ifname = json_object_get(j_iface, "ifname");
if (!json_is_string(j_ifname)) {
ERROR("Got unexpected JSON type for 'ifname'");
continue;
}
err = sub_to_iface(statd, json_string_value(j_ifname));
if (err) {
ERROR("Unable to subscribe to %s", json_string_value(j_ifname));
@@ -447,6 +509,13 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
err = sub_to_routes(&statd);
if (err) {
ERROR("Error register for IPv4 routes");
sr_disconnect(sr_conn);
return EXIT_FAILURE;
}
ev_signal_init(&sigint_watcher, sigint_cb, SIGINT);
sigint_watcher.data = &statd;
ev_signal_start(statd.ev_loop, &sigint_watcher);
@@ -464,7 +533,7 @@ int main(int argc, char *argv[])
/* We should never get here during normal operation */
INFO("Status daemon shutting down");
unsub_to_ifaces(&statd);
unsub_to_all(&statd);
sr_session_stop(statd.sr_ses);
sr_disconnect(sr_conn);
+6 -1
View File
@@ -32,8 +32,13 @@
- "-n vlan1"
- case: run.sh
name: "interface-birdge"
name: "interface-bridge"
opts:
- "json/bloated.json"
- "ietf-interfaces"
- "-n br0"
- case: run.sh
name: "routing"
opts:
- "json/bloated.json"
- "ietf-routing"
+56 -7
View File
@@ -1,4 +1,53 @@
{
"ietf-routing:routing": {
"ribs": {
"rib": [
{
"name": "ipv4",
"address-family": "ietf-routing:ipv4",
"routes": {
"route": [
{
"next-hop": {
"outgoing-interface": "e0"
},
"source-protocol": "infix-routing:kernel",
"ietf-ipv4-unicast-routing:destination-prefix": "1.1.1.0/24"
},
{
"next-hop": {
"ietf-ipv4-unicast-routing:next-hop-address": "1.1.1.1"
},
"source-protocol": "infix-routing:static",
"ietf-ipv4-unicast-routing:destination-prefix": "192.168.100.0/24"
},
{
"next-hop": {
"special-next-hop": "blackhole"
},
"source-protocol": "infix-routing:static",
"ietf-ipv4-unicast-routing:destination-prefix": "192.168.110.2/32"
},
{
"next-hop": {
"special-next-hop": "blackhole"
},
"source-protocol": "infix-routing:static",
"ietf-ipv4-unicast-routing:destination-prefix": "192.168.120.2/32"
},
{
"next-hop": {
"special-next-hop": "unreachable"
},
"source-protocol": "infix-routing:static",
"ietf-ipv4-unicast-routing:destination-prefix": "192.168.130.2/32"
}
]
}
}
]
}
},
"ietf-interfaces:interfaces": {
"interface": [
{
@@ -138,7 +187,7 @@
{
"ip": "10.140.211.247",
"prefix-length": 24,
"origin" : "static"
"origin": "static"
}
]
},
@@ -175,22 +224,22 @@
{
"ip": "192.168.76.120",
"prefix-length": 24,
"origin" : "static"
"origin": "static"
},
{
"ip": "192.168.151.147",
"prefix-length": 24,
"origin" : "static"
"origin": "static"
},
{
"ip": "192.168.161.217",
"prefix-length": 24,
"origin" : "dhcp"
"origin": "dhcp"
},
{
"ip": "192.168.55.37",
"prefix-length": 24,
"origin" : "dhcp"
"origin": "dhcp"
},
{
"ip": "192.168.231.242",
@@ -457,12 +506,12 @@
{
"ip": "172.28.29.76",
"prefix-length": 24,
"origin" : "static"
"origin": "static"
},
{
"ip": "172.16.187.13",
"prefix-length": 24,
"origin" : "static"
"origin": "static"
}
]
},