1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org> 4 */ 5 6#include <linux/kernel.h> 7#include <linux/init.h> 8#include <linux/module.h> 9#include <linux/netlink.h> 10#include <linux/netfilter.h> 11#include <linux/netfilter/nf_tables.h> 12#include <net/netfilter/nf_tables.h> 13#include <net/netfilter/nf_nat.h> 14#include <net/netfilter/nf_nat_masquerade.h> 15 16struct nft_masq { 17 u32 flags; 18 u8 sreg_proto_min; 19 u8 sreg_proto_max; 20}; 21 22static const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = { 23 [NFTA_MASQ_FLAGS] = 24 NLA_POLICY_MASK(NLA_BE32, NF_NAT_RANGE_MASK), 25 [NFTA_MASQ_REG_PROTO_MIN] = { .type = NLA_U32 }, 26 [NFTA_MASQ_REG_PROTO_MAX] = { .type = NLA_U32 }, 27}; 28 29static int nft_masq_validate(const struct nft_ctx *ctx, 30 const struct nft_expr *expr, 31 const struct nft_data **data) 32{ 33 int err; 34 35 err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); 36 if (err < 0) 37 return err; 38 39 return nft_chain_validate_hooks(ctx->chain, 40 (1 << NF_INET_POST_ROUTING)); 41} 42 43static int nft_masq_init(const struct nft_ctx *ctx, 44 const struct nft_expr *expr, 45 const struct nlattr * const tb[]) 46{ 47 u32 plen = sizeof_field(struct nf_nat_range, min_proto.all); 48 struct nft_masq *priv = nft_expr_priv(expr); 49 int err; 50 51 if (tb[NFTA_MASQ_FLAGS]) 52 priv->flags = ntohl(nla_get_be32(tb[NFTA_MASQ_FLAGS])); 53 54 if (tb[NFTA_MASQ_REG_PROTO_MIN]) { 55 err = nft_parse_register_load(tb[NFTA_MASQ_REG_PROTO_MIN], 56 &priv->sreg_proto_min, plen); 57 if (err < 0) 58 return err; 59 60 if (tb[NFTA_MASQ_REG_PROTO_MAX]) { 61 err = nft_parse_register_load(tb[NFTA_MASQ_REG_PROTO_MAX], 62 &priv->sreg_proto_max, 63 plen); 64 if (err < 0) 65 return err; 66 } else { 67 priv->sreg_proto_max = priv->sreg_proto_min; 68 } 69 } 70 71 return nf_ct_netns_get(ctx->net, ctx->family); 72} 73 74static int nft_masq_dump(struct sk_buff *skb, 75 const struct nft_expr *expr, bool reset) 76{ 77 const struct nft_masq *priv = nft_expr_priv(expr); 78 79 if (priv->flags != 0 && 80 nla_put_be32(skb, NFTA_MASQ_FLAGS, htonl(priv->flags))) 81 goto nla_put_failure; 82 83 if (priv->sreg_proto_min) { 84 if (nft_dump_register(skb, NFTA_MASQ_REG_PROTO_MIN, 85 priv->sreg_proto_min) || 86 nft_dump_register(skb, NFTA_MASQ_REG_PROTO_MAX, 87 priv->sreg_proto_max)) 88 goto nla_put_failure; 89 } 90 91 return 0; 92 93nla_put_failure: 94 return -1; 95} 96 97static void nft_masq_eval(const struct nft_expr *expr, 98 struct nft_regs *regs, 99 const struct nft_pktinfo *pkt) 100{ 101 const struct nft_masq *priv = nft_expr_priv(expr); 102 struct nf_nat_range2 range; 103 104 memset(&range, 0, sizeof(range)); 105 range.flags = priv->flags; 106 if (priv->sreg_proto_min) { 107 range.min_proto.all = (__force __be16) 108 nft_reg_load16(®s->data[priv->sreg_proto_min]); 109 range.max_proto.all = (__force __be16) 110 nft_reg_load16(®s->data[priv->sreg_proto_max]); 111 } 112 113 switch (nft_pf(pkt)) { 114 case NFPROTO_IPV4: 115 regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, 116 nft_hook(pkt), 117 &range, 118 nft_out(pkt)); 119 break; 120#ifdef CONFIG_NF_TABLES_IPV6 121 case NFPROTO_IPV6: 122 regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, 123 nft_out(pkt)); 124 break; 125#endif 126 default: 127 WARN_ON_ONCE(1); 128 break; 129 } 130} 131 132static void 133nft_masq_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 134{ 135 nf_ct_netns_put(ctx->net, NFPROTO_IPV4); 136} 137 138static struct nft_expr_type nft_masq_ipv4_type; 139static const struct nft_expr_ops nft_masq_ipv4_ops = { 140 .type = &nft_masq_ipv4_type, 141 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), 142 .eval = nft_masq_eval, 143 .init = nft_masq_init, 144 .destroy = nft_masq_ipv4_destroy, 145 .dump = nft_masq_dump, 146 .validate = nft_masq_validate, 147 .reduce = NFT_REDUCE_READONLY, 148}; 149 150static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { 151 .family = NFPROTO_IPV4, 152 .name = "masq", 153 .ops = &nft_masq_ipv4_ops, 154 .policy = nft_masq_policy, 155 .maxattr = NFTA_MASQ_MAX, 156 .owner = THIS_MODULE, 157}; 158 159#ifdef CONFIG_NF_TABLES_IPV6 160static void 161nft_masq_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 162{ 163 nf_ct_netns_put(ctx->net, NFPROTO_IPV6); 164} 165 166static struct nft_expr_type nft_masq_ipv6_type; 167static const struct nft_expr_ops nft_masq_ipv6_ops = { 168 .type = &nft_masq_ipv6_type, 169 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), 170 .eval = nft_masq_eval, 171 .init = nft_masq_init, 172 .destroy = nft_masq_ipv6_destroy, 173 .dump = nft_masq_dump, 174 .validate = nft_masq_validate, 175 .reduce = NFT_REDUCE_READONLY, 176}; 177 178static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { 179 .family = NFPROTO_IPV6, 180 .name = "masq", 181 .ops = &nft_masq_ipv6_ops, 182 .policy = nft_masq_policy, 183 .maxattr = NFTA_MASQ_MAX, 184 .owner = THIS_MODULE, 185}; 186 187static int __init nft_masq_module_init_ipv6(void) 188{ 189 return nft_register_expr(&nft_masq_ipv6_type); 190} 191 192static void nft_masq_module_exit_ipv6(void) 193{ 194 nft_unregister_expr(&nft_masq_ipv6_type); 195} 196#else 197static inline int nft_masq_module_init_ipv6(void) { return 0; } 198static inline void nft_masq_module_exit_ipv6(void) {} 199#endif 200 201#ifdef CONFIG_NF_TABLES_INET 202static void 203nft_masq_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 204{ 205 nf_ct_netns_put(ctx->net, NFPROTO_INET); 206} 207 208static struct nft_expr_type nft_masq_inet_type; 209static const struct nft_expr_ops nft_masq_inet_ops = { 210 .type = &nft_masq_inet_type, 211 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), 212 .eval = nft_masq_eval, 213 .init = nft_masq_init, 214 .destroy = nft_masq_inet_destroy, 215 .dump = nft_masq_dump, 216 .validate = nft_masq_validate, 217 .reduce = NFT_REDUCE_READONLY, 218}; 219 220static struct nft_expr_type nft_masq_inet_type __read_mostly = { 221 .family = NFPROTO_INET, 222 .name = "masq", 223 .ops = &nft_masq_inet_ops, 224 .policy = nft_masq_policy, 225 .maxattr = NFTA_MASQ_MAX, 226 .owner = THIS_MODULE, 227}; 228 229static int __init nft_masq_module_init_inet(void) 230{ 231 return nft_register_expr(&nft_masq_inet_type); 232} 233 234static void nft_masq_module_exit_inet(void) 235{ 236 nft_unregister_expr(&nft_masq_inet_type); 237} 238#else 239static inline int nft_masq_module_init_inet(void) { return 0; } 240static inline void nft_masq_module_exit_inet(void) {} 241#endif 242 243static int __init nft_masq_module_init(void) 244{ 245 int ret; 246 247 ret = nft_masq_module_init_ipv6(); 248 if (ret < 0) 249 return ret; 250 251 ret = nft_masq_module_init_inet(); 252 if (ret < 0) { 253 nft_masq_module_exit_ipv6(); 254 return ret; 255 } 256 257 ret = nft_register_expr(&nft_masq_ipv4_type); 258 if (ret < 0) { 259 nft_masq_module_exit_inet(); 260 nft_masq_module_exit_ipv6(); 261 return ret; 262 } 263 264 ret = nf_nat_masquerade_inet_register_notifiers(); 265 if (ret < 0) { 266 nft_masq_module_exit_ipv6(); 267 nft_masq_module_exit_inet(); 268 nft_unregister_expr(&nft_masq_ipv4_type); 269 return ret; 270 } 271 272 return ret; 273} 274 275static void __exit nft_masq_module_exit(void) 276{ 277 nft_masq_module_exit_ipv6(); 278 nft_masq_module_exit_inet(); 279 nft_unregister_expr(&nft_masq_ipv4_type); 280 nf_nat_masquerade_inet_unregister_notifiers(); 281} 282 283module_init(nft_masq_module_init); 284module_exit(nft_masq_module_exit); 285 286MODULE_LICENSE("GPL"); 287MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>"); 288MODULE_ALIAS_NFT_EXPR("masq"); 289MODULE_DESCRIPTION("Netfilter nftables masquerade expression support"); 290