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_nat.h> 16 #include <net/netfilter/nf_nat_redirect.h> 17 #include <net/netfilter/nf_tables.h> 18 19 struct nft_redir { 20 enum nft_registers sreg_proto_min:8; 21 enum nft_registers sreg_proto_max:8; 22 u16 flags; 23 }; 24 25 static const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = { 26 [NFTA_REDIR_REG_PROTO_MIN] = { .type = NLA_U32 }, 27 [NFTA_REDIR_REG_PROTO_MAX] = { .type = NLA_U32 }, 28 [NFTA_REDIR_FLAGS] = { .type = NLA_U32 }, 29 }; 30 31 static int nft_redir_validate(const struct nft_ctx *ctx, 32 const struct nft_expr *expr, 33 const struct nft_data **data) 34 { 35 int err; 36 37 err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); 38 if (err < 0) 39 return err; 40 41 return nft_chain_validate_hooks(ctx->chain, 42 (1 << NF_INET_PRE_ROUTING) | 43 (1 << NF_INET_LOCAL_OUT)); 44 } 45 46 static int nft_redir_init(const struct nft_ctx *ctx, 47 const struct nft_expr *expr, 48 const struct nlattr * const tb[]) 49 { 50 struct nft_redir *priv = nft_expr_priv(expr); 51 unsigned int plen; 52 int err; 53 54 plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); 55 if (tb[NFTA_REDIR_REG_PROTO_MIN]) { 56 priv->sreg_proto_min = 57 nft_parse_register(tb[NFTA_REDIR_REG_PROTO_MIN]); 58 59 err = nft_validate_register_load(priv->sreg_proto_min, plen); 60 if (err < 0) 61 return err; 62 63 if (tb[NFTA_REDIR_REG_PROTO_MAX]) { 64 priv->sreg_proto_max = 65 nft_parse_register(tb[NFTA_REDIR_REG_PROTO_MAX]); 66 67 err = nft_validate_register_load(priv->sreg_proto_max, 68 plen); 69 if (err < 0) 70 return err; 71 } else { 72 priv->sreg_proto_max = priv->sreg_proto_min; 73 } 74 } 75 76 if (tb[NFTA_REDIR_FLAGS]) { 77 priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS])); 78 if (priv->flags & ~NF_NAT_RANGE_MASK) 79 return -EINVAL; 80 } 81 82 return nf_ct_netns_get(ctx->net, ctx->family); 83 } 84 85 static int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr) 86 { 87 const struct nft_redir *priv = nft_expr_priv(expr); 88 89 if (priv->sreg_proto_min) { 90 if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MIN, 91 priv->sreg_proto_min)) 92 goto nla_put_failure; 93 if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MAX, 94 priv->sreg_proto_max)) 95 goto nla_put_failure; 96 } 97 98 if (priv->flags != 0 && 99 nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags))) 100 goto nla_put_failure; 101 102 return 0; 103 104 nla_put_failure: 105 return -1; 106 } 107 108 static void nft_redir_ipv4_eval(const struct nft_expr *expr, 109 struct nft_regs *regs, 110 const struct nft_pktinfo *pkt) 111 { 112 struct nft_redir *priv = nft_expr_priv(expr); 113 struct nf_nat_ipv4_multi_range_compat mr; 114 115 memset(&mr, 0, sizeof(mr)); 116 if (priv->sreg_proto_min) { 117 mr.range[0].min.all = (__force __be16)nft_reg_load16( 118 ®s->data[priv->sreg_proto_min]); 119 mr.range[0].max.all = (__force __be16)nft_reg_load16( 120 ®s->data[priv->sreg_proto_max]); 121 mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 122 } 123 124 mr.range[0].flags |= priv->flags; 125 126 regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, nft_hook(pkt)); 127 } 128 129 static void 130 nft_redir_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 131 { 132 nf_ct_netns_put(ctx->net, NFPROTO_IPV4); 133 } 134 135 static struct nft_expr_type nft_redir_ipv4_type; 136 static const struct nft_expr_ops nft_redir_ipv4_ops = { 137 .type = &nft_redir_ipv4_type, 138 .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), 139 .eval = nft_redir_ipv4_eval, 140 .init = nft_redir_init, 141 .destroy = nft_redir_ipv4_destroy, 142 .dump = nft_redir_dump, 143 .validate = nft_redir_validate, 144 }; 145 146 static struct nft_expr_type nft_redir_ipv4_type __read_mostly = { 147 .family = NFPROTO_IPV4, 148 .name = "redir", 149 .ops = &nft_redir_ipv4_ops, 150 .policy = nft_redir_policy, 151 .maxattr = NFTA_REDIR_MAX, 152 .owner = THIS_MODULE, 153 }; 154 155 #ifdef CONFIG_NF_TABLES_IPV6 156 static void nft_redir_ipv6_eval(const struct nft_expr *expr, 157 struct nft_regs *regs, 158 const struct nft_pktinfo *pkt) 159 { 160 struct nft_redir *priv = nft_expr_priv(expr); 161 struct nf_nat_range2 range; 162 163 memset(&range, 0, sizeof(range)); 164 if (priv->sreg_proto_min) { 165 range.min_proto.all = (__force __be16)nft_reg_load16( 166 ®s->data[priv->sreg_proto_min]); 167 range.max_proto.all = (__force __be16)nft_reg_load16( 168 ®s->data[priv->sreg_proto_max]); 169 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 170 } 171 172 range.flags |= priv->flags; 173 174 regs->verdict.code = 175 nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt)); 176 } 177 178 static void 179 nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 180 { 181 nf_ct_netns_put(ctx->net, NFPROTO_IPV6); 182 } 183 184 static struct nft_expr_type nft_redir_ipv6_type; 185 static const struct nft_expr_ops nft_redir_ipv6_ops = { 186 .type = &nft_redir_ipv6_type, 187 .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), 188 .eval = nft_redir_ipv6_eval, 189 .init = nft_redir_init, 190 .destroy = nft_redir_ipv6_destroy, 191 .dump = nft_redir_dump, 192 .validate = nft_redir_validate, 193 }; 194 195 static struct nft_expr_type nft_redir_ipv6_type __read_mostly = { 196 .family = NFPROTO_IPV6, 197 .name = "redir", 198 .ops = &nft_redir_ipv6_ops, 199 .policy = nft_redir_policy, 200 .maxattr = NFTA_REDIR_MAX, 201 .owner = THIS_MODULE, 202 }; 203 #endif 204 205 #ifdef CONFIG_NF_TABLES_INET 206 static void nft_redir_inet_eval(const struct nft_expr *expr, 207 struct nft_regs *regs, 208 const struct nft_pktinfo *pkt) 209 { 210 switch (nft_pf(pkt)) { 211 case NFPROTO_IPV4: 212 return nft_redir_ipv4_eval(expr, regs, pkt); 213 case NFPROTO_IPV6: 214 return nft_redir_ipv6_eval(expr, regs, pkt); 215 } 216 217 WARN_ON_ONCE(1); 218 } 219 220 static void 221 nft_redir_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 222 { 223 nf_ct_netns_put(ctx->net, NFPROTO_INET); 224 } 225 226 static struct nft_expr_type nft_redir_inet_type; 227 static const struct nft_expr_ops nft_redir_inet_ops = { 228 .type = &nft_redir_inet_type, 229 .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), 230 .eval = nft_redir_inet_eval, 231 .init = nft_redir_init, 232 .destroy = nft_redir_inet_destroy, 233 .dump = nft_redir_dump, 234 .validate = nft_redir_validate, 235 }; 236 237 static struct nft_expr_type nft_redir_inet_type __read_mostly = { 238 .family = NFPROTO_INET, 239 .name = "redir", 240 .ops = &nft_redir_inet_ops, 241 .policy = nft_redir_policy, 242 .maxattr = NFTA_MASQ_MAX, 243 .owner = THIS_MODULE, 244 }; 245 246 static int __init nft_redir_module_init_inet(void) 247 { 248 return nft_register_expr(&nft_redir_inet_type); 249 } 250 #else 251 static inline int nft_redir_module_init_inet(void) { return 0; } 252 #endif 253 254 static int __init nft_redir_module_init(void) 255 { 256 int ret = nft_register_expr(&nft_redir_ipv4_type); 257 258 if (ret) 259 return ret; 260 261 #ifdef CONFIG_NF_TABLES_IPV6 262 ret = nft_register_expr(&nft_redir_ipv6_type); 263 if (ret) { 264 nft_unregister_expr(&nft_redir_ipv4_type); 265 return ret; 266 } 267 #endif 268 269 ret = nft_redir_module_init_inet(); 270 if (ret < 0) { 271 nft_unregister_expr(&nft_redir_ipv4_type); 272 #ifdef CONFIG_NF_TABLES_IPV6 273 nft_unregister_expr(&nft_redir_ipv6_type); 274 #endif 275 return ret; 276 } 277 278 return ret; 279 } 280 281 static void __exit nft_redir_module_exit(void) 282 { 283 nft_unregister_expr(&nft_redir_ipv4_type); 284 #ifdef CONFIG_NF_TABLES_IPV6 285 nft_unregister_expr(&nft_redir_ipv6_type); 286 #endif 287 #ifdef CONFIG_NF_TABLES_INET 288 nft_unregister_expr(&nft_redir_inet_type); 289 #endif 290 } 291 292 module_init(nft_redir_module_init); 293 module_exit(nft_redir_module_exit); 294 295 MODULE_LICENSE("GPL"); 296 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>"); 297 MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir"); 298 MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir"); 299