neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-upf/+/31165 )
Change subject: tunmap: refactor nft ruleset ......................................................................
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'