1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012-2016 Pablo Neira Ayuso <pablo@netfilter.org> 4 */ 5 6 #include <linux/init.h> 7 #include <linux/module.h> 8 #include <linux/skbuff.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 14 #define nft_objref_priv(expr) *((struct nft_object **)nft_expr_priv(expr)) 15 16 static void nft_objref_eval(const struct nft_expr *expr, 17 struct nft_regs *regs, 18 const struct nft_pktinfo *pkt) 19 { 20 struct nft_object *obj = nft_objref_priv(expr); 21 22 obj->ops->eval(obj, regs, pkt); 23 } 24 25 static int nft_objref_init(const struct nft_ctx *ctx, 26 const struct nft_expr *expr, 27 const struct nlattr * const tb[]) 28 { 29 struct nft_object *obj = nft_objref_priv(expr); 30 u8 genmask = nft_genmask_next(ctx->net); 31 u32 objtype; 32 33 if (!tb[NFTA_OBJREF_IMM_NAME] || 34 !tb[NFTA_OBJREF_IMM_TYPE]) 35 return -EINVAL; 36 37 objtype = ntohl(nla_get_be32(tb[NFTA_OBJREF_IMM_TYPE])); 38 obj = nft_obj_lookup(ctx->net, ctx->table, 39 tb[NFTA_OBJREF_IMM_NAME], objtype, 40 genmask); 41 if (IS_ERR(obj)) 42 return -ENOENT; 43 44 nft_objref_priv(expr) = obj; 45 obj->use++; 46 47 return 0; 48 } 49 50 static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr) 51 { 52 const struct nft_object *obj = nft_objref_priv(expr); 53 54 if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->key.name) || 55 nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE, 56 htonl(obj->ops->type->type))) 57 goto nla_put_failure; 58 59 return 0; 60 61 nla_put_failure: 62 return -1; 63 } 64 65 static void nft_objref_deactivate(const struct nft_ctx *ctx, 66 const struct nft_expr *expr, 67 enum nft_trans_phase phase) 68 { 69 struct nft_object *obj = nft_objref_priv(expr); 70 71 if (phase == NFT_TRANS_COMMIT) 72 return; 73 74 obj->use--; 75 } 76 77 static void nft_objref_activate(const struct nft_ctx *ctx, 78 const struct nft_expr *expr) 79 { 80 struct nft_object *obj = nft_objref_priv(expr); 81 82 obj->use++; 83 } 84 85 static struct nft_expr_type nft_objref_type; 86 static const struct nft_expr_ops nft_objref_ops = { 87 .type = &nft_objref_type, 88 .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), 89 .eval = nft_objref_eval, 90 .init = nft_objref_init, 91 .activate = nft_objref_activate, 92 .deactivate = nft_objref_deactivate, 93 .dump = nft_objref_dump, 94 }; 95 96 struct nft_objref_map { 97 struct nft_set *set; 98 u8 sreg; 99 struct nft_set_binding binding; 100 }; 101 102 static void nft_objref_map_eval(const struct nft_expr *expr, 103 struct nft_regs *regs, 104 const struct nft_pktinfo *pkt) 105 { 106 struct nft_objref_map *priv = nft_expr_priv(expr); 107 const struct nft_set *set = priv->set; 108 const struct nft_set_ext *ext; 109 struct nft_object *obj; 110 bool found; 111 112 found = set->ops->lookup(nft_net(pkt), set, ®s->data[priv->sreg], 113 &ext); 114 if (!found) { 115 regs->verdict.code = NFT_BREAK; 116 return; 117 } 118 obj = *nft_set_ext_obj(ext); 119 obj->ops->eval(obj, regs, pkt); 120 } 121 122 static int nft_objref_map_init(const struct nft_ctx *ctx, 123 const struct nft_expr *expr, 124 const struct nlattr * const tb[]) 125 { 126 struct nft_objref_map *priv = nft_expr_priv(expr); 127 u8 genmask = nft_genmask_next(ctx->net); 128 struct nft_set *set; 129 int err; 130 131 set = nft_set_lookup_global(ctx->net, ctx->table, 132 tb[NFTA_OBJREF_SET_NAME], 133 tb[NFTA_OBJREF_SET_ID], genmask); 134 if (IS_ERR(set)) 135 return PTR_ERR(set); 136 137 if (!(set->flags & NFT_SET_OBJECT)) 138 return -EINVAL; 139 140 err = nft_parse_register_load(tb[NFTA_OBJREF_SET_SREG], &priv->sreg, 141 set->klen); 142 if (err < 0) 143 return err; 144 145 priv->binding.flags = set->flags & NFT_SET_OBJECT; 146 147 err = nf_tables_bind_set(ctx, set, &priv->binding); 148 if (err < 0) 149 return err; 150 151 priv->set = set; 152 return 0; 153 } 154 155 static int nft_objref_map_dump(struct sk_buff *skb, const struct nft_expr *expr) 156 { 157 const struct nft_objref_map *priv = nft_expr_priv(expr); 158 159 if (nft_dump_register(skb, NFTA_OBJREF_SET_SREG, priv->sreg) || 160 nla_put_string(skb, NFTA_OBJREF_SET_NAME, priv->set->name)) 161 goto nla_put_failure; 162 163 return 0; 164 165 nla_put_failure: 166 return -1; 167 } 168 169 static void nft_objref_map_deactivate(const struct nft_ctx *ctx, 170 const struct nft_expr *expr, 171 enum nft_trans_phase phase) 172 { 173 struct nft_objref_map *priv = nft_expr_priv(expr); 174 175 nf_tables_deactivate_set(ctx, priv->set, &priv->binding, phase); 176 } 177 178 static void nft_objref_map_activate(const struct nft_ctx *ctx, 179 const struct nft_expr *expr) 180 { 181 struct nft_objref_map *priv = nft_expr_priv(expr); 182 183 priv->set->use++; 184 } 185 186 static void nft_objref_map_destroy(const struct nft_ctx *ctx, 187 const struct nft_expr *expr) 188 { 189 struct nft_objref_map *priv = nft_expr_priv(expr); 190 191 nf_tables_destroy_set(ctx, priv->set); 192 } 193 194 static struct nft_expr_type nft_objref_type; 195 static const struct nft_expr_ops nft_objref_map_ops = { 196 .type = &nft_objref_type, 197 .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), 198 .eval = nft_objref_map_eval, 199 .init = nft_objref_map_init, 200 .activate = nft_objref_map_activate, 201 .deactivate = nft_objref_map_deactivate, 202 .destroy = nft_objref_map_destroy, 203 .dump = nft_objref_map_dump, 204 }; 205 206 static const struct nft_expr_ops * 207 nft_objref_select_ops(const struct nft_ctx *ctx, 208 const struct nlattr * const tb[]) 209 { 210 if (tb[NFTA_OBJREF_SET_SREG] && 211 (tb[NFTA_OBJREF_SET_NAME] || 212 tb[NFTA_OBJREF_SET_ID])) 213 return &nft_objref_map_ops; 214 else if (tb[NFTA_OBJREF_IMM_NAME] && 215 tb[NFTA_OBJREF_IMM_TYPE]) 216 return &nft_objref_ops; 217 218 return ERR_PTR(-EOPNOTSUPP); 219 } 220 221 static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { 222 [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING, 223 .len = NFT_OBJ_MAXNAMELEN - 1 }, 224 [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, 225 [NFTA_OBJREF_SET_SREG] = { .type = NLA_U32 }, 226 [NFTA_OBJREF_SET_NAME] = { .type = NLA_STRING, 227 .len = NFT_SET_MAXNAMELEN - 1 }, 228 [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, 229 }; 230 231 static struct nft_expr_type nft_objref_type __read_mostly = { 232 .name = "objref", 233 .select_ops = nft_objref_select_ops, 234 .policy = nft_objref_policy, 235 .maxattr = NFTA_OBJREF_MAX, 236 .owner = THIS_MODULE, 237 }; 238 239 static int __init nft_objref_module_init(void) 240 { 241 return nft_register_expr(&nft_objref_type); 242 } 243 244 static void __exit nft_objref_module_exit(void) 245 { 246 nft_unregister_expr(&nft_objref_type); 247 } 248 249 module_init(nft_objref_module_init); 250 module_exit(nft_objref_module_exit); 251 252 MODULE_LICENSE("GPL"); 253 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 254 MODULE_ALIAS_NFT_EXPR("objref"); 255 MODULE_DESCRIPTION("nftables stateful object reference module"); 256