mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
confd: add support for static ARP and neighbor cache
Add support for static ARP (IPv4) and neighbor cache (IPv6) entries per interface. Static entries are installed as permanent kernel neighbor table entries that are never evicted by normal ARP/NDP aging. Fixes #819 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -681,6 +681,8 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
|
||||
err = err ? : netdag_gen_link_addr(ip, cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(net, ip, "ipv4", cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(net, ip, "ipv6", cif, dif);
|
||||
err = err ? : netdag_gen_ip_neighs(net, ip, "ipv4", cif, dif);
|
||||
err = err ? : netdag_gen_ip_neighs(net, ip, "ipv6", cif, dif);
|
||||
if (err)
|
||||
goto err_close_ip;
|
||||
|
||||
|
||||
@@ -114,6 +114,8 @@ int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
|
||||
struct lyd_node *dif);
|
||||
int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif);
|
||||
int netdag_gen_ip_neighs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif);
|
||||
|
||||
/* if-bridge.c */
|
||||
int bridge_mstpd_gen(struct lyd_node *cifs);
|
||||
|
||||
@@ -242,6 +242,79 @@ static int netdag_set_conf_addrs(FILE *ip, const char *ifname,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_diff_neigh(FILE *ip, const char *ifname,
|
||||
struct lyd_node *neigh)
|
||||
{
|
||||
enum lydx_op op = lydx_get_op(neigh);
|
||||
struct lyd_node *addr, *lladdr;
|
||||
struct lydx_diff addrd, lladrd;
|
||||
|
||||
addr = lydx_get_child(neigh, "ip");
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
lydx_get_diff(addr, &addrd);
|
||||
|
||||
if (op == LYDX_OP_DELETE) {
|
||||
fprintf(ip, "neigh del %s dev %s\n", addrd.old, ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lladdr = lydx_get_child(neigh, "link-layer-address");
|
||||
if (!lladdr)
|
||||
return -EINVAL;
|
||||
|
||||
lydx_get_diff(lladdr, &lladrd);
|
||||
fprintf(ip, "neigh replace %s lladdr %s dev %s nud permanent\n",
|
||||
addrd.new, lladrd.new, ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_set_conf_neighs(FILE *ip, const char *ifname,
|
||||
struct lyd_node *ipvx)
|
||||
{
|
||||
struct lyd_node *neigh;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ipvx), neigh, "neighbor") {
|
||||
fprintf(ip, "neigh replace %s lladdr %s dev %s nud permanent\n",
|
||||
lydx_get_cattr(neigh, "ip"),
|
||||
lydx_get_cattr(neigh, "link-layer-address"),
|
||||
ifname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdag_gen_ip_neighs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, proto);
|
||||
struct lyd_node *ipdiff = lydx_get_child(dif, proto);
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
struct lyd_node *neigh;
|
||||
int err = 0;
|
||||
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled")) {
|
||||
FILE *fp = dagger_fopen_net_exit(net, ifname, NETDAG_EXIT_PRE, "flush-neigh.sh");
|
||||
if (fp) {
|
||||
fprintf(fp, "ip -%c neigh flush dev %s nud permanent\n", proto[3], ifname);
|
||||
fclose(fp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lydx_get_op(lydx_get_child(ipdiff, "enabled")) == LYDX_OP_REPLACE)
|
||||
return netdag_set_conf_neighs(ip, ifname, ipconf);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ipdiff), neigh, "neighbor") {
|
||||
err = netdag_gen_diff_neigh(ip, ifname, neigh);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ MODULES=(
|
||||
"ietf-hardware@2018-03-13.yang -e hardware-state -e hardware-sensor"
|
||||
"infix-hardware@2026-02-08.yang"
|
||||
"ieee802-dot1q-types@2022-10-29.yang"
|
||||
"infix-ip@2025-11-02.yang"
|
||||
"infix-ip@2026-04-28.yang"
|
||||
"infix-if-type@2026-01-07.yang"
|
||||
"infix-routing@2026-03-11.yang"
|
||||
"ieee802-dot1ab-lldp@2022-03-15.yang"
|
||||
|
||||
@@ -18,6 +18,10 @@ module infix-ip {
|
||||
|
||||
description "This module augments ietf-ip with Infix extensions and deviations.";
|
||||
|
||||
revision 2026-04-28 {
|
||||
description "Add support for ARP and neighbor cache (ietf-ip neighbor lists).";
|
||||
reference "Internal, issue #819.";
|
||||
}
|
||||
revision 2025-11-02 {
|
||||
description "Change autoconf to presence container, removing enabled leaf.";
|
||||
reference "Internal, issue #1109.";
|
||||
@@ -65,15 +69,8 @@ module infix-ip {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv4/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/if:interfaces/if:interface/ip:ipv6/ip:neighbor" {
|
||||
deviate not-supported;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,3 +28,17 @@ def ipaddrs(ifname=None, netns=None):
|
||||
return HOST.run_json(pre + ["ip", "-j", "addr", "show"] + filt)
|
||||
|
||||
return { addr["ifname"]: addr for addr in _ipaddrs(ifname, netns) }
|
||||
|
||||
|
||||
@cache
|
||||
def ipneighs(ifname=None, netns=None):
|
||||
def _ipneighs(ifname, netns):
|
||||
pre = ["ip", "netns", "exec", netns] if netns else []
|
||||
filt = ["dev", ifname] if ifname else []
|
||||
return HOST.run_json(pre + ["ip", "-j", "neigh", "show"] + filt, [])
|
||||
|
||||
result = {}
|
||||
for e in _ipneighs(ifname, netns):
|
||||
if dev := e.get("dev"):
|
||||
result.setdefault(dev, []).append(e)
|
||||
return result
|
||||
|
||||
@@ -1,4 +1,55 @@
|
||||
import ipaddress
|
||||
|
||||
from ..host import HOST
|
||||
from . import common
|
||||
|
||||
|
||||
def neigh_state(states):
|
||||
xlate = {
|
||||
"REACHABLE": "reachable",
|
||||
"STALE": "stale",
|
||||
"DELAY": "delay",
|
||||
"PROBE": "probe",
|
||||
"INCOMPLETE": "incomplete",
|
||||
}
|
||||
return next((xlate[s] for s in states if s in xlate), None)
|
||||
|
||||
|
||||
def neighbors(ifname, family):
|
||||
result = []
|
||||
for entry in common.ipneighs().get(ifname, []):
|
||||
dst = entry.get("dst", "")
|
||||
try:
|
||||
version = ipaddress.ip_address(dst).version
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if version != (4 if family == "inet" else 6):
|
||||
continue
|
||||
|
||||
lladdr = entry.get("lladdr")
|
||||
states = entry.get("state", [])
|
||||
|
||||
if not lladdr:
|
||||
continue
|
||||
|
||||
origin = "static" if "PERMANENT" in states else "dynamic"
|
||||
neigh = {
|
||||
"ip": dst,
|
||||
"link-layer-address": lladdr,
|
||||
"origin": origin,
|
||||
}
|
||||
|
||||
if family == "inet6":
|
||||
if state := neigh_state(states):
|
||||
neigh["state"] = state
|
||||
if entry.get("router"):
|
||||
neigh["is-router"] = [None]
|
||||
|
||||
result.append(neigh)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def inet2yang_origin(inet):
|
||||
"""Translate kernel IP address origin to YANG"""
|
||||
@@ -34,23 +85,31 @@ def addresses(ipaddr, proto):
|
||||
|
||||
def ipv4(ipaddr):
|
||||
ipv4 = {}
|
||||
ifname = ipaddr.get("ifname")
|
||||
|
||||
mtu = ipaddr.get("mtu")
|
||||
if mtu and ipaddr.get("ifname") != "lo":
|
||||
if mtu and ifname != "lo":
|
||||
ipv4["mtu"] = mtu
|
||||
|
||||
if addrs := addresses(ipaddr, "inet"):
|
||||
ipv4["address"] = addrs
|
||||
|
||||
if neighs := neighbors(ifname, "inet"):
|
||||
ipv4["neighbor"] = neighs
|
||||
|
||||
return ipv4
|
||||
|
||||
def ipv6(ipaddr):
|
||||
ipv6 = {}
|
||||
ifname = ipaddr.get("ifname")
|
||||
|
||||
if mtu := HOST.read(f"/proc/sys/net/ipv6/conf/{ipaddr['ifname']}/mtu"):
|
||||
if mtu := HOST.read(f"/proc/sys/net/ipv6/conf/{ifname}/mtu"):
|
||||
ipv6["mtu"] = int(mtu.strip())
|
||||
|
||||
if addrs := addresses(ipaddr, "inet6"):
|
||||
ipv6["address"] = addrs
|
||||
|
||||
if neighs := neighbors(ifname, "inet6"):
|
||||
ipv6["neighbor"] = neighs
|
||||
|
||||
return ipv6
|
||||
|
||||
Reference in New Issue
Block a user