neels has uploaded this change for review.

View Change

tunmap: refactor nft ruleset

Take care of two problems:
- limitation of <= 1024 base chains in nftables, so far meaning we can
establish at most 1024 GTP tunnel mappings.
- mangling of source IP in prerouting so far meaning that the system
needs to be configured to permit 'martian' packets

The new ruleset separates in pre- and post-routing, so that we set a new
destination IP address in pre-routing, and set a new source IP address
in post-routing. Hence no problem with martian packet rejection.

The new ruleset uses verdict maps, which are more efficient, and do not
hit a limit of 1024 as base chains do.

Related: SYS#6327 SYS#6264
Change-Id: Iccb975a1c0f8a2087f7b7dc4942a6b41f5675a13
---
M include/osmocom/upf/upf.h
M include/osmocom/upf/upf_nft.h
M src/osmo-upf/upf.c
M src/osmo-upf/upf_nft.c
M src/osmo-upf/upf_vty.c
M tests/nft-rule.vty
6 files changed, 167 insertions(+), 40 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-upf refs/changes/65/31165/1
diff --git a/include/osmocom/upf/upf.h b/include/osmocom/upf/upf.h
index ef7aa2b..13dedd0 100644
--- a/include/osmocom/upf/upf.h
+++ b/include/osmocom/upf/upf.h
@@ -110,7 +110,8 @@

struct nft_ctx *nft_ctx;
char *table_name;
- int priority;
+ int priority_pre;
+ int priority_post;
uint32_t next_id_state;
} nft;

diff --git a/include/osmocom/upf/upf_nft.h b/include/osmocom/upf/upf_nft.h
index 4cdcb51..a1fdb4c 100644
--- a/include/osmocom/upf/upf_nft.h
+++ b/include/osmocom/upf/upf_nft.h
@@ -27,8 +27,6 @@

#include <osmocom/core/socket.h>

-#define NFT_CHAIN_NAME_PREFIX_TUNMAP "tunmap"
-
struct upf_nft_tunmap_desc {
struct {
struct osmo_sockaddr gtp_local_addr;
@@ -49,6 +47,8 @@
int upf_nft_init();
int upf_nft_free();

+char *upf_nft_tunmap_get_table_init_str(void *ctx);
+char *upf_nft_tunmap_get_vmap_init_str(void *ctx);
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_nft_tunmap_desc *tunmap);
int upf_nft_tunmap_create(struct upf_nft_tunmap_desc *tunmap);
int upf_nft_tunmap_delete(struct upf_nft_tunmap_desc *tunmap);
diff --git a/src/osmo-upf/upf.c b/src/osmo-upf/upf.c
index 0a84799..e26d9c7 100644
--- a/src/osmo-upf/upf.c
+++ b/src/osmo-upf/upf.c
@@ -51,7 +51,8 @@
},
},
.nft = {
- .priority = -300,
+ .priority_pre = -300,
+ .priority_post = 400,
},
.gtp = {
/* TODO: recovery count state file; use lower byte of current time, poor person's random. */
diff --git a/src/osmo-upf/upf_nft.c b/src/osmo-upf/upf_nft.c
index f30e30c..6125728 100644
--- a/src/osmo-upf/upf_nft.c
+++ b/src/osmo-upf/upf_nft.c
@@ -35,6 +35,30 @@
return talloc_asprintf(ctx, "add table inet %s\n", table_name);
}

+static char *upf_nft_ruleset_vmap_init(void *ctx, const char *table_name, int priority_pre, int priority_post)
+{
+ /* add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; }
+ * add chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; }
+ * add map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; }
+ * add map inet osmo-upf tunmap-post { typeof meta mark : verdict; }
+ * add rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre
+ * add rule inet osmo-upf post meta mark vmap @tunmap-post
+ */
+ return talloc_asprintf(ctx,
+ "add chain inet %s pre { type filter hook prerouting priority %d; policy accept; }\n"
+ "add chain inet %s post { type filter hook postrouting priority %d; policy accept; }\n"
+ "add map inet %s tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; }\n"
+ "add map inet %s tunmap-post { typeof meta mark : verdict; }\n"
+ "add rule inet %s pre udp dport %u ip daddr . @ih,32,32 vmap @tunmap-pre\n"
+ "add rule inet %s post meta mark vmap @tunmap-post\n",
+ table_name, priority_pre,
+ table_name, priority_post,
+ table_name,
+ table_name,
+ table_name, PORT_GTP1_U,
+ table_name);
+}
+
static int upf_nft_run(const char *ruleset)
{
int rc;
@@ -82,13 +106,19 @@
return -EIO;
}

