mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
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:
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
/*
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user