summaryrefslogtreecommitdiff
path: root/src/platform/nm-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/nm-platform.c')
-rw-r--r--src/platform/nm-platform.c451
1 files changed, 444 insertions, 7 deletions
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index a72159fb86..320234259c 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -28,6 +28,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
+#include <linux/fib_rules.h>
#include <linux/ip.h>
#include <linux/if.h>
#include <linux/if_tun.h>
@@ -6062,6 +6063,258 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
return buf;
}
+static void
+_routing_rule_addr_to_string (char **buf,
+ gsize *len,
+ int addr_family,
+ const NMIPAddr *addr,
+ guint8 plen,
+ gboolean is_src)
+{
+ char s_addr[NM_UTILS_INET_ADDRSTRLEN];
+ gboolean is_zero;
+ gsize addr_size;
+
+ nm_assert_addr_family (addr_family);
+ nm_assert (addr);
+
+ addr_size = nm_utils_addr_family_to_size (addr_family);
+
+ is_zero = nm_utils_memeqzero (addr, addr_size);
+
+ if ( plen == 0
+ && is_zero) {
+ if (is_src)
+ nm_utils_strbuf_append_str (buf, len, " from all");
+ else
+ nm_utils_strbuf_append_str (buf, len, "");
+ return;
+ }
+
+ nm_utils_strbuf_append_str (buf, len, is_src ? " from" : " to");
+
+ if (!is_zero)
+ nm_utils_strbuf_append_str (buf, len, nm_utils_inet_ntop (addr_family, addr, s_addr));
+ else
+ nm_utils_strbuf_append_str (buf, len, "0");
+
+ if (plen != (addr_size * 8))
+ nm_utils_strbuf_append (buf, len, "/%u", plen);
+}
+
+static void
+_routing_rule_port_range_to_string (char **buf,
+ gsize *len,
+ const NMFibRulePortRange *port_range,
+ const char *name)
+{
+ if ( port_range->start == 0
+ && port_range->end == 0)
+ nm_utils_strbuf_append_str (buf, len, "");
+ else {
+ nm_utils_strbuf_append (buf, len, " %s %u", name, port_range->start);
+ if (port_range->start != port_range->end)
+ nm_utils_strbuf_append (buf, len, "-%u", port_range->end);
+ }
+}
+
+const char *
+nm_platform_routing_rule_to_string (const NMPlatformRoutingRule *routing_rule, char *buf, gsize len)
+{
+ const char *buf0;
+ guint32 rr_table;
+ guint32 rr_flags;
+
+ if (!nm_utils_to_string_buffer_init_null (routing_rule, &buf, &len))
+ return buf;
+
+ if (!NM_IN_SET (routing_rule->addr_family, AF_INET, AF_INET6)) {
+ /* invalid addr-family. The other fields are undefined. */
+ if (routing_rule->addr_family == AF_UNSPEC)
+ g_snprintf (buf, len, "[routing-rule]");
+ else
+ g_snprintf (buf, len, "[routing-rule family:%u]", routing_rule->addr_family);
+ return buf;
+ }
+
+ buf0 = buf;
+
+ rr_flags = routing_rule->flags;
+
+ rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_INVERT);
+ nm_utils_strbuf_append (&buf, &len,
+ "[%c] " /* addr-family */
+ "%u: " /* priority */
+ "%s", /* not/FIB_RULE_INVERT */
+ nm_utils_addr_family_to_char (routing_rule->addr_family),
+ routing_rule->priority,
+ ( NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_INVERT)
+ ? "not"
+ : ""));
+
+ _routing_rule_addr_to_string (&buf, &len,
+ routing_rule->addr_family,
+ &routing_rule->src,
+ routing_rule->src_len,
+ TRUE);
+
+ _routing_rule_addr_to_string (&buf, &len,
+ routing_rule->addr_family,
+ &routing_rule->dst,
+ routing_rule->dst_len,
+ FALSE);
+
+ if (routing_rule->tos)
+ nm_utils_strbuf_append (&buf, &len, " tos 0x%02x", routing_rule->tos);
+
+ if ( routing_rule->fwmark != 0
+ || routing_rule->fwmask != 0) {
+ nm_utils_strbuf_append (&buf, &len, " fwmark %#x", (unsigned) routing_rule->fwmark);
+ if (routing_rule->fwmark != 0xFFFFFFFFu)
+ nm_utils_strbuf_append (&buf, &len, "/%#x", (unsigned) routing_rule->fwmask);
+ }
+
+ if (routing_rule->iifname[0]) {
+ nm_utils_strbuf_append (&buf, &len, " iif %s", routing_rule->iifname);
+ rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_IIF_DETACHED);
+ if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_IIF_DETACHED))
+ nm_utils_strbuf_append_str (&buf, &len, " [detached]");
+ }
+
+ if (routing_rule->oifname[0]) {
+ nm_utils_strbuf_append (&buf, &len, " oif %s", routing_rule->oifname);
+ rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_OIF_DETACHED);
+ if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_OIF_DETACHED))
+ nm_utils_strbuf_append_str (&buf, &len, " [detached]");
+ }
+
+ if (routing_rule->l3mdev != 0) {
+ if (routing_rule->l3mdev == 1)
+ nm_utils_strbuf_append_str (&buf, &len, " lookup [l3mdev-table]");
+ else {
+ nm_utils_strbuf_append (&buf, &len, " lookup [l3mdev-table/%u]", (unsigned) routing_rule->l3mdev);
+ }
+ }
+
+ if ( routing_rule->uid_range_has
+ || routing_rule->uid_range.start
+ || routing_rule->uid_range.end) {
+ nm_utils_strbuf_append (&buf, &len,
+ " uid-range %u-%u%s",
+ routing_rule->uid_range.start,
+ routing_rule->uid_range.end,
+ !routing_rule->uid_range_has ? "" : "?");
+ }
+
+ if (routing_rule->ip_proto != 0) {
+ /* we don't call getprotobynumber(), just print the numeric value.
+ * This differs from what ip-rule prints. */
+ nm_utils_strbuf_append (&buf, &len,
+ " ipproto %u",
+ routing_rule->ip_proto);
+ }
+
+ _routing_rule_port_range_to_string (&buf, &len,
+ &routing_rule->sport_range,
+ "sport");
+
+ _routing_rule_port_range_to_string (&buf, &len,
+ &routing_rule->dport_range,
+ "dport");
+
+ if (routing_rule->tun_id != 0) {
+ nm_utils_strbuf_append (&buf, &len,
+ " tun_id %"G_GUINT64_FORMAT,
+ routing_rule->tun_id);
+ }
+
+ rr_table = nm_platform_route_table_uncoerce (routing_rule->table_coerced, FALSE);
+ if (rr_table != 0) {
+ /* ip-rule prints this as "lookup". I find that confusing, deviate from
+ * that. */
+ nm_utils_strbuf_append (&buf, &len,
+ " table %u",
+ rr_table);
+ }
+
+ if (routing_rule->suppress_prefixlen_inverse != 0) {
+ nm_utils_strbuf_append (&buf, &len,
+ " suppress_prefixlen %d",
+ (int) (~routing_rule->suppress_prefixlen_inverse));
+ }
+
+ if (routing_rule->suppress_ifgroup_inverse != 0) {
+ nm_utils_strbuf_append (&buf, &len,
+ " suppress_ifgroup %d",
+ (int) (~routing_rule->suppress_ifgroup_inverse));
+ }
+
+ if (routing_rule->flow) {
+ /* FRA_FLOW is only for IPv4, but we want to print the value for all address-families,
+ * to see when it is set. In practice, this should not be set except for IPv4.
+ *
+ * We don't follow the style how ip-rule prints flow/realms. It's confusing. Just
+ * print the value hex. */
+ nm_utils_strbuf_append (&buf, &len,
+ " realms 0x%08x",
+ routing_rule->flow);
+ }
+
+ if (routing_rule->action == RTN_NAT) {
+ G_STATIC_ASSERT_EXPR (RTN_NAT == 10);
+
+ /* NAT is deprecated for many years. We don't support RTA_GATEWAY/FRA_UNUSED2
+ * for the gateway, and so do recent kernels ignore that parameter. */
+ nm_utils_strbuf_append_str (&buf, &len, " masquerade");
+ } else if (routing_rule->action == FRA_GOTO) {
+ if (routing_rule->goto_target != 0)
+ nm_utils_strbuf_append (&buf, &len, " goto %u", routing_rule->goto_target);
+ else
+ nm_utils_strbuf_append_str (&buf, &len, " goto none");
+ rr_flags = NM_FLAGS_UNSET (rr_flags, FIB_RULE_UNRESOLVED);
+ if (NM_FLAGS_HAS (routing_rule->flags, FIB_RULE_UNRESOLVED))
+ nm_utils_strbuf_append_str (&buf, &len, " unresolved");
+ } else if (routing_rule->action != FR_ACT_TO_TBL) {
+ const char *ss;
+ char ss_buf[60];
+
+ switch (routing_rule->action) {
+ case FR_ACT_UNSPEC /*RTN_UNSPEC*/ : ss = "none"; break;
+ case FR_ACT_TO_TBL /*RTN_UNICAST*/ : ss = "unicast"; break;
+ case FR_ACT_GOTO /*RTN_LOCAL*/ : ss = "local"; break;
+ case FR_ACT_NOP /*RTN_BROADCAST*/ : ss = "nop"; break;
+ case FR_ACT_RES3 /*RTN_ANYCAST*/ : ss = "anycast"; break;
+ case FR_ACT_RES4 /*RTN_MULTICAST*/ : ss = "multicast"; break;
+ case FR_ACT_BLACKHOLE /*RTN_BLACKHOLE*/ : ss = "blackhole"; break;
+ case FR_ACT_UNREACHABLE /*RTN_UNREACHABLE*/ : ss = "unreachable"; break;
+ case FR_ACT_PROHIBIT /*RTN_PROHIBIT*/ : ss = "prohibit"; break;
+ case RTN_THROW : ss = "throw"; break;
+ case RTN_NAT : ss = "nat"; break;
+ case RTN_XRESOLVE : ss = "xresolve"; break;
+ default:
+ ss = nm_sprintf_buf (ss_buf, "action-%u", routing_rule->action);
+ break;
+ }
+ nm_utils_strbuf_append (&buf, &len, " %s", ss);
+ }
+
+ if (routing_rule->protocol != RTPROT_UNSPEC)
+ nm_utils_strbuf_append (&buf, &len, " protocol %u", routing_rule->protocol);
+
+ if ( routing_rule->goto_target != 0
+ && routing_rule->action != FRA_GOTO) {
+ /* a trailing target is set for an unexpected action. Print it. */
+ nm_utils_strbuf_append (&buf, &len, " goto-target %u", routing_rule->goto_target);
+ }
+
+ if (rr_flags != 0) {
+ /* we have some flags we didn't print about yet. */
+ nm_utils_strbuf_append (&buf, &len, " remaining-flags %x", rr_flags);
+ }
+
+ return buf0;
+}
+
const char *
nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len)
{
@@ -7020,6 +7273,182 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
return 0;
}
+void
+nm_platform_routing_rule_hash_update (const NMPlatformRoutingRule *obj,
+ NMPlatformRoutingRuleCmpType cmp_type,
+ NMHashState *h)
+{
+ gboolean cmp_full = TRUE;
+ gsize addr_size;
+ guint32 table;
+
+ // see systemd: routing_policy_rule_hash_func
+
+ if (G_UNLIKELY (!NM_IN_SET (obj->addr_family, AF_INET, AF_INET6))) {
+ /* the address family is not one of the supported ones. That means, the
+ * instance will only compare equal to itself (pointer-equality). */
+ nm_hash_update_val (h, (gconstpointer) obj);
+ return;
+ }
+
+ switch (cmp_type) {
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID:
+ /* for the moment, ID is the same as a full (sementical) comparison. We might
+ * refine that in the future. */
+
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY:
+ cmp_full = FALSE;
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL:
+
+ /* for semantic comparison, we cannot hash the table for IPv4
+ * rules. See also nm_platform_routing_rule_cmp(). */
+ if (cmp_full)
+ table = obj->table_coerced;
+ else if (obj->l3mdev)
+ table = 0;
+ else if (obj->addr_family != AF_INET)
+ table = obj->table_coerced;
+ else
+ table = 0;
+
+ nm_hash_update_vals (h,
+ obj->addr_family,
+ obj->tun_id,
+ table,
+ obj->flags,
+ obj->priority,
+ obj->fwmark,
+ obj->fwmask,
+ obj->goto_target,
+ obj->flow,
+ NM_HASH_COMBINE_BOOLS (guint8,
+ obj->uid_range_has),
+ obj->suppress_prefixlen_inverse,
+ obj->suppress_ifgroup_inverse,
+ ( cmp_full
+ ? obj->l3mdev
+ : (guint8) !!obj->l3mdev),
+ obj->action,
+ obj->tos,
+ obj->src_len,
+ obj->dst_len,
+ obj->protocol,
+ obj->ip_proto);
+ addr_size = nm_utils_addr_family_to_size (obj->addr_family);
+ nm_hash_update (h, &obj->src, addr_size);
+ nm_hash_update (h, &obj->dst, addr_size);
+ if (cmp_full || obj->uid_range_has)
+ nm_hash_update_valp (h, &obj->uid_range);
+ nm_hash_update_valp (h, &obj->sport_range);
+ nm_hash_update_valp (h, &obj->dport_range);
+ nm_hash_update_str (h, obj->iifname);
+ nm_hash_update_str (h, obj->oifname);
+ return;
+ }
+
+ nm_assert_not_reached ();
+}
+
+int
+nm_platform_routing_rule_cmp (const NMPlatformRoutingRule *a,
+ const NMPlatformRoutingRule *b,
+ NMPlatformRoutingRuleCmpType cmp_type)
+{
+ gboolean cmp_full = TRUE;
+ gsize addr_size;
+ bool valid;
+
+ NM_CMP_SELF (a, b);
+
+ valid = NM_IN_SET (a->addr_family, AF_INET, AF_INET6);
+ NM_CMP_DIRECT (valid,
+ (bool) NM_IN_SET (b->addr_family, AF_INET, AF_INET6));
+
+ if (G_UNLIKELY (!valid)) {
+ /* the address family is not one of the supported ones. That means, the
+ * instance will only compare equal to itself. */
+ NM_CMP_DIRECT ((uintptr_t) a, (uintptr_t) b);
+ nm_assert_not_reached ();
+ return 0;
+ }
+
+ switch (cmp_type) {
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID:
+ /* for the moment, ID is the same as a full (sementical) comparison. We might
+ * refine that in the future. */
+
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY:
+ cmp_full = FALSE;
+ /* fall-through */
+ case NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL:
+ NM_CMP_FIELD (a, b, addr_family);
+ NM_CMP_FIELD (a, b, tun_id);
+ if (cmp_full)
+ NM_CMP_FIELD (a, b, l3mdev);
+ else
+ NM_CMP_FIELD_BOOL (a, b, l3mdev);
+
+ if (cmp_full)
+ NM_CMP_FIELD (a, b, table_coerced);
+ else if (a->l3mdev) {
+ /* the table is ignored for semantic comparison. */
+ } else if (a->addr_family != AF_INET)
+ NM_CMP_FIELD (a, b, table_coerced);
+ else {
+ guint32 tb_a = nm_platform_route_table_uncoerce (a->table_coerced, FALSE);
+ guint32 tb_b = nm_platform_route_table_uncoerce (b->table_coerced, FALSE);
+
+ /* `ip rule add table 0` lets kernel create a new table and use
+ * it. For semantic comparison, if one of the tables is RT_TABLE_UNSPEC,
+ * we assume that the rules might match.
+ *
+ * This special case is done by kernel only for IPv4.
+ *
+ * For IPv6, kernel considers RT_TABLE_UNSPEC as invalid, so it's fine
+ * to always compare the tables (above). */
+ if ( tb_a != RT_TABLE_UNSPEC
+ && tb_b != RT_TABLE_UNSPEC) {
+ NM_CMP_DIRECT (tb_a, tb_b);
+ }
+ }
+
+ NM_CMP_FIELD (a, b, flags);
+ NM_CMP_FIELD (a, b, priority);
+ NM_CMP_FIELD (a, b, fwmark);
+ NM_CMP_FIELD (a, b, fwmask);
+ NM_CMP_FIELD (a, b, goto_target);
+ NM_CMP_FIELD (a, b, suppress_prefixlen_inverse);
+ NM_CMP_FIELD (a, b, suppress_ifgroup_inverse);
+ NM_CMP_FIELD (a, b, action);
+ NM_CMP_FIELD (a, b, tos);
+ NM_CMP_FIELD (a, b, src_len);
+ NM_CMP_FIELD (a, b, dst_len);
+ NM_CMP_FIELD (a, b, protocol);
+ NM_CMP_FIELD (a, b, ip_proto);
+ addr_size = nm_utils_addr_family_to_size (a->addr_family);
+ NM_CMP_FIELD_MEMCMP_LEN (a, b, src, addr_size);
+ NM_CMP_FIELD_MEMCMP_LEN (a, b, dst, addr_size);
+ NM_CMP_FIELD_UNSAFE (a, b, uid_range_has);
+ if (cmp_full || a->uid_range_has) {
+ NM_CMP_FIELD (a, b, uid_range.start);
+ NM_CMP_FIELD (a, b, uid_range.end);
+ }
+ NM_CMP_FIELD (a, b, sport_range.start);
+ NM_CMP_FIELD (a, b, sport_range.end);
+ NM_CMP_FIELD (a, b, dport_range.start);
+ NM_CMP_FIELD (a, b, dport_range.end);
+ NM_CMP_FIELD_STR (a, b, iifname);
+ NM_CMP_FIELD_STR (a, b, oifname);
+ return 0;
+ }
+
+ nm_assert_not_reached ();
+ return 0;
+}
+
/**
* nm_platform_ip_address_cmp_expiry:
* @a: a NMPlatformIPAddress to compare
@@ -7117,6 +7546,13 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
}
static void
+log_routing_rule (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformRoutingRule *routing_rule, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+ /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */
+ _LOG3D ("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_routing_rule_to_string (routing_rule, NULL, 0));
+}
+
+static void
log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data)
{
_LOG3D ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0));
@@ -7399,11 +7835,12 @@ nm_platform_class_init (NMPlatformClass *platform_class)
} G_STMT_END
/* Signals */
- SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc);
- SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, log_routing_rule);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC, NM_PLATFORM_SIGNAL_QDISC_CHANGED, log_qdisc);
+ SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER, NM_PLATFORM_SIGNAL_TFILTER_CHANGED, log_tfilter);
}