- rc = upf_nft_run(upf_nft_ruleset_table_create(OTC_SELECT, g_upf->nft.table_name));
+ rc = upf_nft_run(upf_nft_tunmap_get_table_init_str(OTC_SELECT));
if (rc) {
LOGP(DNFT, LOGL_ERROR, "Failed to create nft table %s\n",
osmo_quote_str_c(OTC_SELECT, g_upf->nft.table_name, -1));
return rc;
}
LOGP(DNFT, LOGL_NOTICE, "Created nft table %s\n", osmo_quote_str_c(OTC_SELECT, g_upf->nft.table_name, -1));
+
+ rc = upf_nft_run(upf_nft_tunmap_get_vmap_init_str(OTC_SELECT));
+ if (rc) {
+ LOGP(DNFT, LOGL_ERROR, "Failed to initialize nft verdict map in table %s\n", g_upf->nft.table_name);
+ return rc;
+ }
return 0;
}

@@ -117,46 +147,111 @@
const char *table_name;
/* chain name for this specific tunnel mapping */
uint32_t chain_id;
- int priority;

struct upf_nft_args_peer peer_a;
struct upf_nft_args_peer peer_b;
};

-static int tunmap_single_direction(char *buf, size_t buflen,
- const struct upf_nft_args *args,
- const struct upf_nft_args_peer *from_peer,
- const struct upf_nft_args_peer *to_peer)
+static int tunmap_add_single_direction(char *buf, size_t buflen,
+ const struct upf_nft_args *args,
+ bool dir_a2b)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ const char *dir;
+ const struct upf_nft_args_peer *from_peer;
+ const struct upf_nft_args_peer *to_peer;

- OSMO_STRBUF_PRINTF(sb, "add rule inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u", args->table_name, args->chain_id);
+ if (dir_a2b) {
+ dir = "a";
+ from_peer = &args->peer_a;
+ to_peer = &args->peer_b;
+ } else {
+ dir = "b";
+ from_peer = &args->peer_b;
+ to_peer = &args->peer_a;
+ }

- /* Match only UDP packets */
- OSMO_STRBUF_PRINTF(sb, " meta l4proto udp");
+ /* # add chain for verdict map in prerouting
+ * add chain inet osmo-upf tunmap-pre-123a
+ * # mangle destination address at prerouting
+ * add rule inet osmo-upf tunmap-pre-123a ip daddr set 1.1.1.1 meta mark set 123a counter accept
+ *
+ * # add chain for verdict map in postrouting
+ * add chain inet osmo-upf tunmap-post-123a
+ * # mangle source address and GTP TID at postrouting
+ * add rule inet osmo-upf tunmap-post-123a ip saddr set 2.2.2.1 @ih,32,32 set 0x00000102 counter accept
+ *
+ * # add elements to verdict map, jump to chain
+ * add element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x00000203 : jump tunmap-pre-123a }
+ * add element inet osmo-upf tunmap-post { 123a : jump tunmap-post-123a }
+ */

- /* Match on packets coming in at specific local IP */
- OSMO_STRBUF_PRINTF(sb, " ip daddr ");
- OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
+ OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-pre-%u%s;\n",
+ args->table_name, args->chain_id, dir);

- /* Match on the TEID in the header */
- OSMO_STRBUF_PRINTF(sb, " @ih,32,32 0x%08x", from_peer->teid_local);
-
- /* Change outgoing address to local IP on outgoing interface */
- OSMO_STRBUF_PRINTF(sb, " ip saddr set ");
- OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_local);
-
- /* Change destination address to to_peer */
+ OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-pre-%u%s",
+ args->table_name, args->chain_id, dir);
OSMO_STRBUF_PRINTF(sb, " ip daddr set ");
OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_remote);
+ OSMO_STRBUF_PRINTF(sb, " meta mark set %u%s counter accept;\n", args->chain_id, dir);

- /* Change the TEID in the header to the one to_peer expects */
+ OSMO_STRBUF_PRINTF(sb, "add chain inet %s tunmap-post-%u%s;\n",
+ args->table_name, args->chain_id, dir);
+
+ OSMO_STRBUF_PRINTF(sb, "add rule inet %s tunmap-post-%u%s",
+ args->table_name, args->chain_id, dir);
+ OSMO_STRBUF_PRINTF(sb, " ip saddr set ");
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, to_peer->addr_local);
OSMO_STRBUF_PRINTF(sb, " @ih,32,32 set 0x%08x", to_peer->teid_remote);
+ OSMO_STRBUF_PRINTF(sb, " counter accept;\n");

