Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161488957
D40103.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
44 KB
Referenced Files
None
Subscribers
None
D40103.id.diff
View Options
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -48,14 +48,14 @@
#include <net/route.h>
#include <net/route/nhop.h>
#include <net/route/route_ctl.h>
+#include <netinet/in_var.h>
#include <netinet6/in6_var.h>
+#include <netinet6/scope6_var.h> /* scope deembedding */
#include <netlink/netlink.h>
#include <netlink/netlink_ctl.h>
#include <netlink/netlink_route.h>
#include <netlink/route/route_var.h>
-#include <netinet6/scope6_var.h> /* scope deembedding */
-
#define DEBUG_MOD_NAME nl_iface
#define DEBUG_MAX_LEVEL LOG_DEBUG3
#include <netlink/netlink_debug.h>
@@ -242,6 +242,9 @@
addr_len = ((const struct sockaddr_dl *)sa)->sdl_alen;
addr_data = LLADDR_CONST((const struct sockaddr_dl *)sa);
break;
+ case AF_UNSPEC:
+ /* Ignore empty SAs without warning */
+ return (true);
default:
NL_LOG(LOG_DEBUG2, "unsupported family: %d, skipping", sa->sa_family);
return (true);
@@ -649,16 +652,60 @@
return (modify_link(hdr, &attrs, &bm, nlp, npt));
}
+static void
+set_scope6(struct sockaddr *sa, uint32_t ifindex)
+{
+#ifdef INET6
+ if (sa != NULL && sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
+ in6_set_unicast_scopeid(&sa6->sin6_addr, ifindex);
+ }
+#endif
+}
+
+static bool
+check_sa_family(const struct sockaddr *sa, int family, const char *attr_name,
+ struct nl_pstate *npt)
+{
+ if (sa == NULL || sa->sa_family == family)
+ return (true);
+
+ nlmsg_report_err_msg(npt, "wrong family for %s attribute: %d != %d",
+ attr_name, family, sa->sa_family);
+ return (false);
+}
+
struct nl_parsed_ifa {
- uint8_t ifa_family;
- uint8_t ifa_prefixlen;
- uint8_t ifa_scope;
- uint32_t ifa_index;
- uint32_t ifa_flags;
- struct sockaddr *ifa_address;
- struct sockaddr *ifa_local;
+ uint8_t ifa_family;
+ uint8_t ifa_prefixlen;
+ uint8_t ifa_scope;
+ uint32_t ifa_index;
+ uint32_t ifa_flags;
+ uint32_t ifaf_vhid;
+ uint32_t ifaf_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_dst;
+ struct sockaddr *ifa_broadcast;
+ struct ifa_cacheinfo *ifa_cacheinfo;
+ struct sockaddr *f_ifa_addr;
+ struct sockaddr *f_ifa_dst;
};
+static int
+nlattr_get_cinfo(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg __unused, void *target)
+{
+ if (__predict_false(NLA_DATA_LEN(nla) != sizeof(struct ifa_cacheinfo))) {
+ NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not ifa_cacheinfo",
+ nla->nla_type, NLA_DATA_LEN(nla));
+ return (EINVAL);
+ }
+ *((struct ifa_cacheinfo **)target) = (struct ifa_cacheinfo *)NL_RTA_DATA(nla);
+ return (0);
+}
+
#define _IN(_field) offsetof(struct ifaddrmsg, _field)
#define _OUT(_field) offsetof(struct nl_parsed_ifa, _field)
static const struct nlfield_parser nlf_p_ifa[] = {
@@ -669,14 +716,66 @@
{ .off_in = _IN(ifa_index), .off_out = _OUT(ifa_index), .cb = nlf_get_u32 },
};
+static const struct nlattr_parser nla_p_ifa_fbsd[] = {
+ { .type = IFAF_VHID, .off = _OUT(ifaf_vhid), .cb = nlattr_get_uint32 },
+ { .type = IFAF_FLAGS, .off = _OUT(ifaf_flags), .cb = nlattr_get_uint32 },
+};
+NL_DECLARE_ATTR_PARSER(ifa_fbsd_parser, nla_p_ifa_fbsd);
+
static const struct nlattr_parser nla_p_ifa[] = {
- { .type = IFA_ADDRESS, .off = _OUT(ifa_address), .cb = nlattr_get_ip },
- { .type = IFA_LOCAL, .off = _OUT(ifa_local), .cb = nlattr_get_ip },
+ { .type = IFA_ADDRESS, .off = _OUT(ifa_addr), .cb = nlattr_get_ip },
+ { .type = IFA_LOCAL, .off = _OUT(ifa_dst), .cb = nlattr_get_ip },
+ { .type = IFA_BROADCAST, .off = _OUT(ifa_broadcast), .cb = nlattr_get_ip },
+ { .type = IFA_CACHEINFO, .off = _OUT(ifa_cacheinfo), .cb = nlattr_get_cinfo },
{ .type = IFA_FLAGS, .off = _OUT(ifa_flags), .cb = nlattr_get_uint32 },
+ { .type = IFA_FREEBSD, .arg = &ifa_fbsd_parser, .cb = nlattr_get_nested },
};
#undef _IN
#undef _OUT
-NL_DECLARE_PARSER(ifaddrmsg_parser, struct ifaddrmsg, nlf_p_ifa, nla_p_ifa);
+
+static bool
+post_p_ifa(void *_attrs, struct nl_pstate *npt)
+{
+ struct nl_parsed_ifa *attrs = (struct nl_parsed_ifa *)_attrs;
+
+ if (!check_sa_family(attrs->ifa_addr, attrs->ifa_family, "IFA_ADDRESS", npt))
+ return (false);
+ if (!check_sa_family(attrs->ifa_dst, attrs->ifa_family, "IFA_LOCAL", npt))
+ return (false);
+ if (!check_sa_family(attrs->ifa_broadcast, attrs->ifa_family, "IFA_BROADADDR", npt))
+ return (false);
+
+ set_scope6(attrs->ifa_addr, attrs->ifa_index);
+ set_scope6(attrs->ifa_dst, attrs->ifa_index);
+
+ /*
+ * Map the Netlink attributes to FreeBSD ifa layout.
+ * If only IFA_ADDRESS or IFA_LOCAL is set OR
+ * both are set to the same value => ifa is not broadcast
+ * and the attribute value contains interface address.
+ *
+ * Otherwise (both IFA_ADDRESS and IFA_LOCAL are set and
+ * different), IFA_LOCAL contains an interface address and
+ * IFA_ADDRESS contains peer address.
+ */
+ struct sockaddr *addr, *dst;
+
+ addr = attrs->ifa_addr;
+ if ((dst = attrs->ifa_dst) != NULL) {
+ if (addr != NULL && !sa_equal(addr, dst)) {
+ /* Ptp address */
+ attrs->ifa_addr = dst;
+ attrs->ifa_dst = addr;
+ } else {
+ attrs->ifa_addr = dst;
+ attrs->ifa_dst = NULL;
+ }
+ }
+
+ return (true);
+}
+
+NL_DECLARE_PARSER_EXT(ifa_parser, struct ifaddrmsg, NULL, nlf_p_ifa, nla_p_ifa, post_p_ifa);
/*
@@ -792,6 +891,25 @@
return (nl_flags);
}
+static uint32_t
+nl_flags_to_in6(uint32_t flags)
+{
+ uint32_t in6_flags = 0;
+
+ if (flags & IFA_F_TEMPORARY)
+ in6_flags |= IN6_IFF_TEMPORARY;
+ if (flags & IFA_F_NODAD)
+ in6_flags |= IN6_IFF_NODAD;
+ if (flags & IFA_F_DEPRECATED)
+ in6_flags |= IN6_IFF_DEPRECATED;
+ if (flags & IFA_F_TENTATIVE)
+ in6_flags |= IN6_IFF_TENTATIVE;
+ if (flags & IFA_F_DADFAILED)
+ in6_flags |= IN6_IFF_DUPLICATED;
+
+ return (in6_flags);
+}
+
static void
export_cache_info6(struct nl_writer *nw, const struct in6_ifaddr *ia)
{
@@ -831,6 +949,7 @@
{
struct ifaddrmsg *ifamsg;
struct sockaddr *sa = ifa->ifa_addr;
+ struct sockaddr *sa_dst = ifa->ifa_dstaddr;
NL_LOG(LOG_DEBUG3, "dumping ifa %p type %s(%d) for interface %s",
ifa, rib_print_family(sa->sa_family), sa->sa_family, if_name(ifp));
@@ -845,8 +964,9 @@
ifamsg->ifa_scope = ifa_get_scope(ifa);
ifamsg->ifa_index = ifp->if_index;
- if (ifp->if_flags & IFF_POINTOPOINT) {
- dump_sa(nw, IFA_ADDRESS, ifa->ifa_dstaddr);
+ if ((ifp->if_flags & IFF_POINTOPOINT) && sa_dst != NULL && sa_dst->sa_family != 0) {
+ /* P2P interface may have IPv6 LL with no dst address */
+ dump_sa(nw, IFA_ADDRESS, sa_dst);
dump_sa(nw, IFA_LOCAL, sa);
} else {
dump_sa(nw, IFA_ADDRESS, sa);
@@ -930,7 +1050,7 @@
int error = 0;
struct nl_parsed_ifa attrs = {};
- error = nl_parse_nlmsg(hdr, &ifaddrmsg_parser, npt, &attrs);
+ error = nl_parse_nlmsg(hdr, &ifa_parser, npt, &attrs);
if (error != 0)
return (error);
@@ -971,6 +1091,188 @@
return (error);
}
+#ifdef INET
+static int
+handle_newaddr_inet(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
+ struct ifnet *ifp, struct nlpcb *nlp, struct nl_pstate *npt)
+{
+ if (attrs->ifa_prefixlen > 32) {
+ nlmsg_report_err_msg(npt, "invalid ifa_prefixlen");
+ return (EINVAL);
+ };
+
+ int if_flags = if_getflags(ifp);
+
+ if (if_flags & IFF_POINTOPOINT) {
+ if (attrs->ifa_addr == NULL || attrs->ifa_dst == NULL) {
+ nlmsg_report_err_msg(npt, "Empty IFA_LOCAL/IFA_ADDRESS");
+ return (EINVAL);
+ }
+ } else {
+ if (attrs->ifa_addr == NULL) {
+ nlmsg_report_err_msg(npt, "Empty IFA_LOCAL/IFA_ADDRESS");
+ return (EINVAL);
+ }
+ attrs->ifa_dst = attrs->ifa_broadcast;
+
+ if (attrs->ifa_dst == NULL && !(if_flags & IFF_LOOPBACK)) {
+ nlmsg_report_err_msg(npt, "empty IFA_BROADCAST for BRD interface");
+ return (EINVAL);
+ }
+ }
+
+ int plen = attrs->ifa_prefixlen;
+ struct sockaddr_in mask = {
+ .sin_len = sizeof(struct sockaddr_in),
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0),
+ };
+ struct in_aliasreq req = {
+ .ifra_addr = *((struct sockaddr_in *)attrs->ifa_addr),
+ .ifra_mask = mask,
+ .ifra_vhid = attrs->ifaf_vhid,
+ };
+ if (attrs->ifa_dst != NULL)
+ req.ifra_dstaddr = *((struct sockaddr_in *)attrs->ifa_dst);
+
+ return (in_control(NULL, SIOCAIFADDR, &req, ifp, curthread));
+}
+
+static int
+handle_deladdr_inet(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
+ struct ifnet *ifp, struct nlpcb *nlp, struct nl_pstate *npt)
+{
+ if (attrs->ifa_addr == NULL) {
+ nlmsg_report_err_msg(npt, "empty IFA_ADDRESS/IFA_LOCAL");
+ return (EINVAL);
+ }
+
+ struct in_aliasreq req = {
+ .ifra_addr = *((struct sockaddr_in *)attrs->ifa_addr),
+ };
+
+ return (in_control(NULL, SIOCDIFADDR, &req, ifp, curthread));
+}
+#endif
+
+#ifdef INET6
+static int
+handle_newaddr_inet6(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
+ struct ifnet *ifp, struct nlpcb *nlp, struct nl_pstate *npt)
+{
+ if (attrs->ifa_prefixlen > 128) {
+ nlmsg_report_err_msg(npt, "invalid ifa_prefixlen");
+ return (EINVAL);
+ }
+
+ if (attrs->ifa_addr == NULL) {
+ nlmsg_report_err_msg(npt, "Empty IFA_LOCAL/IFA_ADDRESS");
+ return (EINVAL);
+ }
+
+ /* TODO: Clarify addition of prefixes on p2p interfaces w/o ifa_dst */
+
+ uint32_t flags = nl_flags_to_in6(attrs->ifa_flags) | attrs->ifaf_flags;
+
+ uint32_t pltime = 0, vltime = 0;
+ if (attrs->ifa_cacheinfo != 0) {
+ pltime = attrs->ifa_cacheinfo->ifa_prefered;
+ vltime = attrs->ifa_cacheinfo->ifa_valid;
+ }
+
+ struct sockaddr_in6 mask = {
+ .sin6_len = sizeof(struct sockaddr_in6),
+ .sin6_family = AF_INET6,
+ };
+ ip6_writemask(&mask.sin6_addr, attrs->ifa_prefixlen);
+
+ struct in6_aliasreq req = {
+ .ifra_addr = *((struct sockaddr_in6 *)attrs->ifa_addr),
+ .ifra_prefixmask = mask,
+ .ifra_flags = flags,
+ .ifra_lifetime = { .ia6t_vltime = vltime, .ia6t_pltime = pltime },
+ .ifra_vhid = attrs->ifaf_vhid,
+ };
+ if (attrs->ifa_dst != NULL)
+ req.ifra_dstaddr = *((struct sockaddr_in6 *)attrs->ifa_dst);
+
+ return (in6_control(NULL, SIOCAIFADDR_IN6, &req, ifp, curthread));
+}
+
+static int
+handle_deladdr_inet6(struct nlmsghdr *hdr, struct nl_parsed_ifa *attrs,
+ struct ifnet *ifp, struct nlpcb *nlp, struct nl_pstate *npt)
+{
+ if (attrs->ifa_addr == NULL) {
+ nlmsg_report_err_msg(npt, "Empty IFA_LOCAL/IFA_ADDRESS");
+ return (EINVAL);
+ }
+
+ struct in6_aliasreq req = {
+ .ifra_addr = *((struct sockaddr_in6 *)attrs->ifa_addr),
+ };
+
+ return (in6_control(NULL, SIOCDIFADDR_IN6, &req, ifp, curthread));
+}
+#endif
+
+
+static int
+rtnl_handle_addr(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
+{
+ struct epoch_tracker et;
+ int error;
+
+ struct nl_parsed_ifa attrs = {};
+ error = nl_parse_nlmsg(hdr, &ifa_parser, npt, &attrs);
+ if (error != 0)
+ return (error);
+
+ NET_EPOCH_ENTER(et);
+ struct ifnet *ifp = ifnet_byindex_ref(attrs.ifa_index);
+ NET_EPOCH_EXIT(et);
+
+ if (ifp == NULL) {
+ nlmsg_report_err_msg(npt, "Unable to find interface with index %u",
+ attrs.ifa_index);
+ return (ENOENT);
+ }
+
+ bool new = hdr->nlmsg_type == NL_RTM_NEWADDR;
+
+ /*
+ * TODO: Properly handle NLM_F_CREATE / NLM_F_EXCL.
+ * The current ioctl-based KPI always does an implicit create-or-replace.
+ * It is not possible to specify fine-grained options.
+ */
+
+ switch (attrs.ifa_family) {
+#ifdef INET
+ case AF_INET:
+ if (new)
+ error = handle_newaddr_inet(hdr, &attrs, ifp, nlp, npt);
+ else
+ error = handle_deladdr_inet(hdr, &attrs, ifp, nlp, npt);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (new)
+ error = handle_newaddr_inet6(hdr, &attrs, ifp, nlp, npt);
+ else
+ error = handle_deladdr_inet6(hdr, &attrs, ifp, nlp, npt);
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ }
+
+ if_rele(ifp);
+
+ return (error);
+}
+
+
static void
rtnl_handle_ifaddr(void *arg __unused, struct ifaddr *ifa, int cmd)
{
@@ -1084,16 +1386,22 @@
{
.cmd = NL_RTM_NEWADDR,
.name = "RTM_NEWADDR",
- .cb = &rtnl_handle_getaddr,
+ .cb = &rtnl_handle_addr,
+ .priv = PRIV_NET_ADDIFADDR,
+ .flags = RTNL_F_NOEPOCH,
},
{
.cmd = NL_RTM_DELADDR,
.name = "RTM_DELADDR",
- .cb = &rtnl_handle_getaddr,
+ .cb = &rtnl_handle_addr,
+ .priv = PRIV_NET_DELIFADDR,
+ .flags = RTNL_F_NOEPOCH,
},
};
-static const struct nlhdr_parser *all_parsers[] = { &ifmsg_parser, &ifaddrmsg_parser };
+static const struct nlhdr_parser *all_parsers[] = {
+ &ifmsg_parser, &ifa_parser, &ifa_fbsd_parser,
+};
void
rtnl_iface_add_cloner(struct nl_cloner *cloner)
diff --git a/sys/netlink/route/ifaddrs.h b/sys/netlink/route/ifaddrs.h
--- a/sys/netlink/route/ifaddrs.h
+++ b/sys/netlink/route/ifaddrs.h
@@ -55,9 +55,9 @@
IFA_LABEL = 3, /* string, interface name */
IFA_BROADCAST = 4, /* binary, broadcast ifa */
IFA_ANYCAST = 5, /* not supported */
- IFA_CACHEINFO = 6, /* not supported */
+ IFA_CACHEINFO = 6, /* binary, struct ifa_cacheinfo */
IFA_MULTICAST = 7, /* not supported */
- IFA_FLAGS = 8, /* not supported */
+ IFA_FLAGS = 8, /* u32, IFA_F flags */
IFA_RT_PRIORITY = 9, /* not supported */
IFA_TARGET_NETNSID = 10, /* not supported */
IFA_FREEBSD = 11, /* nested, FreeBSD-specific */
diff --git a/tests/atf_python/sys/netlink/netlink_route.py b/tests/atf_python/sys/netlink/netlink_route.py
--- a/tests/atf_python/sys/netlink/netlink_route.py
+++ b/tests/atf_python/sys/netlink/netlink_route.py
@@ -291,61 +291,76 @@
class IflattrType(Enum):
IFLA_UNSPEC = 0
- IFLA_ADDRESS = auto()
- IFLA_BROADCAST = auto()
- IFLA_IFNAME = auto()
- IFLA_MTU = auto()
- IFLA_LINK = auto()
- IFLA_QDISC = auto()
- IFLA_STATS = auto()
- IFLA_COST = auto()
- IFLA_PRIORITY = auto()
- IFLA_MASTER = auto()
- IFLA_WIRELESS = auto()
- IFLA_PROTINFO = auto()
- IFLA_TXQLEN = auto()
- IFLA_MAP = auto()
- IFLA_WEIGHT = auto()
- IFLA_OPERSTATE = auto()
- IFLA_LINKMODE = auto()
- IFLA_LINKINFO = auto()
- IFLA_NET_NS_PID = auto()
- IFLA_IFALIAS = auto()
- IFLA_NUM_VF = auto()
- IFLA_VFINFO_LIST = auto()
- IFLA_STATS64 = auto()
- IFLA_VF_PORTS = auto()
- IFLA_PORT_SELF = auto()
- IFLA_AF_SPEC = auto()
- IFLA_GROUP = auto()
- IFLA_NET_NS_FD = auto()
- IFLA_EXT_MASK = auto()
- IFLA_PROMISCUITY = auto()
- IFLA_NUM_TX_QUEUES = auto()
- IFLA_NUM_RX_QUEUES = auto()
- IFLA_CARRIER = auto()
- IFLA_PHYS_PORT_ID = auto()
- IFLA_CARRIER_CHANGES = auto()
- IFLA_PHYS_SWITCH_ID = auto()
- IFLA_LINK_NETNSID = auto()
- IFLA_PHYS_PORT_NAME = auto()
- IFLA_PROTO_DOWN = auto()
- IFLA_GSO_MAX_SEGS = auto()
- IFLA_GSO_MAX_SIZE = auto()
- IFLA_PAD = auto()
- IFLA_XDP = auto()
- IFLA_EVENT = auto()
- IFLA_NEW_NETNSID = auto()
- IFLA_IF_NETNSID = auto()
- IFLA_CARRIER_UP_COUNT = auto()
- IFLA_CARRIER_DOWN_COUNT = auto()
- IFLA_NEW_IFINDEX = auto()
- IFLA_MIN_MTU = auto()
- IFLA_MAX_MTU = auto()
- IFLA_PROP_LIST = auto()
- IFLA_ALT_IFNAME = auto()
- IFLA_PERM_ADDRESS = auto()
- IFLA_PROTO_DOWN_REASON = auto()
+ IFLA_ADDRESS = 1
+ IFLA_BROADCAST = 2
+ IFLA_IFNAME = 3
+ IFLA_MTU = 4
+ IFLA_LINK = 5
+ IFLA_QDISC = 6
+ IFLA_STATS = 7
+ IFLA_COST = 8
+ IFLA_PRIORITY = 9
+ IFLA_MASTER = 10
+ IFLA_WIRELESS = 11
+ IFLA_PROTINFO = 12
+ IFLA_TXQLEN = 13
+ IFLA_MAP = 14
+ IFLA_WEIGHT = 15
+ IFLA_OPERSTATE = 16
+ IFLA_LINKMODE = 17
+ IFLA_LINKINFO = 18
+ IFLA_NET_NS_PID = 19
+ IFLA_IFALIAS = 20
+ IFLA_NUM_VF = 21
+ IFLA_VFINFO_LIST = 22
+ IFLA_STATS64 = 23
+ IFLA_VF_PORTS = 24
+ IFLA_PORT_SELF = 25
+ IFLA_AF_SPEC = 26
+ IFLA_GROUP = 27
+ IFLA_NET_NS_FD = 28
+ IFLA_EXT_MASK = 29
+ IFLA_PROMISCUITY = 30
+ IFLA_NUM_TX_QUEUES = 31
+ IFLA_NUM_RX_QUEUES = 32
+ IFLA_CARRIER = 33
+ IFLA_PHYS_PORT_ID = 34
+ IFLA_CARRIER_CHANGES = 35
+ IFLA_PHYS_SWITCH_ID = 36
+ IFLA_LINK_NETNSID = 37
+ IFLA_PHYS_PORT_NAME = 38
+ IFLA_PROTO_DOWN = 39
+ IFLA_GSO_MAX_SEGS = 40
+ IFLA_GSO_MAX_SIZE = 41
+ IFLA_PAD = 42
+ IFLA_XDP = 43
+ IFLA_EVENT = 44
+ IFLA_NEW_NETNSID = 45
+ IFLA_IF_NETNSID = 46
+ IFLA_CARRIER_UP_COUNT = 47
+ IFLA_CARRIER_DOWN_COUNT = 48
+ IFLA_NEW_IFINDEX = 49
+ IFLA_MIN_MTU = 50
+ IFLA_MAX_MTU = 51
+ IFLA_PROP_LIST = 52
+ IFLA_ALT_IFNAME = 53
+ IFLA_PERM_ADDRESS = 54
+ IFLA_PROTO_DOWN_REASON = 55
+ IFLA_PARENT_DEV_NAME = 56
+ IFLA_PARENT_DEV_BUS_NAME = 57
+ IFLA_GRO_MAX_SIZE = 58
+ IFLA_TSO_MAX_SEGS = 59
+ IFLA_ALLMULTI = 60
+ IFLA_DEVLINK_PORT = 61
+ IFLA_GSO_IPV4_MAX_SIZE = 62
+ IFLA_GRO_IPV4_MAX_SIZE = 63
+ IFLA_FREEBSD = 64
+
+
+class IflafAttrType(Enum):
+ IFLAF_UNSPEC = 0
+ IFLAF_ORIG_IFNAME = 1
+ IFLAF_ORIG_HWADDR = 2
class IflinkInfo(Enum):
@@ -378,16 +393,59 @@
class IfaAttrType(Enum):
IFA_UNSPEC = 0
- IFA_ADDRESS = auto()
- IFA_LOCAL = auto()
- IFA_LABEL = auto()
- IFA_BROADCAST = auto()
- IFA_ANYCAST = auto()
- IFA_CACHEINFO = auto()
- IFA_MULTICAST = auto()
- IFA_FLAGS = auto()
- IFA_RT_PRIORITY = auto()
- IFA_TARGET_NETNSID = auto()
+ IFA_ADDRESS = 1
+ IFA_LOCAL = 2
+ IFA_LABEL = 3
+ IFA_BROADCAST = 4
+ IFA_ANYCAST = 5
+ IFA_CACHEINFO = 6
+ IFA_MULTICAST = 7
+ IFA_FLAGS = 8
+ IFA_RT_PRIORITY = 9
+ IFA_TARGET_NETNSID = 10
+ IFA_FREEBSD = 11
+
+
+class IfafAttrType(Enum):
+ IFAF_UNSPEC = 0
+ IFAF_VHID = 1
+ IFAF_FLAGS = 2
+
+
+class IfaCacheInfo(Structure):
+ _fields_ = [
+ ("ifa_prefered", c_uint), # seconds till the end of the prefix considered preferred
+ ("ifa_valid", c_uint), # seconds till the end of the prefix considered valid
+ ("cstamp", c_uint), # creation time in 1ms intervals from the boot time
+ ("tstamp", c_uint), # update time in 1ms intervals from the boot time
+ ]
+
+
+class IfaFlags(Enum):
+ IFA_F_TEMPORARY = 0x01
+ IFA_F_NODAD = 0x02
+ IFA_F_OPTIMISTIC = 0x04
+ IFA_F_DADFAILED = 0x08
+ IFA_F_HOMEADDRESS = 0x10
+ IFA_F_DEPRECATED = 0x20
+ IFA_F_TENTATIVE = 0x40
+ IFA_F_PERMANENT = 0x80
+ IFA_F_MANAGETEMPADDR = 0x100
+ IFA_F_NOPREFIXROUTE = 0x200
+ IFA_F_MCAUTOJOIN = 0x400
+ IFA_F_STABLE_PRIVACY = 0x800
+
+
+class IfafFlags6(Enum):
+ IN6_IFF_ANYCAST = 0x01
+ IN6_IFF_TENTATIVE = 0x02
+ IN6_IFF_DUPLICATED = 0x04
+ IN6_IFF_DETACHED = 0x08
+ IN6_IFF_DEPRECATED = 0x10
+ IN6_IFF_NODAD = 0x20
+ IN6_IFF_AUTOCONF = 0x40
+ IN6_IFF_TEMPORARY = 0x80
+ IN6_IFF_PREFER_SOURCE = 0x100
class NdMsg(Structure):
@@ -464,6 +522,25 @@
return " stats={...}"
+class NlAttrCacheInfo(NlAttr):
+ def __init__(self, nla_type, data):
+ super().__init__(nla_type, data)
+ self.ci = IfaCacheInfo.from_buffer_copy(data)
+
+ @staticmethod
+ def _validate(data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ data_len = nla_len - 4
+ if data_len != sizeof(IfaCacheInfo):
+ raise ValueError(
+ "Error validating attr {}: wrong size".format(nla_type)
+ ) # noqa: E501
+
+ def _print_attr_value(self):
+ return " ifa_prefered={} ifa_valid={} cstamp={} tstamp={}".format(
+ self.ci.ifa_prefered, self.ci.ifa_valid, self.ci.cstamp, self.ci.tstamp)
+
+
class NlAttrVia(NlAttr):
def __init__(self, nla_type, family, addr: str):
super().__init__(nla_type, b"")
@@ -565,6 +642,13 @@
AttrDescr(IflinkInfo.IFLA_INFO_DATA, NlAttr),
],
),
+ AttrDescr(
+ IflattrType.IFLA_FREEBSD,
+ NlAttrNested,
+ [
+ AttrDescr(IflafAttrType.IFLAF_ORIG_HWADDR, NlAttrMac),
+ ],
+ ),
]
)
@@ -576,6 +660,15 @@
AttrDescr(IfaAttrType.IFA_BROADCAST, NlAttrIp),
AttrDescr(IfaAttrType.IFA_ANYCAST, NlAttrIp),
AttrDescr(IfaAttrType.IFA_FLAGS, NlAttrU32),
+ AttrDescr(IfaAttrType.IFA_CACHEINFO, NlAttrCacheInfo),
+ AttrDescr(
+ IfaAttrType.IFA_FREEBSD,
+ NlAttrNested,
+ [
+ AttrDescr(IfafAttrType.IFAF_VHID, NlAttrU32),
+ AttrDescr(IfafAttrType.IFAF_FLAGS, NlAttrU32),
+ ],
+ ),
]
)
diff --git a/tests/sys/netlink/test_rtnl_ifaddr.py b/tests/sys/netlink/test_rtnl_ifaddr.py
--- a/tests/sys/netlink/test_rtnl_ifaddr.py
+++ b/tests/sys/netlink/test_rtnl_ifaddr.py
@@ -2,18 +2,34 @@
import socket
import struct
+import pytest
from atf_python.sys.net.vnet import SingleVnetTestTemplate
+from atf_python.sys.netlink.attrs import NlAttr
+from atf_python.sys.netlink.attrs import NlAttrIp
+from atf_python.sys.netlink.attrs import NlAttrNested
+from atf_python.sys.netlink.attrs import NlAttrU32
from atf_python.sys.netlink.base_headers import NlmBaseFlags
+from atf_python.sys.netlink.base_headers import NlmNewFlags
from atf_python.sys.netlink.base_headers import Nlmsghdr
+from atf_python.sys.netlink.message import NlMsgType
from atf_python.sys.netlink.netlink import NetlinkTestTemplate
+from atf_python.sys.netlink.netlink import Nlsock
+from atf_python.sys.netlink.netlink_generic import CarpAttrType
+from atf_python.sys.netlink.netlink_generic import CarpGenMessage
+from atf_python.sys.netlink.netlink_generic import CarpMsgType
from atf_python.sys.netlink.netlink_route import IfaAttrType
+from atf_python.sys.netlink.netlink_route import IfaCacheInfo
+from atf_python.sys.netlink.netlink_route import IfafAttrType
+from atf_python.sys.netlink.netlink_route import IfafFlags6
+from atf_python.sys.netlink.netlink_route import IfaFlags
from atf_python.sys.netlink.netlink_route import NetlinkIfaMessage
from atf_python.sys.netlink.netlink_route import NlRtMsgType
from atf_python.sys.netlink.netlink_route import RtScope
+from atf_python.sys.netlink.utils import enum_or_int
from atf_python.sys.netlink.utils import NlConst
-class TestRtNlIfaddr(NetlinkTestTemplate, SingleVnetTestTemplate):
+class TestRtNlIfaddrList(NetlinkTestTemplate, SingleVnetTestTemplate):
def setup_method(self, method):
method_name = method.__name__
if "4" in method_name:
@@ -26,9 +42,7 @@
def test_46_nofilter(self):
"""Tests that listing outputs both IPv4/IPv6 and interfaces"""
msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
- msg.nl_hdr.nlmsg_flags = (
- NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
- )
+ msg.set_request()
self.write_message(msg)
ret = []
@@ -49,9 +63,7 @@
epair_ifname = self.vnet.iface_alias_map["if1"].name
msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
- msg.nl_hdr.nlmsg_flags = (
- NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
- )
+ msg.set_request()
msg.base_hdr.ifa_index = socket.if_nametoindex(epair_ifname)
self.write_message(msg)
@@ -70,11 +82,11 @@
"""Tests that family filtering works with the stripped header"""
hdr = Nlmsghdr(
- nlmsg_len=17,
- nlmsg_type=NlRtMsgType.RTM_GETADDR.value,
- nlmsg_flags=NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value,
- nlmsg_seq=self.helper.get_seq()
- )
+ nlmsg_len=17,
+ nlmsg_type=NlRtMsgType.RTM_GETADDR.value,
+ nlmsg_flags=NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value,
+ nlmsg_seq=self.helper.get_seq(),
+ )
data = bytes(hdr) + struct.pack("@B", socket.AF_INET)
self.nlsock.write_data(data)
@@ -90,9 +102,7 @@
epair_ifname = self.vnet.iface_alias_map["if1"].name
msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
- msg.nl_hdr.nlmsg_flags = (
- NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
- )
+ msg.set_request()
msg.base_hdr.ifa_family = family
msg.base_hdr.ifa_index = socket.if_nametoindex(epair_ifname)
self.write_message(msg)
@@ -159,3 +169,549 @@
epair_ifname = self.vnet.iface_alias_map["if1"].name
assert msg.get_nla(IfaAttrType.IFA_LABEL).text == epair_ifname
+
+
+class RtnlIfaOps(NetlinkTestTemplate, SingleVnetTestTemplate):
+ def setup_method(self, method):
+ super().setup_method(method)
+ self.setup_netlink(NlConst.NETLINK_ROUTE)
+
+ def send_check_success(self, msg):
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+ @staticmethod
+ def get_family_from_ip(ip):
+ if ip.version == 4:
+ return socket.AF_INET
+ return socket.AF_INET6
+
+ def create_msg(self, ifa):
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_NEWADDR.value)
+ msg.set_request()
+ msg.nl_hdr.nlmsg_flags |= (
+ NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ )
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+ return msg
+
+ def get_ifa_list(self, ifindex=0, family=0):
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_GETADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = family
+ msg.base_hdr.ifa_index = ifindex
+ self.write_message(msg)
+ return self.read_msg_list(msg.nl_hdr.nlmsg_seq, NlRtMsgType.RTM_NEWADDR)
+
+ def find_msg_by_ifa(self, msg_list, ip):
+ for msg in msg_list:
+ if msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ip):
+ return msg
+ return None
+
+ def setup_dummy_carp(self, ifindex: int, vhid: int):
+ self.require_module("carp")
+
+ nlsock = Nlsock(NlConst.NETLINK_GENERIC, self.helper)
+ family_id = nlsock.get_genl_family_id("carp")
+
+ msg = CarpGenMessage(self.helper, family_id, CarpMsgType.CARP_NL_CMD_SET)
+ msg.set_request()
+ msg.add_nla(NlAttrU32(CarpAttrType.CARP_NL_VHID, vhid))
+ msg.add_nla(NlAttrU32(CarpAttrType.CARP_NL_IFINDEX, ifindex))
+ rx_msg = nlsock.get_reply(msg)
+
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code == 0
+
+
+class TestRtNlIfaddrOpsBroadcast(RtnlIfaOps):
+ def test_add_4(self):
+ """Tests IPv4 address addition to the standard broadcast interface"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
+
+ def test_add_6(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+
+ def test_add_4_carp(self):
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+ vhid = 77
+
+ self.setup_dummy_carp(iface.ifindex, vhid)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_VHID, vhid)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_BROADCAST).addr == str(ifa_brd)
+ ifa_bsd = rx_msg.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_VHID).u32 == vhid
+
+ def test_add_6_carp(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+ vhid = 77
+
+ self.setup_dummy_carp(iface.ifindex, vhid)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_VHID, vhid)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ ifa_bsd = rx_msg_gu.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_VHID).u32 == vhid
+
+ def test_add_6_lifetime(self):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+ pref_time = 43200
+ valid_time = 86400
+
+ ci = IfaCacheInfo(ifa_prefered=pref_time, ifa_valid=valid_time)
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttr(IfaAttrType.IFA_CACHEINFO, bytes(ci)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ ci = rx_msg.get_nla(IfaAttrType.IFA_CACHEINFO).ci
+ assert pref_time - 5 <= ci.ifa_prefered <= pref_time
+ assert valid_time - 5 <= ci.ifa_valid <= valid_time
+ assert ci.cstamp > 0
+ assert ci.tstamp > 0
+ assert ci.tstamp >= ci.cstamp
+
+ @pytest.mark.parametrize(
+ "flags_str",
+ [
+ "autoconf",
+ "deprecated",
+ "autoconf,deprecated",
+ "prefer_source",
+ ],
+ )
+ def test_add_6_flags(self, flags_str):
+ ifa = ipaddress.ip_interface("2001:db8::1/64")
+ iface = self.vnet.iface_alias_map["if1"]
+
+ flags_map = {
+ "autoconf": {"nl": 0, "f": IfafFlags6.IN6_IFF_AUTOCONF},
+ "deprecated": {
+ "nl": IfaFlags.IFA_F_DEPRECATED,
+ "f": IfafFlags6.IN6_IFF_DEPRECATED,
+ },
+ "prefer_source": {"nl": 0, "f": IfafFlags6.IN6_IFF_PREFER_SOURCE},
+ }
+ nl_flags = 0
+ f_flags = 0
+
+ for flag_str in flags_str.split(","):
+ d = flags_map.get(flag_str, {})
+ nl_flags |= enum_or_int(d.get("nl", 0))
+ f_flags |= enum_or_int(d.get("f", 0))
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrU32(IfaAttrType.IFA_FLAGS, nl_flags))
+ attrs_bsd = [NlAttrU32(IfafAttrType.IFAF_FLAGS, f_flags)]
+ msg.add_nla(NlAttrNested(IfaAttrType.IFA_FREEBSD, attrs_bsd))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_FLAGS).u32 == nl_flags
+ ifa_bsd = rx_msg.get_nla(IfaAttrType.IFA_FREEBSD)
+ assert ifa_bsd.get_nla(IfafAttrType.IFAF_FLAGS).u32 == f_flags
+
+ def test_add_4_empty_message(self):
+ """Tests correct failure w/ empty message"""
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_NEWADDR.value)
+ msg.set_request()
+ msg.nl_hdr.nlmsg_flags |= (
+ NlmNewFlags.NLM_F_EXCL.value | NlmNewFlags.NLM_F_CREATE.value
+ )
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ def test_add_4_empty_ifindex(self):
+ """Tests correct failure w/ empty ifindex"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+
+ msg = self.create_msg(ifa)
+ msg.base_hdr.ifa_index = 0
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ def test_add_4_empty_addr(self):
+ """Tests correct failure w/ empty address"""
+ ifa = ipaddress.ip_interface("192.0.2.1/24")
+ ifa_brd = ifa.network.broadcast_address
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ rx_msg = self.get_reply(msg)
+ assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
+ assert rx_msg.error_code != 0
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/32", id="ipv4_host"),
+ pytest.param("192.0.2.1/24", id="ipv4_prefix"),
+ pytest.param("2001:db8::1/64", id="ipv6_gu_prefix"),
+ pytest.param("2001:db8::1/128", id="ipv6_gu_host"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv",
+ [
+ pytest.param("local", id="ifa_local"),
+ pytest.param("address", id="ifa_address"),
+ ],
+ )
+ def test_del(self, tlv, ifa_str):
+ """Tests address deletion from the standard broadcast interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ ifa_brd = ifa.network.broadcast_address
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_BROADCAST, str(ifa_brd)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if tlv == "local":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if tlv == "address":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
+
+
+class TestRtNlIfaddrOpsP2p(RtnlIfaOps):
+ IFTYPE = "gif"
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(["192.0.2.1/24", "192.0.2.2"], id="dst_inside_24"),
+ pytest.param(["192.0.2.1/30", "192.0.2.2"], id="dst_inside_30"),
+ pytest.param(["192.0.2.1/31", "192.0.2.2"], id="dst_inside_31"),
+ pytest.param(["192.0.2.1/32", "192.0.2.2"], id="dst_outside_32"),
+ pytest.param(["192.0.2.1/30", "192.0.2.100"], id="dst_outside_30"),
+ ],
+ )
+ def test_add_4(self, ifa_pair):
+ """Tests IPv4 address addition to the p2p interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(peer_ip)
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(
+ ["2001:db8::1/64", "2001:db8::2"],
+ id="dst_inside_64",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ pytest.param(
+ ["2001:db8::1/127", "2001:db8::2"],
+ id="dst_inside_127",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ pytest.param(["2001:db8::1/128", "2001:db8::2"], id="dst_outside_128"),
+ pytest.param(
+ ["2001:db8::1/64", "2001:db8:2::2"],
+ id="dst_outside_64",
+ marks=pytest.mark.xfail(reason="currently fails"),
+ ),
+ ],
+ )
+ def test_add_6(self, ifa_pair):
+ """Tests IPv6 address addition to the p2p interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 2
+ rx_msg_gu = self.find_msg_by_ifa(lst, peer_ip)
+ assert rx_msg_gu is not None
+
+ assert rx_msg_gu.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg_gu.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+ assert rx_msg_gu.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(peer_ip)
+
+ @pytest.mark.parametrize(
+ "ifa_pair",
+ [
+ pytest.param(["192.0.2.1/30", "192.0.2.2"], id="ipv4_dst_inside_30"),
+ pytest.param(["192.0.2.1/32", "192.0.2.2"], id="ipv4_dst_outside_32"),
+ pytest.param(["2001:db8::1/128", "2001:db8::2"], id="ip6_dst_outside_128"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv_pair",
+ [
+ pytest.param(["a", ""], id="ifa_addr=addr"),
+ pytest.param(["", "a"], id="ifa_local=addr"),
+ pytest.param(["a", "a"], id="ifa_addr=addr,ifa_local=addr"),
+ ],
+ )
+ def test_del(self, tlv_pair, ifa_pair):
+ """Tests address deletion from the P2P interface"""
+ ifa = ipaddress.ip_interface(ifa_pair[0])
+ peer_ip = ipaddress.ip_address(ifa_pair[1])
+ iface = self.vnet.iface_alias_map["if1"]
+ ifa_addr_str, ifa_local_str = tlv_pair
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, peer_ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if "a" in ifa_addr_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+ if "p" in ifa_addr_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(peer_ip)))
+ if "a" in ifa_local_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if "p" in ifa_local_str:
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(peer_ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
+
+
+class TestRtNlAddIfaddrLo(RtnlIfaOps):
+ IFTYPE = "lo"
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/24", id="prefix"),
+ pytest.param("192.0.2.1/32", id="host"),
+ ],
+ )
+ def test_add_4(self, ifa_str):
+ """Tests IPv4 address addition to the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = lst[0]
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+ assert rx_msg.get_nla(IfaAttrType.IFA_LOCAL).addr == str(ifa.ip)
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("2001:db8::1/64", id="gu_prefix"),
+ pytest.param("2001:db8::1/128", id="gu_host"),
+ ],
+ )
+ def test_add_6(self, ifa_str):
+ """Tests IPv6 address addition to the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ assert len(lst) == 1
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ assert rx_msg.base_hdr.ifa_prefixlen == ifa.network.prefixlen
+ assert rx_msg.base_hdr.ifa_scope == RtScope.RT_SCOPE_UNIVERSE.value
+ assert rx_msg.get_nla(IfaAttrType.IFA_ADDRESS).addr == str(ifa.ip)
+
+ @pytest.mark.parametrize(
+ "ifa_str",
+ [
+ pytest.param("192.0.2.1/32", id="ipv4_host"),
+ pytest.param("192.0.2.1/24", id="ipv4_prefix"),
+ pytest.param("2001:db8::1/64", id="ipv6_gu_prefix"),
+ pytest.param("2001:db8::1/128", id="ipv6_gu_host"),
+ ],
+ )
+ @pytest.mark.parametrize(
+ "tlv",
+ [
+ pytest.param("local", id="ifa_local"),
+ pytest.param("address", id="ifa_address"),
+ ],
+ )
+ def test_del(self, tlv, ifa_str):
+ """Tests address deletion from the loopback interface"""
+ ifa = ipaddress.ip_interface(ifa_str)
+ iface = self.vnet.iface_alias_map["if1"]
+
+ msg = self.create_msg(ifa)
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is not None
+
+ msg = NetlinkIfaMessage(self.helper, NlRtMsgType.RTM_DELADDR.value)
+ msg.set_request()
+ msg.base_hdr.ifa_family = self.get_family_from_ip(ifa.ip)
+ msg.base_hdr.ifa_index = iface.ifindex
+ msg.base_hdr.ifa_prefixlen = ifa.network.prefixlen
+
+ if tlv == "local":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_LOCAL, str(ifa.ip)))
+ if tlv == "address":
+ msg.add_nla(NlAttrIp(IfaAttrType.IFA_ADDRESS, str(ifa.ip)))
+
+ self.send_check_success(msg)
+ lst = self.get_ifa_list(iface.ifindex, self.get_family_from_ip(ifa.ip))
+ rx_msg = self.find_msg_by_ifa(lst, ifa.ip)
+ assert rx_msg is None
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Jul 5, 6:17 AM (5 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34696202
Default Alt Text
D40103.id.diff (44 KB)
Attached To
Mode
D40103: netlink: add support for adding/deleting interface addresses
Attached
Detach File
Event Timeline
Log In to Comment