1 /* 2 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/netlink.h> 13 #include <linux/netfilter.h> 14 #include <linux/netfilter/nf_tables.h> 15 #include <net/netfilter/nf_tables.h> 16 #include <net/netfilter/nf_nat.h> 17 #include <net/netfilter/ipv4/nf_nat_masquerade.h> 18 #include <net/netfilter/ipv6/nf_nat_masquerade.h> 19 20 struct nft_masq { 21 u32 flags; 22 enum nft_registers sreg_proto_min:8; 23 enum nft_registers sreg_proto_max:8; 24 }; 25 26 static const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = { 27 [NFTA_MASQ_FLAGS] = { .type = NLA_U32 }, 28 [NFTA_MASQ_REG_PROTO_MIN] = { .type = NLA_U32 }, 29 [NFTA_MASQ_REG_PROTO_MAX] = { .type = NLA_U32 }, 30 }; 31 32 static int nft_masq_validate(const struct nft_ctx *ctx, 33 const struct nft_expr *expr, 34 const struct nft_data **data) 35 { 36 int err; 37 38 err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); 39 if (err < 0) 40 return err; 41 42 return nft_chain_validate_hooks(ctx->chain, 43 (1 << NF_INET_POST_ROUTING)); 44 } 45 46 static int nft_masq_init(const struct nft_ctx *ctx, 47 const struct nft_expr *expr, 48 const struct nlattr * const tb[]) 49 { 50 u32 plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); 51 struct nft_masq *priv = nft_expr_priv(expr); 52 int err; 53 54 if (tb[NFTA_MASQ_FLAGS]) { 55 priv->flags = ntohl(nla_get_be32(tb[NFTA_MASQ_FLAGS])); 56 if (priv->flags & ~NF_NAT_RANGE_MASK) 57 return -EINVAL; 58 } 59 60 if (tb[NFTA_MASQ_REG_PROTO_MIN]) { 61 priv->sreg_proto_min = 62 nft_parse_register(tb[NFTA_MASQ_REG_PROTO_MIN]); 63 64 err = nft_validate_register_load(priv->sreg_proto_min, plen); 65 if (err < 0) 66 return err; 67 68 if (tb[NFTA_MASQ_REG_PROTO_MAX]) { 69 priv->sreg_proto_max = 70 nft_parse_register(tb[NFTA_MASQ_REG_PROTO_MAX]); 71 72 err = nft_validate_register_load(priv->sreg_proto_max, 73 plen); 74 if (err < 0) 75 return err; 76 } else { 77 priv->sreg_proto_max = priv->sreg_proto_min; 78 } 79 } 80 81 return nf_ct_netns_get(ctx->net, ctx->family); 82 } 83 84 static int nft_masq_dump(struct sk_buff *skb, const struct nft_expr *expr) 85 { 86 const struct nft_masq *priv = nft_expr_priv(expr); 87 88 if (priv->flags != 0 && 89 nla_put_be32(skb, NFTA_MASQ_FLAGS, htonl(priv->flags))) 90 goto nla_put_failure; 91 92 if (priv->sreg_proto_min) { 93 if (nft_dump_register(skb, NFTA_MASQ_REG_PROTO_MIN, 94 priv->sreg_proto_min) || 95 nft_dump_register(skb, NFTA_MASQ_REG_PROTO_MAX, 96 priv->sreg_proto_max)) 97 goto nla_put_failure; 98 } 99 100 return 0; 101 102 nla_put_failure: 103 return -1; 104 } 105 106 static void nft_masq_ipv4_eval(const struct nft_expr *expr, 107 struct nft_regs *regs, 108 const struct nft_pktinfo *pkt) 109 { 110 struct nft_masq *priv = nft_expr_priv(expr); 111 struct nf_nat_range2 range; 112 113 memset(&range, 0, sizeof(range)); 114 range.flags = priv->flags; 115 if (priv->sreg_proto_min) { 116 range.min_proto.all = (__force __be16)nft_reg_load16( 117 ®s->data[priv->sreg_proto_min]); 118 range.max_proto.all = (__force __be16)nft_reg_load16( 119 ®s->data[priv->sreg_proto_max]); 120 } 121 regs->verdict.code = nf_nat_masquerade_ipv4(pkt->skb, nft_hook(pkt), 122 &range, nft_out(pkt)); 123 } 124 125 static void 126 nft_masq_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 127 { 128 nf_ct_netns_put(ctx->net, NFPROTO_IPV4); 129 } 130 131 static struct nft_expr_type nft_masq_ipv4_type; 132 static const struct nft_expr_ops nft_masq_ipv4_ops = { 133 .type = &nft_masq_ipv4_type, 134 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), 135 .eval = nft_masq_ipv4_eval, 136 .init = nft_masq_init, 137 .destroy = nft_masq_ipv4_destroy, 138 .dump = nft_masq_dump, 139 .validate = nft_masq_validate, 140 }; 141 142 static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { 143 .family = NFPROTO_IPV4, 144 .name = "masq", 145 .ops = &nft_masq_ipv4_ops, 146 .policy = nft_masq_policy, 147 .maxattr = NFTA_MASQ_MAX, 148 .owner = THIS_MODULE, 149 }; 150 151 #ifdef CONFIG_NF_TABLES_IPV6 152 static void nft_masq_ipv6_eval(const struct nft_expr *expr, 153 struct nft_regs *regs, 154 const struct nft_pktinfo *pkt) 155 { 156 struct nft_masq *priv = nft_expr_priv(expr); 157 struct nf_nat_range2 range; 158 159 memset(&range, 0, sizeof(range)); 160 range.flags = priv->flags; 161 if (priv->sreg_proto_min) { 162 range.min_proto.all = (__force __be16)nft_reg_load16( 163 ®s->data[priv->sreg_proto_min]); 164 range.max_proto.all = (__force __be16)nft_reg_load16( 165 ®s->data[priv->sreg_proto_max]); 166 } 167 regs->verdict.code = nf_nat_masquerade_ipv6(pkt->skb, &range, 168 nft_out(pkt)); 169 } 170 171 static void 172 nft_masq_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 173 { 174 nf_ct_netns_put(ctx->net, NFPROTO_IPV6); 175 } 176 177 static struct nft_expr_type nft_masq_ipv6_type; 178 static const struct nft_expr_ops nft_masq_ipv6_ops = { 179 .type = &nft_masq_ipv6_type, 180 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)), 181 .eval = nft_masq_ipv6_eval, 182 .init = nft_masq_init, 183 .destroy = nft_masq_ipv6_destroy, 184 .dump = nft_masq_dump, 185 .validate = nft_masq_validate, 186 }; 187 188 static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { 189 .family = NFPROTO_IPV6, 190 .name = "masq", 191 .ops = &nft_masq_ipv6_ops, 192 .policy = nft_masq_policy, 193 .maxattr = NFTA_MASQ_MAX, 194 .owner = THIS_MODULE, 195 }; 196 197 static int __init nft_masq_module_init_ipv6(void) 198 { 199 int ret = nft_register_expr(&nft_masq_ipv6_type); 200 201 if (ret) 202 return ret; 203 204 ret = nf_nat_masquerade_ipv6_register_notifier(); 205 if (ret < 0) 206 nft_unregister_expr(&nft_masq_ipv6_type); 207 208 return ret; 209 } 210 211 static void nft_masq_module_exit_ipv6(void) 212 { 213 nft_unregister_expr(&nft_masq_ipv6_type); 214 nf_nat_masquerade_ipv6_unregister_notifier(); 215 } 216 #else 217 static inline int nft_masq_module_init_ipv6(void) { return 0; } 218 static inline void nft_masq_module_exit_ipv6(void) {} 219 #endif 220 221 static int __init nft_masq_module_init(void) 222 { 223 int ret; 224 225 ret = nft_masq_module_init_ipv6(); 226 if (ret < 0) 227 return ret; 228 229 ret = nft_register_expr(&nft_masq_ipv4_type); 230 if (ret < 0) { 231 nft_masq_module_exit_ipv6(); 232 return ret; 233 } 234 235 ret = nf_nat_masquerade_ipv4_register_notifier(); 236 if (ret < 0) { 237 nft_masq_module_exit_ipv6(); 238 nft_unregister_expr(&nft_masq_ipv4_type); 239 return ret; 240 } 241 242 return ret; 243 } 244 245 static void __exit nft_masq_module_exit(void) 246 { 247 nft_masq_module_exit_ipv6(); 248 nft_unregister_expr(&nft_masq_ipv4_type); 249 nf_nat_masquerade_ipv4_unregister_notifier(); 250 } 251 252 module_init(nft_masq_module_init); 253 module_exit(nft_masq_module_exit); 254 255 MODULE_LICENSE("GPL"); 256 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>"); 257 MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "masq"); 258 MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "masq"); 259