2

我想使用 nftables 添加以下snat规则dnat

nft add rule nat post udp sport 29000 ip saddr 192.168.101.102 udp dport 40000 ip daddr 192.168.101.102 snat 192.168.101.55:35000
nft add rule nat pre udp sport 29000 ip saddr 192.168.101.103 udp dport 32000 ip daddr 192.168.101.55 dnat 192.168.101.102:40000

在这里,preandpostnat表中链的名称,我使用以下命令添加了这些链:

nft add table nat
nft add chain nat pre { type nat hook prerouting priority 0 \; }
nft add chain nat post { type nat hook postrouting priority 100 \; }

我已经用nft命令检查了它,它工作得很好。

我想通过我的应用程序中的 API 方法来实现相同的目标。libnftnl库中提供了一些示例和测试程序。根据nft-rule-add.cnft-expr_nat-test.c的参考,我做了一个相同的测试应用程序,如下所示:

/* Packets coming from */
#define SADDR "192.168.1.103"
#define SPORT 29000
/* Packets coming to (the machine on which nat happen) */
#define DADDR "192.168.101.55"
#define DPORT 32000
/* Packets should go from (the machine on which nat happen) */
#define SNATADDR DADDR
#define SNATPORT 35000
/* Packets should go to */
#define DNATADDR SADDR
#define DNATPORT 38000

static void add_payload(struct nftnl_rule *rRule, uint32_t base, uint32_t dreg,
            uint32_t offset, uint32_t len)
{
    struct nftnl_expr *tExpression = nftnl_expr_alloc("payload");
    if (tExpression == NULL) {
        perror("expr payload oom");
        exit(EXIT_FAILURE);
    }

    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_PAYLOAD_BASE, base);
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_PAYLOAD_DREG, dreg);
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_PAYLOAD_LEN, len);

    nftnl_rule_add_expr(rRule, tExpression);
}

static void add_cmp(struct nftnl_rule *rRule, uint32_t sreg, uint32_t op,
            const void *data, uint32_t data_len)
{
    struct nftnl_expr *tExpression = nftnl_expr_alloc("cmp");
    if (tExpression == NULL) {
        perror("expr cmp oom");
        exit(EXIT_FAILURE);
    }

    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_CMP_SREG, sreg);
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_CMP_OP, op);
    nftnl_expr_set(tExpression, NFTNL_EXPR_CMP_DATA, data, data_len);

    nftnl_rule_add_expr(rRule, tExpression);
}

static void add_nat(struct nftnl_rule *rRule, uint32_t sreg,
                    const void *ip, uint32_t ip_len,
                    const void *port, uint32_t port_len)
{
    struct nftnl_expr *tExpression = nftnl_expr_alloc("nat");
    if (tExpression == NULL) {
        perror("expr cmp oom");
        exit(EXIT_FAILURE);
    }

    /* Performing snat */
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_TYPE, NFT_NAT_SNAT);
    /* Nat family IPv4 */
    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_FAMILY, NFPROTO_IPV4);

    /* Don't know what to do with these four calls */
    /* How to add IP address and port that should be used in snat */
//    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_REG_ADDR_MIN, NFT_REG_1);
//    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_REG_ADDR_MAX, NFT_REG_3);
//    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_REG_PROTO_MIN, 0x6124385);
//    nftnl_expr_set_u32(tExpression, NFTNL_EXPR_NAT_REG_PROTO_MAX, 0x2153846);

    nftnl_rule_add_expr(rRule, tExpression);
}

static struct nftnl_rule *setup_rule(uint8_t family, const char *table,
                   const char *chain, const char *handle)
{
    struct nftnl_rule *rule = NULL;
    uint8_t proto = 0;
    uint16_t dport = 0, sport = 0, sNatPort = 0, dNatPort = 0;
    uint8_t saddr[sizeof(struct in6_addr)];     /* Packets coming from */
    uint8_t daddr[sizeof(struct in6_addr)];     /* Packets coming to (the machine on which nat happens) */
    uint8_t snataddr[sizeof(struct in6_addr)];  /* Packets should go from (the machine on which nat happens) */
    uint8_t dnataddr[sizeof(struct in6_addr)];  /* Packets should go to */
    uint64_t handle_num;

    rule = nftnl_rule_alloc();
    if (rule == NULL) {
        perror("OOM");
        exit(EXIT_FAILURE);
    }

    nftnl_rule_set(rule, NFTNL_RULE_TABLE, table);
    nftnl_rule_set(rule, NFTNL_RULE_CHAIN, chain);
    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);

    if (handle != NULL) {
        handle_num = atoll(handle);
        nftnl_rule_set_u64(rule, NFTNL_RULE_POSITION, handle_num);
    }

    /* Declaring tcp port or udp port */
    proto = IPPROTO_UDP;
    add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
            offsetof(struct iphdr, protocol), sizeof(uint8_t));
    add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &proto, sizeof(uint8_t));

    /* Declaring dport */
    dport = htons(DPORT);
    add_payload(rule, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
            offsetof(struct udphdr, dest), sizeof(uint16_t));
    add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &dport, sizeof(uint16_t));

    /* Declaring dest ip */
    add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
            offsetof(struct iphdr, daddr), sizeof(daddr));
    inet_pton(AF_INET, DADDR, daddr);
    add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, daddr, sizeof(daddr));

    /* Declaring sport */
    sport = htons(SPORT);
    add_payload(rule, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1,
            offsetof(struct udphdr, source), sizeof(uint16_t));
    add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, &sport, sizeof(uint16_t));

    /* Declaring src ip */
    add_payload(rule, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1,
            offsetof(struct iphdr, saddr), sizeof(saddr));
    inet_pton(AF_INET, SADDR, saddr);
    add_cmp(rule, NFT_REG_1, NFT_CMP_EQ, saddr, sizeof(saddr));

    /* Addding snat params */
    inet_pton(AF_INET, SNATADDR, snataddr);
    sNatPort = htons(SNATPORT);
    add_nat(rule, NFT_REG_1, snataddr, sizeof(snataddr), &sNatPort, sizeof(uint16_t));

    return rule;
}