- OSMO_STRBUF_PRINTF(sb, " counter");
+ OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-pre { ",
+ args->table_name);
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
+ OSMO_STRBUF_PRINTF(sb, " . 0x%08x : jump tunmap-pre-%u%s };\n",
+ from_peer->teid_local, args->chain_id, dir);

- OSMO_STRBUF_PRINTF(sb, " accept");
- OSMO_STRBUF_PRINTF(sb, ";\n");
+ OSMO_STRBUF_PRINTF(sb, "add element inet %s tunmap-post { %u%s : jump tunmap-post-%u%s };\n",
+ args->table_name, args->chain_id, dir, args->chain_id, dir);
+
+ return sb.chars_needed;
+}
+
+static int tunmap_del_single_direction(char *buf, size_t buflen,
+ const struct upf_nft_args *args,
+ bool dir_a2b)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ const char *dir;
+ const struct upf_nft_args_peer *from_peer;
+
+ if (dir_a2b) {
+ dir = "a";
+ from_peer = &args->peer_a;
+ } else {
+ dir = "b";
+ from_peer = &args->peer_b;
+ }
+
+ /* delete element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x00000203 }
+ * delete element inet osmo-upf tunmap-post { 123a }
+ * delete chain inet osmo-upf tunmap-pre-123a
+ * delete chain inet osmo-upf tunmap-post-123a
+ */
+
+ OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-pre { ",
+ args->table_name);
+ OSMO_STRBUF_APPEND(sb, osmo_sockaddr_to_str_buf2, from_peer->addr_local);
+ OSMO_STRBUF_PRINTF(sb, " . 0x%08x };\n", from_peer->teid_local);
+
+ OSMO_STRBUF_PRINTF(sb, "delete element inet %s tunmap-post { %u%s };\n",
+ args->table_name, args->chain_id, dir);
+
+ OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-pre-%u%s;\n",
+ args->table_name, args->chain_id, dir);
+
+ OSMO_STRBUF_PRINTF(sb, "delete chain inet %s tunmap-post-%u%s;\n",
+ args->table_name, args->chain_id, dir);

return sb.chars_needed;
}
@@ -165,14 +260,10 @@
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };

- /* Add a chain for this tunnel mapping */
- OSMO_STRBUF_PRINTF(sb, "add chain inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u { type filter hook prerouting priority %d; }\n",
- args->table_name, args->chain_id, args->priority);
-
/* Forwarding from peer_a to peer_b */
- OSMO_STRBUF_APPEND(sb, tunmap_single_direction, args, &args->peer_a, &args->peer_b);
+ OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, true);
/* And from peer_b to peer_a */
- OSMO_STRBUF_APPEND(sb, tunmap_single_direction, args, &args->peer_b, &args->peer_a);
+ OSMO_STRBUF_APPEND(sb, tunmap_add_single_direction, args, false);

return sb.chars_needed;
}
@@ -185,8 +276,12 @@
static int upf_nft_ruleset_tunmap_delete_buf(char *buf, size_t buflen, const struct upf_nft_args *args)
{
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
- OSMO_STRBUF_PRINTF(sb, "delete chain inet %s " NFT_CHAIN_NAME_PREFIX_TUNMAP "%u\n",
- args->table_name, args->chain_id);
+
+ /* Forwarding from peer_a to peer_b */
+ OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, true);
+ /* And from peer_b to peer_a */
+ OSMO_STRBUF_APPEND(sb, tunmap_del_single_direction, args, false);
+
return sb.chars_needed;
}

@@ -205,7 +300,6 @@
*args = (struct upf_nft_args){
.table_name = g_upf->nft.table_name,
.chain_id = tunmap->id,
- .priority = g_upf->nft.priority,
.peer_a = {
.addr_remote = &tunmap->access.gtp_remote_addr,
.teid_remote = tunmap->access.remote_teid,
@@ -221,6 +315,17 @@
};
}

