如何通过libnftnl API添加指定的nftables SNAT/DNAT规则?
Implementing SNAT/DNAT Rules with libnftnl API
Alright, let's walk through how to translate your nftables CLI rules into working C code using the libnftnl API. Since you've already created the nat table and pre/post chains, we'll focus solely on adding the two rules you specified.
First, here's a complete code example that implements both rules, followed by a breakdown of key parts:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <libnftnl/libnftnl.h> static int add_snat_rule(struct nft_ctx *ctx) { struct nft_rule *r; struct nft_expr *expr; int ret; // Allocate new rule object r = nft_rule_alloc(); if (!r) { perror("nft_rule_alloc"); return -1; } // Bind rule to nat table and post chain nft_rule_set_str(r, NFT_RULE_TABLE, "nat"); nft_rule_set_str(r, NFT_RULE_CHAIN, "post"); // Match UDP source port 29000 expr = nft_expr_alloc("udp"); nft_expr_set_u16(expr, NFT_EXPR_UDP_SPORT, htons(29000)); nft_rule_add_expr(r, expr); // Match IP source address 192.168.101.102 expr = nft_expr_alloc("ip"); struct in_addr saddr; inet_pton(AF_INET, "192.168.101.102", &saddr); nft_expr_set_data(expr, NFT_EXPR_IP_SADDR, &saddr, sizeof(saddr)); nft_rule_add_expr(r, expr); // Match UDP destination port 40000 expr = nft_expr_alloc("udp"); nft_expr_set_u16(expr, NFT_EXPR_UDP_DPORT, htons(40000)); nft_rule_add_expr(r, expr); // Match IP destination address 192.168.101.102 expr = nft_expr_alloc("ip"); inet_pton(AF_INET, "192.168.101.102", &saddr); nft_expr_set_data(expr, NFT_EXPR_IP_DADDR, &saddr, sizeof(saddr)); nft_rule_add_expr(r, expr); // Add SNAT action: translate to 192.168.101.55:35000 expr = nft_expr_alloc("snat"); struct nft_addr snat_addr; memset(&snat_addr, 0, sizeof(snat_addr)); snat_addr.family = AF_INET; inet_pton(AF_INET, "192.168.101.55", &snat_addr.addr.ipv4); snat_addr.port = htons(35000); nft_expr_set_data(expr, NFT_EXPR_SNAT_ADDR, &snat_addr, sizeof(snat_addr)); nft_rule_add_expr(r, expr); // Send rule to kernel ret = nft_rule_add(ctx, r, 0); if (ret < 0) { fprintf(stderr, "Failed to add SNAT rule: %s\n", strerror(-ret)); nft_rule_free(r); return ret; } nft_rule_free(r); return 0; } static int add_dnat_rule(struct nft_ctx *ctx) { struct nft_rule *r; struct nft_expr *expr; int ret; // Allocate new rule object r = nft_rule_alloc(); if (!r) { perror("nft_rule_alloc"); return -1; } // Bind rule to nat table and pre chain nft_rule_set_str(r, NFT_RULE_TABLE, "nat"); nft_rule_set_str(r, NFT_RULE_CHAIN, "pre"); // Match UDP source port 29000 expr = nft_expr_alloc("udp"); nft_expr_set_u16(expr, NFT_EXPR_UDP_SPORT, htons(29000)); nft_rule_add_expr(r, expr); // Match IP source address 192.168.101.103 expr = nft_expr_alloc("ip"); struct in_addr saddr; inet_pton(AF_INET, "192.168.101.103", &saddr); nft_expr_set_data(expr, NFT_EXPR_IP_SADDR, &saddr, sizeof(saddr)); nft_rule_add_expr(r, expr); // Match UDP destination port 32000 expr = nft_expr_alloc("udp"); nft_expr_set_u16(expr, NFT_EXPR_UDP_DPORT, htons(32000)); nft_rule_add_expr(r, expr); // Match IP destination address 192.168.101.55 expr = nft_expr_alloc("ip"); inet_pton(AF_INET, "192.168.101.55", &saddr); nft_expr_set_data(expr, NFT_EXPR_IP_DADDR, &saddr, sizeof(saddr)); nft_rule_add_expr(r, expr); // Add DNAT action: translate to 192.168.101.102:40000 expr = nft_expr_alloc("dnat"); struct nft_addr dnat_addr; memset(&dnat_addr, 0, sizeof(dnat_addr)); dnat_addr.family = AF_INET; inet_pton(AF_INET, "192.168.101.102", &dnat_addr.addr.ipv4); dnat_addr.port = htons(40000); nft_expr_set_data(expr, NFT_EXPR_DNAT_ADDR, &dnat_addr, sizeof(dnat_addr)); nft_rule_add_expr(r, expr); // Send rule to kernel ret = nft_rule_add(ctx, r, 0); if (ret < 0) { fprintf(stderr, "Failed to add DNAT rule: %s\n", strerror(-ret)); nft_rule_free(r); return ret; } nft_rule_free(r); return 0; } int main(void) { struct nft_ctx *ctx; int ret; // Initialize libnftnl context and netlink socket ctx = nft_ctx_alloc(); if (!ctx) { perror("nft_ctx_alloc"); return EXIT_FAILURE; } // Add SNAT rule ret = add_snat_rule(ctx); if (ret != 0) { nft_ctx_free(ctx); return EXIT_FAILURE; } // Add DNAT rule ret = add_dnat_rule(ctx); if (ret != 0) { nft_ctx_free(ctx); return EXIT_FAILURE; } printf("Successfully added SNAT and DNAT rules\n"); nft_ctx_free(ctx); return EXIT_SUCCESS; }
Key Breakdown of the Code
Initialization & Cleanup
- We start by initializing the libnftnl context with
nft_ctx_alloc()to create a connection to the kernel's netlink interface. - All allocated objects (rules, expressions) are freed at the end to avoid memory leaks.
SNAT Rule (post Chain)
- Bind to table/chain: We link the rule to your existing
nattable andpostchain usingnft_rule_set_str(). - Add match conditions: Each match (UDP ports, IP addresses) is created as a separate expression. We use
inet_pton()to convert human-readable IPs to binary andhtons()to convert port numbers to network byte order (required by libnftnl). - SNAT action: The
snatexpression is configured with the target IP and port, then attached to the rule.
DNAT Rule (pre Chain)
This follows the same structure as the SNAT rule, with these key differences:
- Bound to the
prechain instead ofpost. - Matches a different source IP (
192.168.101.103) and destination port (32000). - Uses the
dnatexpression with the target192.168.101.102:40000.
Compilation Note
When building this code, don't forget to link against the libnftnl library:
gcc -o nft_snat_dnat nft_snat_dnat.c -lnftnl
内容的提问来源于stack exchange,提问作者UserXYZ
相关产品推荐
相关产品推荐