int main(int argc, char *argv[])
{
    struct mnl_socket *nl;
    struct nftnl_rule *r;
    struct nlmsghdr *nlh;
    struct mnl_nlmsg_batch *batch;
    uint8_t family;
    char buf[MNL_SOCKET_BUFFER_SIZE];
    uint32_t seq = time(NULL);
    int ret, batching;

    if (argc < 4 || argc > 5) {
        fprintf(stderr, "Usage: %s <family> <table> <chain>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (strcmp(argv[1], "ip") == 0)
        family = NFPROTO_IPV4;
    else if (strcmp(argv[1], "ip6") == 0)
        family = NFPROTO_IPV6;
    else {
        fprintf(stderr, "Unknown family: ip, ip6\n");
        exit(EXIT_FAILURE);
    }

    // Now creating rule
    if (argc != 5)
        r = setup_rule(family, argv[2], argv[3], NULL);
    else
        r = setup_rule(family, argv[2], argv[3], argv[4]);

    // Now adding rule through mnl socket
    nl = mnl_socket_open(NETLINK_NETFILTER);
    if (nl == NULL) {
        perror("mnl_socket_open");
        exit(EXIT_FAILURE);
    }

    if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
        perror("mnl_socket_bind");
        exit(EXIT_FAILURE);
    }

    batching = nftnl_batch_is_supported();
    if (batching < 0) {
        perror("cannot talk to nfnetlink");
        exit(EXIT_FAILURE);
    }

    batch = mnl_nlmsg_batch_start(buf, sizeof(buf));

    if (batching) {
        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);
    }

    nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
            NFT_MSG_NEWRULE,
            nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY),
            NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++);

    nftnl_rule_nlmsg_build_payload(nlh, r);
    nftnl_rule_free(r);
    mnl_nlmsg_batch_next(batch);

    if (batching) {
        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);
    }

    ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                mnl_nlmsg_batch_size(batch));
    if (ret == -1) {
        perror("mnl_socket_sendto");
        exit(EXIT_FAILURE);
    }

    mnl_nlmsg_batch_stop(batch);

    ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
    if (ret == -1) {
        perror("mnl_socket_recvfrom");
        exit(EXIT_FAILURE);
    }

    ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL);
    if (ret < 0) {
        perror("mnl_cb_run");
        exit(EXIT_FAILURE);
    }

    mnl_socket_close(nl);

    return EXIT_SUCCESS;
}

在这里,main()函数调用setup_rule()是构建整个规则的地方。setup_rule()调用构建add_nat()表达式的snat位置。应该用于snatdnat作为参数传递给的 IP 和端口add_nat()。据我了解,建立规则的哲学是用各种表达方式建立规则。在add_nat()一个表达是为做snat而建的,这就是我开始摸索的地方。不知道如何处理NFTNL_EXPR_NAT_REG_ADDR_MINNFTNL_EXPR_NAT_REG_PROTO_MIN种类的宏。如何传递将在 snat 或 dnat 中使用的 IP 地址和端口地址。

我按如下方式运行上述测试应用程序:

./application ip nat pre

并使用以下命令

nft list table nat -a

得到如下输出:

table ip nat {
        chain pre {
                type nat hook prerouting priority 0; policy accept;
                udp dport 32000 ip daddr 192.168.101.55 @nh,160,96 541307071 udp sport 29000 ip saddr 192.168.1.103 ip daddr 0.0.0.0 @nh,160,64 3293205175 snat to  # handle 250
        } # handle 33

        chain post {
                type nat hook postrouting priority 100; policy accept;
        } # handle 34
} # handle 0

可能是方法不对,但是,如上图,最后显示snat to出来了。这让我希望这是前进的道路。我还挖掘了nftables实用程序代码,但无济于事。

所以要列出所有查询:

  1. 如何使用 nftable api添加snat或规则?dnat
  2. 有什么用NFT_REG_1?这些寄存器应该如何使用以及应该在哪里使用?
  3. 命令输出中@之后写的是nft list table nat -a什么?当我使用命令应用规则时,nft它不会在 @ 之后的输出中显示信息nft list table
  4. 此外,命令ip addr 0.0.0.0的输出中还显示了一个额外的内容。nft list table nat -a在哪里添加?
4

0 回答 0