]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: trigger layer 4 checksum when pseudoheader fields are modified
authorPablo Neira <pablo@netfilter.org>
Thu, 24 Nov 2016 11:12:33 +0000 (12:12 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sun, 4 Dec 2016 20:24:48 +0000 (21:24 +0100)
This patch sets the NFT_PAYLOAD_L4CSUM_PSEUDOHDR when any of the
pseudoheader fields are modified. This implicitly enables stateless NAT,
that can be useful under some circuntances.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/nf_tables.h
include/proto.h
src/netlink_linearize.c
src/proto.c

index 14e5f619167ec8ad1deaeb37895df08f8f4f05b1..f030e59aa2ece17b5dfaa2d248983449cb83bde6 100644 (file)
@@ -659,6 +659,10 @@ enum nft_payload_csum_types {
        NFT_PAYLOAD_CSUM_INET,
 };
 
+enum nft_payload_csum_flags {
+       NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0),
+};
+
 /**
  * enum nft_payload_attributes - nf_tables payload expression netlink attributes
  *
@@ -669,6 +673,7 @@ enum nft_payload_csum_types {
  * @NFTA_PAYLOAD_SREG: source register to load data from (NLA_U32: nft_registers)
  * @NFTA_PAYLOAD_CSUM_TYPE: checksum type (NLA_U32)
  * @NFTA_PAYLOAD_CSUM_OFFSET: checksum offset relative to base (NLA_U32)
+ * @NFTA_PAYLOAD_CSUM_FLAGS: checksum flags (NLA_U32)
  */
 enum nft_payload_attributes {
        NFTA_PAYLOAD_UNSPEC,
@@ -679,6 +684,7 @@ enum nft_payload_attributes {
        NFTA_PAYLOAD_SREG,
        NFTA_PAYLOAD_CSUM_TYPE,
        NFTA_PAYLOAD_CSUM_OFFSET,
+       NFTA_PAYLOAD_CSUM_FLAGS,
        __NFTA_PAYLOAD_MAX
 };
 #define NFTA_PAYLOAD_MAX       (__NFTA_PAYLOAD_MAX - 1)
index 4fa54a74b00c1eb4ca219d4e1c5730340c9db1e3..01188ab6eee4059609d6a68972446943ddfd9e0e 100644 (file)
@@ -73,6 +73,7 @@ struct proto_hdr_template {
  * @length:    total size of the header, in bits
  * @protocols: link to upper layer protocol descriptions indexed by protocol value
  * @templates: header templates
+ * @pseudohdr:  header fields that are part of upper layer checksum pseudoheader
  */
 struct proto_desc {
        const char                      *name;
@@ -89,6 +90,7 @@ struct proto_desc {
                uint8_t                         order[PROTO_HDRS_MAX];
                uint32_t                        filter;
        }                               format;
+       unsigned int                    pseudohdr[PROTO_HDRS_MAX];
 
 };
 
index 6bc0bee8340d14ed8670c42a80c60577df1840bf..4a0001a4ce177a36a1ff68a2a7ecb89350e3c90f 100644 (file)
@@ -761,6 +761,18 @@ static void netlink_gen_verdict_stmt(struct netlink_linearize_ctx *ctx,
        return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
 }
 
+static bool payload_needs_l4csum_update_pseudohdr(const struct expr *expr,
+                                                 const struct proto_desc *desc)
+{
+       int i;
+
+       for (i = 0; i < PROTO_HDRS_MAX; i++) {
+               if (payload_hdr_field(expr) == desc->pseudohdr[i])
+                       return true;
+       }
+       return false;
+}
+
 static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
                                     const struct stmt *stmt)
 {
@@ -794,6 +806,11 @@ static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx,
                                   NFT_PAYLOAD_CSUM_INET);
                nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET,
                                   csum_off / BITS_PER_BYTE);
+
+               if (expr->payload.base == PROTO_BASE_NETWORK_HDR &&
+                   payload_needs_l4csum_update_pseudohdr(expr, desc))
+                       nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS,
+                                          NFT_PAYLOAD_L4CSUM_PSEUDOHDR);
        }
 
        nftnl_rule_add_expr(ctx->nlr, nle);
index df5439ccda3c9ac449494620c5401050c306c112..8930bed65891227fa8cf1ebd23b0ac67c6a2d932 100644 (file)
@@ -616,6 +616,9 @@ const struct proto_desc proto_ip = {
                .filter = (1 << IPHDR_VERSION)  | (1 << IPHDR_HDRLENGTH) |
                          (1 << IPHDR_FRAG_OFF),
        },
+       .pseudohdr      = {
+               IPHDR_SADDR, IPHDR_DADDR, IPHDR_PROTOCOL, IPHDR_LENGTH,
+       },
 };
 
 /*
@@ -721,6 +724,9 @@ const struct proto_desc proto_ip6 = {
                },
                .filter = (1 << IP6HDR_VERSION),
        },
+       .pseudohdr      = {
+               IP6HDR_SADDR, IP6HDR_DADDR, IP6HDR_NEXTHDR, IP6HDR_LENGTH,
+       },
 };
 
 /*