+char *upf_nft_tunmap_get_table_init_str(void *ctx)
+{
+ return upf_nft_ruleset_table_create(ctx, g_upf->nft.table_name);
+}
+
+char *upf_nft_tunmap_get_vmap_init_str(void *ctx)
+{
+ return upf_nft_ruleset_vmap_init(ctx, g_upf->nft.table_name, g_upf->nft.priority_pre,
+ g_upf->nft.priority_post);
+}
+
char *upf_nft_tunmap_get_ruleset_str(void *ctx, struct upf_nft_tunmap_desc *tunmap)
{
struct upf_nft_args args;
diff --git a/src/osmo-upf/upf_vty.c b/src/osmo-upf/upf_vty.c
index 436d91f..43b1aa5 100644
--- a/src/osmo-upf/upf_vty.c
+++ b/src/osmo-upf/upf_vty.c
@@ -320,6 +320,8 @@
osmo_sockaddr_str_from_str2(&str, "3.3.3.3");
osmo_sockaddr_str_to_sockaddr(&str, &d.core.gtp_remote_addr.u.sas);

+ vty_out(vty, "%s%s", upf_nft_tunmap_get_table_init_str(OTC_SELECT), VTY_NEWLINE);
+ vty_out(vty, "%s%s", upf_nft_tunmap_get_vmap_init_str(OTC_SELECT), VTY_NEWLINE);
vty_out(vty, "%s%s", upf_nft_tunmap_get_ruleset_str(OTC_SELECT, &d), VTY_NEWLINE);
return CMD_SUCCESS;
}
diff --git a/tests/nft-rule.vty b/tests/nft-rule.vty
index ec719ab..68992cb 100644
--- a/tests/nft-rule.vty
+++ b/tests/nft-rule.vty
@@ -3,9 +3,27 @@
OsmoUPF(config)# tunmap

OsmoUPF(config-tunmap)# show nft-rule tunmap example
-add chain inet osmo-upf tunmap123 { type filter hook prerouting priority -300; }
-add rule inet osmo-upf tunmap123 meta l4proto udp ip daddr 2.2.2.1 @ih,32,32 0x00000201 ip saddr set 2.2.2.3 ip daddr set 3.3.3.3 @ih,32,32 set 0x00000302 counter accept;
-add rule inet osmo-upf tunmap123 meta l4proto udp ip daddr 2.2.2.3 @ih,32,32 0x00000203 ip saddr set 2.2.2.1 ip daddr set 1.1.1.1 @ih,32,32 set 0x00000102 counter accept;
+add table inet osmo-upf
+
+add chain inet osmo-upf pre { type filter hook prerouting priority -300; policy accept; }
+add chain inet osmo-upf post { type filter hook postrouting priority 400; policy accept; }
+add map inet osmo-upf tunmap-pre { typeof ip daddr . @ih,32,32 : verdict; }
+add map inet osmo-upf tunmap-post { typeof meta mark : verdict; }
+add rule inet osmo-upf pre udp dport 2152 ip daddr . @ih,32,32 vmap @tunmap-pre
+add rule inet osmo-upf post meta mark vmap @tunmap-post
+
+add chain inet osmo-upf tunmap-pre-123a;
+add rule inet osmo-upf tunmap-pre-123a ip daddr set 3.3.3.3 meta mark set 123a counter accept;
+add chain inet osmo-upf tunmap-post-123a;
+add rule inet osmo-upf tunmap-post-123a ip saddr set 2.2.2.3 @ih,32,32 set 0x00000302 counter accept;
+add element inet osmo-upf tunmap-pre { 2.2.2.1 . 0x00000201 : jump tunmap-pre-123a };
+add element inet osmo-upf tunmap-post { 123a : jump tunmap-post-123a };
+add chain inet osmo-upf tunmap-pre-123b;
+add rule inet osmo-upf tunmap-pre-123b ip daddr set 1.1.1.1 meta mark set 123b counter accept;
+add chain inet osmo-upf tunmap-post-123b;
+add rule inet osmo-upf tunmap-post-123b ip saddr set 2.2.2.1 @ih,32,32 set 0x00000102 counter accept;
+add element inet osmo-upf tunmap-pre { 2.2.2.3 . 0x00000203 : jump tunmap-pre-123b };
+add element inet osmo-upf tunmap-post { 123b : jump tunmap-post-123b };

OsmoUPF(config-tunmap)# show nft-rule tunmap append
% deprecated config option: 'show nft-rule tunmap append'

To view, visit change 31165. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-upf
Gerrit-Branch: master
Gerrit-Change-Id: Iccb975a1c0f8a2087f7b7dc4942a6b41f5675a13
Gerrit-Change-Number: 31165
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr@sysmocom.de>
Gerrit-MessageType: newchange